credit_card_validations 1.0.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.
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +75 -0
- data/Rakefile +13 -0
- data/credit_card_validations.gemspec +22 -0
- data/lib/credit_card_number_validator.rb +11 -0
- data/lib/credit_card_validations.rb +13 -0
- data/lib/credit_card_validations/card_rules.rb +58 -0
- data/lib/credit_card_validations/detector.rb +72 -0
- data/lib/credit_card_validations/luhn.rb +17 -0
- data/lib/credit_card_validations/version.rb +3 -0
- data/test/credit_card_validations_test.rb +92 -0
- metadata +82 -0
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,75 @@
|
|
1
|
+
# CreditCardValidations
|
2
|
+
|
3
|
+
Gem adds validator to check whether or not a given number actually falls within the ranges of possible numbers prior to performing such verification, and, as such, CreditCardValidations simply verifies that the credit card number provided is well-formed.
|
4
|
+
This is a port of Zend Framework `Zend\Validator\CreditCard` .
|
5
|
+
|
6
|
+
The following issuing institutes are accepted:
|
7
|
+
|
8
|
+
|
9
|
+
American Express
|
10
|
+
China UnionPay
|
11
|
+
Diners Club
|
12
|
+
Dinner Club US
|
13
|
+
Discover
|
14
|
+
JCB
|
15
|
+
Maestro
|
16
|
+
MasterCard
|
17
|
+
Solo
|
18
|
+
Visa
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
Also You can add your own rules to detect other credit card brands/types
|
23
|
+
passing name,length(integer/array of integers) and prefix(string/array of strings)
|
24
|
+
Example
|
25
|
+
|
26
|
+
CreditCardValidations::Detector.add_rule(:voyager, 15, '86')
|
27
|
+
voyager_test_card_number = '869926275400212'
|
28
|
+
CreditCardValidations::Detector.new(voyager_test_card_number).brand #:voyager
|
29
|
+
CreditCardValidations::Detector.new(voyager_test_card_number).voyager? #true
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
AvtiveModel support
|
34
|
+
|
35
|
+
only for certain brads
|
36
|
+
|
37
|
+
class CreditCardModel
|
38
|
+
attr_accessor :number
|
39
|
+
include ActiveModel::Validations
|
40
|
+
validates :number, presence: true, credit_card_number: {brands: [:amex, :maestro]}
|
41
|
+
end
|
42
|
+
|
43
|
+
for all known brands
|
44
|
+
|
45
|
+
validates :number, presence: true, credit_card_number: true
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
## Installation
|
52
|
+
|
53
|
+
Add this line to your application's Gemfile:
|
54
|
+
|
55
|
+
gem 'credit_card_validations'
|
56
|
+
|
57
|
+
And then execute:
|
58
|
+
|
59
|
+
$ bundle
|
60
|
+
|
61
|
+
Or install it yourself as:
|
62
|
+
|
63
|
+
$ gem install credit_card_validations
|
64
|
+
|
65
|
+
## Usage
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
## Contributing
|
70
|
+
|
71
|
+
1. Fork it
|
72
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
73
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
74
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
75
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'credit_card_validations/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "credit_card_validations"
|
8
|
+
gem.version = CreditCardValidations::VERSION
|
9
|
+
gem.authors = ["Igor"]
|
10
|
+
gem.email = ["fedoronchuk@gmail.com"]
|
11
|
+
gem.description = %q{A ruby gem for validating credit card numbers (a port of ZF2 Zend\Validator\CreditCard) —}
|
12
|
+
gem.summary = "gem for credit card numbers validation, card brands detections"
|
13
|
+
gem.homepage = "https://github.com/Fivell/credit_card_validations"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency('activemodel', '~> 3.0')
|
21
|
+
gem.add_dependency('activesupport', '~> 3.0')
|
22
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class CreditCardNumberValidator < ActiveModel::EachValidator
|
2
|
+
|
3
|
+
def validate_each(record, attribute, value)
|
4
|
+
brands = options.fetch(:brands)
|
5
|
+
record.errors[attribute] << (options[:message] || "is not valid") unless credit_card_valid?(value, Array.wrap(brands))
|
6
|
+
end
|
7
|
+
|
8
|
+
def credit_card_valid?(number, brands = [])
|
9
|
+
CreditCardValidations::Detector.new(number).valid?(*brands)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'credit_card_validations/version'
|
2
|
+
require 'active_model'
|
3
|
+
require 'active_support/all'
|
4
|
+
require 'active_model/validations'
|
5
|
+
require 'credit_card_number_validator'
|
6
|
+
|
7
|
+
module CreditCardValidations
|
8
|
+
extend ActiveSupport::Autoload
|
9
|
+
autoload :VERSION, 'credit_card_validations/version'
|
10
|
+
autoload :Luhn, 'credit_card_validations/luhn'
|
11
|
+
autoload :CardRules , 'credit_card_validations/card_rules'
|
12
|
+
autoload :Detector , 'credit_card_validations/detector'
|
13
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module CreditCardValidations
|
2
|
+
module CardRules
|
3
|
+
######## most used brands #########
|
4
|
+
|
5
|
+
|
6
|
+
VISA = [
|
7
|
+
{length: [16], prefixes: ['4']}
|
8
|
+
]
|
9
|
+
MASTERCARD = [
|
10
|
+
{length: [16], prefixes: ['51', '52', '53', '54', '55']}
|
11
|
+
]
|
12
|
+
######## other brands ########
|
13
|
+
AMEX = [
|
14
|
+
{length: [15], prefixes: ['34', '37']}
|
15
|
+
]
|
16
|
+
|
17
|
+
DINERS = [
|
18
|
+
{length: [14], prefixes: ['300', '301', '302', '303', '304', '305', '36']},
|
19
|
+
]
|
20
|
+
|
21
|
+
#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
|
22
|
+
DINERS_US = [
|
23
|
+
{length: [16], prefixes: ['54', '55']}
|
24
|
+
]
|
25
|
+
|
26
|
+
DISCOVER = [
|
27
|
+
{length: [16], prefixes: ['6011', '622126', '622127', '622128', '622129', '62213',
|
28
|
+
'62214', '62215', '62216', '62217', '62218', '62219',
|
29
|
+
'6222', '6223', '6224', '6225', '6226', '6227', '6228',
|
30
|
+
'62290', '62291', '622920', '622921', '622922', '622923',
|
31
|
+
'622924', '622925', '644', '645', '646', '647', '648',
|
32
|
+
'649', '65']}
|
33
|
+
]
|
34
|
+
|
35
|
+
JCB = [
|
36
|
+
{length: [16], prefixes: ['3528', '3529', '353', '354', '355', '356', '357', '358']}
|
37
|
+
]
|
38
|
+
|
39
|
+
|
40
|
+
LASER = [
|
41
|
+
{length: [16, 17, 18, 19], prefixes: ['6304', '6706', '6771', '6709']}
|
42
|
+
]
|
43
|
+
|
44
|
+
MAESTRO = [
|
45
|
+
{length: [12, 13, 14, 15, 16, 17, 18, 19], prefixes: ['5018', '5020', '5038', '6304', '6759', '6761', '6763']}
|
46
|
+
]
|
47
|
+
|
48
|
+
SOLO = [
|
49
|
+
{length: [16, 18, 19], prefixes: ['6334', '6767']}
|
50
|
+
]
|
51
|
+
|
52
|
+
UNIONPAY = [
|
53
|
+
{length: [16, 17, 18, 19], prefixes: ['620', '621', '623', '625', '626']}
|
54
|
+
]
|
55
|
+
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module CreditCardValidations
|
2
|
+
class Detector
|
3
|
+
class_attribute :rules
|
4
|
+
self.rules = {}
|
5
|
+
|
6
|
+
attr_reader :number
|
7
|
+
|
8
|
+
def initialize(number)
|
9
|
+
@number = number.to_s.delete(' ')
|
10
|
+
end
|
11
|
+
|
12
|
+
# credit card number
|
13
|
+
def valid?(*brands)
|
14
|
+
!!valid_number?(*brands)
|
15
|
+
end
|
16
|
+
|
17
|
+
#brand name
|
18
|
+
def brand
|
19
|
+
valid_number?
|
20
|
+
end
|
21
|
+
|
22
|
+
def valid_number?(*brands)
|
23
|
+
return nil unless valid_luhn?
|
24
|
+
number_length = number.length
|
25
|
+
brand_rules = brands.blank? ? self.rules : self.rules.slice(*brands.map{|el| el.downcase })
|
26
|
+
unless brand_rules.blank?
|
27
|
+
brand_rules.each do |brand_name, rules|
|
28
|
+
rules.each do |rule|
|
29
|
+
return brand_name if (rule[:length].include?(number_length) and number.match(rule[:regexp]))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
#check if luhn valid
|
37
|
+
def valid_luhn?
|
38
|
+
Luhn.valid?(number)
|
39
|
+
end
|
40
|
+
|
41
|
+
class << self
|
42
|
+
|
43
|
+
#create regexp by array of prefixes
|
44
|
+
def compile_regexp(prefixes)
|
45
|
+
Regexp.new("^((#{prefixes.join(")|(")}))")
|
46
|
+
end
|
47
|
+
|
48
|
+
#create rule for detecting brand
|
49
|
+
def add_rule(brand, length, prefixes)
|
50
|
+
prefixes = Array.wrap(prefixes)
|
51
|
+
length = Array.wrap(length)
|
52
|
+
rules[brand] = [] if rules[brand].blank?
|
53
|
+
rules[brand] << {length: length, regexp: compile_regexp(prefixes), prefixes: prefixes}
|
54
|
+
#create methods like visa? mastercard? etc
|
55
|
+
class_eval <<-BOOLEAN_RULE, __FILE__, __LINE__
|
56
|
+
def #{brand}?
|
57
|
+
valid?(:#{brand})
|
58
|
+
end
|
59
|
+
BOOLEAN_RULE
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
CardRules.constants.each do |const_name|
|
64
|
+
CardRules.const_get(const_name).each do |const_value|
|
65
|
+
self.add_rule(const_name.to_s.downcase.to_sym , const_value[:length], const_value[:prefixes])
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CreditCardValidations
|
2
|
+
class Luhn
|
3
|
+
def self.valid?(number)
|
4
|
+
s1 = s2 = 0
|
5
|
+
number.to_s.reverse.chars.each_slice(2) do |odd, even|
|
6
|
+
s1 += odd.to_i
|
7
|
+
|
8
|
+
double = even.to_i * 2
|
9
|
+
double -= 9 if double >= 10
|
10
|
+
s2 += double
|
11
|
+
end
|
12
|
+
(s1 + s2) % 10 == 0
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
|
3
|
+
unit_tests = File.expand_path("#{File.dirname(__FILE__)}/../test")
|
4
|
+
$:.unshift(lib)
|
5
|
+
$:.unshift(unit_tests)
|
6
|
+
|
7
|
+
require 'credit_card_validations'
|
8
|
+
|
9
|
+
class CreditCardValidationsTest < Test::Unit::TestCase
|
10
|
+
|
11
|
+
class CreditCardModel
|
12
|
+
attr_accessor :number
|
13
|
+
include ActiveModel::Validations
|
14
|
+
validates :number, presence: true, credit_card_number: {brands: [:amex, :maestro]}
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def initialize name
|
19
|
+
super name
|
20
|
+
@test_numbers = {
|
21
|
+
visa: '4012 8888 8888 1881',
|
22
|
+
mastercard: '5274 5763 9425 9961',
|
23
|
+
diners: '3020 4169 3226 43',
|
24
|
+
amex: '3400 0000 0000 009',
|
25
|
+
discover: '6011 1111 1111 1117',
|
26
|
+
maestro: '6759 6498 2643 8453',
|
27
|
+
jcb: '3575 7591 5225 4876',
|
28
|
+
solo: '6767 6222 2222 2222 222',
|
29
|
+
unionpay: '6264185212922132067',
|
30
|
+
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_card_brand_detection
|
35
|
+
@test_numbers.each do |key, value|
|
36
|
+
assert_equal key, detector(value).brand
|
37
|
+
assert detector(value).send("#{key}?")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_card_brand_is_nil_if_credit_card_invalid
|
42
|
+
assert_nil detector('1111111111111111').brand
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_card_valid_method
|
46
|
+
@test_numbers.each do |key, value|
|
47
|
+
assert detector(value).valid?(key)
|
48
|
+
assert detector(value).valid?
|
49
|
+
end
|
50
|
+
assert !detector('1111111111111111').valid?
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def test_card_particular_brand_valid
|
55
|
+
assert !detector(@test_numbers[:visa]).valid?(:mastercard)
|
56
|
+
assert !detector(@test_numbers[:mastercard]).valid?(:visa)
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
def test_card_particular_brands_valid
|
61
|
+
assert detector(@test_numbers[:visa]).valid?(:mastercard, :visa)
|
62
|
+
assert !detector(@test_numbers[:visa]).valid?(:mastercard, :amex)
|
63
|
+
end
|
64
|
+
|
65
|
+
#add rules which were not present before
|
66
|
+
def test_card_valid_after_rules_added
|
67
|
+
voyager_test_card_number = '869926275400212'
|
68
|
+
assert !detector(voyager_test_card_number).valid?
|
69
|
+
CreditCardValidations::Detector.add_rule(:voyager, 15, '86')
|
70
|
+
assert detector(voyager_test_card_number).valid?
|
71
|
+
assert_equal :voyager, detector(voyager_test_card_number).brand
|
72
|
+
assert detector(voyager_test_card_number).voyager?
|
73
|
+
assert !detector(voyager_test_card_number).visa?
|
74
|
+
assert !detector(voyager_test_card_number).mastercard?
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_active_model_validator
|
78
|
+
cc = CreditCardModel.new
|
79
|
+
cc.number = @test_numbers[:mastercard]
|
80
|
+
assert !cc.valid?
|
81
|
+
cc.number = @test_numbers[:amex]
|
82
|
+
assert cc.valid?
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
def detector(number)
|
88
|
+
CreditCardValidations::Detector.new(number)
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: credit_card_validations
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Igor
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-19 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activemodel
|
16
|
+
requirement: &70262684219780 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70262684219780
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: activesupport
|
27
|
+
requirement: &70262684219080 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '3.0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70262684219080
|
36
|
+
description: A ruby gem for validating credit card numbers (a port of ZF2 Zend\Validator\CreditCard)
|
37
|
+
—
|
38
|
+
email:
|
39
|
+
- fedoronchuk@gmail.com
|
40
|
+
executables: []
|
41
|
+
extensions: []
|
42
|
+
extra_rdoc_files: []
|
43
|
+
files:
|
44
|
+
- Gemfile
|
45
|
+
- LICENSE.txt
|
46
|
+
- README.md
|
47
|
+
- Rakefile
|
48
|
+
- credit_card_validations.gemspec
|
49
|
+
- lib/credit_card_number_validator.rb
|
50
|
+
- lib/credit_card_validations.rb
|
51
|
+
- lib/credit_card_validations/card_rules.rb
|
52
|
+
- lib/credit_card_validations/detector.rb
|
53
|
+
- lib/credit_card_validations/luhn.rb
|
54
|
+
- lib/credit_card_validations/version.rb
|
55
|
+
- test/credit_card_validations_test.rb
|
56
|
+
homepage: https://github.com/Fivell/credit_card_validations
|
57
|
+
licenses: []
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options: []
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ! '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ! '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 1.8.17
|
77
|
+
signing_key:
|
78
|
+
specification_version: 3
|
79
|
+
summary: gem for credit card numbers validation, card brands detections
|
80
|
+
test_files:
|
81
|
+
- test/credit_card_validations_test.rb
|
82
|
+
has_rdoc:
|