credit_card_detector 0.3
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 +25 -0
- data/Changelog.md +8 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +74 -0
- data/Rakefile +14 -0
- data/credit_card_detector.gemspec +24 -0
- data/lib/credit_card_detector/brand.rb +26 -0
- data/lib/credit_card_detector/data.rb +22 -0
- data/lib/credit_card_detector/detector.rb +51 -0
- data/lib/credit_card_detector/error.rb +4 -0
- data/lib/credit_card_detector/luhn.rb +18 -0
- data/lib/credit_card_detector/rule.rb +26 -0
- data/lib/credit_card_detector/version.rb +4 -0
- data/lib/credit_card_detector.rb +6 -0
- data/lib/data/brands.yaml +281 -0
- data/spec/brand_spec.rb +70 -0
- data/spec/detector_spec.rb +78 -0
- data/spec/fixtures/invalid_cards.yml +6 -0
- data/spec/fixtures/valid_cards.yml +115 -0
- data/spec/luhn_spec.rb +20 -0
- data/spec/rule_spec.rb +26 -0
- data/spec/test_helper.rb +9 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b275b2204494c66c28dfb5eb23925b6ab6afc37b
|
4
|
+
data.tar.gz: 8baec80bedec36a582aab73f65b6f552255459d2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8d7bc3057b56ff82481011092797d445d651f63bb3e04a81e551da38fa54157cc3740b5f5624bd233e721e5869d1a5bb46a92d28363f9fdf6155ff4fef501d91
|
7
|
+
data.tar.gz: 762b2d41da9ff1779d406f9c6672e1c73addfd302ca240303c7b78c173566ee0f1c531a3d1e98eeae7cf9560afa9a9015072d4694fb04be4dd8a7e5b05b4d31d
|
data/.hound.yml
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 1.9.3
|
4
|
+
- 2.0.0
|
5
|
+
- 2.1.9
|
6
|
+
- 2.2.5
|
7
|
+
- 2.3.1
|
8
|
+
- jruby-19mode # JRuby in 1.9 mode
|
9
|
+
gemfile:
|
10
|
+
- gemfiles/rails1-4.gemfile
|
11
|
+
- gemfiles/rails5.gemfile
|
12
|
+
matrix:
|
13
|
+
exclude:
|
14
|
+
- gemfile: gemfiles/rails5.gemfile
|
15
|
+
rvm: 1.9.3
|
16
|
+
- gemfile: gemfiles/rails5.gemfile
|
17
|
+
rvm: 2.0.0
|
18
|
+
- gemfile: gemfiles/rails5.gemfile
|
19
|
+
rvm: 2.1.9
|
20
|
+
- gemfile: gemfiles/rails5.gemfile
|
21
|
+
rvm: jruby-19mode
|
22
|
+
before_install:
|
23
|
+
- gem install bundler # use the very latest Bundler
|
24
|
+
- gem update --system
|
25
|
+
- gem --version
|
data/Changelog.md
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# 0.2
|
2
|
+
|
3
|
+
Refactor the source code completely to have cleaner objects that are easier to test.
|
4
|
+
|
5
|
+
# 0.1
|
6
|
+
|
7
|
+
Fork [credit_card_validations](https://github.com/Fivell/credit_card_validations) gem to have credit card validation and detector without any extra dependency (specifically, ActiveModel).
|
8
|
+
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Igor Fedoronchuk
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# CreditCardDetector
|
2
|
+
|
3
|
+
Gem provides credit card number validator and type detector.
|
4
|
+
|
5
|
+
It checks whether or not a given number actually falls within the ranges of possible numbers for given brands and provides an optional Luhn check.
|
6
|
+
|
7
|
+
More info about card BIN numbers http://en.wikipedia.org/wiki/Bank_card_number
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'credit_card_detector'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install credit_card_detector
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
|
26
|
+
The following issuing institutes are accepted:
|
27
|
+
|
28
|
+
Name | Key |
|
29
|
+
--------------------- | ------------|
|
30
|
+
[American Express](http://en.wikipedia.org/wiki/American_Express) | :amex
|
31
|
+
[China UnionPay](http://en.wikipedia.org/wiki/China_UnionPay) | :unionpay
|
32
|
+
[Dankort](http://en.wikipedia.org/wiki/Dankort) | :dankort
|
33
|
+
[Diners Club](http://en.wikipedia.org/wiki/Diners_Club_International) | :diners
|
34
|
+
[Elo](https://pt.wikipedia.org/wiki/Elo_Participa%C3%A7%C3%B5es_S/A) | :elo
|
35
|
+
[Discover](http://en.wikipedia.org/wiki/Discover_Card) | :discover
|
36
|
+
[Hipercard](http://pt.wikipedia.org/wiki/Hipercard) | :hipercard
|
37
|
+
[JCB](http://en.wikipedia.org/wiki/Japan_Credit_Bureau) | :jcb
|
38
|
+
[Maestro](http://en.wikipedia.org/wiki/Maestro_%28debit_card%29) | :maestro
|
39
|
+
[MasterCard](http://en.wikipedia.org/wiki/MasterCard) | :mastercard
|
40
|
+
[Rupay](http://en.wikipedia.org/wiki/RuPay) | :rupay
|
41
|
+
[Solo](http://en.wikipedia.org/wiki/Solo_(debit_card)) | :solo
|
42
|
+
[Switch](http://en.wikipedia.org/wiki/Switch_(debit_card)) | :switch
|
43
|
+
[Visa](http://en.wikipedia.org/wiki/Visa_Inc.) | :visa
|
44
|
+
|
45
|
+
|
46
|
+
### Examples
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
number = "4111111111111111"
|
50
|
+
detector = CreditCardDetector::Detector.new(number)
|
51
|
+
detector.brand # Visa brand
|
52
|
+
detector.brand_name # Visa
|
53
|
+
detector.valid?(:mastercard, :maestro) #false
|
54
|
+
detector.valid?(:visa, :mastercard) #true
|
55
|
+
```
|
56
|
+
|
57
|
+
### Check luhn
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
CreditCardDetector::Detector.new(number).valid_luhn?
|
61
|
+
```
|
62
|
+
|
63
|
+
## Contributing
|
64
|
+
|
65
|
+
1. Fork it
|
66
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
67
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
68
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
69
|
+
5. Create new Pull Request
|
70
|
+
|
71
|
+
## Original version
|
72
|
+
|
73
|
+
[credit_card_validations](https://github.com/Fivell/credit_card_validations) - provides more extended API, but dependes on activemodel and activesupport.
|
74
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
require "rake"
|
4
|
+
require "rake/testtask"
|
5
|
+
|
6
|
+
task default: [:specs]
|
7
|
+
|
8
|
+
desc "Run basic specs"
|
9
|
+
Rake::TestTask.new("specs") { |t|
|
10
|
+
t.pattern = "spec/*_spec.rb"
|
11
|
+
t.verbose = true
|
12
|
+
t.warning = true
|
13
|
+
t.libs << "lib" << "spec"
|
14
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
|
4
|
+
require "credit_card_detector/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "credit_card_detector"
|
8
|
+
gem.version = CreditCardDetector::VERSION
|
9
|
+
gem.authors = ["Andrew Volozhanin", "Igor"]
|
10
|
+
gem.email = ["scarfacedeb@gmail.com", "fedoronchuk@gmail.com"]
|
11
|
+
gem.description = %q{A ruby gem for validating credit card numbers}
|
12
|
+
gem.summary = "Gem provides basic credit card number validation and type detection without any extra dependencies"
|
13
|
+
gem.homepage = ""
|
14
|
+
gem.license = "MIT"
|
15
|
+
|
16
|
+
gem.files = `git ls-files`.split($/)
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
|
21
|
+
gem.add_development_dependency "rake", "~> 10.5"
|
22
|
+
gem.add_development_dependency "minitest", "~> 5.9"
|
23
|
+
gem.add_development_dependency "pry"
|
24
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "credit_card_detector/rule"
|
2
|
+
|
3
|
+
module CreditCardDetector
|
4
|
+
class Brand
|
5
|
+
attr_reader :id, :name, :options
|
6
|
+
|
7
|
+
def initialize(id, data)
|
8
|
+
@id = id
|
9
|
+
@name = data.fetch(:name)
|
10
|
+
@raw_rules = data.fetch(:rules)
|
11
|
+
@options = data.fetch(:options, {})
|
12
|
+
end
|
13
|
+
|
14
|
+
def matches?(number)
|
15
|
+
rules.any? { |rule| rule.matches? number }
|
16
|
+
end
|
17
|
+
|
18
|
+
def luhn?
|
19
|
+
!@options.fetch(:skip_luhn, false)
|
20
|
+
end
|
21
|
+
|
22
|
+
def rules
|
23
|
+
@rules ||= @raw_rules.map { |rule| Rule.new rule }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "credit_card_detector/brand"
|
3
|
+
|
4
|
+
module CreditCardDetector
|
5
|
+
module Data
|
6
|
+
DATA = File.expand_path("../../data/brands.yaml", __FILE__)
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def brands
|
10
|
+
@data ||= raw_data.map { |key, data| Brand.new key, data }
|
11
|
+
end
|
12
|
+
|
13
|
+
def ids
|
14
|
+
@ids ||= brands.map(&:id)
|
15
|
+
end
|
16
|
+
|
17
|
+
def raw_data
|
18
|
+
YAML.load_file DATA
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "credit_card_detector/data"
|
2
|
+
require "credit_card_detector/luhn"
|
3
|
+
|
4
|
+
module CreditCardDetector
|
5
|
+
class Detector
|
6
|
+
def initialize(number)
|
7
|
+
@number = number.to_s.tr('- ', '')
|
8
|
+
end
|
9
|
+
|
10
|
+
def valid?(*brands)
|
11
|
+
brands.compact!
|
12
|
+
|
13
|
+
if brands.empty?
|
14
|
+
!brand.nil?
|
15
|
+
else
|
16
|
+
validate_brands(brands)
|
17
|
+
brands.include?(brand.id)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def valid_luhn?
|
22
|
+
@valid_luhn ||= Luhn.valid?(@number)
|
23
|
+
end
|
24
|
+
|
25
|
+
def brand
|
26
|
+
@brand ||= Data.brands.find { |brand| matches_brand? brand }
|
27
|
+
end
|
28
|
+
|
29
|
+
def brand_id
|
30
|
+
brand.id if brand
|
31
|
+
end
|
32
|
+
|
33
|
+
def brand_name
|
34
|
+
brand.name if brand
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
def matches_brand?(brand)
|
40
|
+
return if brand.luhn? && !valid_luhn?
|
41
|
+
brand.matches?(@number)
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate_brands(brands)
|
45
|
+
brands.each do |key|
|
46
|
+
next if Data.ids.include?(key)
|
47
|
+
fail Error, "brand #{key} is undefined"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module CreditCardDetector
|
2
|
+
# Class to validate Luhn numbers.
|
3
|
+
class Luhn
|
4
|
+
def self.valid?(number)
|
5
|
+
s1 = s2 = 0
|
6
|
+
|
7
|
+
number.to_s.reverse.chars.each_slice(2) do |odd, even|
|
8
|
+
s1 += odd.to_i
|
9
|
+
|
10
|
+
double = even.to_i * 2
|
11
|
+
double -= 9 if double >= 10
|
12
|
+
s2 += double
|
13
|
+
end
|
14
|
+
|
15
|
+
(s1 + s2) % 10 == 0
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module CreditCardDetector
|
2
|
+
class Rule
|
3
|
+
def initialize(length:, prefixes:)
|
4
|
+
@length = length
|
5
|
+
@regexp = compile_regexp(prefixes)
|
6
|
+
end
|
7
|
+
|
8
|
+
def matches?(number)
|
9
|
+
matches_length?(number.to_s) && matches_prefix?(number.to_s)
|
10
|
+
end
|
11
|
+
|
12
|
+
def matches_length?(number)
|
13
|
+
@length.include? number.size
|
14
|
+
end
|
15
|
+
|
16
|
+
def matches_prefix?(number)
|
17
|
+
!!(number =~ @regexp)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def compile_regexp(prefixes)
|
23
|
+
Regexp.new("^(#{prefixes.join("|")})")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,281 @@
|
|
1
|
+
---
|
2
|
+
:visa:
|
3
|
+
:name: Visa
|
4
|
+
:rules:
|
5
|
+
- :length:
|
6
|
+
- 13
|
7
|
+
- 16
|
8
|
+
:prefixes:
|
9
|
+
- '4'
|
10
|
+
:mastercard:
|
11
|
+
:name: MasterCard
|
12
|
+
:rules:
|
13
|
+
- :length:
|
14
|
+
- 16
|
15
|
+
:prefixes:
|
16
|
+
- '2221'
|
17
|
+
- '2222'
|
18
|
+
- '2223'
|
19
|
+
- '2224'
|
20
|
+
- '2225'
|
21
|
+
- '2226'
|
22
|
+
- '2227'
|
23
|
+
- '2228'
|
24
|
+
- '2229'
|
25
|
+
- '223'
|
26
|
+
- '224'
|
27
|
+
- '225'
|
28
|
+
- '226'
|
29
|
+
- '227'
|
30
|
+
- '228'
|
31
|
+
- '229'
|
32
|
+
- '23'
|
33
|
+
- '24'
|
34
|
+
- '25'
|
35
|
+
- '26'
|
36
|
+
- '271'
|
37
|
+
- '2720'
|
38
|
+
- '51'
|
39
|
+
- '52'
|
40
|
+
- '53'
|
41
|
+
- '54'
|
42
|
+
- '55'
|
43
|
+
:amex:
|
44
|
+
:name: American Express
|
45
|
+
:rules:
|
46
|
+
- :length:
|
47
|
+
- 15
|
48
|
+
:prefixes:
|
49
|
+
- '34'
|
50
|
+
- '37'
|
51
|
+
:diners:
|
52
|
+
:name: Diners Club
|
53
|
+
:rules:
|
54
|
+
- :length:
|
55
|
+
- 14
|
56
|
+
:prefixes:
|
57
|
+
- '300'
|
58
|
+
- '301'
|
59
|
+
- '302'
|
60
|
+
- '303'
|
61
|
+
- '304'
|
62
|
+
- '305'
|
63
|
+
- '36'
|
64
|
+
- '38'
|
65
|
+
:discover:
|
66
|
+
:name: Discover
|
67
|
+
:rules:
|
68
|
+
- :length:
|
69
|
+
- 16
|
70
|
+
:prefixes:
|
71
|
+
- '6011'
|
72
|
+
- '644'
|
73
|
+
- '645'
|
74
|
+
- '646'
|
75
|
+
- '647'
|
76
|
+
- '648'
|
77
|
+
- '649'
|
78
|
+
- '65'
|
79
|
+
:jcb:
|
80
|
+
:name: JCB
|
81
|
+
:rules:
|
82
|
+
- :length:
|
83
|
+
- 15
|
84
|
+
- 16
|
85
|
+
:prefixes:
|
86
|
+
- '3528'
|
87
|
+
- '3529'
|
88
|
+
- '353'
|
89
|
+
- '354'
|
90
|
+
- '355'
|
91
|
+
- '356'
|
92
|
+
- '357'
|
93
|
+
- '358'
|
94
|
+
- :length:
|
95
|
+
- 15
|
96
|
+
:prefixes:
|
97
|
+
- '1800'
|
98
|
+
- '2131'
|
99
|
+
- :length:
|
100
|
+
- 19
|
101
|
+
:prefixes:
|
102
|
+
- '357266'
|
103
|
+
:solo:
|
104
|
+
:name: Solo
|
105
|
+
:rules:
|
106
|
+
- :length:
|
107
|
+
- 16
|
108
|
+
- 18
|
109
|
+
- 19
|
110
|
+
:prefixes:
|
111
|
+
- '6334'
|
112
|
+
- '6767'
|
113
|
+
:switch:
|
114
|
+
:name: Switch
|
115
|
+
:rules:
|
116
|
+
- :length:
|
117
|
+
- 16
|
118
|
+
- 18
|
119
|
+
- 19
|
120
|
+
:prefixes:
|
121
|
+
- '633110'
|
122
|
+
- '633312'
|
123
|
+
- '633304'
|
124
|
+
- '633303'
|
125
|
+
- '633301'
|
126
|
+
- '633300'
|
127
|
+
:maestro:
|
128
|
+
:name: Maestro
|
129
|
+
:rules:
|
130
|
+
- :length:
|
131
|
+
- 12
|
132
|
+
- 13
|
133
|
+
- 14
|
134
|
+
- 15
|
135
|
+
- 16
|
136
|
+
- 17
|
137
|
+
- 18
|
138
|
+
- 19
|
139
|
+
:prefixes:
|
140
|
+
- '500'
|
141
|
+
- '5010'
|
142
|
+
- '5011'
|
143
|
+
- '5012'
|
144
|
+
- '5013'
|
145
|
+
- '5014'
|
146
|
+
- '5015'
|
147
|
+
- '5016'
|
148
|
+
- '5017'
|
149
|
+
- '5018'
|
150
|
+
- '502'
|
151
|
+
- '503'
|
152
|
+
- '504'
|
153
|
+
- '505'
|
154
|
+
- '506'
|
155
|
+
- '507'
|
156
|
+
- '508'
|
157
|
+
- '509'
|
158
|
+
- '56'
|
159
|
+
- '57'
|
160
|
+
- '58'
|
161
|
+
- '59'
|
162
|
+
- '6010'
|
163
|
+
- '6012'
|
164
|
+
- '6013'
|
165
|
+
- '6014'
|
166
|
+
- '6015'
|
167
|
+
- '6016'
|
168
|
+
- '6017'
|
169
|
+
- '6018'
|
170
|
+
- '6019'
|
171
|
+
- '602'
|
172
|
+
- '603'
|
173
|
+
- '604'
|
174
|
+
- '605'
|
175
|
+
- '6060'
|
176
|
+
- '621'
|
177
|
+
- '627'
|
178
|
+
- '629'
|
179
|
+
- '6304'
|
180
|
+
- '6390'
|
181
|
+
- '670'
|
182
|
+
- '671'
|
183
|
+
- '672'
|
184
|
+
- '673'
|
185
|
+
- '674'
|
186
|
+
- '675'
|
187
|
+
- '677'
|
188
|
+
- '6760'
|
189
|
+
- '6761'
|
190
|
+
- '6762'
|
191
|
+
- '6763'
|
192
|
+
- '6764'
|
193
|
+
- '6765'
|
194
|
+
- '6766'
|
195
|
+
- '6768'
|
196
|
+
- '6769'
|
197
|
+
- '6771'
|
198
|
+
- '679'
|
199
|
+
:unionpay:
|
200
|
+
:name: China UnionPay
|
201
|
+
:rules:
|
202
|
+
- :length:
|
203
|
+
- 16
|
204
|
+
- 17
|
205
|
+
- 18
|
206
|
+
- 19
|
207
|
+
:prefixes:
|
208
|
+
- '622'
|
209
|
+
- '624'
|
210
|
+
- '625'
|
211
|
+
- '626'
|
212
|
+
- '628'
|
213
|
+
:options:
|
214
|
+
:skip_luhn: true
|
215
|
+
:dankort:
|
216
|
+
:name: Dankort
|
217
|
+
:rules:
|
218
|
+
- :length:
|
219
|
+
- 16
|
220
|
+
:prefixes:
|
221
|
+
- '5019'
|
222
|
+
:rupay:
|
223
|
+
:name: Rupay
|
224
|
+
:rules:
|
225
|
+
- :length:
|
226
|
+
- 16
|
227
|
+
:prefixes:
|
228
|
+
- '6061'
|
229
|
+
- '6062'
|
230
|
+
- '6063'
|
231
|
+
- '6064'
|
232
|
+
- '6065'
|
233
|
+
- '6066'
|
234
|
+
- '6067'
|
235
|
+
- '6068'
|
236
|
+
- '6069'
|
237
|
+
- '607'
|
238
|
+
- '608'
|
239
|
+
:options:
|
240
|
+
:skip_luhn: true
|
241
|
+
:hipercard:
|
242
|
+
:name: Hipercard
|
243
|
+
:rules:
|
244
|
+
- :length:
|
245
|
+
- 19
|
246
|
+
:prefixes:
|
247
|
+
- '384'
|
248
|
+
:elo:
|
249
|
+
:name: Elo
|
250
|
+
:rules:
|
251
|
+
- :length:
|
252
|
+
- 16
|
253
|
+
:prefixes:
|
254
|
+
- '4011'
|
255
|
+
- '438935'
|
256
|
+
- '451416'
|
257
|
+
- '4576'
|
258
|
+
- '504175'
|
259
|
+
- '506699'
|
260
|
+
- '5067'
|
261
|
+
- '509040'
|
262
|
+
- '509042'
|
263
|
+
- '509043'
|
264
|
+
- '509045'
|
265
|
+
- '509046'
|
266
|
+
- '509047'
|
267
|
+
- '509048'
|
268
|
+
- '509049'
|
269
|
+
- '509050'
|
270
|
+
- '509051'
|
271
|
+
- '509052'
|
272
|
+
- '509064'
|
273
|
+
- '509066'
|
274
|
+
- '509067'
|
275
|
+
- '509068'
|
276
|
+
- '509069'
|
277
|
+
- '509074'
|
278
|
+
- '636297'
|
279
|
+
- '63636'
|
280
|
+
:options:
|
281
|
+
:skip_luhn: true
|
data/spec/brand_spec.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "credit_card_detector/brand"
|
3
|
+
|
4
|
+
module CreditCardDetector
|
5
|
+
describe Brand do
|
6
|
+
describe "#matches?" do
|
7
|
+
let(:data) { { name: "Visa", rules: rules, options: {} } }
|
8
|
+
let(:brand) { Brand.new(:visa, data) }
|
9
|
+
|
10
|
+
describe "when brand doesn't have any rules" do
|
11
|
+
let(:rules) { [] }
|
12
|
+
it "doesn't matches any number" do
|
13
|
+
assert_equal brand.matches?("1234"), false
|
14
|
+
assert_equal brand.matches?(""), false
|
15
|
+
assert_equal brand.matches?("4012888888881881"), false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "when brand has a single rule" do
|
20
|
+
let(:rules) {
|
21
|
+
[ { length: [16, 13], prefixes: ['4'] } ]
|
22
|
+
}
|
23
|
+
|
24
|
+
it "matches valid numbers" do
|
25
|
+
numbers = VALID_NUMBERS.fetch(:visa)
|
26
|
+
numbers.each do |number|
|
27
|
+
assert_equal brand.matches?(number.tr("- ", "")), true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "doesn't match invalid numbers" do
|
32
|
+
assert_equal brand.matches?("42222222222221"), false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "when brand has multiple rules" do
|
37
|
+
let(:rules) {
|
38
|
+
[
|
39
|
+
{ length: [15, 16], prefixes: ['357', '356', '353'] },
|
40
|
+
{ length: [15], prefixes: ['1800'] },
|
41
|
+
{ length: [19], prefixes: ['357266'] }
|
42
|
+
]
|
43
|
+
}
|
44
|
+
|
45
|
+
it "matches valid numbers" do
|
46
|
+
numbers = VALID_NUMBERS.fetch(:jcb)
|
47
|
+
numbers.each do |number|
|
48
|
+
assert_equal brand.matches?(number.tr("- ", "")), true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it "doesn't match invalid numbers" do
|
53
|
+
assert_equal brand.matches?("1800016382773921"), false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#luhn?" do
|
59
|
+
it "returns true when NO skip_luhn option is set on brand" do
|
60
|
+
brand = Brand.new(:maestro, name: "maestro", options: {}, rules: [])
|
61
|
+
assert_equal brand.luhn?, true
|
62
|
+
end
|
63
|
+
|
64
|
+
it "returns false when skip_luhn option is set on brand" do
|
65
|
+
brand = Brand.new(:maestro, name: "rupay", options: { skip_luhn: true }, rules: [])
|
66
|
+
assert_equal brand.luhn?, false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "credit_card_detector/detector"
|
3
|
+
|
4
|
+
module CreditCardDetector
|
5
|
+
describe Detector do
|
6
|
+
describe "#valid?" do
|
7
|
+
def perform(numbers, expected, *brands)
|
8
|
+
numbers.each do |number|
|
9
|
+
valid = Detector.new(number).valid?(*brands)
|
10
|
+
assert_equal valid, expected, number
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it "validates valid numbers against all of the available brands" do
|
15
|
+
VALID_NUMBERS.each do |_, numbers|
|
16
|
+
perform numbers, true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "validates invalid numbers against all of the available brands" do
|
21
|
+
perform INVALID_NUMBERS, false
|
22
|
+
end
|
23
|
+
|
24
|
+
it "returns true when numbers belong to the given brand" do
|
25
|
+
visa = VALID_NUMBERS.fetch(:visa)
|
26
|
+
perform visa, true, :visa
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns false when numbers belong to another brand" do
|
30
|
+
mastercard = VALID_NUMBERS.fetch(:mastercard)
|
31
|
+
perform mastercard, false, :visa
|
32
|
+
end
|
33
|
+
|
34
|
+
it "validates number against multiple brands" do
|
35
|
+
visa = VALID_NUMBERS.fetch(:visa)
|
36
|
+
mastercard = VALID_NUMBERS.fetch(:mastercard)
|
37
|
+
maestro = VALID_NUMBERS.fetch(:maestro)
|
38
|
+
|
39
|
+
perform (visa + maestro), true, :visa, :maestro, :jcb
|
40
|
+
perform mastercard, false, :visa, :rupay
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "when brand doesn't support luhn check" do
|
44
|
+
it "returns true when number fails luhn check" do
|
45
|
+
unionpay = VALID_NUMBERS.fetch(:unionpay)
|
46
|
+
perform unionpay, true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#brand" do
|
52
|
+
it "returns matched brand for valid number" do
|
53
|
+
detector = Detector.new("4012888888881881")
|
54
|
+
assert_instance_of Brand, detector.brand
|
55
|
+
assert_equal detector.brand.id, :visa
|
56
|
+
end
|
57
|
+
|
58
|
+
it "returns nil when number is invalid" do
|
59
|
+
detector = Detector.new("40128888")
|
60
|
+
assert_equal detector.brand, nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#brand_id" do
|
65
|
+
it "returns brand id" do
|
66
|
+
detector = Detector.new("4012888888881881")
|
67
|
+
assert_equal detector.brand_id, :visa
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#brand_name" do
|
72
|
+
it "returns brand name" do
|
73
|
+
detector = Detector.new("4012888888881881")
|
74
|
+
assert_equal detector.brand_name, "Visa"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,115 @@
|
|
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 4141 3850 9070'
|
91
|
+
- '4389 3555 4728 7502'
|
92
|
+
- '4514 1620 5642 0699'
|
93
|
+
- '4576 5598 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'
|
data/spec/luhn_spec.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "credit_card_detector/luhn"
|
3
|
+
|
4
|
+
module CreditCardDetector
|
5
|
+
describe Luhn do
|
6
|
+
describe ".valid?" do
|
7
|
+
it "returns true when number is valid" do
|
8
|
+
LUHN_ENABLED.each do |brand, card_numbers|
|
9
|
+
card_numbers.each do |number|
|
10
|
+
assert_equal Luhn.valid?(number.to_s.tr("- ", "")), true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns false when number is invalid" do
|
16
|
+
assert_equal Luhn.valid?("4111111111111110"), false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/spec/rule_spec.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "credit_card_detector/rule"
|
3
|
+
|
4
|
+
module CreditCardDetector
|
5
|
+
describe Rule do
|
6
|
+
describe "#matches?" do
|
7
|
+
let(:rule) { Rule.new(length: [8, 9], prefixes: [1234, 2345]) }
|
8
|
+
|
9
|
+
it "matches when number has correct length AND starts from any prefix" do
|
10
|
+
assert_equal rule.matches?(12345678), true
|
11
|
+
assert_equal rule.matches?(234567890), true
|
12
|
+
end
|
13
|
+
|
14
|
+
it "doesn't match when number has incorrect length" do
|
15
|
+
assert_equal rule.matches?(123), false
|
16
|
+
assert_equal rule.matches?(""), false
|
17
|
+
assert_equal rule.matches?(1234567890123), false
|
18
|
+
end
|
19
|
+
|
20
|
+
it "doesn't match when number doesn't start from any prefix" do
|
21
|
+
assert_equal rule.matches?(01234567), false
|
22
|
+
assert_equal rule.matches?(00000000), false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/spec/test_helper.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require "minitest/autorun"
|
2
|
+
require "credit_card_detector"
|
3
|
+
|
4
|
+
VALID_NUMBERS = YAML.load_file File.join(File.dirname(__FILE__), "fixtures/valid_cards.yml")
|
5
|
+
INVALID_NUMBERS = YAML.load_file File.join(File.dirname(__FILE__), "fixtures/invalid_cards.yml")
|
6
|
+
|
7
|
+
SKIP_LUHN = [:rupay, :elo, :unionpay]
|
8
|
+
LUHN_ENABLED = VALID_NUMBERS.reject { |k, _| SKIP_LUHN.include?(k) }
|
9
|
+
LUHN_DISABLED = VALID_NUMBERS.select { |k, _| SKIP_LUHN.include?(k) }
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: credit_card_detector
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.3'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrew Volozhanin
|
8
|
+
- Igor
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2016-09-20 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '10.5'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '10.5'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: minitest
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '5.9'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '5.9'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: pry
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
description: A ruby gem for validating credit card numbers
|
57
|
+
email:
|
58
|
+
- scarfacedeb@gmail.com
|
59
|
+
- fedoronchuk@gmail.com
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- ".hound.yml"
|
65
|
+
- ".travis.yml"
|
66
|
+
- Changelog.md
|
67
|
+
- Gemfile
|
68
|
+
- LICENSE.txt
|
69
|
+
- README.md
|
70
|
+
- Rakefile
|
71
|
+
- credit_card_detector.gemspec
|
72
|
+
- lib/credit_card_detector.rb
|
73
|
+
- lib/credit_card_detector/brand.rb
|
74
|
+
- lib/credit_card_detector/data.rb
|
75
|
+
- lib/credit_card_detector/detector.rb
|
76
|
+
- lib/credit_card_detector/error.rb
|
77
|
+
- lib/credit_card_detector/luhn.rb
|
78
|
+
- lib/credit_card_detector/rule.rb
|
79
|
+
- lib/credit_card_detector/version.rb
|
80
|
+
- lib/data/brands.yaml
|
81
|
+
- spec/brand_spec.rb
|
82
|
+
- spec/detector_spec.rb
|
83
|
+
- spec/fixtures/invalid_cards.yml
|
84
|
+
- spec/fixtures/valid_cards.yml
|
85
|
+
- spec/luhn_spec.rb
|
86
|
+
- spec/rule_spec.rb
|
87
|
+
- spec/test_helper.rb
|
88
|
+
homepage: ''
|
89
|
+
licenses:
|
90
|
+
- MIT
|
91
|
+
metadata: {}
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 2.5.1
|
109
|
+
signing_key:
|
110
|
+
specification_version: 4
|
111
|
+
summary: Gem provides basic credit card number validation and type detection without
|
112
|
+
any extra dependencies
|
113
|
+
test_files:
|
114
|
+
- spec/brand_spec.rb
|
115
|
+
- spec/detector_spec.rb
|
116
|
+
- spec/fixtures/invalid_cards.yml
|
117
|
+
- spec/fixtures/valid_cards.yml
|
118
|
+
- spec/luhn_spec.rb
|
119
|
+
- spec/rule_spec.rb
|
120
|
+
- spec/test_helper.rb
|