credit_card_support 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development, :test do
4
+ gem "activemodel" # for rails validations
5
+ gem "rspec"
6
+ gem "yard"
7
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 mxrguspxrt
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,119 @@
1
+ CreditCardSupport
2
+ =================
3
+
4
+ Intro
5
+ -----
6
+
7
+ * Internationalization and localization support for validations
8
+ * At the moment knows AMEX, DinersClub, Discover, Enroute, Maestro, MasterCard, JCB, Solo, VISA (if you need more, create an issue and request for it :)
9
+
10
+
11
+ Basic usage
12
+ -----------
13
+
14
+ ```ruby
15
+ credit_card = CreditCardSupport.Instrument.new(
16
+ number: '4222222222222',
17
+ expire_year: 13,
18
+ expire_month: 11,
19
+ holder_name: 'A B' # optional!
20
+ verification: '1234' # optional!
21
+ )
22
+
23
+ credit_card.expired? # returns false
24
+ credit_card.expire_date # Date (last day of the month for expire month)
25
+ credit_card.issuer # VISA
26
+ credit_card.is_testcard? # true
27
+ ```
28
+
29
+ For better understanding read the source and RSpec.
30
+
31
+
32
+ Validations support
33
+ -------------------
34
+
35
+ ### Define
36
+
37
+ ##### ActiveRecord or Mongoid
38
+
39
+ ```ruby
40
+ class CreditCard < ActiveRecord::Base
41
+
42
+ attr_accessable :number, :expiry_year, :expiry_month
43
+
44
+ validates :number, credit_card: {
45
+ expiry_year: :expiry_year, # default: :expiry_year
46
+ expiry_month: :expiry_month, # default: :expiry_month
47
+ allowed_issuers: [:visa, :mastercard], # default: all known issuers
48
+ allow_testcards: !Rails.env.production? # default: False
49
+ }
50
+
51
+ end
52
+ ```
53
+
54
+ ##### Only ActiveModel
55
+
56
+ ```ruby
57
+ class CreditCard
58
+ extend ActiveModel::Naming
59
+ extend ActiveModel::Translation
60
+ include ActiveModel::Validations
61
+ include ActiveModel::Conversion
62
+
63
+ attr_accessor :number, :expiry_year, :expiry_month
64
+ attr_accessable :number, :expiry_year, :expiry_month
65
+
66
+ validates :number, credit_card: {
67
+ expiry_year: :expiry_year, # default: :expiry_year
68
+ expiry_month: :expiry_month, # default: :expiry_month
69
+ allowed_issuers: [:visa, :mastercard], # default: all known issuers
70
+ allow_testcards: !Rails.env.production? # default: False
71
+ }
72
+
73
+ end
74
+ ```
75
+
76
+ ### Use
77
+
78
+ After you have defined your CreditCard
79
+
80
+ ```ruby
81
+ today = Date.today
82
+ ```
83
+
84
+ ```ruby
85
+ # RAILS_ENV == 'test'
86
+ creditcard = CreditCard.new(number: '4000111111111115', expiry_year: today.year, expiry_month: today.month)
87
+ creditcard.valid? # True
88
+
89
+ # RAILS_ENV == 'production'
90
+ creditcard = CreditCard.new(number: '4000111111111115', expiry_year: today.year, expiry_month: today.month)
91
+ creditcard.valid? # False
92
+ creditcard.errors # {number: ["is a test number"]}
93
+
94
+ # RAILS_ENV == 'test'
95
+ creditcard = CreditCard.new(number: '3566003566003566', expiry_year: today.year-1, expiry_month: today.month)
96
+ creditcard.valid? # False
97
+ creditcard.errors # {number: ["is a JCB card number and is not supported", expiry_year: ["is in the past"], expiry_month: ["is in the past"]]}
98
+
99
+ ```
100
+
101
+
102
+ Problems?
103
+ =========
104
+
105
+ Create an issue or submit a patch.
106
+
107
+
108
+ Inspired by
109
+ ===========
110
+
111
+ * https://github.com/tobias/credit_card_validator
112
+ * https://github.com/pda/am_credit_card
113
+ * http://rubygems.org/gems/creditcard
114
+
115
+
116
+ Author
117
+ ======
118
+
119
+ Margus Pärt
@@ -0,0 +1,78 @@
1
+ raise "ActiveModel not included" unless Object.const_defined?(:ActiveModel)
2
+ I18n.load_path << File.dirname(__FILE__) + '/locale/en.yml'
3
+
4
+ class CreditCardValidator < ActiveModel::EachValidator
5
+
6
+ def validate_each(record, attribute, value)
7
+ @record = record
8
+
9
+ opts = options.dup
10
+ opts[:number] ||= attribute
11
+
12
+ number = record.send(opts[:number]||:number) rescue (raise ":number not defined")
13
+ expiry_year = record.send(opts[:expiry_year]||:expiry_year) rescue (raise ":expiry_year not defined")
14
+ expiry_month = record.send(opts[:expiry_month]||:expiry_month) rescue (raise ":expiry_month not defined")
15
+ @allowed_issuers = opts[:allowed_issuers]
16
+ @allow_testcards = opts[:allow_testcards]
17
+
18
+ @opts = opts
19
+ @instrument = CreditCardSupport::Instrument.new(number: number, expiry_year: expiry_year, expiry_month: expiry_month)
20
+
21
+ validate_number
22
+ end
23
+
24
+
25
+ private
26
+
27
+ def errors
28
+ @record.errors
29
+ end
30
+
31
+ def instrument
32
+ @instrument
33
+ end
34
+
35
+ def validate_number
36
+ validate_luhn
37
+ validate_issuer
38
+ validate_testcard
39
+ validate_expiry_year
40
+ validate_expiry_month
41
+ validate_expiration_date rescue false
42
+ end
43
+
44
+ def validate_luhn
45
+ errors.add(@opts[:number], t(:luhn_not_valid)) unless @instrument.has_valid_luhn?
46
+ end
47
+
48
+ def validate_issuer
49
+ errors.add(@opts[:number], t(:issuer_not_known)) unless @instrument.issuer
50
+ if @allowed_issuers and @instrument.issuer and !@allowed_issuers.map(&:to_sym).include?(@instrument.issuer)
51
+ errors.add(@opts[:number], t(:issuer_not_supported, issuer: @instrument.issuer.to_s.upcase))
52
+ end
53
+ end
54
+
55
+ def validate_testcard
56
+ errors.add(@opts[:number], t(:testcard_not_supported)) if !@allow_testcards && @instrument.is_testcard?
57
+ end
58
+
59
+ def validate_expiry_year
60
+ errors.add(@opts[:expiry_year], t(:cant_be_blank)) unless @instrument.expiry_year
61
+ end
62
+
63
+ def validate_expiry_month
64
+ errors.add(@opts[:expiry_month], t(:cant_be_blank)) unless @instrument.expiry_month
65
+ end
66
+
67
+ def validate_expiration_date
68
+ if @instrument.expired?
69
+ errors.add(@opts[:expiry_year], t(:cant_be_expired))
70
+ errors.add(@opts[:expiry_month], t(:cant_be_expired))
71
+ end
72
+ end
73
+
74
+ def t(code, *args)
75
+ I18n.t("credit_card_validator.messages.#{code}", *args)
76
+ end
77
+
78
+ end
@@ -0,0 +1,162 @@
1
+ module CreditCardSupport
2
+
3
+ # Usage:
4
+ #
5
+ # credit_card = CreditCardSupport.Instrument.new(
6
+ # number: '4222222222222',
7
+ # expiry_year: 13,
8
+ # expiry_month: 11,
9
+ # holder_name: 'A B'
10
+ # verification: '1234' # optional!
11
+ # )
12
+ #
13
+ # credit_card.expired? # returns false
14
+ # credit_card.expiration_date # Date (last day of the month for expiry month)
15
+ # credit_card.issuer # VISA
16
+ # credit_card.is_testcard? # true
17
+
18
+
19
+ class Instrument
20
+
21
+ NUMBERS = {
22
+ amex: /^3[47][0-9]{13}$/,
23
+ diners_club: /^3(?:0[0-5]|[68][0-9])[0-9]{11}$/,
24
+ discover: /^6(?:011|5[0-9]{2})[0-9]{12}$/,
25
+ enroute: /^2(014|149)\d{11}$/,
26
+ maestro: /(^6759[0-9]{2}([0-9]{10})$)|(^6759[0-9]{2}([0-9]{12})$)|(^6759[0-9]{2}([0-9]{13})$)/,
27
+ master_card: /^5[1-5][0-9]{14}$/,
28
+ jcb: /^35[0-9]{14}$/,
29
+ solo: /^6(3(34[5-9][0-9])|767[0-9]{2})\d{10}(\d{2,3})?$/,
30
+ visa: /^4[0-9]{12}(?:[0-9]{3})?$/
31
+ }.freeze
32
+
33
+ TESTCARD_NUMBERS = {
34
+ amex: [
35
+ '378282246310005',
36
+ '371449635398431',
37
+ '378734493671000',
38
+ '343434343434343',
39
+ '371144371144376',
40
+ '341134113411347'
41
+ ],
42
+ diners_club: [
43
+ '30569309025904',
44
+ '38520000023237',
45
+ '36438936438936'
46
+ ],
47
+ discover: [
48
+ '6011000990139424',
49
+ '6011111111111117',
50
+ '6011016011016011',
51
+ '6011000000000004',
52
+ '6011000995500000',
53
+ '6500000000000002'
54
+ ],
55
+ master_card: [
56
+ '5555555555554444',
57
+ '5105105105105100',
58
+ '5500005555555559',
59
+ '5555555555555557',
60
+ '5454545454545454',
61
+ '5555515555555551',
62
+ '5405222222222226',
63
+ '5478050000000007',
64
+ '5111005111051128',
65
+ '5112345112345114'
66
+ ],
67
+ visa: [
68
+ '4111111111111111',
69
+ '4012888888881881',
70
+ '4222222222222',
71
+ '4005519200000004',
72
+ '4009348888881881',
73
+ '4012000033330026',
74
+ '4012000077777777',
75
+ '4217651111111119',
76
+ '4500600000000061',
77
+ '4000111111111115'
78
+ ],
79
+ jcb: [
80
+ '3566003566003566',
81
+ '3528000000000007'
82
+ ]
83
+ }.freeze
84
+
85
+ def self.numbers
86
+ self::NUMBERS
87
+ end
88
+
89
+ def self.testcard_numbers
90
+ self::TESTCARD_NUMBERS
91
+ end
92
+
93
+ def self.determine_issuer(number)
94
+ numbers.each do |issuer, issuer_regexp|
95
+ return issuer if number.match(issuer_regexp)
96
+ end
97
+ nil
98
+ end
99
+
100
+ def self.is_testcard?(number)
101
+ testcard_numbers.values.flatten.include?(number)
102
+ end
103
+
104
+ def self.has_valid_luhn?(number)
105
+ CreditCardSupport::LuhnNumber.new(number).valid?
106
+ end
107
+
108
+ attr_accessor :number,
109
+ :expiry_year,
110
+ :expiry_month,
111
+ :holder_name,
112
+ :verification
113
+
114
+ attr_reader :issuer
115
+
116
+ def initialize(opts={}, &block)
117
+ self.number = opts[:number]
118
+ self.expiry_year = opts[:expiry_year]
119
+ self.expiry_month = opts[:expiry_month]
120
+ self.holder_name = opts[:holder_name]
121
+ self.verification = opts[:verification]
122
+
123
+ block.call(self) if block
124
+ end
125
+
126
+ def number=(number)
127
+ @number = number.to_s.gsub(/[^0-9]/, '')
128
+ @issuer = self.class.determine_issuer(self.number)
129
+ @is_testcard = self.class.is_testcard?(self.number)
130
+ @has_valid_luhn = self.class.has_valid_luhn?(self.number)
131
+ end
132
+
133
+ def expiry_year=(expiry_year)
134
+ @expiry_year = expiry_year.to_i if expiry_year
135
+ end
136
+
137
+ def expiry_month=(expiry_month)
138
+ @expiry_month = expiry_month.to_i if expiry_month
139
+ end
140
+
141
+ def expiry_year
142
+ (@expiry_year < 1900 ? 2000 + @expiry_year : @expiry_year) if @expiry_year
143
+ end
144
+
145
+ def expiration_date
146
+ Date.new(expiry_year, expiry_month, -1)
147
+ end
148
+
149
+ def expired?
150
+ expiration_date < Date.today
151
+ end
152
+
153
+ def is_testcard?
154
+ @is_testcard
155
+ end
156
+
157
+ def has_valid_luhn?
158
+ @has_valid_luhn
159
+ end
160
+
161
+ end
162
+ end
@@ -0,0 +1,9 @@
1
+ en:
2
+ credit_card_validator:
3
+ messages:
4
+ luhn_not_valid: "is not valid"
5
+ issuer_not_known: "not known issuer"
6
+ issuer_not_supported: "%{issuer} not supported"
7
+ testcard_not_supported: "testcards not supported"
8
+ cant_be_blank: "can't be blank"
9
+ is_expired: "is expired"
@@ -0,0 +1,44 @@
1
+ module CreditCardSupport
2
+
3
+ class LuhnNumber
4
+
5
+ attr_accessor :full_number
6
+
7
+ def initialize(full_number)
8
+ self.full_number = full_number
9
+ end
10
+
11
+ def full_number=(full_number)
12
+ @full_number = full_number.gsub(/[^0-9]/, '').to_s
13
+ end
14
+
15
+ def number_part
16
+ full_number[0, full_number.length-1] if full_number
17
+ end
18
+
19
+ def check_digit_part
20
+ full_number[-1, 1] if full_number
21
+ end
22
+
23
+ def valid?
24
+ return false unless full_number and full_number.length > 2
25
+ parts = full_number.split(//).map(&:to_i)
26
+
27
+ double = parts.length % 2 == 0
28
+ parts = parts.collect do |part|
29
+ number = double ? part*2 : part
30
+ double = !double
31
+ number
32
+ end
33
+
34
+ sum = parts.join("").split(//).map(&:to_i).inject(:+)
35
+ sum % 10 == 0
36
+ end
37
+
38
+ def invalid?
39
+ !valid?
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -0,0 +1,9 @@
1
+ module CreditCardSupport
2
+ module Version
3
+ MAJOR = 1
4
+ MINOR = 0
5
+ PATCH = 0
6
+
7
+ STRING = [MAJOR, MINOR, PATCH].compact.join('.')
8
+ end
9
+ end
@@ -0,0 +1,2 @@
1
+ require 'credit_card_support/luhn_number'
2
+ require 'credit_card_support/instrument'
@@ -0,0 +1,120 @@
1
+ require 'spec_helper'
2
+ require 'active_model'
3
+ require 'credit_card_support/credit_card_validator'
4
+
5
+
6
+ class CreditCard
7
+ extend ActiveModel::Naming
8
+ extend ActiveModel::Translation
9
+ include ActiveModel::Validations
10
+ include ActiveModel::Conversion
11
+
12
+ attr_accessor :number, :expiry_year, :expiry_month
13
+
14
+ def initialize(opts={})
15
+ @errors = ActiveModel::Errors.new(self)
16
+ opts.each do |k,v|
17
+ send(:"#{k}=", v)
18
+ end
19
+ end
20
+
21
+ end
22
+
23
+ class CreditCardTest < CreditCard
24
+ validates :number, credit_card: {
25
+ expiry_year: :expiry_year,
26
+ expiry_month: :expiry_month,
27
+ allowed_issuers: [:visa, :mastercard],
28
+ allow_testcards: true
29
+ }
30
+ end
31
+
32
+ class CreditCardProduction < CreditCard
33
+ validates :number, credit_card: {
34
+ expiry_year: :expiry_year,
35
+ expiry_month: :expiry_month,
36
+ allowed_issuers: [:visa, :mastercard],
37
+ allow_testcards: false
38
+ }
39
+ end
40
+
41
+
42
+
43
+ describe CreditCardValidator do
44
+ let(:today) { Date.today }
45
+
46
+ describe "validatable" do
47
+
48
+ subject { CreditCardTest.new(number: '4012888888881881', expiry_year: today.year, expiry_month: today.month) }
49
+
50
+ it "is valid" do
51
+ subject.should be_valid
52
+ end
53
+
54
+ describe "#number" do
55
+ it "must exist" do
56
+ subject.number = nil
57
+ subject.should_not be_valid
58
+ end
59
+ it "must be luhn" do
60
+ subject.number = '4000111111111116'
61
+ subject.should_not be_valid
62
+ end
63
+ context "production" do
64
+ subject { CreditCardProduction.new(number: '4485071359608368', expiry_year: today.year, expiry_month: today.month) }
65
+ context "testnumber" do
66
+ it "is invalid" do
67
+ subject.number = '4012888888881881'
68
+ subject.should be_invalid
69
+ end
70
+ end
71
+ context "valid number" do
72
+ it "is valid" do
73
+ subject.should be_valid
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ describe "#expiry_year" do
80
+ it "must exist" do
81
+ subject.expiry_year = nil
82
+ subject.should_not be_valid
83
+ end
84
+ context "in the future" do
85
+ it "is valid" do
86
+ subject.expiry_year = today.year + 1
87
+ subject.should be_valid
88
+ end
89
+ end
90
+ context "in the past" do
91
+ it "is invalid" do
92
+ subject.expiry_year = today.year - 1
93
+ subject.should be_invalid
94
+ end
95
+ end
96
+ end
97
+
98
+ describe "#expiry_month" do
99
+ it "must exist" do
100
+ subject.expiry_month = nil
101
+ subject.should_not be_valid
102
+ end
103
+ context "in the future" do
104
+ it "is valid" do
105
+ subject.expiry_month = today.month + 1
106
+ subject.should be_valid
107
+ end
108
+ end
109
+ context "in the past" do
110
+ it "is invalid" do
111
+ subject.expiry_month = today.month - 1
112
+ subject.should be_invalid
113
+ end
114
+ end
115
+ end
116
+
117
+ end
118
+
119
+ end
120
+
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+ require 'date'
3
+
4
+ describe CreditCardSupport::Instrument do
5
+ let(:today) { Date.today }
6
+ subject { described_class.new(number: 4222222222222, expiry_year: "#{today.year}", expiry_month: "12", holder_name: 'A B', verification: '1234') }
7
+
8
+ describe ".numbers" do
9
+ it "returns hash containing of issuer: number_regexp" do
10
+ described_class.numbers.should be_a(Hash)
11
+ end
12
+ end
13
+
14
+ describe ".testcard_numbers" do
15
+ it "returns hash containing of testcard numbers" do
16
+ described_class.testcard_numbers.should be_a(Hash)
17
+ end
18
+ end
19
+
20
+ describe ".determine_issuer" do
21
+ described_class.testcard_numbers.each do |issuer, numbers|
22
+ context "#{issuer}" do
23
+ it "knows the numbers" do
24
+ numbers.each do |number|
25
+ described_class.determine_issuer(number).should == issuer
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ describe ".is_testcard?" do
33
+ it "determines if number is testnumber" do
34
+ described_class.is_testcard?("4000111111111115").should be_true
35
+ described_class.is_testcard?("4000111111111116").should be_false
36
+ end
37
+ end
38
+
39
+ describe "#initialize" do
40
+ it "supports a hash" do
41
+ instrument = described_class.new(number: "123")
42
+ instrument.number.should == "123"
43
+ end
44
+ it "supports a block" do
45
+ instrument = described_class.new do |instrument_object|
46
+ instrument_object.number = "123"
47
+ end
48
+ instrument.number.should == "123"
49
+ end
50
+ end
51
+
52
+ describe "#number" do
53
+ it "returns number as a string" do
54
+ subject.number = "1-2-3-412345"
55
+ subject.number.should == "123412345"
56
+ end
57
+ end
58
+
59
+ describe "#expiry_year" do
60
+ it "returns expiry year as NNNN integer" do
61
+ subject.expiry_year = "#{today.year-2000}"
62
+ subject.expiry_year.should == today.year
63
+ end
64
+ end
65
+
66
+ describe "#expiry_month" do
67
+ it "returns expiry month as NN integer" do
68
+ subject.expiry_month = "#{today.month}"
69
+ subject.expiry_month.should == today.month
70
+ end
71
+ end
72
+
73
+ describe "#expiration_date" do
74
+ it "returns last day of the month when expiring" do
75
+ subject.expiration_date.should == Date.new(today.year, 12, 31)
76
+ end
77
+ end
78
+
79
+ describe "#expired?" do
80
+ context "not expired" do
81
+ it "returns false" do
82
+ subject.expired?.should be_false
83
+ end
84
+ end
85
+ context "expired" do
86
+ it "returns true" do
87
+ subject.expiry_year = today.year
88
+ subject.expiry_month = today.month-1
89
+ subject.expired?.should be_true
90
+ end
91
+ end
92
+ end
93
+
94
+ describe "#is_testcard?" do
95
+ context "not a testcard" do
96
+ it "returns false" do
97
+ subject.number = 4222222222223
98
+ subject.is_testcard?.should be_false
99
+ end
100
+ end
101
+ context "a testcard" do
102
+ it "returns true" do
103
+ subject.number = 4222222222222
104
+ subject.is_testcard?.should be_true
105
+ end
106
+ end
107
+ end
108
+
109
+ describe "has_valid_luhn?" do
110
+ context "valid luhn" do
111
+ it "returns true" do
112
+ subject.number = 4222222222222
113
+ subject.is_testcard?.should be_true
114
+ end
115
+ end
116
+ context "invalid luhn" do
117
+ it "returns false" do
118
+ subject.number = 4222222222223
119
+ subject.is_testcard?.should be_false
120
+ end
121
+ end
122
+ end
123
+
124
+
125
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ describe CreditCardSupport::LuhnNumber do
4
+
5
+ subject { described_class.new("4485071359608368") }
6
+
7
+ describe "#initialize" do
8
+ it "sets full_number" do
9
+ described_class.any_instance.should_receive(:full_number=).with('123')
10
+ described_class.new('123')
11
+ end
12
+ end
13
+
14
+ describe "#full_number" do
15
+ it "returns only number part of set value" do
16
+ subject.full_number = '123 abc-'
17
+ subject.full_number.should == '123'
18
+ end
19
+ end
20
+
21
+ describe "#number_part" do
22
+ it "returns number part of full_number" do
23
+ subject.number_part.should == "448507135960836"
24
+ end
25
+ end
26
+
27
+ describe "#check_digit_part" do
28
+ it "returns number part of full_number" do
29
+ subject.check_digit_part.should == "8"
30
+ end
31
+ end
32
+
33
+ describe "#valid?" do
34
+ it "returns if luhn algorytm % 10 is true" do
35
+ subject.should be_valid
36
+ end
37
+ end
38
+
39
+ describe "#valid?" do
40
+ context "valid luhn" do
41
+ it "returns true" do
42
+ described_class.new("5500005555555559").should be_valid
43
+ described_class.new("5555555555555557").should be_valid
44
+ described_class.new("5454545454545454").should be_valid
45
+ described_class.new("5555515555555551").should be_valid
46
+ described_class.new("5405222222222226").should be_valid
47
+ described_class.new("5478050000000007").should be_valid
48
+ end
49
+ end
50
+ context "invalid luhn" do
51
+ it "returns false" do
52
+ described_class.new("5500005555555558").should be_invalid
53
+ described_class.new("5555555555555556").should be_invalid
54
+ described_class.new("5454545454545453").should be_invalid
55
+ described_class.new("5555515555555550").should be_invalid
56
+ described_class.new("5405222222222225").should be_invalid
57
+ described_class.new("5478050000000006").should be_invalid
58
+ end
59
+ end
60
+ end
61
+
62
+ end
@@ -0,0 +1 @@
1
+ require 'credit_card_support'
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: credit_card_support
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Margus Pärt
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-21 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Detects issuer from number, has ActiveModel validations and translations
15
+ support.
16
+ email: margus@tione.eu
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - Gemfile
22
+ - README.md
23
+ - LICENSE.txt
24
+ - lib/credit_card_support/credit_card_validator.rb
25
+ - lib/credit_card_support/instrument.rb
26
+ - lib/credit_card_support/locale/en.yml
27
+ - lib/credit_card_support/luhn_number.rb
28
+ - lib/credit_card_support/version.rb
29
+ - lib/credit_card_support.rb
30
+ - spec/lib/credit_card_support/credit_card_validator_spec.rb
31
+ - spec/lib/credit_card_support/instrument_spec.rb
32
+ - spec/lib/credit_card_support/luhn_number_spec.rb
33
+ - spec/spec_helper.rb
34
+ homepage: https://github.com/tione/credit_card_support
35
+ licenses: []
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.9.3
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 1.8.24
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: Libary to handle CreditCard number, expiration and Luhn.
58
+ test_files: []
59
+ has_rdoc: