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