lib/active_merchant/billing/credit_card.rb in activemerchant-1.0.3 vs lib/active_merchant/billing/credit_card.rb in activemerchant-1.1.0
- old
+ new
@@ -1,65 +1,29 @@
require 'time'
-require 'delegate'
require 'date'
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
-
# This credit card object can be used as a stand alone object. It acts just like a active record object
# but doesn't support the .save method as its not backed by a database.
class CreditCard
+ include CreditCardMethods
+
cattr_accessor :require_verification_value
self.require_verification_value = false
def self.requires_verification_value?
require_verification_value
end
include Validateable
- class ExpiryMonth < DelegateClass(Fixnum)#:nodoc:
- def to_s(format = :default) #:nodoc:
- case format
- when :default
- __getobj__.to_s
- when :two_digit
- sprintf("%.2i", self)[-2..-1]
- else
- super
- end
- end
-
- def valid? #:nodoc:
- (1..12).include?(self)
- end
- end
-
- class ExpiryYear < DelegateClass(Fixnum)#:nodoc:
- def to_s(format = :default) #:nodoc:
- case format
- when :default
- __getobj__.to_s
- when :two_digit
- sprintf("%.2i", self)[-2..-1]
- when :four_digit
- sprintf("%.4i", self)
- else
- super
- end
- end
-
- def valid? #:nodoc:
- (Time.now.year..Time.now.year + 20).include?(self)
- end
- end
-
class ExpiryDate #:nodoc:
attr_reader :month, :year
def initialize(month, year)
- @month = ExpiryMonth.new(month)
- @year = ExpiryYear.new(year)
+ @month = month
+ @year = year
end
def expired? #:nodoc:
Time.now > expiration rescue true
end
@@ -76,10 +40,13 @@
end
end
# required
attr_accessor :number, :month, :year, :type, :first_name, :last_name
+
+ # required for Switch / Solo
+ attr_accessor :start_month, :start_year, :issue_number
# Optional verification_value (CVV, CVV2 etc)
#
# Gateways will try their best to run validation on the passed in value if it is supplied
#
@@ -91,28 +58,36 @@
self.year = year.to_i
self.number.to_s.gsub!(/[^\d]/, "")
end
def validate
- @errors.add "year", "expired" if expired?
-
- @errors.add "first_name", "cannot be empty" if @first_name.blank?
- @errors.add "last_name", "cannot be empty" if @last_name.blank?
- @errors.add "month", "cannot be empty" unless month.valid?
- @errors.add "year", "cannot be empty" unless year.valid?
+ errors.add "year", "expired" if expired?
+
+ errors.add "first_name", "cannot be empty" if @first_name.blank?
+ errors.add "last_name", "cannot be empty" if @last_name.blank?
+ errors.add "month", "cannot be empty" unless valid_month?(@month)
+ errors.add "year", "cannot be empty" unless valid_expiry_year?(@year)
# Bogus card is pretty much for testing purposes. Lets just skip these extra tests if its used
return if type == 'bogus'
- @errors.add "number", "is not a valid credit card number" unless CreditCard.valid_number?(number)
- @errors.add "type", "is invalid" unless CreditCard.card_companies.keys.include?(type)
- @errors.add "type", "is not the correct card type" unless CreditCard.type?(number) == type
+ errors.add "number", "is not a valid credit card number" unless CreditCard.valid_number?(number)
+ errors.add "type", "is invalid" unless CreditCard.card_companies.keys.include?(type)
+ errors.add "type", "is not the correct card type" unless CreditCard.type?(number) == type
if CreditCard.requires_verification_value?
- @errors.add "verification_value", "is required" unless verification_value?
+ errors.add "verification_value", "is required" unless verification_value?
end
+
+ if [ 'switch', 'solo' ].include?(type)
+ unless valid_month?(@start_month) && valid_start_year?(@start_year) || valid_issue_number?(@issue_number)
+ errors.add "start_month", "is invalid" unless valid_month?(@start_month)
+ errors.add "start_year", "is invalid" unless valid_start_year?(@start_year)
+ errors.add "issue_number", "cannot be empty" unless valid_issue_number?(@issue_number)
+ end
+ end
end
def expired?
expiry_date.expired?
end
@@ -135,79 +110,16 @@
def verification_value?
!@verification_value.blank?
end
- # Regular expressions for the known card companies
- # == Known card types
- # Card Type Prefix Length
- # --------------------------------------------------------------------------
- # master 51-55 16
- # visa 4 13, 16
- # american_express 34, 37 15
- # diners_club 300-305, 36, 38 14
- # discover 6011 16
- # jcb 3 16
- # jcb 2131, 1800 15
- # switch various 16,18,19
- # solo 63, 6767 16,18,19
- def self.card_companies
- {
- 'visa' => /^4\d{12}(\d{3})?$/,
- 'master' => /^5[1-5]\d{14}$/,
- 'discover' => /^6011\d{12}$/,
- 'american_express' => /^3[47]\d{13}$/,
- 'diners_club' => /^3(0[0-5]|[68]\d)\d{11}$/,
- 'jcb' => /^(3\d{4}|2131|1800)\d{11}$/,
- 'switch' => [/^49(03(0[2-9]|3[5-9])|11(0[1-2]|7[4-9]|8[1-2])|36[0-9]{2})\d{10}(\d{2,3})?$/, /^564182\d{10}(\d{2,3})?$/, /^6(3(33[0-4][0-9])|759[0-9]{2})\d{10}(\d{2,3})?$/],
- 'solo' => /^6(3(34[5-9][0-9])|767[0-9]{2})\d{10}(\d{2,3})?$/
- }
- end
-
- # Returns a string containing the type of card from the list of known information below.
- def self.type?(number)
- return 'visa' if Base.gateway_mode == :test and ['1','2','3','success','failure','error'].include?(number.to_s)
-
- card_companies.each do |company, patterns|
- return company if [patterns].flatten.any? { |pattern| number =~ pattern }
- end
-
- return nil
- end
-
- # Returns true if it validates. Optionally, you can pass a card type as an argument and make sure it is of the correct type.
- # == References
- # - http://perl.about.com/compute/perl/library/nosearch/P073000.htm
- # - http://www.beachnet.com/~hstiles/cardtype.html
- def self.valid_number?(number)
- return true if Base.gateway_mode == :test and ['1','2','3','success','failure','error'].include?(number.to_s)
-
- return false unless number.to_s.length >= 13
-
- sum = 0
- for i in 0..number.length
- weight = number[-1 * (i + 2), 1].to_i * (2 - (i % 2))
- sum += (weight < 10) ? weight : weight - 9
- end
-
- (number[-1,1].to_i == (10 - sum % 10) % 10)
- end
-
# Show the card number, with all but last 4 numbers replace with "X". (XXXX-XXXX-XXXX-4338)
def display_number
"XXXX-XXXX-XXXX-#{last_digits}"
end
def last_digits
number.nil? ? "" : number.last(4)
- end
-
- def month
- expiry_date.month
- end
-
- def year
- expiry_date.year
end
def expiry_date
ExpiryDate.new(@month, @year)
end