credit_card_detector 0.3

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,2 @@
1
+ StringLiterals:
2
+ EnforcedStyle: single_quotes
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
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+ group :test do
3
+ gem 'coveralls', require: false # Test coverage website. Go to https://coveralls.io
4
+ end
5
+ # Specify your gem's dependencies in credit_card_detector.gemspec
6
+ gemspec
7
+
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,4 @@
1
+ module CreditCardDetector
2
+ class Error < StandardError
3
+ end
4
+ 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,4 @@
1
+ module CreditCardDetector
2
+ VERSION = "0.3"
3
+ end
4
+
@@ -0,0 +1,6 @@
1
+ require "credit_card_detector/version"
2
+ require "credit_card_detector/error"
3
+ require "credit_card_detector/detector"
4
+
5
+ module CreditCardDetector
6
+ 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
@@ -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,6 @@
1
+ ---
2
+ - '1111111111111111'
3
+ - '00000000000000'
4
+ - fakenumber
5
+ - c4111111111111111
6
+ - 4111111111111111i
@@ -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
@@ -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