Gem adds a validator to check whether a given number actually falls within the ranges of possible numbers prior to performing verification — CreditCardValidations verifies that the credit card number provided is well-formed.
More info about card BIN numbers: http://en.wikipedia.org/wiki/Bank_card_number
Add this line to your application's Gemfile:
$ gem 'credit_card_validations'And then execute:
$ bundleOr install it yourself as:
$ gem install credit_card_validationsThese brands are detected out of the box. They are the international majors that most acquirers, gateways, and payment forms care about:
| Name | Key |
|---|---|
| American Express | :amex |
| China UnionPay | :unionpay |
| Diners Club | :diners |
| Discover | :discover |
| JCB | :jcb |
| Maestro | :maestro |
| MasterCard | :mastercard |
| Visa | :visa |
Everything else is detected only when its plugin is explicitly required. Plugins add no startup cost, no API surface, and no chance of misdetection to apps that don't accept the brand.
| Name | Key |
|---|---|
| Cabal | :cabal |
| Carnet | :carnet |
| Cartes Bancaires | :cartes_bancaires |
| Dankort | :dankort |
| DinaCard | :dinacard |
| Elo | :elo |
| Girocard | :girocard |
| Hiper | :hiper |
| Hipercard | :hipercard |
| Humo | :humocard |
| Mada | :mada |
| MIR | :mir |
| Naranja | :naranja |
| RuPay | :rupay |
| Troy | :troy |
| UATP | :uatp |
| Uzcard | :uzcard |
| V Pay | :vpay |
| Verve | :verve |
| Voyager | :voyager |
| Name | Key | Status |
|---|---|---|
| Diners Club US | :diners_us |
Merged into Discover for US routing in 2008 |
| EnRoute | :en_route |
Withdrawn 1989 |
| Laser | :laser |
Withdrawn 2014 |
| Solo | :solo |
Withdrawn 2011 |
| Switch | :switch |
Withdrawn 2002 |
# in an initializer or before first use
require 'credit_card_validations/plugins/mir'
require 'credit_card_validations/plugins/elo'
require 'credit_card_validations/plugins/hipercard'
# ... whichever brands the app actually acceptsSeven brands moved from the default brand set to opt-in plugins in v9.0. The auto-require shim keeps existing code working for one major version with a one-time deprecation warning per brand.
| Brand | Status | Auto-loaded until |
|---|---|---|
:dankort |
Active (Denmark) | v10.0 |
:elo |
Active (Brazil) | v10.0 |
:hipercard |
Active (Brazil) | v10.0 |
:mir |
Active (Russia) | v10.0 |
:rupay |
Active (India) | v10.0 |
:solo |
Withdrawn 2011 | v10.0 |
:switch |
Withdrawn 2002 | v10.0 |
If your code references any of these brands by symbol, add the matching require to your initializer to silence the warning and survive v10:
# config/initializers/credit_card_validations.rb
require 'credit_card_validations/plugins/mir'
require 'credit_card_validations/plugins/elo'
# ...When v10 lands, the auto-load disappears. Code that names these brands without a matching require will see them as unknown — Detector#brand returns nil, predicate methods (mir?, elo?, …) are not defined, and valid?(:mir) returns false.
Hipercardcleaned up. Length changed from 19 to 16 (which is the issued length); legacy637*prefixes that actually belong to Hiper were dropped. If you used Hipercard before, your detection now matches the brand's real spec.Discovercleaned up. Diners-only prefixes (300-305, 3095, 36, 38, 39) were dropped from Discover. Diners cards now correctly detect as:dinersinstead of:discover. Apps that branched on:discoverfor routing should branch on[:diners, :discover].Luhn.valid?is now strict. It accepts a digit-only string and returnsfalsefornil, empty input, or any non-digit character. User-facing input handling moved intoDetector#initialize, which strips whitespace and dashes before delegating.- Brand YAML is loaded via
YAML.safe_load_file. Custom brand sources may need to declarepermitted_classes: [Symbol]if they relied on extra Ruby objects.
require 'credit_card_validations/string'
'5274 5763 9425 9961'.credit_card_brand #=> :mastercard
'5274 5763 9425 9961'.credit_card_brand_name #=> "MasterCard"
'5274 5763 9425 9961'.valid_credit_card_brand?(:mastercard, :visa) #=> true
'5274 5763 9425 9961'.valid_credit_card_brand?(:amex) #=> false
'5274 5763 9425 9961'.valid_credit_card_brand?('MasterCard') #=> trueRestrict to a brand list:
class CreditCardModel
attr_accessor :number
include ActiveModel::Validations
validates :number, credit_card_number: { brands: [:amex, :maestro] }
endAccept any known brand:
validates :number, presence: true, credit_card_number: trueCVV against a brand pulled from another attribute (the PAN):
class Payment
include ActiveModel::Validations
attr_accessor :card_number, :cvv
validates :card_number, credit_card_number: true
validates :cvv, credit_card_cvv: { brand_from: :card_number }
endCVV against a literal brand:
validates :cvv, credit_card_cvv: { brand: :amex }A single string attribute (MM/YY, MM/YYYY, MMYY, ...):
validates :expiration, credit_card_expiration: trueTwo separate fields (typical month + year dropdowns) — use the Expiration class in a validate block:
class Payment
attr_accessor :exp_month, :exp_year
validate do
exp = CreditCardValidations::Expiration.new(exp_month, exp_year)
errors.add(:exp_month, :invalid) unless exp.valid?
end
endA composite model wrapping Detector and Expiration behind a single ActiveModel-aware object:
card = CreditCardValidations::Card.new(
number: '4111 1111 1111 1111',
month: 12, year: 2027,
verification_value: '123',
name: 'John Smith'
)
card.valid? # => true
card.brand # => :visa
card.display_number # => "************1111"
card.last_digits # => "1111"
card.expired? # => false
card.formatted_number # => "4111 1111 1111 1111"number = '4111111111111111'
detector = CreditCardValidations::Detector.new(number)
detector.brand # => :visa
detector.visa? # => true
detector.valid?(:mastercard, :maestro) # => false
detector.valid?(:visa, :mastercard) # => true
detector.issuer_category # => "Banking and financial"
detector.last4 # => "1111"
detector.masked # => "************1111"
detector.formatted # => "4111 1111 1111 1111"
detector.possible_brands # => [:visa] (during live input)
detector.valid_cvv?('123') # => trueCreditCardValidations.add_brand(:voyager, { length: 15, prefixes: '86' })
CreditCardValidations::Detector.new('869926275400212').voyager? # => trueCreditCardValidations::Detector.delete_brand(:maestro)CreditCardValidations::Detector.new(number).valid_luhn?
# or, on a clean digit string:
CreditCardValidations::Luhn.valid?(number)CreditCardValidations::Factory.random(:amex)
# => "348051773827666"
CreditCardValidations::Factory.random(:maestro)
# => "6010430241237266856"To override the default brand source, copy the bundled brands.yaml, edit it, and point the gem at it in a Rails initializer:
CreditCardValidations.configure do |config|
config.source = '/path/to/my_brands.yml'
end- Fork it
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Open a Pull Request