cw_credit_card_validations 3.4.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 +7 -0
- data/.hound.yml +2 -0
- data/.travis.yml +26 -0
- data/Changelog.md +115 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +162 -0
- data/Rakefile +13 -0
- data/credit_card_validations.gemspec +29 -0
- data/gemfiles/rails1-4.gemfile +11 -0
- data/gemfiles/rails5.gemfile +10 -0
- data/lib/active_model/credit_card_number_validator.rb +81 -0
- data/lib/credit_card_validations.rb +36 -0
- data/lib/credit_card_validations/detector.rb +148 -0
- data/lib/credit_card_validations/error.rb +4 -0
- data/lib/credit_card_validations/factory.rb +64 -0
- data/lib/credit_card_validations/luhn.rb +22 -0
- data/lib/credit_card_validations/mmi.rb +43 -0
- data/lib/credit_card_validations/plugins/diners_us.rb +6 -0
- data/lib/credit_card_validations/plugins/en_route.rb +5 -0
- data/lib/credit_card_validations/plugins/laser.rb +4 -0
- data/lib/credit_card_validations/string.rb +22 -0
- data/lib/credit_card_validations/version.rb +3 -0
- data/lib/data/brands.yaml +409 -0
- data/spec/active_model_spec.rb +70 -0
- data/spec/credit_card_validations_spec.rb +175 -0
- data/spec/factory_spec.rb +18 -0
- data/spec/fixtures/invalid_cards.yml +6 -0
- data/spec/fixtures/valid_cards.yml +119 -0
- data/spec/models/credit_card.rb +19 -0
- data/spec/string_spec.rb +32 -0
- data/spec/test_helper.rb +20 -0
- metadata +153 -0
@@ -0,0 +1,70 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
describe 'ActiveModel Validator' do
|
4
|
+
|
5
|
+
let(:model) { CreditCard.new }
|
6
|
+
|
7
|
+
describe 'Proc support' do
|
8
|
+
it 'should be valid if brands from proc valid' do
|
9
|
+
card = model.dup
|
10
|
+
card.card_type = 'Master Card'
|
11
|
+
card.number6 = CreditCardValidations::Factory.random(:visa)
|
12
|
+
card.valid?.must_equal false
|
13
|
+
card.number6 = CreditCardValidations::Factory.random(:mastercard)
|
14
|
+
card.valid?.must_equal true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'Any Brand' do
|
19
|
+
it 'should be valid for all prepared valid numbers' do
|
20
|
+
VALID_NUMBERS.each do |_, numbers|
|
21
|
+
numbers.each do |number|
|
22
|
+
card = model
|
23
|
+
card.number4 = number
|
24
|
+
card.number5 = number
|
25
|
+
card.valid?.must_equal true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'Except Amex and Maestro brand' do
|
32
|
+
it 'should reject all other valid numbers' do
|
33
|
+
VALID_NUMBERS.except(:amex, :maestro).each do |_, numbers|
|
34
|
+
card = model
|
35
|
+
card.number = numbers.first
|
36
|
+
card.valid?.must_equal false
|
37
|
+
card.errors[:number].include?(card.errors.generate_message(:number, :invalid)).must_equal true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should accept using except options' do
|
42
|
+
VALID_NUMBERS.except(:amex, :maestro).each do |_, numbers|
|
43
|
+
card = model
|
44
|
+
card.number3 = numbers.first
|
45
|
+
card.valid?.must_equal true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'Only Amex and Mestro brands' do
|
51
|
+
it 'should accept amex and maestro brand if valid' do
|
52
|
+
VALID_NUMBERS.slice(:amex, :maestro).each do |_, numbers|
|
53
|
+
card = model
|
54
|
+
card.number = numbers.first
|
55
|
+
card.number2 = numbers.first
|
56
|
+
card.valid?.must_equal true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'Custom error message' do
|
62
|
+
it 'should allow custom message' do
|
63
|
+
card = model
|
64
|
+
card.number7 = 'wrong'
|
65
|
+
card.valid?.must_equal false
|
66
|
+
card.errors[:number7].must_equal ['Custom message']
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
describe CreditCardValidations do
|
4
|
+
|
5
|
+
|
6
|
+
before do
|
7
|
+
CreditCardValidations.reload!
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'MMI' do
|
11
|
+
it 'should detect issuer category' do
|
12
|
+
d = detector(VALID_NUMBERS[:visa].first)
|
13
|
+
d.issuer_category.must_equal CreditCardValidations::Mmi::ISSUER_CATEGORIES[d.number[0]]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'Luhn#valid?' do
|
18
|
+
let(:card_detector) {
|
19
|
+
detector(VALID_NUMBERS[:unionpay].first)
|
20
|
+
}
|
21
|
+
it 'should call Luhn.valid? once' do
|
22
|
+
CreditCardValidations::Luhn.expects(:valid?).with(card_detector.number).once
|
23
|
+
card_detector.valid?(:visa, :unionpay).must_equal true
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should call Luhn.valid? twice' do
|
27
|
+
CreditCardValidations::Luhn.expects(:valid?).with(card_detector.number).twice
|
28
|
+
card_detector.valid?(:visa, :mastercard).must_equal false
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should not call Luhn.valid?' do
|
32
|
+
CreditCardValidations::Luhn.expects(:valid?).never
|
33
|
+
card_detector.valid?(:unionpay).must_equal true
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
it 'should check luhn' do
|
40
|
+
VALID_NUMBERS.each do |brand, card_numbers|
|
41
|
+
if has_luhn_check_rule?(brand)
|
42
|
+
card_numbers.each do |number|
|
43
|
+
luhn_valid?(detector(number).number).must_equal true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should check valid brand' do
|
50
|
+
VALID_NUMBERS.each do |brand, card_numbers|
|
51
|
+
card_numbers.each do |card_number|
|
52
|
+
detector(card_number).send("#{brand}?").must_equal true
|
53
|
+
detector(card_number).brand.must_equal brand
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should check if card invalid' do
|
59
|
+
INVALID_NUMBERS.each do |card_number|
|
60
|
+
detector(card_number).valid?.must_equal false
|
61
|
+
detector(card_number).brand.must_be_nil
|
62
|
+
VALID_NUMBERS.keys.each do |brand|
|
63
|
+
detector(card_number).send("#{brand}?").must_equal false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should detect by full brand name' do
|
69
|
+
amex = CreditCardValidations::Factory.random(:amex)
|
70
|
+
detector(amex).valid?('American Express').must_equal true
|
71
|
+
visa = CreditCardValidations::Factory.random(:visa)
|
72
|
+
detector(visa).valid?('American Express').must_equal false
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should support multiple brands for single check' do
|
76
|
+
VALID_NUMBERS.slice(:visa, :mastercard).each do |key, value|
|
77
|
+
detector(value.first).brand(:visa, :mastercard).must_equal key
|
78
|
+
end
|
79
|
+
|
80
|
+
VALID_NUMBERS.except(:visa, :mastercard).each do |_, value|
|
81
|
+
detector(value.first).brand(:visa, :mastercard).must_be_nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should check if valid brand without arguments' do
|
86
|
+
VALID_NUMBERS.each do |key, value|
|
87
|
+
value.each do |card_number|
|
88
|
+
detector(card_number).valid?(key).must_equal true
|
89
|
+
assert detector(card_number).valid?.must_equal true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should not be valid? if wrong brand' do
|
95
|
+
detector(VALID_NUMBERS[:visa].first).valid?(:mastercard).must_equal false
|
96
|
+
detector(VALID_NUMBERS[:mastercard].first).valid?(:visa).must_equal false
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should be valid? if right brand' do
|
100
|
+
detector(VALID_NUMBERS[:visa].first).valid?(:mastercard, :visa).must_equal true
|
101
|
+
detector(VALID_NUMBERS[:visa].first).valid?(:mastercard, :amex).must_equal false
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
describe 'adding/removing brand' do
|
106
|
+
|
107
|
+
describe 'adding rules' do
|
108
|
+
|
109
|
+
let(:voyager_number) { '869926275400212' }
|
110
|
+
|
111
|
+
it 'should validate number as voyager' do
|
112
|
+
CreditCardValidations::Detector.add_brand(:voyager, length: 15, prefixes: '86')
|
113
|
+
detector(voyager_number).valid?(:voyager).must_equal true
|
114
|
+
detector(voyager_number).voyager?.must_equal true
|
115
|
+
detector(voyager_number).brand.must_equal :voyager
|
116
|
+
end
|
117
|
+
|
118
|
+
describe 'Add voyager rule' do
|
119
|
+
before do
|
120
|
+
CreditCardValidations::Detector.add_brand(:voyager, length: 15, prefixes: '86')
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'should validate number as voyager' do
|
124
|
+
detector(voyager_number).valid?(:voyager).must_equal true
|
125
|
+
detector(voyager_number).voyager?.must_equal true
|
126
|
+
detector(voyager_number).brand.must_equal :voyager
|
127
|
+
end
|
128
|
+
|
129
|
+
describe 'Remove voyager rule' do
|
130
|
+
before do
|
131
|
+
CreditCardValidations::Detector.delete_brand(:voyager)
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'should not validate number as voyager' do
|
135
|
+
detector(voyager_number).respond_to?(:voyager?).must_equal false
|
136
|
+
detector(voyager_number).brand.must_be_nil
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe 'plugins' do
|
143
|
+
[:diners_us, :en_route, :laser].each do |brand|
|
144
|
+
it "should support #{brand}" do
|
145
|
+
-> { CreditCardValidations::Factory.random(brand) }.
|
146
|
+
must_raise(CreditCardValidations::Error)
|
147
|
+
custom_number = 'some_number'
|
148
|
+
detector(custom_number).respond_to?("#{brand}?").must_equal false
|
149
|
+
require "credit_card_validations/plugins/#{brand}"
|
150
|
+
number = CreditCardValidations::Factory.random(brand)
|
151
|
+
detector(number).valid?("#{brand}".to_sym).must_equal true
|
152
|
+
detector(custom_number).respond_to?("#{brand}?").must_equal true
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should raise Error if no brand added before' do
|
158
|
+
-> { CreditCardValidations::Detector::add_rule(:undefined_brand, 20, [20]) }.
|
159
|
+
must_raise(CreditCardValidations::Error)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def luhn_valid?(number)
|
164
|
+
CreditCardValidations::Luhn.valid?(number)
|
165
|
+
end
|
166
|
+
|
167
|
+
def detector(number)
|
168
|
+
CreditCardValidations::Detector.new(number)
|
169
|
+
end
|
170
|
+
|
171
|
+
def has_luhn_check_rule?(key)
|
172
|
+
CreditCardValidations::Detector.has_luhn_check_rule?(key)
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
describe CreditCardValidations::Factory do
|
4
|
+
|
5
|
+
it 'should generate random brand' do
|
6
|
+
number = CreditCardValidations::Factory.random
|
7
|
+
CreditCardValidations::Detector.new(number).valid?.must_equal true
|
8
|
+
end
|
9
|
+
|
10
|
+
CreditCardValidations::Detector.brands.keys.sort.each do |key|
|
11
|
+
describe "#{key}" do
|
12
|
+
it "should generate valid #{key}" do
|
13
|
+
number = CreditCardValidations::Factory.random(key)
|
14
|
+
CreditCardValidations::Detector.new(number).valid?(key).must_equal true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
---
|
2
|
+
:visa:
|
3
|
+
- 4012 8888 8888 1881
|
4
|
+
- 4111 1111 1111 1111
|
5
|
+
- 4222 2222 2222 2
|
6
|
+
- 4917 6100 0000 0000
|
7
|
+
:mastercard:
|
8
|
+
- 5274 5763 9425 9961
|
9
|
+
- 5555 5555 5555 4444
|
10
|
+
- 5105 1051 0510 5100
|
11
|
+
- 2720 1700 0000 0006
|
12
|
+
- 2223 4800 0000 0001
|
13
|
+
- 2223 0400 0000 0003
|
14
|
+
- 2223 0700 0000 0000
|
15
|
+
- 2223 2700 0000 0006
|
16
|
+
- 2720 3500 0000 0004
|
17
|
+
- 2223 1000 0000 0005
|
18
|
+
- 2720 0500 0000 0000
|
19
|
+
:diners:
|
20
|
+
- 3020 4169 3226 43
|
21
|
+
- 3021 8047 1965 57
|
22
|
+
- 3022 1511 5632 52
|
23
|
+
- 3600 0000 0000 08
|
24
|
+
- 3614 8900 6479 13
|
25
|
+
- 3670 0102 0000 00
|
26
|
+
- 3852 0000 0232 37
|
27
|
+
- 3056 9309 0259 04
|
28
|
+
- 3020 4169 3226 43
|
29
|
+
:amex:
|
30
|
+
- 3714 4963 5398 431
|
31
|
+
- 3787 3449 3671 000
|
32
|
+
- 3400 0000 0000 009
|
33
|
+
- 3411 1111 1111 111
|
34
|
+
- 3434 3434 3434 343
|
35
|
+
- 3468 2763 0435 344
|
36
|
+
- 3700 0000 0000 002
|
37
|
+
- 3700 0020 0000 000
|
38
|
+
- 3704 0726 9909 809
|
39
|
+
- 3705 5601 9309 221
|
40
|
+
- 3714 4963 5398 431
|
41
|
+
- 3742 0000 0000 004
|
42
|
+
- 3764 6228 0921 451
|
43
|
+
- 3777 5274 9896 404
|
44
|
+
- 3782 8224 6310 005
|
45
|
+
:discover:
|
46
|
+
- 6011 1111 1111 1117
|
47
|
+
- 6011 0009 9013 9424
|
48
|
+
- 6011 0000 0000 0004
|
49
|
+
- 6011 0004 0000 0000
|
50
|
+
- 6011 1532 1637 1980
|
51
|
+
- 6011 6011 6011 6611
|
52
|
+
- 6011 6874 8256 4166
|
53
|
+
- 6011 8148 3690 5651
|
54
|
+
:maestro:
|
55
|
+
- 6759 6498 2643 8453
|
56
|
+
- 5641 8200 0000 0005
|
57
|
+
- 5033 9619 8909 17
|
58
|
+
- 5868 2416 0825 5333 38
|
59
|
+
- 6799 9901 0000 0000 019
|
60
|
+
- 6390 0200 0000 000003
|
61
|
+
- 6304 9506 0000 0000 00
|
62
|
+
- 6304 9000 1774 0292 441
|
63
|
+
:jcb:
|
64
|
+
- 3575 7591 5225 4876
|
65
|
+
- 3566 0020 2036 0505
|
66
|
+
- 1800 0163 8277 392
|
67
|
+
- 3569 9900 0000 0009
|
68
|
+
- 3530 1113 3330 0000
|
69
|
+
- 3572 6600 0000 0000 006
|
70
|
+
:solo:
|
71
|
+
- 6767 6222 2222 2222 222
|
72
|
+
- 6334 5805 0000 0000
|
73
|
+
- 6334 9000 0000 0005
|
74
|
+
- 6334 7306 0000 0000 00
|
75
|
+
- 6767 6767 6767 6767 671
|
76
|
+
:unionpay:
|
77
|
+
- 6264 1852 1292 2132 067
|
78
|
+
- 6288 9977 1545 2584
|
79
|
+
- 6269 9920 5813 4322
|
80
|
+
:dankort:
|
81
|
+
- 5019 7170 1010 3742
|
82
|
+
:switch:
|
83
|
+
- 6331 1019 9999 0016
|
84
|
+
:rupay:
|
85
|
+
- 6076 6000 0619 9992
|
86
|
+
- 6070 5500 5000 0047
|
87
|
+
:hipercard:
|
88
|
+
- 3841 0058 9908 8180 330
|
89
|
+
:elo:
|
90
|
+
- '4011 7841 3850 9070'
|
91
|
+
- '4389 3555 4728 7502'
|
92
|
+
- '4573 9320 5642 0699'
|
93
|
+
- '4576 3198 7564 9522'
|
94
|
+
- '5041 7572 2687 1008'
|
95
|
+
- '5066 9917 1703 9876'
|
96
|
+
- '5067 5391 6526 0388'
|
97
|
+
- '5090 4071 4078 7707'
|
98
|
+
- '5090 4270 1119 7080'
|
99
|
+
- '5090 4344 4788 5021'
|
100
|
+
- '5090 4514 6675 8555'
|
101
|
+
- '5090 4673 1437 9707'
|
102
|
+
- '5090 4791 2220 0924'
|
103
|
+
- '5090 4854 1570 5059'
|
104
|
+
- '5090 4968 1280 8652'
|
105
|
+
- '5090 5097 0678 9193'
|
106
|
+
- '5090 5157 9762 1083'
|
107
|
+
- '5090 5225 6947 6964'
|
108
|
+
- '5090 6473 8030 4070'
|
109
|
+
- '5090 6644 0324 2820'
|
110
|
+
- '5090 6724 8089 9317'
|
111
|
+
- '5090 6888 2173 4488'
|
112
|
+
- '5090 6948 3784 4758'
|
113
|
+
- '5090 7463 8779 6948'
|
114
|
+
- '6362 9745 2190 7765'
|
115
|
+
- '6363 6850 2897 2643'
|
116
|
+
- '6504 9799 9991 0279'
|
117
|
+
- '6516 5299 9991 0328'
|
118
|
+
:mir:
|
119
|
+
- 2202 1234 1234 1234
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreditCard
|
2
|
+
attr_accessor :number, :number2, :number3, :number4, :number5, :number6, :number7, :card_type
|
3
|
+
include ActiveModel::Validations
|
4
|
+
validates :number, credit_card_number: { brands: [:amex, :maestro] }, allow_blank: true
|
5
|
+
validates :number2, credit_card_number: { only: [:amex, :maestro] }, allow_blank: true
|
6
|
+
validates :number3, credit_card_number: { except: [:amex, :maestro] }, allow_blank: true
|
7
|
+
validates :number4, credit_card_number: { brands: :any }, allow_blank: true
|
8
|
+
validates :number5, credit_card_number: true, allow_blank: true
|
9
|
+
validates :number6, credit_card_number: { brands: ->(record) { record.supported_brand } }, allow_blank: true
|
10
|
+
validates :number7, credit_card_number: { message: 'Custom message' }, allow_blank: true
|
11
|
+
|
12
|
+
def supported_brand
|
13
|
+
{
|
14
|
+
'Master Card' => :mastercard,
|
15
|
+
'Visa' => :visa
|
16
|
+
}[self.card_type]
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
data/spec/string_spec.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require 'credit_card_validations/string'
|
3
|
+
|
4
|
+
describe 'String ext' do
|
5
|
+
|
6
|
+
let(:mastercard) { CreditCardValidations::Factory.random(:mastercard) }
|
7
|
+
let(:visa) { CreditCardValidations::Factory.random(:visa) }
|
8
|
+
let(:invalid) { INVALID_NUMBERS.sample }
|
9
|
+
|
10
|
+
it 'should allow detect brand for mastercard' do
|
11
|
+
mastercard.credit_card_brand.must_equal :mastercard
|
12
|
+
mastercard.credit_card_brand_name.must_equal 'MasterCard'
|
13
|
+
mastercard.valid_credit_card_brand?(:mastercard).must_equal true
|
14
|
+
mastercard.valid_credit_card_brand?('MasterCard').must_equal true
|
15
|
+
mastercard.valid_credit_card_brand?(:visa, :amex).must_equal false
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should allow detect brand for visa' do
|
19
|
+
visa.credit_card_brand.must_equal :visa
|
20
|
+
visa.credit_card_brand_name.must_equal 'Visa'
|
21
|
+
visa.valid_credit_card_brand?(:mastercard).must_equal false
|
22
|
+
visa.valid_credit_card_brand?(:visa, :amex).must_equal true
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should not allow detect brand for invalid card' do
|
26
|
+
invalid.credit_card_brand.must_be_nil
|
27
|
+
invalid.credit_card_brand_name.must_be_nil
|
28
|
+
invalid.valid_credit_card_brand?(:mastercard).must_equal false
|
29
|
+
invalid.valid_credit_card_brand?(:visa, :amex).must_equal false
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|