credit_officer 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +26 -3
- data/VERSION +1 -1
- data/credit_officer.gemspec +109 -0
- data/lib/credit_officer/credit_card.rb +37 -5
- data/spec/credit_officer/credit_card_spec.rb +53 -2
- data/spec/support/factories.rb +0 -1
- metadata +4 -3
data/README.rdoc
CHANGED
@@ -11,7 +11,7 @@ Use this library so that you can validate credit card information before sending
|
|
11
11
|
Checks credit card number formats, checksums, and other required details. Supports i18n for better message
|
12
12
|
customization
|
13
13
|
|
14
|
-
cc = CreditOfficer::CreditCard.new(
|
14
|
+
cc = CreditOfficer::CreditCard.new({
|
15
15
|
:number => "411111111111111",
|
16
16
|
:provider_name => "visa",
|
17
17
|
:name_on_card => "John Doe",
|
@@ -29,7 +29,7 @@ customization
|
|
29
29
|
If you want to turn requiring verification values off, make it so:
|
30
30
|
|
31
31
|
CreditOfficer::CreditCard.require_verification_value = false
|
32
|
-
cc = CreditOfficer::CreditCard.new(
|
32
|
+
cc = CreditOfficer::CreditCard.new({
|
33
33
|
:number => "411111111111111",
|
34
34
|
:provider_name => "visa",
|
35
35
|
:name_on_card => "John Doe",
|
@@ -48,7 +48,7 @@ Want to only support certain credit cards and card number formats? Make it so:
|
|
48
48
|
'amex'
|
49
49
|
]
|
50
50
|
|
51
|
-
cc = CreditOfficer::CreditCard.new(
|
51
|
+
cc = CreditOfficer::CreditCard.new({
|
52
52
|
:number => "411111111111111",
|
53
53
|
:provider_name => "visa",
|
54
54
|
:name_on_card => "John Doe",
|
@@ -57,6 +57,29 @@ Want to only support certain credit cards and card number formats? Make it so:
|
|
57
57
|
:verification_value => ""
|
58
58
|
}).valid? => false
|
59
59
|
|
60
|
+
== Deriving provider names
|
61
|
+
|
62
|
+
Most of the time, you can derive the credit card provider (Mastercard, AMEX, Visa, etc) based on the format
|
63
|
+
of the card number. By default, credit officer attempts to derive this provider name automatically
|
64
|
+
|
65
|
+
cc = CreditOfficer::CreditCard.new({
|
66
|
+
:number => "411111111111111",
|
67
|
+
:name_on_card => "John Doe",
|
68
|
+
:expiration_year => 2010,
|
69
|
+
:expiration_month => 1,
|
70
|
+
}).valid? => true
|
71
|
+
|
72
|
+
cc.provider_name => visa
|
73
|
+
|
74
|
+
|
75
|
+
You can toggle this so that the user must provide a valid provider name like so:
|
76
|
+
|
77
|
+
CreditOfficer::CreditCard.automatically_derive_provider_name = false
|
78
|
+
|
79
|
+
You can manually attempt the provider name like so (this will set the provider name according to your number):
|
80
|
+
|
81
|
+
cc.derive_provider_name
|
82
|
+
|
60
83
|
== i18n
|
61
84
|
|
62
85
|
Error messages can be customized with i18n translations
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{credit_officer}
|
8
|
+
s.version = "0.2.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Dan Pickett"]
|
12
|
+
s.date = %q{2010-12-22}
|
13
|
+
s.description = %q{An upgrade/port of ActiveMerchant's credit card class}
|
14
|
+
s.email = %q{dpickett@enlightsolutions.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".rspec",
|
22
|
+
".rvmrc",
|
23
|
+
"Gemfile",
|
24
|
+
"Gemfile.lock",
|
25
|
+
"LICENSE.txt",
|
26
|
+
"README.rdoc",
|
27
|
+
"Rakefile",
|
28
|
+
"VERSION",
|
29
|
+
"credit_officer.gemspec",
|
30
|
+
"lib/credit_officer.rb",
|
31
|
+
"lib/credit_officer/base.rb",
|
32
|
+
"lib/credit_officer/credit_card.rb",
|
33
|
+
"lib/credit_officer/month_year_pair.rb",
|
34
|
+
"spec/credit_officer/base_spec.rb",
|
35
|
+
"spec/credit_officer/credit_card_spec.rb",
|
36
|
+
"spec/credit_officer/month_year_pair_spec.rb",
|
37
|
+
"spec/credit_officer_spec.rb",
|
38
|
+
"spec/spec_helper.rb",
|
39
|
+
"spec/support/active_model_shared_examples.rb",
|
40
|
+
"spec/support/factories.rb"
|
41
|
+
]
|
42
|
+
s.homepage = %q{http://github.com/dpickett/credit_officer}
|
43
|
+
s.licenses = ["MIT"]
|
44
|
+
s.require_paths = ["lib"]
|
45
|
+
s.rubygems_version = %q{1.3.7}
|
46
|
+
s.summary = %q{An activemodel compliant credit card validator}
|
47
|
+
s.test_files = [
|
48
|
+
"spec/credit_officer/base_spec.rb",
|
49
|
+
"spec/credit_officer/credit_card_spec.rb",
|
50
|
+
"spec/credit_officer/month_year_pair_spec.rb",
|
51
|
+
"spec/credit_officer_spec.rb",
|
52
|
+
"spec/spec_helper.rb",
|
53
|
+
"spec/support/active_model_shared_examples.rb",
|
54
|
+
"spec/support/factories.rb"
|
55
|
+
]
|
56
|
+
|
57
|
+
if s.respond_to? :specification_version then
|
58
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
59
|
+
s.specification_version = 3
|
60
|
+
|
61
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
62
|
+
s.add_runtime_dependency(%q<activemodel>, [">= 3.0.3"])
|
63
|
+
s.add_runtime_dependency(%q<luhney_bin>, [">= 0"])
|
64
|
+
s.add_development_dependency(%q<timecop>, ["= 0.3.5"])
|
65
|
+
s.add_development_dependency(%q<ruby-debug>, [">= 0"])
|
66
|
+
s.add_development_dependency(%q<factory_girl>, [">= 0"])
|
67
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
|
68
|
+
s.add_development_dependency(%q<remarkable_activemodel>, [">= 4.0.0.alpha4"])
|
69
|
+
s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
|
70
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
71
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
|
72
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
73
|
+
s.add_development_dependency(%q<reek>, ["~> 1.2.8"])
|
74
|
+
s.add_development_dependency(%q<roodi>, ["~> 2.1.0"])
|
75
|
+
s.add_development_dependency(%q<fuubar>, [">= 0"])
|
76
|
+
else
|
77
|
+
s.add_dependency(%q<activemodel>, [">= 3.0.3"])
|
78
|
+
s.add_dependency(%q<luhney_bin>, [">= 0"])
|
79
|
+
s.add_dependency(%q<timecop>, ["= 0.3.5"])
|
80
|
+
s.add_dependency(%q<ruby-debug>, [">= 0"])
|
81
|
+
s.add_dependency(%q<factory_girl>, [">= 0"])
|
82
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
83
|
+
s.add_dependency(%q<remarkable_activemodel>, [">= 4.0.0.alpha4"])
|
84
|
+
s.add_dependency(%q<yard>, ["~> 0.6.0"])
|
85
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
86
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
|
87
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
88
|
+
s.add_dependency(%q<reek>, ["~> 1.2.8"])
|
89
|
+
s.add_dependency(%q<roodi>, ["~> 2.1.0"])
|
90
|
+
s.add_dependency(%q<fuubar>, [">= 0"])
|
91
|
+
end
|
92
|
+
else
|
93
|
+
s.add_dependency(%q<activemodel>, [">= 3.0.3"])
|
94
|
+
s.add_dependency(%q<luhney_bin>, [">= 0"])
|
95
|
+
s.add_dependency(%q<timecop>, ["= 0.3.5"])
|
96
|
+
s.add_dependency(%q<ruby-debug>, [">= 0"])
|
97
|
+
s.add_dependency(%q<factory_girl>, [">= 0"])
|
98
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
99
|
+
s.add_dependency(%q<remarkable_activemodel>, [">= 4.0.0.alpha4"])
|
100
|
+
s.add_dependency(%q<yard>, ["~> 0.6.0"])
|
101
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
102
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
|
103
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
104
|
+
s.add_dependency(%q<reek>, ["~> 1.2.8"])
|
105
|
+
s.add_dependency(%q<roodi>, ["~> 2.1.0"])
|
106
|
+
s.add_dependency(%q<fuubar>, [">= 0"])
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
@@ -11,7 +11,7 @@ module CreditOfficer
|
|
11
11
|
#store this private information
|
12
12
|
#
|
13
13
|
#@example
|
14
|
-
# cc = CreditOfficer::CreditCard.new(
|
14
|
+
# cc = CreditOfficer::CreditCard.new({
|
15
15
|
# :number => "411111111111111",
|
16
16
|
# :provider_name => "visa",
|
17
17
|
# :name_on_card => "John Doe",
|
@@ -91,7 +91,8 @@ module CreditOfficer
|
|
91
91
|
|
92
92
|
validate :expiration_date_is_in_future
|
93
93
|
validate :expiration_date_is_in_recent_future
|
94
|
-
validate :number_is_valid
|
94
|
+
validate :number_is_valid
|
95
|
+
|
95
96
|
validate :provider_name_is_supported
|
96
97
|
|
97
98
|
#SOLO or Switch validations
|
@@ -107,9 +108,17 @@ module CreditOfficer
|
|
107
108
|
validate :start_date_is_in_the_past,
|
108
109
|
:if => proc{|cc| cc.switch_or_solo? }
|
109
110
|
|
110
|
-
#set this flag accordingly to enable/disable validating verification codes
|
111
|
+
#set this flag accordingly to enable/disable validating verification codes
|
112
|
+
#(CVV/CVV2)
|
113
|
+
#@note defaults to true
|
111
114
|
cattr_accessor :require_verification_value
|
112
115
|
self.require_verification_value = true
|
116
|
+
|
117
|
+
#set this flag accordingly if you want CreditOfficer to attempt to derive
|
118
|
+
#the provider name before validation takes place
|
119
|
+
#@note defaults to true
|
120
|
+
cattr_accessor :automatically_derive_provider_name
|
121
|
+
self.automatically_derive_provider_name = true
|
113
122
|
|
114
123
|
#checks the configuration setting require_verification_value to see if
|
115
124
|
#verification is required
|
@@ -161,7 +170,27 @@ module CreditOfficer
|
|
161
170
|
SWITCH_OR_SOLO_PROVIDERS.include?(provider_name)
|
162
171
|
end
|
163
172
|
|
173
|
+
def derive_provider_name
|
174
|
+
self.class.supported_providers_and_formats.each do |name, format|
|
175
|
+
if number =~ format
|
176
|
+
self.provider_name = name
|
177
|
+
return
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def masked_number
|
183
|
+
if number.present? && number.size >= 4
|
184
|
+
"X" * (number.size - 4) + number[-4..-1]
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
164
188
|
protected
|
189
|
+
def run_validations!
|
190
|
+
derive_provider_name if self.class.automatically_derive_provider_name
|
191
|
+
super
|
192
|
+
end
|
193
|
+
|
165
194
|
I18N_ERROR_SCOPE = [:credit_officer, :errors, :messages]
|
166
195
|
|
167
196
|
def expiration_date_is_in_future
|
@@ -182,7 +211,8 @@ module CreditOfficer
|
|
182
211
|
end
|
183
212
|
|
184
213
|
def number_is_valid
|
185
|
-
if provider_name.present?
|
214
|
+
if (provider_name.present? || self.class.automatically_derive_provider_name) &&
|
215
|
+
number.present?
|
186
216
|
if self.class.supported_providers_and_formats[provider_name].nil? ||
|
187
217
|
!(number =~ self.class.supported_providers_and_formats[provider_name]) ||
|
188
218
|
!checksum_valid?
|
@@ -195,7 +225,9 @@ module CreditOfficer
|
|
195
225
|
end
|
196
226
|
|
197
227
|
def provider_name_is_supported
|
198
|
-
|
228
|
+
if !self.class.automatically_derive_provider_name &&
|
229
|
+
!self.class.supported_providers.include?(provider_name.try(:downcase))
|
230
|
+
|
199
231
|
errors.add(:provider_name, translate(:unsupported_provider,
|
200
232
|
:scope => I18N_ERROR_SCOPE,
|
201
233
|
:default => "is not supported"))
|
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe CreditOfficer::CreditCard do
|
4
|
+
TEST_AMEX = "378282246310005"
|
5
|
+
TEST_MASTER = "5555555555554444"
|
6
|
+
|
4
7
|
subject { Factory.build(:credit_card) }
|
5
8
|
it_should_behave_like "ActiveModel"
|
6
9
|
|
@@ -65,14 +68,14 @@ describe CreditOfficer::CreditCard do
|
|
65
68
|
old_supported_providers = subject.class.supported_providers.dup
|
66
69
|
subject.class.supported_providers = ['master']
|
67
70
|
subject.should_not be_valid
|
68
|
-
subject.errors[:
|
71
|
+
subject.errors[:number].should_not be_blank
|
69
72
|
|
70
73
|
#reset supported providers
|
71
74
|
subject.class.supported_providers = old_supported_providers
|
72
75
|
end
|
73
76
|
|
74
77
|
it "rejects a provider that is not in the whitelist" do
|
75
|
-
old_supported_providers = subject.class.supported_providers
|
78
|
+
old_supported_providers = subject.class.supported_providers.dup
|
76
79
|
subject.class.supported_providers = ["gaga", "ohlala"]
|
77
80
|
subject.class.supported_providers.should be_empty
|
78
81
|
|
@@ -124,4 +127,52 @@ describe CreditOfficer::CreditCard do
|
|
124
127
|
subject.errors[:start_year].should_not be_blank
|
125
128
|
end
|
126
129
|
end
|
130
|
+
|
131
|
+
it "reveals the last 4 digits in your masked card number" do
|
132
|
+
subject.masked_number.should =~ /#{subject.number[-4..-1]}$/
|
133
|
+
end
|
134
|
+
|
135
|
+
it "obfuscates all the digits except the last for in your masked card numbers" do
|
136
|
+
subject.masked_number.should =~ /^#{"X" * (subject.number.size - 4)}/
|
137
|
+
end
|
138
|
+
|
139
|
+
it "returns a nil masked number if I don't have more than 4 digits in my credit card number" do
|
140
|
+
subject.number = ""
|
141
|
+
subject.masked_number.should be_nil
|
142
|
+
end
|
143
|
+
|
144
|
+
context "deriving provider name" do
|
145
|
+
it "derives visa from a visa formatted card number" do
|
146
|
+
subject.provider_name = ""
|
147
|
+
subject.derive_provider_name
|
148
|
+
subject.provider_name.should eql("visa")
|
149
|
+
end
|
150
|
+
|
151
|
+
it "derives master from a mastercard formatted number" do
|
152
|
+
subject.provider_name = ""
|
153
|
+
subject.number = TEST_MASTER
|
154
|
+
subject.derive_provider_name
|
155
|
+
subject.provider_name.should eql('master')
|
156
|
+
end
|
157
|
+
|
158
|
+
it "derives american express from an amex formatted number" do
|
159
|
+
subject.provider_name = ""
|
160
|
+
subject.number = TEST_AMEX
|
161
|
+
subject.derive_provider_name
|
162
|
+
subject.provider_name.should eql('american_express')
|
163
|
+
end
|
164
|
+
|
165
|
+
it "does not validate the provider name if provider name derivation is on" do
|
166
|
+
subject.provider_name = ""
|
167
|
+
subject.number = TEST_AMEX
|
168
|
+
subject.should be_valid
|
169
|
+
subject.provider_name.should eql('american_express')
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should not set an error on provider name if the credit card number is invalid" do
|
173
|
+
subject.number = "fasdfas"
|
174
|
+
subject.should_not be_valid
|
175
|
+
subject.errors[:provider_name].should be_blank
|
176
|
+
end
|
177
|
+
end
|
127
178
|
end
|
data/spec/support/factories.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: credit_officer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Dan Pickett
|
@@ -252,6 +252,7 @@ files:
|
|
252
252
|
- README.rdoc
|
253
253
|
- Rakefile
|
254
254
|
- VERSION
|
255
|
+
- credit_officer.gemspec
|
255
256
|
- lib/credit_officer.rb
|
256
257
|
- lib/credit_officer/base.rb
|
257
258
|
- lib/credit_officer/credit_card.rb
|