aktivemerchant 2.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.
- checksums.yaml +7 -0
- data/CHANGELOG +1596 -0
- data/CONTRIBUTORS +511 -0
- data/MIT-LICENSE +20 -0
- data/README.md +18 -0
- data/lib/active_merchant.rb +108 -0
- data/lib/active_merchant/billing.rb +13 -0
- data/lib/active_merchant/billing/apple_pay_payment_token.rb +22 -0
- data/lib/active_merchant/billing/avs_result.rb +98 -0
- data/lib/active_merchant/billing/base.rb +72 -0
- data/lib/active_merchant/billing/check.rb +76 -0
- data/lib/active_merchant/billing/compatibility.rb +120 -0
- data/lib/active_merchant/billing/credit_card.rb +352 -0
- data/lib/active_merchant/billing/credit_card_formatting.rb +24 -0
- data/lib/active_merchant/billing/credit_card_methods.rb +160 -0
- data/lib/active_merchant/billing/cvv_result.rb +38 -0
- data/lib/active_merchant/billing/gateway.rb +268 -0
- data/lib/active_merchant/billing/gateways.rb +17 -0
- data/lib/active_merchant/billing/gateways/adyen.rb +209 -0
- data/lib/active_merchant/billing/gateways/alfabank.rb +117 -0
- data/lib/active_merchant/billing/gateways/app55.rb +176 -0
- data/lib/active_merchant/billing/gateways/authorize_net.rb +419 -0
- data/lib/active_merchant/billing/gateways/authorize_net_arb.rb +417 -0
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +976 -0
- data/lib/active_merchant/billing/gateways/balanced.rb +256 -0
- data/lib/active_merchant/billing/gateways/bank_frick.rb +225 -0
- data/lib/active_merchant/billing/gateways/banwire.rb +105 -0
- data/lib/active_merchant/billing/gateways/barclays_epdq.rb +314 -0
- data/lib/active_merchant/billing/gateways/barclays_epdq_extra_plus.rb +15 -0
- data/lib/active_merchant/billing/gateways/be2bill.rb +131 -0
- data/lib/active_merchant/billing/gateways/beanstream.rb +188 -0
- data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +393 -0
- data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
- data/lib/active_merchant/billing/gateways/blue_pay.rb +506 -0
- data/lib/active_merchant/billing/gateways/bogus.rb +140 -0
- data/lib/active_merchant/billing/gateways/borgun.rb +210 -0
- data/lib/active_merchant/billing/gateways/braintree.rb +19 -0
- data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +9 -0
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +515 -0
- data/lib/active_merchant/billing/gateways/braintree_orange.rb +20 -0
- data/lib/active_merchant/billing/gateways/bridge_pay.rb +189 -0
- data/lib/active_merchant/billing/gateways/card_save.rb +23 -0
- data/lib/active_merchant/billing/gateways/card_stream.rb +220 -0
- data/lib/active_merchant/billing/gateways/cashnet.rb +191 -0
- data/lib/active_merchant/billing/gateways/cc5.rb +201 -0
- data/lib/active_merchant/billing/gateways/cecabank.rb +229 -0
- data/lib/active_merchant/billing/gateways/certo_direct.rb +278 -0
- data/lib/active_merchant/billing/gateways/checkout.rb +213 -0
- data/lib/active_merchant/billing/gateways/commercegate.rb +143 -0
- data/lib/active_merchant/billing/gateways/conekta.rb +209 -0
- data/lib/active_merchant/billing/gateways/cyber_source.rb +709 -0
- data/lib/active_merchant/billing/gateways/data_cash.rb +600 -0
- data/lib/active_merchant/billing/gateways/efsnet.rb +219 -0
- data/lib/active_merchant/billing/gateways/elavon.rb +348 -0
- data/lib/active_merchant/billing/gateways/epay.rb +275 -0
- data/lib/active_merchant/billing/gateways/evo_ca.rb +308 -0
- data/lib/active_merchant/billing/gateways/eway.rb +214 -0
- data/lib/active_merchant/billing/gateways/eway_managed.rb +291 -0
- data/lib/active_merchant/billing/gateways/eway_rapid.rb +524 -0
- data/lib/active_merchant/billing/gateways/exact.rb +218 -0
- data/lib/active_merchant/billing/gateways/fat_zebra.rb +173 -0
- data/lib/active_merchant/billing/gateways/federated_canada.rb +160 -0
- data/lib/active_merchant/billing/gateways/finansbank.rb +23 -0
- data/lib/active_merchant/billing/gateways/first_giving.rb +143 -0
- data/lib/active_merchant/billing/gateways/first_pay.rb +160 -0
- data/lib/active_merchant/billing/gateways/firstdata_e4.rb +355 -0
- data/lib/active_merchant/billing/gateways/garanti.rb +257 -0
- data/lib/active_merchant/billing/gateways/global_transport.rb +183 -0
- data/lib/active_merchant/billing/gateways/hdfc.rb +207 -0
- data/lib/active_merchant/billing/gateways/hps.rb +288 -0
- data/lib/active_merchant/billing/gateways/iats_payments.rb +251 -0
- data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +246 -0
- data/lib/active_merchant/billing/gateways/ideal/ideal_rabobank.pem +13 -0
- data/lib/active_merchant/billing/gateways/ideal/ideal_response.rb +29 -0
- data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +66 -0
- data/lib/active_merchant/billing/gateways/inspire.rb +213 -0
- data/lib/active_merchant/billing/gateways/instapay.rb +163 -0
- data/lib/active_merchant/billing/gateways/iridium.rb +457 -0
- data/lib/active_merchant/billing/gateways/itransact.rb +448 -0
- data/lib/active_merchant/billing/gateways/jetpay.rb +275 -0
- data/lib/active_merchant/billing/gateways/linkpoint.rb +438 -0
- data/lib/active_merchant/billing/gateways/litle.rb +346 -0
- data/lib/active_merchant/billing/gateways/maxipago.rb +197 -0
- data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +170 -0
- data/lib/active_merchant/billing/gateways/merchant_one.rb +114 -0
- data/lib/active_merchant/billing/gateways/merchant_ware.rb +319 -0
- data/lib/active_merchant/billing/gateways/merchant_ware_version_four.rb +268 -0
- data/lib/active_merchant/billing/gateways/merchant_warrior.rb +195 -0
- data/lib/active_merchant/billing/gateways/mercury.rb +333 -0
- data/lib/active_merchant/billing/gateways/metrics_global.rb +303 -0
- data/lib/active_merchant/billing/gateways/migs.rb +265 -0
- data/lib/active_merchant/billing/gateways/migs/migs_codes.rb +100 -0
- data/lib/active_merchant/billing/gateways/modern_payments.rb +37 -0
- data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +219 -0
- data/lib/active_merchant/billing/gateways/moneris.rb +309 -0
- data/lib/active_merchant/billing/gateways/moneris_us.rb +291 -0
- data/lib/active_merchant/billing/gateways/money_movers.rb +152 -0
- data/lib/active_merchant/billing/gateways/nab_transact.rb +280 -0
- data/lib/active_merchant/billing/gateways/net_registry.rb +198 -0
- data/lib/active_merchant/billing/gateways/netaxept.rb +181 -0
- data/lib/active_merchant/billing/gateways/netbilling.rb +190 -0
- data/lib/active_merchant/billing/gateways/netpay.rb +223 -0
- data/lib/active_merchant/billing/gateways/network_merchants.rb +242 -0
- data/lib/active_merchant/billing/gateways/nmi.rb +256 -0
- data/lib/active_merchant/billing/gateways/ogone.rb +435 -0
- data/lib/active_merchant/billing/gateways/openpay.rb +194 -0
- data/lib/active_merchant/billing/gateways/optimal_payment.rb +313 -0
- data/lib/active_merchant/billing/gateways/orbital.rb +803 -0
- data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +47 -0
- data/lib/active_merchant/billing/gateways/pac_net_raven.rb +207 -0
- data/lib/active_merchant/billing/gateways/pago_facil.rb +122 -0
- data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +261 -0
- data/lib/active_merchant/billing/gateways/pay_junction.rb +390 -0
- data/lib/active_merchant/billing/gateways/pay_secure.rb +112 -0
- data/lib/active_merchant/billing/gateways/pay_u_latam.rb +462 -0
- data/lib/active_merchant/billing/gateways/paybox_direct.rb +188 -0
- data/lib/active_merchant/billing/gateways/payex.rb +412 -0
- data/lib/active_merchant/billing/gateways/payflow.rb +304 -0
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +209 -0
- data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
- data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
- data/lib/active_merchant/billing/gateways/payflow_express.rb +224 -0
- data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
- data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
- data/lib/active_merchant/billing/gateways/payment_express.rb +353 -0
- data/lib/active_merchant/billing/gateways/paymill.rb +281 -0
- data/lib/active_merchant/billing/gateways/paypal.rb +117 -0
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +670 -0
- data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +65 -0
- data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +262 -0
- data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
- data/lib/active_merchant/billing/gateways/paypal_digital_goods.rb +44 -0
- data/lib/active_merchant/billing/gateways/paypal_express.rb +264 -0
- data/lib/active_merchant/billing/gateways/paypal_express_common.rb +30 -0
- data/lib/active_merchant/billing/gateways/payscout.rb +162 -0
- data/lib/active_merchant/billing/gateways/paystation.rb +199 -0
- data/lib/active_merchant/billing/gateways/payway.rb +207 -0
- data/lib/active_merchant/billing/gateways/pin.rb +197 -0
- data/lib/active_merchant/billing/gateways/plugnpay.rb +283 -0
- data/lib/active_merchant/billing/gateways/psigate.rb +216 -0
- data/lib/active_merchant/billing/gateways/psl_card.rb +303 -0
- data/lib/active_merchant/billing/gateways/qbms.rb +292 -0
- data/lib/active_merchant/billing/gateways/quantum.rb +276 -0
- data/lib/active_merchant/billing/gateways/quickpay.rb +367 -0
- data/lib/active_merchant/billing/gateways/realex.rb +298 -0
- data/lib/active_merchant/billing/gateways/redsys.rb +391 -0
- data/lib/active_merchant/billing/gateways/sage.rb +175 -0
- data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +87 -0
- data/lib/active_merchant/billing/gateways/sage/sage_core.rb +114 -0
- data/lib/active_merchant/billing/gateways/sage/sage_vault.rb +149 -0
- data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +102 -0
- data/lib/active_merchant/billing/gateways/sage_pay.rb +398 -0
- data/lib/active_merchant/billing/gateways/sallie_mae.rb +143 -0
- data/lib/active_merchant/billing/gateways/secure_net.rb +252 -0
- data/lib/active_merchant/billing/gateways/secure_pay.rb +201 -0
- data/lib/active_merchant/billing/gateways/secure_pay_au.rb +281 -0
- data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +105 -0
- data/lib/active_merchant/billing/gateways/skip_jack.rb +452 -0
- data/lib/active_merchant/billing/gateways/smart_ps.rb +283 -0
- data/lib/active_merchant/billing/gateways/so_easy_pay.rb +194 -0
- data/lib/active_merchant/billing/gateways/spreedly_core.rb +247 -0
- data/lib/active_merchant/billing/gateways/stripe.rb +411 -0
- data/lib/active_merchant/billing/gateways/swipe_checkout.rb +157 -0
- data/lib/active_merchant/billing/gateways/tns.rb +227 -0
- data/lib/active_merchant/billing/gateways/trans_first.rb +126 -0
- data/lib/active_merchant/billing/gateways/transax.rb +23 -0
- data/lib/active_merchant/billing/gateways/transnational.rb +10 -0
- data/lib/active_merchant/billing/gateways/trust_commerce.rb +416 -0
- data/lib/active_merchant/billing/gateways/usa_epay.rb +25 -0
- data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +1516 -0
- data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +254 -0
- data/lib/active_merchant/billing/gateways/verifi.rb +225 -0
- data/lib/active_merchant/billing/gateways/viaklix.rb +183 -0
- data/lib/active_merchant/billing/gateways/vindicia.rb +385 -0
- data/lib/active_merchant/billing/gateways/webpay.rb +97 -0
- data/lib/active_merchant/billing/gateways/wepay.rb +189 -0
- data/lib/active_merchant/billing/gateways/wirecard.rb +421 -0
- data/lib/active_merchant/billing/gateways/worldpay.rb +331 -0
- data/lib/active_merchant/billing/gateways/worldpay_us.rb +181 -0
- data/lib/active_merchant/billing/model.rb +30 -0
- data/lib/active_merchant/billing/payment_token.rb +21 -0
- data/lib/active_merchant/billing/rails.rb +3 -0
- data/lib/active_merchant/billing/response.rb +91 -0
- data/lib/active_merchant/country.rb +332 -0
- data/lib/active_merchant/empty.rb +20 -0
- data/lib/active_merchant/errors.rb +29 -0
- data/lib/active_merchant/offsite_payments_shim.rb +19 -0
- data/lib/active_merchant/version.rb +3 -0
- data/lib/activemerchant.rb +1 -0
- data/lib/support/gateway_support.rb +71 -0
- data/lib/support/outbound_hosts.rb +25 -0
- data/lib/support/ssl_verify.rb +93 -0
- metadata +400 -0
@@ -0,0 +1,352 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'date'
|
3
|
+
require "active_merchant/billing/model"
|
4
|
+
|
5
|
+
module ActiveMerchant #:nodoc:
|
6
|
+
module Billing #:nodoc:
|
7
|
+
# A +CreditCard+ object represents a physical credit card, and is capable of validating the various
|
8
|
+
# data associated with these.
|
9
|
+
#
|
10
|
+
# At the moment, the following credit card types are supported:
|
11
|
+
#
|
12
|
+
# * Visa
|
13
|
+
# * MasterCard
|
14
|
+
# * Discover
|
15
|
+
# * American Express
|
16
|
+
# * Diner's Club
|
17
|
+
# * JCB
|
18
|
+
# * Switch
|
19
|
+
# * Solo
|
20
|
+
# * Dankort
|
21
|
+
# * Maestro
|
22
|
+
# * Forbrugsforeningen
|
23
|
+
# * Laser
|
24
|
+
#
|
25
|
+
# For testing purposes, use the 'bogus' credit card brand. This skips the vast majority of
|
26
|
+
# validations, allowing you to focus on your core concerns until you're ready to be more concerned
|
27
|
+
# with the details of particular credit cards or your gateway.
|
28
|
+
#
|
29
|
+
# == Testing With CreditCard
|
30
|
+
# Often when testing we don't care about the particulars of a given card brand. When using the 'test'
|
31
|
+
# mode in your {Gateway}, there are six different valid card numbers: 1, 2, 3, 'success', 'fail',
|
32
|
+
# and 'error'.
|
33
|
+
#
|
34
|
+
# For details, see {CreditCardMethods::ClassMethods#valid_number?}
|
35
|
+
#
|
36
|
+
# == Example Usage
|
37
|
+
# cc = CreditCard.new(
|
38
|
+
# :first_name => 'Steve',
|
39
|
+
# :last_name => 'Smith',
|
40
|
+
# :month => '9',
|
41
|
+
# :year => '2010',
|
42
|
+
# :brand => 'visa',
|
43
|
+
# :number => '4242424242424242'
|
44
|
+
# )
|
45
|
+
#
|
46
|
+
# cc.validate # => {}
|
47
|
+
# cc.display_number # => XXXX-XXXX-XXXX-4242
|
48
|
+
#
|
49
|
+
class CreditCard < Model
|
50
|
+
include CreditCardMethods
|
51
|
+
|
52
|
+
cattr_accessor :require_verification_value
|
53
|
+
self.require_verification_value = true
|
54
|
+
|
55
|
+
# Returns or sets the credit card number.
|
56
|
+
#
|
57
|
+
# @return [String]
|
58
|
+
attr_reader :number
|
59
|
+
|
60
|
+
def number=(value)
|
61
|
+
@number = (empty?(value) ? value : value.to_s.gsub(/[^\d]/, ""))
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns or sets the expiry month for the card.
|
65
|
+
#
|
66
|
+
# @return [Integer]
|
67
|
+
attr_reader :month
|
68
|
+
|
69
|
+
# Returns or sets the expiry year for the card.
|
70
|
+
#
|
71
|
+
# @return [Integer]
|
72
|
+
attr_reader :year
|
73
|
+
|
74
|
+
# Returns or sets the credit card brand.
|
75
|
+
#
|
76
|
+
# Valid card types are
|
77
|
+
#
|
78
|
+
# * +'visa'+
|
79
|
+
# * +'master'+
|
80
|
+
# * +'discover'+
|
81
|
+
# * +'american_express'+
|
82
|
+
# * +'diners_club'+
|
83
|
+
# * +'jcb'+
|
84
|
+
# * +'switch'+
|
85
|
+
# * +'solo'+
|
86
|
+
# * +'dankort'+
|
87
|
+
# * +'maestro'+
|
88
|
+
# * +'forbrugsforeningen'+
|
89
|
+
# * +'laser'+
|
90
|
+
#
|
91
|
+
# Or, if you wish to test your implementation, +'bogus'+.
|
92
|
+
#
|
93
|
+
# @return (String) the credit card brand
|
94
|
+
def brand
|
95
|
+
if !defined?(@brand) || empty?(@brand)
|
96
|
+
self.class.brand?(number)
|
97
|
+
else
|
98
|
+
@brand
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def brand=(value)
|
103
|
+
value = value && value.to_s.dup
|
104
|
+
@brand = (value.respond_to?(:downcase) ? value.downcase : value)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Returns or sets the first name of the card holder.
|
108
|
+
#
|
109
|
+
# @return [String]
|
110
|
+
attr_accessor :first_name
|
111
|
+
|
112
|
+
# Returns or sets the last name of the card holder.
|
113
|
+
#
|
114
|
+
# @return [String]
|
115
|
+
attr_accessor :last_name
|
116
|
+
|
117
|
+
# Required for Switch / Solo cards
|
118
|
+
attr_reader :start_month, :start_year
|
119
|
+
attr_accessor :issue_number
|
120
|
+
|
121
|
+
# Returns or sets the card verification value.
|
122
|
+
#
|
123
|
+
# This attribute is optional but recommended. The verification value is
|
124
|
+
# a {card security code}[http://en.wikipedia.org/wiki/Card_security_code]. If provided,
|
125
|
+
# the gateway will attempt to validate the value.
|
126
|
+
#
|
127
|
+
# @return [String] the verification value
|
128
|
+
attr_accessor :verification_value
|
129
|
+
|
130
|
+
# Returns or sets the track data for the card
|
131
|
+
#
|
132
|
+
# @return [String]
|
133
|
+
attr_accessor :track_data
|
134
|
+
|
135
|
+
def type
|
136
|
+
ActiveMerchant.deprecated "CreditCard#type is deprecated and will be removed from a future release of ActiveMerchant. Please use CreditCard#brand instead."
|
137
|
+
brand
|
138
|
+
end
|
139
|
+
|
140
|
+
def type=(value)
|
141
|
+
ActiveMerchant.deprecated "CreditCard#type is deprecated and will be removed from a future release of ActiveMerchant. Please use CreditCard#brand instead."
|
142
|
+
self.brand = value
|
143
|
+
end
|
144
|
+
|
145
|
+
# Provides proxy access to an expiry date object
|
146
|
+
#
|
147
|
+
# @return [ExpiryDate]
|
148
|
+
def expiry_date
|
149
|
+
ExpiryDate.new(@month, @year)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Returns whether the credit card has expired.
|
153
|
+
#
|
154
|
+
# @return +true+ if the card has expired, +false+ otherwise
|
155
|
+
def expired?
|
156
|
+
expiry_date.expired?
|
157
|
+
end
|
158
|
+
|
159
|
+
# Returns whether either the +first_name+ or the +last_name+ attributes has been set.
|
160
|
+
def name?
|
161
|
+
first_name? || last_name?
|
162
|
+
end
|
163
|
+
|
164
|
+
# Returns whether the +first_name+ attribute has been set.
|
165
|
+
def first_name?
|
166
|
+
first_name.present?
|
167
|
+
end
|
168
|
+
|
169
|
+
# Returns whether the +last_name+ attribute has been set.
|
170
|
+
def last_name?
|
171
|
+
last_name.present?
|
172
|
+
end
|
173
|
+
|
174
|
+
# Returns the full name of the card holder.
|
175
|
+
#
|
176
|
+
# @return [String] the full name of the card holder
|
177
|
+
def name
|
178
|
+
[first_name, last_name].compact.join(' ')
|
179
|
+
end
|
180
|
+
|
181
|
+
def name=(full_name)
|
182
|
+
names = full_name.split
|
183
|
+
self.last_name = names.pop
|
184
|
+
self.first_name = names.join(" ")
|
185
|
+
end
|
186
|
+
|
187
|
+
%w(month year start_month start_year).each do |m|
|
188
|
+
class_eval %(
|
189
|
+
def #{m}=(v)
|
190
|
+
@#{m} = case v
|
191
|
+
when "", nil, 0
|
192
|
+
nil
|
193
|
+
else
|
194
|
+
v.to_i
|
195
|
+
end
|
196
|
+
end
|
197
|
+
)
|
198
|
+
end
|
199
|
+
|
200
|
+
def verification_value?
|
201
|
+
!verification_value.blank?
|
202
|
+
end
|
203
|
+
|
204
|
+
# Returns a display-friendly version of the card number.
|
205
|
+
#
|
206
|
+
# All but the last 4 numbers are replaced with an "X", and hyphens are
|
207
|
+
# inserted in order to improve legibility.
|
208
|
+
#
|
209
|
+
# @example
|
210
|
+
# credit_card = CreditCard.new(:number => "2132542376824338")
|
211
|
+
# credit_card.display_number # "XXXX-XXXX-XXXX-4338"
|
212
|
+
#
|
213
|
+
# @return [String] a display-friendly version of the card number
|
214
|
+
def display_number
|
215
|
+
self.class.mask(number)
|
216
|
+
end
|
217
|
+
|
218
|
+
def first_digits
|
219
|
+
self.class.first_digits(number)
|
220
|
+
end
|
221
|
+
|
222
|
+
def last_digits
|
223
|
+
self.class.last_digits(number)
|
224
|
+
end
|
225
|
+
|
226
|
+
# Validates the credit card details.
|
227
|
+
#
|
228
|
+
# Any validation errors are added to the {#errors} attribute.
|
229
|
+
def validate
|
230
|
+
errors = validate_essential_attributes + validate_verification_value
|
231
|
+
|
232
|
+
# Bogus card is pretty much for testing purposes. Lets just skip these extra tests if its used
|
233
|
+
return errors_hash(errors) if brand == 'bogus'
|
234
|
+
|
235
|
+
errors_hash(
|
236
|
+
errors +
|
237
|
+
validate_card_brand_and_number +
|
238
|
+
validate_switch_or_solo_attributes
|
239
|
+
)
|
240
|
+
end
|
241
|
+
|
242
|
+
def self.requires_verification_value?
|
243
|
+
require_verification_value
|
244
|
+
end
|
245
|
+
|
246
|
+
private
|
247
|
+
|
248
|
+
def validate_essential_attributes #:nodoc:
|
249
|
+
errors = []
|
250
|
+
|
251
|
+
errors << [:first_name, "cannot be empty"] if first_name.blank?
|
252
|
+
errors << [:last_name, "cannot be empty"] if last_name.blank?
|
253
|
+
|
254
|
+
if(empty?(month) || empty?(year))
|
255
|
+
errors << [:month, "is required"] if empty?(month)
|
256
|
+
errors << [:year, "is required"] if empty?(year)
|
257
|
+
else
|
258
|
+
errors << [:month, "is not a valid month"] if !valid_month?(month)
|
259
|
+
|
260
|
+
if expired?
|
261
|
+
errors << [:year, "expired"]
|
262
|
+
else
|
263
|
+
errors << [:year, "is not a valid year"] if !valid_expiry_year?(year)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
errors
|
268
|
+
end
|
269
|
+
|
270
|
+
def validate_card_brand_and_number #:nodoc:
|
271
|
+
errors = []
|
272
|
+
|
273
|
+
if !empty?(brand)
|
274
|
+
errors << [:brand, "is invalid"] if !CreditCard.card_companies.keys.include?(brand)
|
275
|
+
end
|
276
|
+
|
277
|
+
if empty?(number)
|
278
|
+
errors << [:number, "is required"]
|
279
|
+
elsif !CreditCard.valid_number?(number)
|
280
|
+
errors << [:number, "is not a valid credit card number"]
|
281
|
+
end
|
282
|
+
|
283
|
+
if errors.empty?
|
284
|
+
errors << [:brand, "does not match the card number"] if !CreditCard.matching_brand?(number, brand)
|
285
|
+
end
|
286
|
+
|
287
|
+
errors
|
288
|
+
end
|
289
|
+
|
290
|
+
def validate_verification_value #:nodoc:
|
291
|
+
errors = []
|
292
|
+
|
293
|
+
if verification_value?
|
294
|
+
unless valid_card_verification_value?(verification_value, brand)
|
295
|
+
errors << [:verification_value, "should be #{card_verification_value_length(brand)} digits"]
|
296
|
+
end
|
297
|
+
elsif CreditCard.requires_verification_value?
|
298
|
+
errors << [:verification_value, "is required"]
|
299
|
+
end
|
300
|
+
errors
|
301
|
+
end
|
302
|
+
|
303
|
+
def validate_switch_or_solo_attributes #:nodoc:
|
304
|
+
errors = []
|
305
|
+
|
306
|
+
if %w[switch solo].include?(brand)
|
307
|
+
valid_start_month = valid_month?(start_month)
|
308
|
+
valid_start_year = valid_start_year?(start_year)
|
309
|
+
|
310
|
+
if((!valid_start_month || !valid_start_year) && !valid_issue_number?(issue_number))
|
311
|
+
if empty?(issue_number)
|
312
|
+
errors << [:issue_number, "cannot be empty"]
|
313
|
+
errors << [:start_month, "is invalid"] if !valid_start_month
|
314
|
+
errors << [:start_year, "is invalid"] if !valid_start_year
|
315
|
+
else
|
316
|
+
errors << [:issue_number, "is invalid"] if !valid_issue_number?(issue_number)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
errors
|
322
|
+
end
|
323
|
+
|
324
|
+
class ExpiryDate #:nodoc:
|
325
|
+
attr_reader :month, :year
|
326
|
+
def initialize(month, year)
|
327
|
+
@month = month.to_i
|
328
|
+
@year = year.to_i
|
329
|
+
end
|
330
|
+
|
331
|
+
def expired? #:nodoc:
|
332
|
+
Time.now.utc > expiration
|
333
|
+
end
|
334
|
+
|
335
|
+
def expiration #:nodoc:
|
336
|
+
begin
|
337
|
+
Time.utc(year, month, month_days, 23, 59, 59)
|
338
|
+
rescue ArgumentError
|
339
|
+
Time.at(0).utc
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
private
|
344
|
+
def month_days
|
345
|
+
mdays = [nil,31,28,31,30,31,30,31,31,30,31,30,31]
|
346
|
+
mdays[2] = 29 if Date.leap?(year)
|
347
|
+
mdays[month]
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
module CreditCardFormatting
|
4
|
+
|
5
|
+
def expdate(credit_card)
|
6
|
+
"#{format(credit_card.month, :two_digits)}#{format(credit_card.year, :two_digits)}"
|
7
|
+
end
|
8
|
+
|
9
|
+
# This method is used to format numerical information pertaining to credit cards.
|
10
|
+
#
|
11
|
+
# format(2005, :two_digits) # => "05"
|
12
|
+
# format(05, :four_digits) # => "0005"
|
13
|
+
def format(number, option)
|
14
|
+
return '' if number.blank?
|
15
|
+
|
16
|
+
case option
|
17
|
+
when :two_digits ; sprintf("%.2i", number.to_i)[-2..-1]
|
18
|
+
when :four_digits ; sprintf("%.4i", number.to_i)[-4..-1]
|
19
|
+
else number
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
# Convenience methods that can be included into a custom Credit Card object, such as an ActiveRecord based Credit Card object.
|
4
|
+
module CreditCardMethods
|
5
|
+
CARD_COMPANIES = {
|
6
|
+
'visa' => /^4\d{12}(\d{3})?$/,
|
7
|
+
'master' => /^(5[1-5]\d{4}|677189)\d{10}$/,
|
8
|
+
'discover' => /^(6011|65\d{2}|64[4-9]\d)\d{12}|(62\d{14})$/,
|
9
|
+
'american_express' => /^3[47]\d{13}$/,
|
10
|
+
'diners_club' => /^3(0[0-5]|[68]\d)\d{11}$/,
|
11
|
+
'jcb' => /^35(28|29|[3-8]\d)\d{12}$/,
|
12
|
+
'switch' => /^6759\d{12}(\d{2,3})?$/,
|
13
|
+
'solo' => /^6767\d{12}(\d{2,3})?$/,
|
14
|
+
'dankort' => /^5019\d{12}$/,
|
15
|
+
'maestro' => /^(5[06-8]|6\d)\d{10,17}$/,
|
16
|
+
'forbrugsforeningen' => /^600722\d{10}$/,
|
17
|
+
'laser' => /^(6304|6706|6709|6771(?!89))\d{8}(\d{4}|\d{6,7})?$/
|
18
|
+
}
|
19
|
+
|
20
|
+
def self.included(base)
|
21
|
+
base.extend(ClassMethods)
|
22
|
+
end
|
23
|
+
|
24
|
+
def valid_month?(month)
|
25
|
+
(1..12).include?(month.to_i)
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid_expiry_year?(year)
|
29
|
+
(Time.now.year..Time.now.year + 20).include?(year.to_i)
|
30
|
+
end
|
31
|
+
|
32
|
+
def valid_start_year?(year)
|
33
|
+
((year.to_s =~ /^\d{4}$/) && (year.to_i > 1987))
|
34
|
+
end
|
35
|
+
|
36
|
+
# Credit card providers have 3 digit verification values
|
37
|
+
# This isn't standardised, these are called various names such as
|
38
|
+
# CVC, CVV, CID, CSC and more
|
39
|
+
# See: http://en.wikipedia.org/wiki/Card_security_code
|
40
|
+
# American Express is the exception with 4 digits
|
41
|
+
#
|
42
|
+
# Below are links from the card providers with their requirements
|
43
|
+
# visa: http://usa.visa.com/personal/security/3-digit-security-code.jsp
|
44
|
+
# master: http://www.mastercard.com/ca/merchant/en/getstarted/Anatomy_MasterCard.html
|
45
|
+
# jcb: http://www.jcbcard.com/security/info.html
|
46
|
+
# diners_club: http://www.dinersclub.com/assets/DinersClub_card_ID_features.pdf
|
47
|
+
# discover: https://www.discover.com/credit-cards/help-center/glossary.html
|
48
|
+
# american_express: https://online.americanexpress.com/myca/fuidfyp/us/action?request_type=un_fuid&Face=en_US
|
49
|
+
def valid_card_verification_value?(cvv, brand)
|
50
|
+
cvv.to_s =~ /^\d{#{card_verification_value_length(brand)}}$/
|
51
|
+
end
|
52
|
+
|
53
|
+
def card_verification_value_length(brand)
|
54
|
+
brand == 'american_express' ? 4 : 3
|
55
|
+
end
|
56
|
+
|
57
|
+
def valid_issue_number?(number)
|
58
|
+
(number.to_s =~ /^\d{1,2}$/)
|
59
|
+
end
|
60
|
+
|
61
|
+
module ClassMethods
|
62
|
+
# Returns true if it validates. Optionally, you can pass a card brand as an argument and
|
63
|
+
# make sure it is of the correct brand.
|
64
|
+
#
|
65
|
+
# References:
|
66
|
+
# - http://perl.about.com/compute/perl/library/nosearch/P073000.htm
|
67
|
+
# - http://www.beachnet.com/~hstiles/cardtype.html
|
68
|
+
def valid_number?(number)
|
69
|
+
valid_test_mode_card_number?(number) ||
|
70
|
+
valid_card_number_length?(number) &&
|
71
|
+
valid_checksum?(number)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Regular expressions for the known card companies.
|
75
|
+
#
|
76
|
+
# References:
|
77
|
+
# - http://en.wikipedia.org/wiki/Credit_card_number
|
78
|
+
# - http://www.barclaycardbusiness.co.uk/information_zone/processing/bin_rules.html
|
79
|
+
def card_companies
|
80
|
+
CARD_COMPANIES
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns a string containing the brand of card from the list of known information below.
|
84
|
+
# Need to check the cards in a particular order, as there is some overlap of the allowable ranges
|
85
|
+
#--
|
86
|
+
# TODO Refactor this method. We basically need to tighten up the Maestro Regexp.
|
87
|
+
#
|
88
|
+
# Right now the Maestro regexp overlaps with the MasterCard regexp (IIRC). If we can tighten
|
89
|
+
# things up, we can boil this whole thing down to something like...
|
90
|
+
#
|
91
|
+
# def brand?(number)
|
92
|
+
# return 'visa' if valid_test_mode_card_number?(number)
|
93
|
+
# card_companies.find([nil]) { |brand, regexp| number =~ regexp }.first.dup
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
def brand?(number)
|
97
|
+
return 'bogus' if valid_test_mode_card_number?(number)
|
98
|
+
|
99
|
+
card_companies.reject { |c,p| c == 'maestro' }.each do |company, pattern|
|
100
|
+
return company.dup if number =~ pattern
|
101
|
+
end
|
102
|
+
|
103
|
+
return 'maestro' if number =~ card_companies['maestro']
|
104
|
+
|
105
|
+
return nil
|
106
|
+
end
|
107
|
+
|
108
|
+
def type?(number)
|
109
|
+
ActiveMerchant.deprecated "CreditCard#type? is deprecated and will be removed from a future release of ActiveMerchant. Please use CreditCard#brand? instead."
|
110
|
+
brand?(number)
|
111
|
+
end
|
112
|
+
|
113
|
+
def first_digits(number)
|
114
|
+
number.to_s.slice(0,6)
|
115
|
+
end
|
116
|
+
|
117
|
+
def last_digits(number)
|
118
|
+
number.to_s.length <= 4 ? number : number.to_s.slice(-4..-1)
|
119
|
+
end
|
120
|
+
|
121
|
+
def mask(number)
|
122
|
+
"XXXX-XXXX-XXXX-#{last_digits(number)}"
|
123
|
+
end
|
124
|
+
|
125
|
+
# Checks to see if the calculated brand matches the specified brand
|
126
|
+
def matching_brand?(number, brand)
|
127
|
+
brand?(number) == brand
|
128
|
+
end
|
129
|
+
|
130
|
+
def matching_type?(number, brand)
|
131
|
+
ActiveMerchant.deprecated "CreditCard#matching_type? is deprecated and will be removed from a future release of ActiveMerchant. Please use CreditCard#matching_brand? instead."
|
132
|
+
matching_brand?(number, brand)
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def valid_card_number_length?(number) #:nodoc:
|
138
|
+
number.to_s.length >= 12
|
139
|
+
end
|
140
|
+
|
141
|
+
def valid_test_mode_card_number?(number) #:nodoc:
|
142
|
+
ActiveMerchant::Billing::Base.test? &&
|
143
|
+
%w[1 2 3 success failure error].include?(number.to_s)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Checks the validity of a card number by use of the Luhn Algorithm.
|
147
|
+
# Please see http://en.wikipedia.org/wiki/Luhn_algorithm for details.
|
148
|
+
def valid_checksum?(number) #:nodoc:
|
149
|
+
sum = 0
|
150
|
+
for i in 0..number.length
|
151
|
+
weight = number[-1 * (i + 2), 1].to_i * (2 - (i % 2))
|
152
|
+
sum += (weight < 10) ? weight : weight - 9
|
153
|
+
end
|
154
|
+
|
155
|
+
(number[-1,1].to_i == (10 - sum % 10) % 10)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|