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,36 @@
|
|
1
|
+
require 'credit_card_validations/version'
|
2
|
+
require 'credit_card_validations/error'
|
3
|
+
require 'active_model'
|
4
|
+
require 'active_support/core_ext'
|
5
|
+
require 'active_model/validations'
|
6
|
+
require 'active_model/credit_card_number_validator'
|
7
|
+
require 'yaml'
|
8
|
+
|
9
|
+
module CreditCardValidations
|
10
|
+
extend ActiveSupport::Autoload
|
11
|
+
autoload :VERSION, 'credit_card_validations/version'
|
12
|
+
autoload :Luhn, 'credit_card_validations/luhn'
|
13
|
+
autoload :Detector, 'credit_card_validations/detector'
|
14
|
+
autoload :Factory, 'credit_card_validations/factory'
|
15
|
+
autoload :Mmi, 'credit_card_validations/mmi'
|
16
|
+
|
17
|
+
|
18
|
+
def self.add_brand(key, rules, options = {})
|
19
|
+
Detector.add_brand(key, rules, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
DATA = YAML.load_file(File.join(File.dirname(__FILE__), 'data', 'brands.yaml')) || {}
|
23
|
+
|
24
|
+
def self.reload!
|
25
|
+
Detector.brands = {}
|
26
|
+
DATA.each do |key, data|
|
27
|
+
add_brand(key, data.fetch(:rules), data.fetch(:options, {}))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
CreditCardValidations.reload!
|
34
|
+
|
35
|
+
|
36
|
+
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# == CreditCardValidations Detector
|
2
|
+
#
|
3
|
+
# class provides credit card number validations
|
4
|
+
module CreditCardValidations
|
5
|
+
class Detector
|
6
|
+
|
7
|
+
include Mmi
|
8
|
+
|
9
|
+
class_attribute :brands
|
10
|
+
self.brands = {}
|
11
|
+
|
12
|
+
attr_reader :number
|
13
|
+
|
14
|
+
def initialize(number)
|
15
|
+
@number = number.to_s.tr('- ', '')
|
16
|
+
end
|
17
|
+
|
18
|
+
# credit card number validation
|
19
|
+
def valid?(*brands)
|
20
|
+
!!valid_number?(*brands)
|
21
|
+
end
|
22
|
+
|
23
|
+
#brand name
|
24
|
+
def brand(*keys)
|
25
|
+
valid_number?(*keys)
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid_number?(*keys)
|
29
|
+
selected_brands = keys.blank? ? self.brands : resolve_keys(*keys)
|
30
|
+
if selected_brands.any?
|
31
|
+
selected_brands.each do |key, brand|
|
32
|
+
return key if matches_brand?(brand)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
#check if luhn valid
|
39
|
+
def valid_luhn?
|
40
|
+
@valid_luhn ||= Luhn.valid?(number)
|
41
|
+
end
|
42
|
+
|
43
|
+
def brand_name
|
44
|
+
self.class.brand_name(brand)
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
def resolve_keys(*keys)
|
50
|
+
brand_keys = keys.map do |el|
|
51
|
+
if el.is_a? String
|
52
|
+
#try to find key by name
|
53
|
+
el = (self.class.brand_key(el) || el).to_sym
|
54
|
+
end
|
55
|
+
el.downcase
|
56
|
+
end
|
57
|
+
self.brands.slice(*brand_keys)
|
58
|
+
end
|
59
|
+
|
60
|
+
def matches_brand?(brand)
|
61
|
+
rules = brand.fetch(:rules)
|
62
|
+
options = brand.fetch(:options, {})
|
63
|
+
|
64
|
+
rules.each do |rule|
|
65
|
+
if (options[:skip_luhn] || valid_luhn?) &&
|
66
|
+
rule[:length].include?(number.length) &&
|
67
|
+
number.match(rule[:regexp])
|
68
|
+
return true
|
69
|
+
end
|
70
|
+
end
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
class << self
|
75
|
+
|
76
|
+
def has_luhn_check_rule?(key)
|
77
|
+
!brands[key].fetch(:options, {}).fetch(:skip_luhn, false)
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# add brand
|
82
|
+
#
|
83
|
+
# CreditCardValidations.add_brand(:en_route, {length: 15, prefixes: ['2014', '2149']}, {skip_luhn: true}) #skip luhn
|
84
|
+
#
|
85
|
+
def add_brand(key, rules, options = {})
|
86
|
+
|
87
|
+
brands[key] = {rules: [], options: options || {}}
|
88
|
+
|
89
|
+
Array.wrap(rules).each do |rule|
|
90
|
+
add_rule(key, rule[:length], rule[:prefixes])
|
91
|
+
end
|
92
|
+
|
93
|
+
define_brand_method(key)
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
def brand_name(brand_key)
|
98
|
+
brand = brands[brand_key]
|
99
|
+
if brand
|
100
|
+
brand.fetch(:options, {})[:brand_name] || brand_key.to_s.titleize
|
101
|
+
else
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def brand_key(brand_name)
|
107
|
+
brands.detect do |_, brand|
|
108
|
+
brand[:options][:brand_name] == brand_name
|
109
|
+
end.try(:first)
|
110
|
+
end
|
111
|
+
|
112
|
+
# CreditCardValidations.delete_brand(:en_route)
|
113
|
+
def delete_brand(key)
|
114
|
+
key = key.to_sym
|
115
|
+
undef_brand_method(key)
|
116
|
+
brands.reject! { |k, _| k == key }
|
117
|
+
end
|
118
|
+
|
119
|
+
#create rule for detecting brand
|
120
|
+
def add_rule(key, length, prefixes)
|
121
|
+
unless brands.has_key?(key)
|
122
|
+
raise Error.new("brand #{key} is undefined, please use #add_brand method")
|
123
|
+
end
|
124
|
+
length, prefixes = Array(length), Array(prefixes)
|
125
|
+
brands[key][:rules] << {length: length, regexp: compile_regexp(prefixes), prefixes: prefixes}
|
126
|
+
end
|
127
|
+
|
128
|
+
protected
|
129
|
+
|
130
|
+
# create methods like visa?, maestro? etc
|
131
|
+
def define_brand_method(key)
|
132
|
+
define_method "#{key}?".to_sym do
|
133
|
+
valid?(key)
|
134
|
+
end unless method_defined? "#{key}?".to_sym
|
135
|
+
end
|
136
|
+
|
137
|
+
def undef_brand_method(key)
|
138
|
+
undef_method "#{key}?".to_sym if method_defined? "#{key}?".to_sym
|
139
|
+
end
|
140
|
+
|
141
|
+
#create regexp by array of prefixes
|
142
|
+
def compile_regexp(prefixes)
|
143
|
+
Regexp.new("^((#{prefixes.join(")|(")}))")
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# == CreditCardValidations Factory
|
2
|
+
# Generates card number that passes validation
|
3
|
+
#
|
4
|
+
# #random
|
5
|
+
# CreditCardValidations::Factory.random
|
6
|
+
# #or particular brand
|
7
|
+
# CreditCardValidations::Factory.random(:maestro) # "6010430241237266856"
|
8
|
+
#
|
9
|
+
#
|
10
|
+
#
|
11
|
+
module CreditCardValidations
|
12
|
+
class Factory
|
13
|
+
class << self
|
14
|
+
def random(brand = nil)
|
15
|
+
brand = Detector.brands.keys.sample if brand.nil?
|
16
|
+
if Detector.brands[brand].nil?
|
17
|
+
raise Error.new('Unsupported brand')
|
18
|
+
end
|
19
|
+
generate(Detector.brands[brand][:rules].sample)
|
20
|
+
end
|
21
|
+
|
22
|
+
def generate(rule)
|
23
|
+
number(rule[:prefixes].sample, rule[:length].sample, rule.fetch(:options, {})[:skip_luhn])
|
24
|
+
end
|
25
|
+
|
26
|
+
def number(prefix, length, skip_luhn = false)
|
27
|
+
number = prefix.dup
|
28
|
+
1.upto(length - (prefix.length + 1)) do
|
29
|
+
number << "#{rand(9)}"
|
30
|
+
end
|
31
|
+
#if skip luhn
|
32
|
+
if skip_luhn
|
33
|
+
number += "#{rand(9)}"
|
34
|
+
else
|
35
|
+
number += last_digit(number).to_s
|
36
|
+
end
|
37
|
+
number
|
38
|
+
end
|
39
|
+
|
40
|
+
#extracted from darkcoding-credit-card
|
41
|
+
|
42
|
+
def last_digit(number)
|
43
|
+
# Calculate sum
|
44
|
+
sum, pos = 0, 0
|
45
|
+
length = number.length + 1
|
46
|
+
|
47
|
+
reversed_number = number.reverse
|
48
|
+
while pos < length do
|
49
|
+
odd = reversed_number[pos].to_i * 2
|
50
|
+
odd -= 9 if odd > 9
|
51
|
+
|
52
|
+
sum += odd
|
53
|
+
|
54
|
+
sum += reversed_number[pos + 1].to_i if pos != (length - 2)
|
55
|
+
|
56
|
+
pos += 2
|
57
|
+
end
|
58
|
+
|
59
|
+
(((sum / 10).floor + 1) * 10 - sum) % 10
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# == CreditCardValidations Luhn
|
2
|
+
# simple class to validate Luhn numbers.
|
3
|
+
#
|
4
|
+
# Luhn.valid? 4111111111111111
|
5
|
+
#
|
6
|
+
module CreditCardValidations
|
7
|
+
class Luhn
|
8
|
+
def self.valid?(number)
|
9
|
+
s1 = s2 = 0
|
10
|
+
number.to_s.reverse.chars.each_slice(2) do |odd, even|
|
11
|
+
s1 += odd.to_i
|
12
|
+
|
13
|
+
double = even.to_i * 2
|
14
|
+
double -= 9 if double >= 10
|
15
|
+
s2 += double
|
16
|
+
end
|
17
|
+
(s1 + s2) % 10 == 0
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# == CreditCardValidations Mmi
|
2
|
+
#
|
3
|
+
# Implements Major Industry Identifier (MII) detection
|
4
|
+
#
|
5
|
+
# The first digit of a credit card number is the Major Industry Identifier (MII), which represents the category of entity which issued the card. MII digits represent the following issuer categories:
|
6
|
+
# 0 – ISO/TC 68 and other future industry assignments
|
7
|
+
# 1 – Airlines
|
8
|
+
# 2 – Airlines and other future industry assignments
|
9
|
+
# 3 – Travel and entertainment and banking/financial
|
10
|
+
# 4 – Banking and financial
|
11
|
+
# 5 – Banking and financial
|
12
|
+
# 6 – Merchandising and banking/financial
|
13
|
+
# 7 – Petroleum and other future industry assignments
|
14
|
+
# 8 – Healthcare, telecommunications and other future industry assignments
|
15
|
+
# 9 – National assignment
|
16
|
+
# For example, American Express, Diner's Club, Carte Blanche,
|
17
|
+
# and JCB are in the travel and entertainment category;
|
18
|
+
# VISA, MasterCard, and Discover are in the banking and financial category (Discover being in the Merchandising and banking/financial category);
|
19
|
+
# and Sun Oil and Exxon are in the petroleum category.
|
20
|
+
|
21
|
+
|
22
|
+
module CreditCardValidations
|
23
|
+
module Mmi
|
24
|
+
|
25
|
+
ISSUER_CATEGORIES = {
|
26
|
+
'0' => 'ISO/TC 68 nd other industry assignments',
|
27
|
+
'1' => 'Airlines',
|
28
|
+
'2' => 'Airlines and other industry assignments',
|
29
|
+
'3' => 'Travel and entertainment and banking/financial',
|
30
|
+
'4' => 'Banking and financial',
|
31
|
+
'5' => 'Banking and financial',
|
32
|
+
'6' => 'Merchandising and banking/financial',
|
33
|
+
'7' => 'Petroleum and other industry assignments',
|
34
|
+
'8' => 'Healthcare, telecommunications and other industry assignments',
|
35
|
+
'9' => 'National assignment'
|
36
|
+
|
37
|
+
}
|
38
|
+
|
39
|
+
def issuer_category
|
40
|
+
ISSUER_CATEGORIES[@number.to_s[0]]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# String extension for brand detecting and number validation
|
2
|
+
#
|
3
|
+
# require 'credit_card_validations/string'
|
4
|
+
# '5274 5763 9425 9961'.credit_card_brand
|
5
|
+
# '5274 5763 9425 9961'.credit_card_brand_name
|
6
|
+
# '5274 5763 9425 9961'.valid_credit_card_brand?(:mastercard, :visa)
|
7
|
+
# '5274 5763 9425 9961'.valid_credit_card_brand?(:amex)
|
8
|
+
#
|
9
|
+
class String
|
10
|
+
def credit_card_brand
|
11
|
+
CreditCardValidations::Detector.new(self).brand
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid_credit_card_brand?(*brands)
|
15
|
+
CreditCardValidations::Detector.new(self).valid?(*brands)
|
16
|
+
end
|
17
|
+
|
18
|
+
def credit_card_brand_name
|
19
|
+
CreditCardValidations::Detector.new(self).brand_name
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,409 @@
|
|
1
|
+
---
|
2
|
+
:visa:
|
3
|
+
:rules:
|
4
|
+
- :length:
|
5
|
+
- 13
|
6
|
+
- 16
|
7
|
+
- 19
|
8
|
+
:prefixes:
|
9
|
+
- '4'
|
10
|
+
:options:
|
11
|
+
:brand_name: Visa
|
12
|
+
:mastercard:
|
13
|
+
:rules:
|
14
|
+
- :length:
|
15
|
+
- 16
|
16
|
+
:prefixes:
|
17
|
+
- '2221'
|
18
|
+
- '2222'
|
19
|
+
- '2223'
|
20
|
+
- '2224'
|
21
|
+
- '2225'
|
22
|
+
- '2226'
|
23
|
+
- '2227'
|
24
|
+
- '2228'
|
25
|
+
- '2229'
|
26
|
+
- '223'
|
27
|
+
- '224'
|
28
|
+
- '225'
|
29
|
+
- '226'
|
30
|
+
- '227'
|
31
|
+
- '228'
|
32
|
+
- '229'
|
33
|
+
- '23'
|
34
|
+
- '24'
|
35
|
+
- '25'
|
36
|
+
- '26'
|
37
|
+
- '271'
|
38
|
+
- '2720'
|
39
|
+
- '51'
|
40
|
+
- '52'
|
41
|
+
- '53'
|
42
|
+
- '54'
|
43
|
+
- '55'
|
44
|
+
:options:
|
45
|
+
:brand_name: MasterCard
|
46
|
+
:amex:
|
47
|
+
:rules:
|
48
|
+
- :length:
|
49
|
+
- 15
|
50
|
+
:prefixes:
|
51
|
+
- '34'
|
52
|
+
- '37'
|
53
|
+
:options:
|
54
|
+
:brand_name: American Express
|
55
|
+
:diners:
|
56
|
+
:rules:
|
57
|
+
- :length:
|
58
|
+
- 14
|
59
|
+
:prefixes:
|
60
|
+
- '300'
|
61
|
+
- '301'
|
62
|
+
- '302'
|
63
|
+
- '303'
|
64
|
+
- '304'
|
65
|
+
- '305'
|
66
|
+
- '36'
|
67
|
+
- '38'
|
68
|
+
:options:
|
69
|
+
:brand_name: Diners Club
|
70
|
+
:jcb:
|
71
|
+
:rules:
|
72
|
+
- :length:
|
73
|
+
- 15
|
74
|
+
- 16
|
75
|
+
:prefixes:
|
76
|
+
- '3528'
|
77
|
+
- '3529'
|
78
|
+
- '353'
|
79
|
+
- '354'
|
80
|
+
- '355'
|
81
|
+
- '356'
|
82
|
+
- '357'
|
83
|
+
- '358'
|
84
|
+
- :length:
|
85
|
+
- 15
|
86
|
+
:prefixes:
|
87
|
+
- '1800'
|
88
|
+
- '2131'
|
89
|
+
- :length:
|
90
|
+
- 19
|
91
|
+
:prefixes:
|
92
|
+
- '357266'
|
93
|
+
:options:
|
94
|
+
:brand_name: JCB
|
95
|
+
:solo:
|
96
|
+
:rules:
|
97
|
+
- :length:
|
98
|
+
- 16
|
99
|
+
- 18
|
100
|
+
- 19
|
101
|
+
:prefixes:
|
102
|
+
- '6334'
|
103
|
+
- '6767'
|
104
|
+
:switch:
|
105
|
+
:rules:
|
106
|
+
- :length:
|
107
|
+
- 16
|
108
|
+
- 18
|
109
|
+
- 19
|
110
|
+
:prefixes:
|
111
|
+
- '633110'
|
112
|
+
- '633312'
|
113
|
+
- '633304'
|
114
|
+
- '633303'
|
115
|
+
- '633301'
|
116
|
+
- '633300'
|
117
|
+
:maestro:
|
118
|
+
:rules:
|
119
|
+
- :length:
|
120
|
+
- 12
|
121
|
+
- 13
|
122
|
+
- 14
|
123
|
+
- 15
|
124
|
+
- 16
|
125
|
+
- 17
|
126
|
+
- 18
|
127
|
+
- 19
|
128
|
+
:prefixes:
|
129
|
+
- '500'
|
130
|
+
- '5010'
|
131
|
+
- '5011'
|
132
|
+
- '5012'
|
133
|
+
- '5013'
|
134
|
+
- '5014'
|
135
|
+
- '5015'
|
136
|
+
- '5016'
|
137
|
+
- '5017'
|
138
|
+
- '5018'
|
139
|
+
- '502'
|
140
|
+
- '503'
|
141
|
+
- '504'
|
142
|
+
- '505'
|
143
|
+
- '506'
|
144
|
+
- '507'
|
145
|
+
- '508'
|
146
|
+
- '509'
|
147
|
+
- '56'
|
148
|
+
- '57'
|
149
|
+
- '58'
|
150
|
+
- '59'
|
151
|
+
- '6010'
|
152
|
+
- '6012'
|
153
|
+
- '6013'
|
154
|
+
- '6014'
|
155
|
+
- '6015'
|
156
|
+
- '6016'
|
157
|
+
- '6017'
|
158
|
+
- '6018'
|
159
|
+
- '6019'
|
160
|
+
- '602'
|
161
|
+
- '603'
|
162
|
+
- '604'
|
163
|
+
- '605'
|
164
|
+
- '6060'
|
165
|
+
- '621'
|
166
|
+
- '627'
|
167
|
+
- '629'
|
168
|
+
- '6304'
|
169
|
+
- '639'
|
170
|
+
- '670'
|
171
|
+
- '671'
|
172
|
+
- '672'
|
173
|
+
- '673'
|
174
|
+
- '674'
|
175
|
+
- '675'
|
176
|
+
- '677'
|
177
|
+
- '6760'
|
178
|
+
- '6761'
|
179
|
+
- '6762'
|
180
|
+
- '6763'
|
181
|
+
- '6764'
|
182
|
+
- '6765'
|
183
|
+
- '6766'
|
184
|
+
- '6768'
|
185
|
+
- '6769'
|
186
|
+
- '6771'
|
187
|
+
- '679'
|
188
|
+
:unionpay:
|
189
|
+
:rules:
|
190
|
+
- :length:
|
191
|
+
- 16
|
192
|
+
- 17
|
193
|
+
- 18
|
194
|
+
- 19
|
195
|
+
:prefixes:
|
196
|
+
- '622'
|
197
|
+
- '624'
|
198
|
+
- '625'
|
199
|
+
- '626'
|
200
|
+
- '628'
|
201
|
+
:options:
|
202
|
+
:skip_luhn: true
|
203
|
+
:brand_name: China UnionPay
|
204
|
+
:dankort:
|
205
|
+
:rules:
|
206
|
+
- :length:
|
207
|
+
- 16
|
208
|
+
:prefixes:
|
209
|
+
- '5019'
|
210
|
+
:rupay:
|
211
|
+
:rules:
|
212
|
+
- :length:
|
213
|
+
- 16
|
214
|
+
:prefixes:
|
215
|
+
- '6061'
|
216
|
+
- '6062'
|
217
|
+
- '6063'
|
218
|
+
- '6064'
|
219
|
+
- '6065'
|
220
|
+
- '6066'
|
221
|
+
- '6067'
|
222
|
+
- '6068'
|
223
|
+
- '6069'
|
224
|
+
- '607'
|
225
|
+
- '608'
|
226
|
+
:options:
|
227
|
+
:skip_luhn: true
|
228
|
+
:hipercard:
|
229
|
+
:rules:
|
230
|
+
- :length:
|
231
|
+
- 19
|
232
|
+
:prefixes:
|
233
|
+
- '384'
|
234
|
+
- '606282'
|
235
|
+
- '637095'
|
236
|
+
- '637568'
|
237
|
+
- '637599'
|
238
|
+
- '637609'
|
239
|
+
- '637612'
|
240
|
+
:elo:
|
241
|
+
:rules:
|
242
|
+
- :length:
|
243
|
+
- 16
|
244
|
+
:prefixes:
|
245
|
+
- '401178'
|
246
|
+
- '401179'
|
247
|
+
- '431274'
|
248
|
+
- '438935'
|
249
|
+
- '451416'
|
250
|
+
- '457393'
|
251
|
+
- '457631'
|
252
|
+
- '457632'
|
253
|
+
- '504175'
|
254
|
+
- '506699'
|
255
|
+
- '50670'
|
256
|
+
- '50671'
|
257
|
+
- '506717'
|
258
|
+
- '506718'
|
259
|
+
- '50672'
|
260
|
+
- '506720'
|
261
|
+
- '506721'
|
262
|
+
- '506724'
|
263
|
+
- '506725'
|
264
|
+
- '506726'
|
265
|
+
- '506727'
|
266
|
+
- '506728'
|
267
|
+
- '506729'
|
268
|
+
- '50673'
|
269
|
+
- '50674'
|
270
|
+
- '50675'
|
271
|
+
- '50676'
|
272
|
+
- '506770'
|
273
|
+
- '506771'
|
274
|
+
- '506772'
|
275
|
+
- '506773'
|
276
|
+
- '506774'
|
277
|
+
- '506775'
|
278
|
+
- '506776'
|
279
|
+
- '506777'
|
280
|
+
- '506778'
|
281
|
+
- '50900'
|
282
|
+
- '509013'
|
283
|
+
- '50902'
|
284
|
+
- '509031'
|
285
|
+
- '509033'
|
286
|
+
- '509035'
|
287
|
+
- '509036'
|
288
|
+
- '509037'
|
289
|
+
- '509038'
|
290
|
+
- '509039'
|
291
|
+
- '50904'
|
292
|
+
- '509050'
|
293
|
+
- '509051'
|
294
|
+
- '509052'
|
295
|
+
- '509053'
|
296
|
+
- '509064'
|
297
|
+
- '509066'
|
298
|
+
- '509067'
|
299
|
+
- '509068'
|
300
|
+
- '509069'
|
301
|
+
- '509072'
|
302
|
+
- '509074'
|
303
|
+
- '509076'
|
304
|
+
- '509077'
|
305
|
+
- '509078'
|
306
|
+
- '509079'
|
307
|
+
- '509080'
|
308
|
+
- '509081'
|
309
|
+
- '509082'
|
310
|
+
- '509083'
|
311
|
+
- '509085'
|
312
|
+
- '636297'
|
313
|
+
- '636368'
|
314
|
+
- '650031'
|
315
|
+
- '650032'
|
316
|
+
- '650033'
|
317
|
+
- '650035'
|
318
|
+
- '650036'
|
319
|
+
- '650037'
|
320
|
+
- '650038'
|
321
|
+
- '650039'
|
322
|
+
- '65004'
|
323
|
+
- '650050'
|
324
|
+
- '650051'
|
325
|
+
- '650405'
|
326
|
+
- '650406'
|
327
|
+
- '650407'
|
328
|
+
- '650408'
|
329
|
+
- '650409'
|
330
|
+
- '65041'
|
331
|
+
- '65042'
|
332
|
+
- '65043'
|
333
|
+
- '650485'
|
334
|
+
- '650486'
|
335
|
+
- '650487'
|
336
|
+
- '650488'
|
337
|
+
- '650489'
|
338
|
+
- '65049'
|
339
|
+
- '65050'
|
340
|
+
- '65051'
|
341
|
+
- '65052'
|
342
|
+
- '650530'
|
343
|
+
- '650531'
|
344
|
+
- '650532'
|
345
|
+
- '650533'
|
346
|
+
- '650534'
|
347
|
+
- '650535'
|
348
|
+
- '650536'
|
349
|
+
- '650537'
|
350
|
+
- '650538'
|
351
|
+
- '650541'
|
352
|
+
- '650700'
|
353
|
+
- '650720'
|
354
|
+
- '650901'
|
355
|
+
- '650921'
|
356
|
+
- '650922'
|
357
|
+
- '650923'
|
358
|
+
- '650924'
|
359
|
+
- '650925'
|
360
|
+
- '650926'
|
361
|
+
- '650927'
|
362
|
+
- '650928'
|
363
|
+
- '650929'
|
364
|
+
- '65093'
|
365
|
+
- '65094'
|
366
|
+
- '65095'
|
367
|
+
- '65096'
|
368
|
+
- '650970'
|
369
|
+
- '650971'
|
370
|
+
- '650972'
|
371
|
+
- '650973'
|
372
|
+
- '650974'
|
373
|
+
- '650975'
|
374
|
+
- '650976'
|
375
|
+
- '650977'
|
376
|
+
- '650978'
|
377
|
+
- '651652'
|
378
|
+
- '65500'
|
379
|
+
- '65501'
|
380
|
+
- '655021'
|
381
|
+
- '65503'
|
382
|
+
- '65504'
|
383
|
+
:options:
|
384
|
+
:skip_luhn: true
|
385
|
+
:brand_name: Elo
|
386
|
+
:mir:
|
387
|
+
:rules:
|
388
|
+
- :length:
|
389
|
+
- 16
|
390
|
+
:prefixes:
|
391
|
+
- '2200'
|
392
|
+
- '2201'
|
393
|
+
- '2202'
|
394
|
+
- '2203'
|
395
|
+
- '2204'
|
396
|
+
:discover:
|
397
|
+
:rules:
|
398
|
+
- :length:
|
399
|
+
- 16
|
400
|
+
- 19
|
401
|
+
:prefixes:
|
402
|
+
- '6011'
|
403
|
+
- '644'
|
404
|
+
- '645'
|
405
|
+
- '646'
|
406
|
+
- '647'
|
407
|
+
- '648'
|
408
|
+
- '649'
|
409
|
+
- '65'
|