credit_card_validations 1.5.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -1
- data/Changelog.md +85 -0
- data/README.md +32 -9
- data/Rakefile +4 -4
- data/credit_card_validations.gemspec +2 -2
- data/lib/active_model/credit_card_number_validator.rb +67 -2
- data/lib/credit_card_validations.rb +25 -17
- data/lib/credit_card_validations/detector.rb +110 -27
- data/lib/credit_card_validations/factory.rb +66 -0
- data/lib/credit_card_validations/luhn.rb +5 -0
- data/lib/credit_card_validations/mmi.rb +18 -13
- data/lib/credit_card_validations/string.rb +11 -1
- data/lib/credit_card_validations/version.rb +1 -1
- data/lib/data/brands.yaml +238 -0
- data/spec/active_model_spec.rb +69 -0
- data/spec/credit_card_validations_spec.rb +168 -0
- data/spec/factory_spec.rb +21 -0
- data/{test → spec}/fixtures/invalid_cards.yml +0 -0
- data/{test → spec}/fixtures/valid_cards.yml +1 -0
- data/spec/models/credit_card.rb +19 -0
- data/spec/string_spec.rb +43 -0
- data/{test → spec}/test_helper.rb +5 -4
- metadata +25 -17
- data/lib/credit_card_validations/card_rules.rb +0 -84
- data/test/credit_card_validations_test.rb +0 -151
- data/test/models/any_credit_card.rb +0 -6
- data/test/models/credit_card.rb +0 -5
@@ -0,0 +1,168 @@
|
|
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
|
+
|
33
|
+
CreditCardValidations::Luhn.expects(:valid?).never
|
34
|
+
card_detector.valid?(:unionpay).must_equal true
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
it "should check luhn" do
|
41
|
+
VALID_NUMBERS.each do |brand, card_numbers|
|
42
|
+
if has_luhn_check_rule?(brand)
|
43
|
+
card_numbers.each do |number|
|
44
|
+
luhn_valid?(detector(number).number).must_equal true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should check valid brand" do
|
51
|
+
VALID_NUMBERS.each do |brand, card_numbers|
|
52
|
+
card_numbers.each do |card_number|
|
53
|
+
detector(card_number).send("#{brand}?").must_equal true
|
54
|
+
detector(card_number).brand.must_equal brand
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should check if card invalid" do
|
60
|
+
INVALID_NUMBERS.each do |card_number|
|
61
|
+
detector(card_number).valid?.must_equal false
|
62
|
+
detector(card_number).brand.must_be_nil
|
63
|
+
VALID_NUMBERS.keys.each do |brand|
|
64
|
+
detector(card_number).send("#{brand}?").must_equal false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should detect by full brand name" do
|
70
|
+
amex = CreditCardValidations::Factory.random(:amex)
|
71
|
+
detector(amex).valid?('American Express').must_equal true
|
72
|
+
visa = CreditCardValidations::Factory.random(:visa)
|
73
|
+
detector(visa).valid?('American Express').must_equal false
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should support multiple brands for single check" do
|
77
|
+
VALID_NUMBERS.slice(:visa, :mastercard).each do |key, value|
|
78
|
+
detector(value.first).brand(:visa, :mastercard).must_equal key
|
79
|
+
end
|
80
|
+
|
81
|
+
VALID_NUMBERS.except(:visa, :mastercard).each do |_, value|
|
82
|
+
detector(value.first).brand(:visa, :mastercard).must_be_nil
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should check if valid brand without arguments" do
|
87
|
+
VALID_NUMBERS.each do |key, value|
|
88
|
+
value.each do |card_number|
|
89
|
+
detector(card_number).valid?(key).must_equal true
|
90
|
+
assert detector(card_number).valid?.must_equal true
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should not be valid? if wrong brand" do
|
96
|
+
detector(VALID_NUMBERS[:visa].first).valid?(:mastercard).must_equal false
|
97
|
+
detector(VALID_NUMBERS[:mastercard].first).valid?(:visa).must_equal false
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should be valid? if right brand" do
|
101
|
+
detector(VALID_NUMBERS[:visa].first).valid?(:mastercard, :visa).must_equal true
|
102
|
+
detector(VALID_NUMBERS[:visa].first).valid?(:mastercard, :amex).must_equal false
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
describe "adding/removing brand" do
|
107
|
+
|
108
|
+
|
109
|
+
let(:voyager_number) {
|
110
|
+
'869926275400212'
|
111
|
+
}
|
112
|
+
|
113
|
+
it "should validate number as voyager" do
|
114
|
+
CreditCardValidations::Detector.add_brand(:voyager, {length: 15, prefixes: '86'})
|
115
|
+
detector(voyager_number).valid?(:voyager).must_equal true
|
116
|
+
detector(voyager_number).voyager?.must_equal true
|
117
|
+
detector(voyager_number).brand.must_equal :voyager
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
describe "Add voyager rule" do
|
122
|
+
before do
|
123
|
+
CreditCardValidations::Detector.add_brand(:voyager, {length: 15, prefixes: '86'})
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should validate number as voyager" do
|
127
|
+
|
128
|
+
detector(voyager_number).valid?(:voyager).must_equal true
|
129
|
+
detector(voyager_number).voyager?.must_equal true
|
130
|
+
detector(voyager_number).brand.must_equal :voyager
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "Remove voyager rule" do
|
135
|
+
before do
|
136
|
+
CreditCardValidations::Detector.delete_brand(:voyager)
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should not validate number as voyager" do
|
140
|
+
detector(voyager_number).respond_to?(:voyager?).must_equal false
|
141
|
+
detector(voyager_number).brand.must_be_nil
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
it "should raise RuntimeError" do
|
149
|
+
proc { CreditCardValidations::Detector::add_rule(:undefined_brand, 20, [20]) }.must_raise RuntimeError
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
def luhn_valid?(number)
|
156
|
+
CreditCardValidations::Luhn.valid?(number)
|
157
|
+
end
|
158
|
+
|
159
|
+
def detector(number)
|
160
|
+
CreditCardValidations::Detector.new(number)
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
def has_luhn_check_rule?(key)
|
165
|
+
CreditCardValidations::Detector.has_luhn_check_rule?(key)
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
@@ -0,0 +1,21 @@
|
|
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
|
+
|
13
|
+
it "should generate valid #{key}" do
|
14
|
+
number = CreditCardValidations::Factory.random(key)
|
15
|
+
CreditCardValidations::Detector.new(number).valid?(key).must_equal true
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
File without changes
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreditCard
|
2
|
+
attr_accessor :number, :number2, :number3, :number4, :number5, :number6, :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
|
+
|
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,43 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require 'credit_card_validations/string'
|
3
|
+
|
4
|
+
describe "String ext" do
|
5
|
+
|
6
|
+
let(:mastercard) {
|
7
|
+
CreditCardValidations::Factory.random(:mastercard)
|
8
|
+
}
|
9
|
+
|
10
|
+
let(:visa) {
|
11
|
+
CreditCardValidations::Factory.random(:visa)
|
12
|
+
}
|
13
|
+
|
14
|
+
let(:invalid) {
|
15
|
+
INVALID_NUMBERS.sample
|
16
|
+
}
|
17
|
+
|
18
|
+
it "should allow detect brand for mastercard" do
|
19
|
+
|
20
|
+
mastercard.credit_card_brand.must_equal :mastercard
|
21
|
+
mastercard.credit_card_brand_name.must_equal 'MasterCard'
|
22
|
+
mastercard.valid_credit_card_brand?(:mastercard).must_equal true
|
23
|
+
mastercard.valid_credit_card_brand?('MasterCard').must_equal true
|
24
|
+
mastercard.valid_credit_card_brand?(:visa, :amex).must_equal false
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should allow detect brand for visa" do
|
28
|
+
|
29
|
+
visa.credit_card_brand.must_equal :visa
|
30
|
+
visa.credit_card_brand_name.must_equal 'Visa'
|
31
|
+
visa.valid_credit_card_brand?(:mastercard).must_equal false
|
32
|
+
visa.valid_credit_card_brand?(:visa, :amex).must_equal true
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should not allow detect brand for invalid card" do
|
36
|
+
|
37
|
+
invalid.credit_card_brand.must_be_nil
|
38
|
+
invalid.credit_card_brand_name.must_be_nil
|
39
|
+
invalid.valid_credit_card_brand?(:mastercard).must_equal false
|
40
|
+
invalid.valid_credit_card_brand?(:visa, :amex).must_equal false
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -1,19 +1,20 @@
|
|
1
1
|
require 'coveralls'
|
2
2
|
Coveralls.wear!
|
3
|
+
|
3
4
|
require 'minitest/autorun'
|
4
5
|
require 'i18n'
|
5
6
|
require 'mocha/mini_test'
|
6
7
|
|
7
8
|
lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
|
8
|
-
|
9
|
+
specs = File.expand_path("#{File.dirname(__FILE__)}/../spec")
|
9
10
|
$:.unshift(lib)
|
10
|
-
$:.unshift(
|
11
|
+
$:.unshift(specs)
|
11
12
|
|
12
13
|
I18n.config.enforce_available_locales = true
|
13
14
|
|
14
15
|
require 'credit_card_validations'
|
15
16
|
require 'models/credit_card'
|
16
|
-
require 'models/any_credit_card'
|
17
|
-
|
18
17
|
|
18
|
+
VALID_NUMBERS = YAML.load_file File.join(File.dirname(__FILE__), 'fixtures/valid_cards.yml')
|
19
|
+
INVALID_NUMBERS = YAML.load_file File.join(File.dirname(__FILE__), 'fixtures/invalid_cards.yml')
|
19
20
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: credit_card_validations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Igor
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-01-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -78,7 +78,7 @@ dependencies:
|
|
78
78
|
- - "~>"
|
79
79
|
- !ruby/object:Gem::Version
|
80
80
|
version: '10'
|
81
|
-
description: A ruby gem for validating credit card numbers
|
81
|
+
description: A ruby gem for validating credit card numbers
|
82
82
|
email:
|
83
83
|
- fedoronchuk@gmail.com
|
84
84
|
executables: []
|
@@ -86,6 +86,7 @@ extensions: []
|
|
86
86
|
extra_rdoc_files: []
|
87
87
|
files:
|
88
88
|
- ".travis.yml"
|
89
|
+
- Changelog.md
|
89
90
|
- Gemfile
|
90
91
|
- LICENSE.txt
|
91
92
|
- README.md
|
@@ -93,18 +94,21 @@ files:
|
|
93
94
|
- credit_card_validations.gemspec
|
94
95
|
- lib/active_model/credit_card_number_validator.rb
|
95
96
|
- lib/credit_card_validations.rb
|
96
|
-
- lib/credit_card_validations/card_rules.rb
|
97
97
|
- lib/credit_card_validations/detector.rb
|
98
|
+
- lib/credit_card_validations/factory.rb
|
98
99
|
- lib/credit_card_validations/luhn.rb
|
99
100
|
- lib/credit_card_validations/mmi.rb
|
100
101
|
- lib/credit_card_validations/string.rb
|
101
102
|
- lib/credit_card_validations/version.rb
|
102
|
-
-
|
103
|
-
-
|
104
|
-
-
|
105
|
-
-
|
106
|
-
-
|
107
|
-
-
|
103
|
+
- lib/data/brands.yaml
|
104
|
+
- spec/active_model_spec.rb
|
105
|
+
- spec/credit_card_validations_spec.rb
|
106
|
+
- spec/factory_spec.rb
|
107
|
+
- spec/fixtures/invalid_cards.yml
|
108
|
+
- spec/fixtures/valid_cards.yml
|
109
|
+
- spec/models/credit_card.rb
|
110
|
+
- spec/string_spec.rb
|
111
|
+
- spec/test_helper.rb
|
108
112
|
homepage: https://github.com/Fivell/credit_card_validations
|
109
113
|
licenses:
|
110
114
|
- MIT
|
@@ -128,11 +132,15 @@ rubyforge_project:
|
|
128
132
|
rubygems_version: 2.2.2
|
129
133
|
signing_key:
|
130
134
|
specification_version: 4
|
131
|
-
summary: gem for credit card numbers validation, card brands detections
|
135
|
+
summary: gem should be used for credit card numbers validation, card brands detections,
|
136
|
+
luhn checks
|
132
137
|
test_files:
|
133
|
-
-
|
134
|
-
-
|
135
|
-
-
|
136
|
-
-
|
137
|
-
-
|
138
|
-
-
|
138
|
+
- spec/active_model_spec.rb
|
139
|
+
- spec/credit_card_validations_spec.rb
|
140
|
+
- spec/factory_spec.rb
|
141
|
+
- spec/fixtures/invalid_cards.yml
|
142
|
+
- spec/fixtures/valid_cards.yml
|
143
|
+
- spec/models/credit_card.rb
|
144
|
+
- spec/string_spec.rb
|
145
|
+
- spec/test_helper.rb
|
146
|
+
has_rdoc:
|
@@ -1,84 +0,0 @@
|
|
1
|
-
module CreditCardValidations
|
2
|
-
module CardRules
|
3
|
-
######## most used brands #########
|
4
|
-
mattr_accessor :rules
|
5
|
-
|
6
|
-
self.rules = {
|
7
|
-
visa: [
|
8
|
-
{length: [13, 16], prefixes: ['4']}
|
9
|
-
],
|
10
|
-
mastercard: [
|
11
|
-
{length: [16], prefixes: ['51', '52', '53', '54', '55']}
|
12
|
-
],
|
13
|
-
|
14
|
-
amex: [
|
15
|
-
{length: [15], prefixes: ['34', '37']}
|
16
|
-
],
|
17
|
-
######## other brands ########
|
18
|
-
diners: [
|
19
|
-
{length: [14], prefixes: ['300', '301', '302', '303', '304', '305', '36', '38']},
|
20
|
-
],
|
21
|
-
|
22
|
-
#There are Diners Club (North America) cards that begin with 5. These are a joint venture between Diners Club and MasterCard, and are processed like a MasterCard
|
23
|
-
# will be removed in next major version
|
24
|
-
|
25
|
-
diners_us: [
|
26
|
-
{length: [16], prefixes: ['54', '55']}
|
27
|
-
],
|
28
|
-
|
29
|
-
discover: [
|
30
|
-
{length: [16], prefixes: ['6011', '644', '645', '646', '647', '648',
|
31
|
-
'649', '65']}
|
32
|
-
],
|
33
|
-
|
34
|
-
jcb: [
|
35
|
-
{length: [15,16], prefixes: ['3528', '3529', '353', '354', '355', '356', '357', '358']},
|
36
|
-
{length: [15], prefixes: ['1800', '2131']}
|
37
|
-
],
|
38
|
-
|
39
|
-
|
40
|
-
laser: [
|
41
|
-
{length: [16, 17, 18, 19], prefixes: ['6304', '6706', '6771']}
|
42
|
-
],
|
43
|
-
|
44
|
-
solo: [
|
45
|
-
{length: [16, 18, 19], prefixes: ['6334', '6767']}
|
46
|
-
],
|
47
|
-
|
48
|
-
switch: [
|
49
|
-
{length: [16, 18, 19], prefixes: ['633110', '633312', '633304', '633303', '633301', '633300']}
|
50
|
-
|
51
|
-
],
|
52
|
-
|
53
|
-
maestro: [
|
54
|
-
{length: [12, 13, 14, 15, 16, 17, 18, 19], prefixes: ['5010', '5011', '5012', '5013', '5014', '5015', '5016', '5017', '5018',
|
55
|
-
'502', '503', '504', '505', '506', '507', '508',
|
56
|
-
'56','57', '58', '59',
|
57
|
-
'6010', '6012', '6013', '6014', '6015', '6016', '6017', '6018', '6019',
|
58
|
-
'602', '603', '604', '605', '6060',
|
59
|
-
'621', '627', '629',
|
60
|
-
'670','671', '672','673', '674', '675', '677',
|
61
|
-
'6760', '6761', '6762', '6763', '6764', '6765', '6766', '6768', '6769',
|
62
|
-
'679'
|
63
|
-
]}
|
64
|
-
],
|
65
|
-
|
66
|
-
# Luhn validation are skipped for union pay cards because they have unknown generation algoritm
|
67
|
-
unionpay: [
|
68
|
-
{length: [16, 17, 18, 19], prefixes: ['622', '624', '625', '626', '628'], skip_luhn: true}
|
69
|
-
],
|
70
|
-
|
71
|
-
dankrot: [
|
72
|
-
{length: [16], prefixes: ['5019']}
|
73
|
-
],
|
74
|
-
|
75
|
-
rupay: [
|
76
|
-
{length: [16], prefixes: ['6061', '6062', '6063', '6064', '6065', '6066', '6067', '6068', '6069', '607', '608'], skip_luhn: true}
|
77
|
-
],
|
78
|
-
|
79
|
-
hipercard: [
|
80
|
-
length: [19], prefixes: ['384']
|
81
|
-
]
|
82
|
-
}
|
83
|
-
end
|
84
|
-
end
|