class CreditOfficer::CreditCard

ActiveModel compliant class that represents credit card information Use this to populate and validate credit card details

It is not recommended that you persist credit card information unless you absolutely must. Many payment processors proviider you with a mechanism to store this private information

@example

cc = CreditOfficer::CreditCard.new({
  :number => "411111111111111",
  :provider_name => "visa",
  :name_on_card => "John Doe",
  :expiration_year => 2010,
  :expiration_month => 1
}).valid? => true

cc.number = ""
cc.valid? => false 
cc.errors.full_messages => ["Number can't be blank"]

Constants

I18N_ERROR_SCOPE
PROVIDERS_AND_FORMATS
SWITCH_OR_SOLO_PROVIDERS

Attributes

brand[RW]
String

downcased name of the credit card provider

(see {PROVIDERS_AND_FORMATS} for a valid list

cvv[RW]
String

the CVV/CVV2 value found on the back or front of cards depending on their brand

validation of this string can be turned off via class setting require_verification_value

cvv=[RW]
String

the CVV/CVV2 value found on the back or front of cards depending on their brand

validation of this string can be turned off via class setting require_verification_value

expiration_month[RW]
Integer

the integer based representation of the month when the credit card expires (1-12 e.g)

expiration_year[RW]
Integer

the year when the credit card expires

@note paired with the month, this must be in the future for the credit card to be valid

issue_number[RW]
String

Solo or Switch Card attribute representing the issue number found on the card

name_on_card[RW]
String

the name found on the front of the card

number[RW]
String

the number found on card

provider_name[RW]
String

downcased name of the credit card provider

(see {PROVIDERS_AND_FORMATS} for a valid list

start_month[RW]
Integer

Solo or Switch card attribute representing the start date found on the card

start_year[RW]
Integer

Solo or Switch card attribute representing the start year found on the card

verification_value[RW]
String

the CVV/CVV2 value found on the back or front of cards depending on their brand

validation of this string can be turned off via class setting require_verification_value

Public Class Methods

supported_providers() click to toggle source

@return [Array<String>] list of providers @note defaults to {PROVIDERS_AND_FORMATS}.keys

# File lib/credit_officer/credit_card.rb, line 164
def self.supported_providers
  @supported_providers
end
supported_providers=(providers) click to toggle source

configure your list of supported providers @param Array<String> providers you wish to support (amex, visa, etc) (refer to {PROVIDERS_AND_FORMATS}) @note matches specified providers against the supported whitelist {PROVIDERS_AND_FORMATS}

# File lib/credit_officer/credit_card.rb, line 158
def self.supported_providers=(providers)
  @supported_providers = providers.collect{|i| i.downcase} & PROVIDERS_AND_FORMATS.keys
end
verification_value_required?() click to toggle source

checks the configuration setting require_verification_value to see if verification is required

# File lib/credit_officer/credit_card.rb, line 127
def self.verification_value_required?
  require_verification_value
end

Protected Class Methods

supported_providers_and_formats() click to toggle source
# File lib/credit_officer/credit_card.rb, line 259
def self.supported_providers_and_formats
  #match supported providers against constant's whitelist
  valid_supported_providers = supported_providers & PROVIDERS_AND_FORMATS.keys

  supported_providers.inject(ActiveSupport::OrderedHash.new) do |ordered_hash, provider_name|
    ordered_hash[provider_name] = PROVIDERS_AND_FORMATS[provider_name]
    ordered_hash
  end
end

Public Instance Methods

derive_provider_name() click to toggle source
# File lib/credit_officer/credit_card.rb, line 175
def derive_provider_name
  self.class.supported_providers_and_formats.each do |name, format|
    if number =~ format
      self.provider_name = name
      return
    end
  end
end
expiration_date() click to toggle source

@return [CreditOfficer::MonthYearPair] month year pair that represents the expiration date

# File lib/credit_officer/credit_card.rb, line 132
def expiration_date
  CreditOfficer::MonthYearPair.new(:month => expiration_month, 
    :year => expiration_year)
end
masked_number() click to toggle source
# File lib/credit_officer/credit_card.rb, line 184
def masked_number
  if number.present? && number.size >= 4
    "X" * (number.size - 4) + number[-4..-1]
  end
end
provider_name=(provider) click to toggle source

sets the provider name @param [String] the provider name you wish to set sets the provider name to its downcased equivalent @example note the downcase

credit_card.provider_name = "VISA" => "visa"
# File lib/credit_officer/credit_card.rb, line 149
def provider_name=(provider)
  unless provider.nil?
    @provider_name = provider.downcase
  end
end
start_date() click to toggle source

@return [CreditOfficer::MonthYearPair] month year pair that represents the start date @note this applies to switch and solo cards only

# File lib/credit_officer/credit_card.rb, line 139
def start_date
  CreditOfficer::MonthYearPair.new(:month => start_month,
    :year => start_year)
end
switch_or_solo?() click to toggle source

@return [Boolean] whether or not the provider name indicates the card is a switch or solo card

# File lib/credit_officer/credit_card.rb, line 171
def switch_or_solo?
  SWITCH_OR_SOLO_PROVIDERS.include?(provider_name)
end

Protected Instance Methods

checksum_valid?() click to toggle source
# File lib/credit_officer/credit_card.rb, line 255
def checksum_valid?
  LuhneyBin.validate(number)
end
expiration_date_is_in_future() click to toggle source
# File lib/credit_officer/credit_card.rb, line 198
def expiration_date_is_in_future
  if expiration_date.valid? && expiration_date.end_is_in_past?
    errors.add(:expiration_year, 
      translate(:expired, 
        :scope   => I18N_ERROR_SCOPE, 
        :default => "is expired"))
  end
end
expiration_date_is_in_recent_future() click to toggle source
# File lib/credit_officer/credit_card.rb, line 207
def expiration_date_is_in_recent_future
  if expiration_date.valid? && expiration_date.exceeds_recent_future?
    errors.add(:expiration_year, translate(:exceeds_recent_future, 
      :scope   => I18N_ERROR_SCOPE, 
      :default => "is not a valid year"))
  end
end
issue_number_is_valid() click to toggle source
# File lib/credit_officer/credit_card.rb, line 239
def issue_number_is_valid
  unless issue_number =~ /^\d{1,2}$/
    errors.add(:issue_number, translate(:invalid_issue_number,
      :scope   => I18N_ERROR_SCOPE,
      :default => "is not valid"))
  end
end
number_is_valid() click to toggle source
# File lib/credit_officer/credit_card.rb, line 215
def number_is_valid
  if (provider_name.present? || self.class.automatically_derive_provider_name) && 
    number.present? 
    if self.class.supported_providers_and_formats[provider_name].nil? || 
      !(number =~ self.class.supported_providers_and_formats[provider_name]) || 
      !checksum_valid?

      errors.add(:number, translate(:invalid_format, 
        :scope   => I18N_ERROR_SCOPE, 
        :default => "is not a valid card number"))
    end
  end
end
provider_name_is_supported() click to toggle source
# File lib/credit_officer/credit_card.rb, line 229
def provider_name_is_supported
  if !self.class.automatically_derive_provider_name &&
    !self.class.supported_providers.include?(provider_name.try(:downcase))

    errors.add(:provider_name, translate(:unsupported_provider,
      :scope   => I18N_ERROR_SCOPE,
      :default => "is not supported"))
  end
end
run_validations!() click to toggle source
Calls superclass method
# File lib/credit_officer/credit_card.rb, line 191
def run_validations!
  derive_provider_name if self.class.automatically_derive_provider_name
  super
end
start_date_is_in_the_past() click to toggle source
# File lib/credit_officer/credit_card.rb, line 247
def start_date_is_in_the_past
  if start_date.valid? && start_date.start_is_in_future?
    errors.add(:start_year, translate(:futuristic_start_date,
      :scope => I18N_ERROR_SCOPE,
      :default => "is in the future"))
  end
end