credit_card_validations 1.4.7 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +52 -34
- data/lib/active_model/credit_card_number_validator.rb +1 -1
- data/lib/credit_card_validations.rb +10 -9
- data/lib/credit_card_validations/card_rules.rb +13 -12
- data/lib/credit_card_validations/detector.rb +9 -8
- data/lib/credit_card_validations/mmi.rb +17 -15
- data/lib/credit_card_validations/string.rb +8 -5
- data/lib/credit_card_validations/version.rb +1 -1
- data/test/credit_card_validations_test.rb +29 -29
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8cc121a408a642f75dfcfc595f63c58299895dfc
|
4
|
+
data.tar.gz: c30600f8e3925bce9cd79c1e30d0cb954696882f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b2c71d6eeef539a4adc6eb3aa3d971a5f0db812f2bf396de7e1d82be082243c657f43b5dcf4221b2d1e5e856dca2414160c567bedb5b651e8f63e15ff1d1f0e
|
7
|
+
data.tar.gz: d9979cffa4dec73c7f01b26c80ce654ede26fc4c4aa1458e932282867913d78d5685dbc74594096e8d7e67beca97849f4c068e99773bb2cb076388792d7f5c62
|
data/README.md
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
Gem adds validator to check whether or not a given number actually falls within the ranges of possible numbers prior to performing such verification, and, as such, CreditCardValidations simply verifies that the credit card number provided is well-formed.
|
4
4
|
|
5
|
+
More info about card BIN numbers http://en.wikipedia.org/wiki/Bank_card_number
|
6
|
+
|
5
7
|
[](https://travis-ci.org/Fivell/credit_card_validations)
|
6
8
|
[](https://coveralls.io/r/Fivell/credit_card_validations)
|
7
9
|
|
@@ -24,33 +26,55 @@ Or install it yourself as:
|
|
24
26
|
|
25
27
|
The following issuing institutes are accepted:
|
26
28
|
|
27
|
-
name | key
|
28
|
-
|
29
|
-
American Express | :amex
|
30
|
-
China UnionPay | :unionpay
|
31
|
-
Dankrot | :dankrot
|
32
|
-
Diners Club | :diners
|
33
|
-
Dinner Club US | :diners_us
|
34
|
-
Discover | :discover
|
35
|
-
JCB | :jcb
|
36
|
-
Laser | :laser
|
37
|
-
Maestro | :maestro
|
38
|
-
MasterCard | :mastercard
|
39
|
-
|
40
|
-
|
29
|
+
name | key |
|
30
|
+
-------------------------------------------------------------------
|
31
|
+
American Express | :amex | http://en.wikipedia.org/wiki/American_Express
|
32
|
+
China UnionPay | :unionpay | http://en.wikipedia.org/wiki/China_UnionPay
|
33
|
+
Dankrot | :dankrot | http://en.wikipedia.org/wiki/Dankort
|
34
|
+
Diners Club | :diners | http://en.wikipedia.org/wiki/Diners_Club_International
|
35
|
+
Dinner Club US | :diners_us | http://en.wikipedia.org/wiki/Diners_Club_International#MasterCard_alliance
|
36
|
+
Discover | :discover | http://en.wikipedia.org/wiki/Discover_Card
|
37
|
+
JCB | :jcb | http://en.wikipedia.org/wiki/Japan_Credit_Bureau
|
38
|
+
Laser | :laser | http://en.wikipedia.org/wiki/Laser_%28debit_card%29
|
39
|
+
Maestro | :maestro | http://en.wikipedia.org/wiki/Maestro_%28debit_card%29
|
40
|
+
MasterCard | :mastercard | http://en.wikipedia.org/wiki/MasterCard
|
41
|
+
Rupay | :rupay | http://en.wikipedia.org/wiki/RuPay
|
42
|
+
Solo | :solo | http://en.wikipedia.org/wiki/Solo_(debit_card)
|
43
|
+
Switch | :switch | http://en.wikipedia.org/wiki/Switch_(debit_card)
|
44
|
+
Visa | :visa | http://en.wikipedia.org/wiki/Visa_Inc.
|
41
45
|
|
42
46
|
|
43
47
|
|
44
48
|
Examples using string monkey patch
|
45
49
|
|
50
|
+
```ruby
|
46
51
|
require 'credit_card_validations/string'
|
47
52
|
'5274 5763 9425 9961'.credit_card_brand
|
48
53
|
'5274 5763 9425 9961'.valid_credit_card_brand?(:mastercard, :visa)
|
49
54
|
'5274 5763 9425 9961'.valid_credit_card_brand?(:amex)
|
55
|
+
```
|
56
|
+
|
57
|
+
ActiveModel support
|
58
|
+
|
59
|
+
only for certain brads
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
class CreditCardModel
|
63
|
+
attr_accessor :number
|
64
|
+
include ActiveModel::Validations
|
65
|
+
validates :number, credit_card_number: {brands: [:amex, :maestro]}
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
for all known brands
|
50
70
|
|
71
|
+
```ruby
|
72
|
+
validates :number, presence: true, credit_card_number: true
|
73
|
+
```
|
51
74
|
|
52
75
|
Examples using CreditCardValidations::Detector class
|
53
76
|
|
77
|
+
```ruby
|
54
78
|
number = "4111111111111111"
|
55
79
|
detector = CreditCardValidations::Detector.new(number)
|
56
80
|
detector.brand #:visa
|
@@ -58,38 +82,32 @@ Examples using CreditCardValidations::Detector class
|
|
58
82
|
detector.valid?(:mastercard,:maestro) #false
|
59
83
|
detector.valid?(:visa, :mastercard) #true
|
60
84
|
detector.issuer_category #"Banking and financial"
|
85
|
+
```
|
61
86
|
|
62
87
|
Also You can add your own rules to detect other credit card brands/types
|
63
88
|
passing name,length(integer/array of integers) and prefix(string/array of strings)
|
64
89
|
Example
|
65
90
|
|
66
|
-
|
91
|
+
```ruby
|
92
|
+
CreditCardValidations.add_rule(:voyager, {length: 15, prefixes: '86'})
|
93
|
+
CreditCardValidations.add_rule(:en_route, {length: 15, prefixes: ['2014', '2149'], skip_luhn: true}) #skip luhn
|
94
|
+
|
67
95
|
voyager_test_card_number = '869926275400212'
|
68
96
|
CreditCardValidations::Detector.new(voyager_test_card_number).brand #:voyager
|
69
97
|
CreditCardValidations::Detector.new(voyager_test_card_number).voyager? #true
|
70
|
-
|
71
|
-
|
98
|
+
|
99
|
+
en_route_test_card_number = '2014-0000-0000-001'
|
100
|
+
CreditCardValidations::Detector.new(en_route_test_card_number).brand #:en_route
|
101
|
+
CreditCardValidations::Detector.new(en_route_test_card_number).en_route? #true
|
102
|
+
```
|
72
103
|
|
73
104
|
Check luhn
|
74
105
|
|
106
|
+
```ruby
|
75
107
|
CreditCardValidations::Detector.new(@credit_card_number).valid_luhn?
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
only for certain brads
|
81
|
-
|
82
|
-
class CreditCardModel
|
83
|
-
attr_accessor :number
|
84
|
-
include ActiveModel::Validations
|
85
|
-
validates :number, credit_card_number: {brands: [:amex, :maestro]}
|
86
|
-
end
|
87
|
-
|
88
|
-
for all known brands
|
89
|
-
|
90
|
-
validates :number, presence: true, credit_card_number: true
|
91
|
-
|
92
|
-
|
108
|
+
#or
|
109
|
+
CreditCardValidations::Luhn.valid?(@credit_card_number)
|
110
|
+
```
|
93
111
|
|
94
112
|
|
95
113
|
## Contributing
|
@@ -4,7 +4,7 @@ module ActiveModel
|
|
4
4
|
|
5
5
|
def validate_each(record, attribute, value)
|
6
6
|
brands = options.fetch(:brands, :any)
|
7
|
-
record.errors.add(attribute) unless credit_card_valid?(value, brands == :any ? [] : Array.wrap(brands)
|
7
|
+
record.errors.add(attribute) unless credit_card_valid?(value, brands == :any ? [] : Array.wrap(brands))
|
8
8
|
end
|
9
9
|
|
10
10
|
def credit_card_valid?(number, brands = [])
|
@@ -11,16 +11,17 @@ module CreditCardValidations
|
|
11
11
|
autoload :CardRules , 'credit_card_validations/card_rules'
|
12
12
|
autoload :Detector , 'credit_card_validations/detector'
|
13
13
|
autoload :Mmi, 'credit_card_validations/mmi'
|
14
|
+
|
15
|
+
def self.add_rule(name, options)
|
16
|
+
CreditCardValidations::Detector.add_rule(name, options.fetch(:length), options.fetch(:prefixes), options.fetch(:skip_luhn, false))
|
17
|
+
end
|
18
|
+
|
19
|
+
CardRules.rules.each do |name, rules|
|
20
|
+
rules.each do |rule_value|
|
21
|
+
add_rule(name, rule_value)
|
22
|
+
end
|
23
|
+
end
|
14
24
|
end
|
15
25
|
|
16
26
|
|
17
|
-
CreditCardValidations::CardRules.rules.each do |name, rules|
|
18
|
-
rules.each do |rule_value|
|
19
|
-
CreditCardValidations::Detector.add_rule(name,
|
20
|
-
rule_value[:length],
|
21
|
-
rule_value[:prefixes],
|
22
|
-
rule_value[:skip_validation])
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|
26
27
|
|
@@ -10,11 +10,11 @@ module CreditCardValidations
|
|
10
10
|
mastercard: [
|
11
11
|
{length: [16], prefixes: ['51', '52', '53', '54', '55']}
|
12
12
|
],
|
13
|
-
|
13
|
+
|
14
14
|
amex: [
|
15
15
|
{length: [15], prefixes: ['34', '37']}
|
16
16
|
],
|
17
|
-
|
17
|
+
######## other brands ########
|
18
18
|
diners: [
|
19
19
|
{length: [14], prefixes: ['300', '301', '302', '303', '304', '305', '36', '38']},
|
20
20
|
],
|
@@ -43,12 +43,12 @@ module CreditCardValidations
|
|
43
43
|
solo: [
|
44
44
|
{length: [16, 18, 19], prefixes: ['6334', '6767']}
|
45
45
|
],
|
46
|
-
|
46
|
+
|
47
47
|
switch: [
|
48
|
-
|
49
|
-
|
48
|
+
{length: [16, 18, 19], prefixes: ['633110', '633312', '633304', '633303', '633301', '633300']}
|
49
|
+
|
50
50
|
],
|
51
|
-
|
51
|
+
|
52
52
|
maestro: [
|
53
53
|
{length: [12, 13, 14, 15, 16, 17, 18, 19], prefixes: ['5010', '5011', '5012', '5013', '5014', '5015', '5016', '5017', '5018',
|
54
54
|
'502', '503', '504', '505', '506', '507', '508',
|
@@ -56,20 +56,21 @@ module CreditCardValidations
|
|
56
56
|
'602', '603', '604', '605', '6060',
|
57
57
|
'621', '627', '629',
|
58
58
|
'677', '675', '674', '673', '672', '671', '670',
|
59
|
-
'6760', '6761', '6762', '6763', '6764', '6765', '6766', '6768', '6769'
|
60
|
-
]}
|
59
|
+
'6760', '6761', '6762', '6763', '6764', '6765', '6766', '6768', '6769']}
|
61
60
|
],
|
62
|
-
|
61
|
+
|
63
62
|
# Luhn validation are skipped for union pay cards because they have unknown generation algoritm
|
64
63
|
unionpay: [
|
65
|
-
{length: [16, 17, 18, 19], prefixes: ['622', '624', '625', '626', '628'],
|
64
|
+
{length: [16, 17, 18, 19], prefixes: ['622', '624', '625', '626', '628'], skip_luhn: true}
|
66
65
|
],
|
67
66
|
|
68
67
|
dankrot: [
|
69
68
|
{length: [16], prefixes: ['5019']}
|
69
|
+
],
|
70
|
+
|
71
|
+
rupay: [
|
72
|
+
{length: [16], prefixes: ['6061', '6062', '6063', '6064', '6065', '6066', '6067', '6068', '6069', '607', '608'], skip_luhn: true}
|
70
73
|
]
|
71
|
-
|
72
|
-
|
73
74
|
|
74
75
|
}
|
75
76
|
end
|
@@ -9,26 +9,26 @@ module CreditCardValidations
|
|
9
9
|
attr_reader :number
|
10
10
|
|
11
11
|
def initialize(number)
|
12
|
-
@number = number.to_s.tr('- ','')
|
12
|
+
@number = number.to_s.tr('- ', '')
|
13
13
|
end
|
14
14
|
|
15
15
|
# credit card number
|
16
16
|
def valid?(*brands)
|
17
|
-
|
17
|
+
!!valid_number?(*brands)
|
18
18
|
end
|
19
19
|
|
20
20
|
#brand name
|
21
21
|
def brand(*brands)
|
22
|
-
|
22
|
+
valid_number?(*brands)
|
23
23
|
end
|
24
24
|
|
25
25
|
def valid_number?(*brands)
|
26
26
|
number_length = number.length
|
27
|
-
brand_rules = brands.blank? ? self.rules : self.rules.slice(*brands.map{ |el| el.downcase })
|
27
|
+
brand_rules = brands.blank? ? self.rules : self.rules.slice(*brands.map { |el| el.downcase })
|
28
28
|
unless brand_rules.blank?
|
29
29
|
brand_rules.each do |brand_name, rules|
|
30
30
|
rules.each do |rule|
|
31
|
-
return brand_name if (
|
31
|
+
return brand_name if ((rule[:skip_luhn] || valid_luhn?) and rule[:length].include?(number_length) and number.match(rule[:regexp]))
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
@@ -44,21 +44,22 @@ module CreditCardValidations
|
|
44
44
|
|
45
45
|
#create regexp by array of prefixes
|
46
46
|
def compile_regexp(prefixes)
|
47
|
-
|
47
|
+
Regexp.new("^((#{prefixes.join(")|(")}))")
|
48
48
|
end
|
49
49
|
|
50
50
|
#create rule for detecting brand
|
51
|
-
def add_rule(brand, length, prefixes,
|
51
|
+
def add_rule(brand, length, prefixes, skip_luhn = false)
|
52
52
|
prefixes = Array.wrap(prefixes)
|
53
53
|
length = Array.wrap(length)
|
54
54
|
rules[brand] = [] if rules[brand].blank?
|
55
|
-
rules[brand] << {length: length,
|
55
|
+
rules[brand] << {length: length, regexp: compile_regexp(prefixes), prefixes: prefixes, skip_luhn: skip_luhn}
|
56
56
|
#create methods like visa? mastercard? etc
|
57
57
|
class_eval <<-BOOLEAN_RULE, __FILE__, __LINE__
|
58
58
|
def #{brand}?
|
59
59
|
valid?(:#{brand})
|
60
60
|
end
|
61
61
|
BOOLEAN_RULE
|
62
|
+
rules[brand]
|
62
63
|
end
|
63
64
|
end
|
64
65
|
|
@@ -14,23 +14,25 @@
|
|
14
14
|
#For example, American Express, Diner's Club, Carte Blanche, and JCB are in the travel and entertainment category; VISA, MasterCard, and Discover are in the banking and financial category (Discover being in the Merchandising and banking/financial category); and Sun Oil and Exxon are in the petroleum category.
|
15
15
|
|
16
16
|
|
17
|
-
module CreditCardValidations
|
17
|
+
module CreditCardValidations
|
18
|
+
module Mmi
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
20
|
+
ISSUER_CATEGORIES = {
|
21
|
+
'0' => 'ISO/TC 68 nd other industry assignments',
|
22
|
+
'1' => 'Airlines',
|
23
|
+
'2' => 'Airlines and other industry assignments',
|
24
|
+
'3' => 'Travel and entertainment and banking/financial',
|
25
|
+
'4' => 'Banking and financial',
|
26
|
+
'5' => 'Banking and financial',
|
27
|
+
'6' => 'Merchandising and banking/financial',
|
28
|
+
'7' => 'Petroleum and other industry assignments',
|
29
|
+
'8' => 'Healthcare, telecommunications and other industry assignments',
|
30
|
+
'9' => 'National assignment'
|
30
31
|
|
31
|
-
|
32
|
+
}
|
32
33
|
|
33
|
-
|
34
|
-
|
34
|
+
def issuer_category
|
35
|
+
ISSUER_CATEGORIES[@number.to_s[0]]
|
36
|
+
end
|
35
37
|
end
|
36
38
|
end
|
@@ -1,9 +1,12 @@
|
|
1
|
+
#
|
2
|
+
#
|
3
|
+
#
|
1
4
|
class String
|
2
5
|
def credit_card_brand
|
3
|
-
|
4
|
-
end
|
5
|
-
|
6
|
+
CreditCardValidations::Detector.new(self).brand
|
7
|
+
end
|
8
|
+
|
6
9
|
def valid_credit_card_brand?(*brands)
|
7
|
-
|
8
|
-
end
|
10
|
+
CreditCardValidations::Detector.new(self).valid?(*brands)
|
11
|
+
end
|
9
12
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require_relative 'test_helper'
|
2
2
|
|
3
|
-
|
4
3
|
class CreditCardValidationsTest < MiniTest::Test
|
5
4
|
|
6
5
|
class CreditCardModel
|
@@ -18,20 +17,21 @@ class CreditCardValidationsTest < MiniTest::Test
|
|
18
17
|
def initialize name
|
19
18
|
super name
|
20
19
|
@test_numbers = {
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
20
|
+
visa: ['4012 8888 8888 1881', '4111111111111111'],
|
21
|
+
mastercard: ['5274 5763 9425 9961', '5555-5555-5555-4444'],
|
22
|
+
diners: ['3020 4169 3226 43', '30569309025904'],
|
23
|
+
amex: ['3782 8224 6310 005', '371449635398431'],
|
24
|
+
discover: ['6011 1111 1111 1117', '6011000990139424'],
|
25
|
+
maestro: ['6759 6498 2643 8453'],
|
26
|
+
jcb: ['3575 7591 5225 4876', '3566002020360505'],
|
27
|
+
solo: ['6767 6222 2222 2222 222'],
|
28
|
+
unionpay: ['6264-1852-1292-2132-067', '6288997715452584', '6269 9920 5813 4322'],
|
29
|
+
dankrot: ['5019717010103742'],
|
30
|
+
switch: ['6331101999990016'],
|
31
|
+
rupay: ['6076-6000-0619-9992']
|
32
32
|
}
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
def test_card_brand_detection
|
36
36
|
@test_numbers.each do |key, value|
|
37
37
|
value.each do |card_number|
|
@@ -40,21 +40,21 @@ class CreditCardValidationsTest < MiniTest::Test
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
def test_card_brand_is_nil_if_credit_card_invalid
|
45
45
|
assert_nil detector('1111111111111111').brand
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
def test_card_brand_detection_with_restriction
|
49
49
|
@test_numbers.slice(:visa, :mastercard).each do |key, value|
|
50
50
|
assert_equal key, detector(value.first).brand(:visa, :mastercard)
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
@test_numbers.except(:visa, :mastercard).each do |key, value|
|
54
54
|
assert_nil detector(value.first).brand(:visa, :mastercard)
|
55
55
|
end
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
58
|
def test_card_valid_method
|
59
59
|
@test_numbers.each do |key, value|
|
60
60
|
value.each do |card_number|
|
@@ -90,14 +90,14 @@ class CreditCardValidationsTest < MiniTest::Test
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def test_active_model_validator
|
93
|
-
|
94
|
-
|
95
|
-
|
93
|
+
cc = CreditCardModel.new
|
94
|
+
cc.number = @test_numbers[:maestro].first
|
95
|
+
assert cc.valid?
|
96
96
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
97
|
+
cc = CreditCardModel.new
|
98
|
+
cc.number = @test_numbers[:mastercard].first
|
99
|
+
assert !cc.valid?
|
100
|
+
assert cc.errors[:number].include?(cc.errors.generate_message(:number, :invalid))
|
101
101
|
|
102
102
|
|
103
103
|
cc = CreditCardModelAny.new
|
@@ -110,13 +110,13 @@ class CreditCardValidationsTest < MiniTest::Test
|
|
110
110
|
|
111
111
|
def test_string_extension
|
112
112
|
require 'credit_card_validations/string'
|
113
|
-
assert_equal
|
114
|
-
assert
|
113
|
+
assert_equal @test_numbers[:mastercard].first.credit_card_brand, :mastercard
|
114
|
+
assert @test_numbers[:mastercard].first.valid_credit_card_brand?(:mastercard)
|
115
115
|
assert !@test_numbers[:mastercard].first.valid_credit_card_brand?(:visa, :amex)
|
116
116
|
end
|
117
117
|
|
118
118
|
|
119
|
-
def
|
119
|
+
def test_skip_luhn
|
120
120
|
d = detector(@test_numbers[:unionpay].first)
|
121
121
|
|
122
122
|
d.expects(:valid_luhn?).never
|
@@ -133,8 +133,8 @@ class CreditCardValidationsTest < MiniTest::Test
|
|
133
133
|
end
|
134
134
|
|
135
135
|
def test_mmi
|
136
|
-
|
137
|
-
|
136
|
+
d = detector(@test_numbers[:visa])
|
137
|
+
assert_equal d.issuer_category, CreditCardValidations::Mmi::ISSUER_CATEGORIES[@test_numbers[:visa][0]]
|
138
138
|
end
|
139
139
|
|
140
140
|
protected
|