tlconnor-activemerchant 1.20.4
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/CHANGELOG +805 -0
- data/CONTRIBUTORS +274 -0
- data/MIT-LICENSE +20 -0
- data/gem-public_cert.pem +20 -0
- data/lib/active_merchant.rb +63 -0
- data/lib/active_merchant/billing.rb +9 -0
- data/lib/active_merchant/billing/avs_result.rb +98 -0
- data/lib/active_merchant/billing/base.rb +57 -0
- data/lib/active_merchant/billing/check.rb +68 -0
- data/lib/active_merchant/billing/credit_card.rb +264 -0
- data/lib/active_merchant/billing/credit_card_formatting.rb +21 -0
- data/lib/active_merchant/billing/credit_card_methods.rb +129 -0
- data/lib/active_merchant/billing/cvv_result.rb +38 -0
- data/lib/active_merchant/billing/expiry_date.rb +34 -0
- data/lib/active_merchant/billing/gateway.rb +170 -0
- data/lib/active_merchant/billing/gateways.rb +18 -0
- data/lib/active_merchant/billing/gateways/authorize_net.rb +694 -0
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +944 -0
- data/lib/active_merchant/billing/gateways/barclays_epdq.rb +308 -0
- data/lib/active_merchant/billing/gateways/beanstream.rb +167 -0
- data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +388 -0
- data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
- data/lib/active_merchant/billing/gateways/blue_pay.rb +11 -0
- data/lib/active_merchant/billing/gateways/bogus.rb +142 -0
- data/lib/active_merchant/billing/gateways/braintree.rb +17 -0
- data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +9 -0
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +308 -0
- data/lib/active_merchant/billing/gateways/braintree_orange.rb +21 -0
- data/lib/active_merchant/billing/gateways/card_save.rb +23 -0
- data/lib/active_merchant/billing/gateways/card_stream.rb +230 -0
- data/lib/active_merchant/billing/gateways/certo_direct.rb +279 -0
- data/lib/active_merchant/billing/gateways/cyber_source.rb +430 -0
- data/lib/active_merchant/billing/gateways/data_cash.rb +597 -0
- data/lib/active_merchant/billing/gateways/efsnet.rb +235 -0
- data/lib/active_merchant/billing/gateways/elavon.rb +135 -0
- data/lib/active_merchant/billing/gateways/epay.rb +274 -0
- data/lib/active_merchant/billing/gateways/eway.rb +277 -0
- data/lib/active_merchant/billing/gateways/eway_managed.rb +265 -0
- data/lib/active_merchant/billing/gateways/exact.rb +227 -0
- data/lib/active_merchant/billing/gateways/federated_canada.rb +168 -0
- data/lib/active_merchant/billing/gateways/first_pay.rb +177 -0
- data/lib/active_merchant/billing/gateways/garanti.rb +262 -0
- data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +250 -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 +55 -0
- data/lib/active_merchant/billing/gateways/inspire.rb +221 -0
- data/lib/active_merchant/billing/gateways/instapay.rb +164 -0
- data/lib/active_merchant/billing/gateways/iridium.rb +258 -0
- data/lib/active_merchant/billing/gateways/jetpay.rb +276 -0
- data/lib/active_merchant/billing/gateways/linkpoint.rb +454 -0
- data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +157 -0
- data/lib/active_merchant/billing/gateways/merchant_ware.rb +289 -0
- data/lib/active_merchant/billing/gateways/modern_payments.rb +36 -0
- data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +220 -0
- data/lib/active_merchant/billing/gateways/moneris.rb +239 -0
- data/lib/active_merchant/billing/gateways/nab_transact.rb +244 -0
- data/lib/active_merchant/billing/gateways/net_registry.rb +189 -0
- data/lib/active_merchant/billing/gateways/netaxept.rb +239 -0
- data/lib/active_merchant/billing/gateways/netbilling.rb +168 -0
- data/lib/active_merchant/billing/gateways/nmi.rb +13 -0
- data/lib/active_merchant/billing/gateways/ogone.rb +330 -0
- data/lib/active_merchant/billing/gateways/optimal_payment.rb +277 -0
- data/lib/active_merchant/billing/gateways/orbital.rb +344 -0
- data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +46 -0
- data/lib/active_merchant/billing/gateways/pay_junction.rb +397 -0
- data/lib/active_merchant/billing/gateways/pay_secure.rb +120 -0
- data/lib/active_merchant/billing/gateways/paybox_direct.rb +207 -0
- data/lib/active_merchant/billing/gateways/payflow.rb +261 -0
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +208 -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 +222 -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 +235 -0
- data/lib/active_merchant/billing/gateways/paypal.rb +121 -0
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +354 -0
- data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +49 -0
- data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
- data/lib/active_merchant/billing/gateways/paypal_express.rb +229 -0
- data/lib/active_merchant/billing/gateways/paypal_express_common.rb +25 -0
- data/lib/active_merchant/billing/gateways/paystation.rb +201 -0
- data/lib/active_merchant/billing/gateways/plugnpay.rb +298 -0
- data/lib/active_merchant/billing/gateways/psigate.rb +219 -0
- data/lib/active_merchant/billing/gateways/psl_card.rb +304 -0
- data/lib/active_merchant/billing/gateways/qbms.rb +297 -0
- data/lib/active_merchant/billing/gateways/quantum.rb +282 -0
- data/lib/active_merchant/billing/gateways/quickpay.rb +298 -0
- data/lib/active_merchant/billing/gateways/realex.rb +315 -0
- data/lib/active_merchant/billing/gateways/sage.rb +146 -0
- data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +88 -0
- data/lib/active_merchant/billing/gateways/sage/sage_core.rb +116 -0
- data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +97 -0
- data/lib/active_merchant/billing/gateways/sage_pay.rb +320 -0
- data/lib/active_merchant/billing/gateways/sallie_mae.rb +144 -0
- data/lib/active_merchant/billing/gateways/samurai.rb +121 -0
- data/lib/active_merchant/billing/gateways/secure_net.rb +330 -0
- data/lib/active_merchant/billing/gateways/secure_pay.rb +31 -0
- data/lib/active_merchant/billing/gateways/secure_pay_au.rb +280 -0
- data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +113 -0
- data/lib/active_merchant/billing/gateways/skip_jack.rb +458 -0
- data/lib/active_merchant/billing/gateways/smart_ps.rb +271 -0
- data/lib/active_merchant/billing/gateways/stripe.rb +244 -0
- data/lib/active_merchant/billing/gateways/trans_first.rb +127 -0
- data/lib/active_merchant/billing/gateways/transax.rb +25 -0
- data/lib/active_merchant/billing/gateways/trust_commerce.rb +423 -0
- data/lib/active_merchant/billing/gateways/usa_epay.rb +23 -0
- data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +1496 -0
- data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +206 -0
- data/lib/active_merchant/billing/gateways/verifi.rb +233 -0
- data/lib/active_merchant/billing/gateways/viaklix.rb +189 -0
- data/lib/active_merchant/billing/gateways/wirecard.rb +318 -0
- data/lib/active_merchant/billing/gateways/worldpay.rb +280 -0
- data/lib/active_merchant/billing/integrations.rb +17 -0
- data/lib/active_merchant/billing/integrations/action_view_helper.rb +72 -0
- data/lib/active_merchant/billing/integrations/authorize_net_sim.rb +38 -0
- data/lib/active_merchant/billing/integrations/authorize_net_sim/helper.rb +228 -0
- data/lib/active_merchant/billing/integrations/authorize_net_sim/notification.rb +340 -0
- data/lib/active_merchant/billing/integrations/bogus.rb +23 -0
- data/lib/active_merchant/billing/integrations/bogus/helper.rb +17 -0
- data/lib/active_merchant/billing/integrations/bogus/notification.rb +11 -0
- data/lib/active_merchant/billing/integrations/bogus/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/chronopay.rb +23 -0
- data/lib/active_merchant/billing/integrations/chronopay/helper.rb +120 -0
- data/lib/active_merchant/billing/integrations/chronopay/notification.rb +158 -0
- data/lib/active_merchant/billing/integrations/chronopay/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/direc_pay.rb +41 -0
- data/lib/active_merchant/billing/integrations/direc_pay/helper.rb +200 -0
- data/lib/active_merchant/billing/integrations/direc_pay/notification.rb +76 -0
- data/lib/active_merchant/billing/integrations/direc_pay/return.rb +32 -0
- data/lib/active_merchant/billing/integrations/direc_pay/status.rb +37 -0
- data/lib/active_merchant/billing/integrations/directebanking.rb +47 -0
- data/lib/active_merchant/billing/integrations/directebanking/helper.rb +90 -0
- data/lib/active_merchant/billing/integrations/directebanking/notification.rb +120 -0
- data/lib/active_merchant/billing/integrations/directebanking/return.rb +11 -0
- data/lib/active_merchant/billing/integrations/dwolla.rb +30 -0
- data/lib/active_merchant/billing/integrations/dwolla/helper.rb +31 -0
- data/lib/active_merchant/billing/integrations/dwolla/notification.rb +55 -0
- data/lib/active_merchant/billing/integrations/dwolla/return.rb +38 -0
- data/lib/active_merchant/billing/integrations/e_payment_plans.rb +48 -0
- data/lib/active_merchant/billing/integrations/e_payment_plans/helper.rb +34 -0
- data/lib/active_merchant/billing/integrations/e_payment_plans/notification.rb +84 -0
- data/lib/active_merchant/billing/integrations/gestpay.rb +25 -0
- data/lib/active_merchant/billing/integrations/gestpay/common.rb +42 -0
- data/lib/active_merchant/billing/integrations/gestpay/helper.rb +70 -0
- data/lib/active_merchant/billing/integrations/gestpay/notification.rb +85 -0
- data/lib/active_merchant/billing/integrations/gestpay/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/helper.rb +113 -0
- data/lib/active_merchant/billing/integrations/hi_trust.rb +27 -0
- data/lib/active_merchant/billing/integrations/hi_trust/helper.rb +58 -0
- data/lib/active_merchant/billing/integrations/hi_trust/notification.rb +59 -0
- data/lib/active_merchant/billing/integrations/hi_trust/return.rb +67 -0
- data/lib/active_merchant/billing/integrations/moneybookers.rb +26 -0
- data/lib/active_merchant/billing/integrations/moneybookers/helper.rb +59 -0
- data/lib/active_merchant/billing/integrations/moneybookers/notification.rb +129 -0
- data/lib/active_merchant/billing/integrations/nochex.rb +88 -0
- data/lib/active_merchant/billing/integrations/nochex/helper.rb +68 -0
- data/lib/active_merchant/billing/integrations/nochex/notification.rb +94 -0
- data/lib/active_merchant/billing/integrations/nochex/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/notification.rb +62 -0
- data/lib/active_merchant/billing/integrations/payflow_link.rb +21 -0
- data/lib/active_merchant/billing/integrations/payflow_link/helper.rb +100 -0
- data/lib/active_merchant/billing/integrations/payflow_link/notification.rb +78 -0
- data/lib/active_merchant/billing/integrations/paypal.rb +39 -0
- data/lib/active_merchant/billing/integrations/paypal/helper.rb +119 -0
- data/lib/active_merchant/billing/integrations/paypal/notification.rb +154 -0
- data/lib/active_merchant/billing/integrations/paypal/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/quickpay.rb +21 -0
- data/lib/active_merchant/billing/integrations/quickpay/helper.rb +72 -0
- data/lib/active_merchant/billing/integrations/quickpay/notification.rb +74 -0
- data/lib/active_merchant/billing/integrations/return.rb +42 -0
- data/lib/active_merchant/billing/integrations/sage_pay_form.rb +37 -0
- data/lib/active_merchant/billing/integrations/sage_pay_form/encryption.rb +33 -0
- data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +127 -0
- data/lib/active_merchant/billing/integrations/sage_pay_form/notification.rb +210 -0
- data/lib/active_merchant/billing/integrations/sage_pay_form/return.rb +31 -0
- data/lib/active_merchant/billing/integrations/two_checkout.rb +22 -0
- data/lib/active_merchant/billing/integrations/two_checkout/helper.rb +59 -0
- data/lib/active_merchant/billing/integrations/two_checkout/notification.rb +114 -0
- data/lib/active_merchant/billing/integrations/two_checkout/return.rb +17 -0
- data/lib/active_merchant/billing/integrations/valitor.rb +33 -0
- data/lib/active_merchant/billing/integrations/valitor/helper.rb +86 -0
- data/lib/active_merchant/billing/integrations/valitor/notification.rb +13 -0
- data/lib/active_merchant/billing/integrations/valitor/response_fields.rb +97 -0
- data/lib/active_merchant/billing/integrations/valitor/return.rb +13 -0
- data/lib/active_merchant/billing/integrations/world_pay.rb +27 -0
- data/lib/active_merchant/billing/integrations/world_pay/helper.rb +100 -0
- data/lib/active_merchant/billing/integrations/world_pay/notification.rb +160 -0
- data/lib/active_merchant/billing/response.rb +32 -0
- data/lib/active_merchant/version.rb +3 -0
- data/lib/activemerchant.rb +1 -0
- data/lib/support/gateway_support.rb +58 -0
- data/lib/support/outbound_hosts.rb +25 -0
- metadata +411 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
|
2
|
+
module Billing #:nodoc:
|
|
3
|
+
module Base
|
|
4
|
+
# Set ActiveMerchant gateways in test mode.
|
|
5
|
+
#
|
|
6
|
+
# ActiveMerchant::Billing::Base.gateway_mode = :test
|
|
7
|
+
mattr_accessor :gateway_mode
|
|
8
|
+
|
|
9
|
+
# Set ActiveMerchant integrations in test mode.
|
|
10
|
+
#
|
|
11
|
+
# ActiveMerchant::Billing::Base.integration_mode = :test
|
|
12
|
+
mattr_accessor :integration_mode
|
|
13
|
+
|
|
14
|
+
# Set both the mode of both the gateways and integrations
|
|
15
|
+
# at once
|
|
16
|
+
mattr_reader :mode
|
|
17
|
+
|
|
18
|
+
def self.mode=(mode)
|
|
19
|
+
@@mode = mode
|
|
20
|
+
self.gateway_mode = mode
|
|
21
|
+
self.integration_mode = mode
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
self.mode = :production
|
|
25
|
+
|
|
26
|
+
# Return the matching gateway for the provider
|
|
27
|
+
# * <tt>bogus</tt>: BogusGateway - Does nothing (for testing)
|
|
28
|
+
# * <tt>moneris</tt>: MonerisGateway
|
|
29
|
+
# * <tt>authorize_net</tt>: AuthorizeNetGateway
|
|
30
|
+
# * <tt>trust_commerce</tt>: TrustCommerceGateway
|
|
31
|
+
#
|
|
32
|
+
# ActiveMerchant::Billing::Base.gateway('moneris').new
|
|
33
|
+
def self.gateway(name)
|
|
34
|
+
Billing.const_get("#{name.to_s.downcase}_gateway".camelize)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Return the matching integration module
|
|
39
|
+
# You can then get the notification from the module
|
|
40
|
+
# * <tt>bogus</tt>: Bogus - Does nothing (for testing)
|
|
41
|
+
# * <tt>chronopay</tt>: Chronopay - Does nothing (for testing)
|
|
42
|
+
# * <tt>paypal</tt>: Chronopay - Does nothing (for testing)
|
|
43
|
+
#
|
|
44
|
+
# chronopay = ActiveMerchant::Billing::Base.integration('chronopay')
|
|
45
|
+
# notification = chronopay.notification(raw_post)
|
|
46
|
+
#
|
|
47
|
+
def self.integration(name)
|
|
48
|
+
Billing::Integrations.const_get("#{name.to_s.downcase}".camelize)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# A check to see if we're in test mode
|
|
52
|
+
def self.test?
|
|
53
|
+
self.gateway_mode == :test
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
|
2
|
+
module Billing #:nodoc:
|
|
3
|
+
# The Check object is a plain old Ruby object, similar to CreditCard. It supports validation
|
|
4
|
+
# of necessary attributes such as checkholder's name, routing and account numbers, but it is
|
|
5
|
+
# not backed by any database.
|
|
6
|
+
#
|
|
7
|
+
# You may use Check in place of CreditCard with any gateway that supports it. Currently, only
|
|
8
|
+
# +BraintreeGateway+ supports the Check object.
|
|
9
|
+
class Check
|
|
10
|
+
include Validateable
|
|
11
|
+
|
|
12
|
+
attr_accessor :first_name, :last_name, :routing_number, :account_number, :account_holder_type, :account_type, :number
|
|
13
|
+
|
|
14
|
+
# Used for Canadian bank accounts
|
|
15
|
+
attr_accessor :institution_number, :transit_number
|
|
16
|
+
|
|
17
|
+
def name
|
|
18
|
+
@name ||= "#{@first_name} #{@last_name}".strip
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def name=(value)
|
|
22
|
+
return if value.blank?
|
|
23
|
+
|
|
24
|
+
@name = value
|
|
25
|
+
segments = value.split(' ')
|
|
26
|
+
@last_name = segments.pop
|
|
27
|
+
@first_name = segments.join(' ')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def validate
|
|
31
|
+
[:name, :routing_number, :account_number].each do |attr|
|
|
32
|
+
errors.add(attr, "cannot be empty") if self.send(attr).blank?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
errors.add(:routing_number, "is invalid") unless valid_routing_number?
|
|
36
|
+
|
|
37
|
+
errors.add(:account_holder_type, "must be personal or business") if
|
|
38
|
+
!account_holder_type.blank? && !%w[business personal].include?(account_holder_type.to_s)
|
|
39
|
+
|
|
40
|
+
errors.add(:account_type, "must be checking or savings") if
|
|
41
|
+
!account_type.blank? && !%w[checking savings].include?(account_type.to_s)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def type
|
|
45
|
+
'check'
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Routing numbers may be validated by calculating a checksum and dividing it by 10. The
|
|
49
|
+
# formula is:
|
|
50
|
+
# (3(d1 + d4 + d7) + 7(d2 + d5 + d8) + 1(d3 + d6 + d9))mod 10 = 0
|
|
51
|
+
# See http://en.wikipedia.org/wiki/Routing_transit_number#Internal_checksums
|
|
52
|
+
def valid_routing_number?
|
|
53
|
+
d = routing_number.to_s.split('').map(&:to_i).select { |d| (0..9).include?(d) }
|
|
54
|
+
case d.size
|
|
55
|
+
when 9 then
|
|
56
|
+
checksum = ((3 * (d[0] + d[3] + d[6])) +
|
|
57
|
+
(7 * (d[1] + d[4] + d[7])) +
|
|
58
|
+
(d[2] + d[5] + d[8])) % 10
|
|
59
|
+
case checksum
|
|
60
|
+
when 0 then true
|
|
61
|
+
else false
|
|
62
|
+
end
|
|
63
|
+
else false
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
require 'time'
|
|
2
|
+
require 'date'
|
|
3
|
+
require 'active_merchant/billing/expiry_date'
|
|
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 type. 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 type. 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
|
+
# :type => 'visa',
|
|
43
|
+
# :number => '4242424242424242'
|
|
44
|
+
# )
|
|
45
|
+
#
|
|
46
|
+
# cc.valid? # => true
|
|
47
|
+
# cc.display_number # => XXXX-XXXX-XXXX-4242
|
|
48
|
+
#
|
|
49
|
+
class CreditCard
|
|
50
|
+
include CreditCardMethods
|
|
51
|
+
include Validateable
|
|
52
|
+
|
|
53
|
+
cattr_accessor :require_verification_value
|
|
54
|
+
self.require_verification_value = true
|
|
55
|
+
|
|
56
|
+
# Returns or sets the credit card number.
|
|
57
|
+
#
|
|
58
|
+
# @return [String]
|
|
59
|
+
attr_accessor :number
|
|
60
|
+
|
|
61
|
+
# Returns or sets the expiry month for the card.
|
|
62
|
+
#
|
|
63
|
+
# @return [Integer]
|
|
64
|
+
attr_accessor :month
|
|
65
|
+
|
|
66
|
+
# Returns or sets the expiry year for the card.
|
|
67
|
+
#
|
|
68
|
+
# @return [Integer]
|
|
69
|
+
attr_accessor :year
|
|
70
|
+
|
|
71
|
+
# Returns or sets the credit card type.
|
|
72
|
+
#
|
|
73
|
+
# Valid card types are
|
|
74
|
+
#
|
|
75
|
+
# * +'visa'+
|
|
76
|
+
# * +'master'+
|
|
77
|
+
# * +'discover'+
|
|
78
|
+
# * +'american_express'+
|
|
79
|
+
# * +'diners_club'+
|
|
80
|
+
# * +'jcb'+
|
|
81
|
+
# * +'switch'+
|
|
82
|
+
# * +'solo'+
|
|
83
|
+
# * +'dankort'+
|
|
84
|
+
# * +'maestro'+
|
|
85
|
+
# * +'forbrugsforeningen'+
|
|
86
|
+
# * +'laser'+
|
|
87
|
+
#
|
|
88
|
+
# Or, if you wish to test your implementation, +'bogus'+.
|
|
89
|
+
#
|
|
90
|
+
# @return (String) the credit card type
|
|
91
|
+
attr_accessor :type
|
|
92
|
+
|
|
93
|
+
# Returns or sets the first name of the card holder.
|
|
94
|
+
#
|
|
95
|
+
# @return [String]
|
|
96
|
+
attr_accessor :first_name
|
|
97
|
+
|
|
98
|
+
# Returns or sets the last name of the card holder.
|
|
99
|
+
#
|
|
100
|
+
# @return [String]
|
|
101
|
+
attr_accessor :last_name
|
|
102
|
+
|
|
103
|
+
# Required for Switch / Solo cards
|
|
104
|
+
attr_accessor :start_month, :start_year, :issue_number
|
|
105
|
+
|
|
106
|
+
# Returns or sets the card verification value.
|
|
107
|
+
#
|
|
108
|
+
# This attribute is optional but recommended. The verification value is
|
|
109
|
+
# a {card security code}[http://en.wikipedia.org/wiki/Card_security_code]. If provided,
|
|
110
|
+
# the gateway will attempt to validate the value.
|
|
111
|
+
#
|
|
112
|
+
# @return [String] the verification value
|
|
113
|
+
attr_accessor :verification_value
|
|
114
|
+
|
|
115
|
+
alias_method :brand, :type
|
|
116
|
+
|
|
117
|
+
# Provides proxy access to an expiry date object
|
|
118
|
+
#
|
|
119
|
+
# @return [ExpiryDate]
|
|
120
|
+
def expiry_date
|
|
121
|
+
ExpiryDate.new(@month, @year)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Returns whether the credit card has expired.
|
|
125
|
+
#
|
|
126
|
+
# @return +true+ if the card has expired, +false+ otherwise
|
|
127
|
+
def expired?
|
|
128
|
+
expiry_date.expired?
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Returns whether either the +first_name+ or the +last_name+ attributes has been set.
|
|
132
|
+
def name?
|
|
133
|
+
first_name? || last_name?
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Returns whether the +first_name+ attribute has been set.
|
|
137
|
+
def first_name?
|
|
138
|
+
@first_name.present?
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Returns whether the +last_name+ attribute has been set.
|
|
142
|
+
def last_name?
|
|
143
|
+
@last_name.present?
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Returns the full name of the card holder.
|
|
147
|
+
#
|
|
148
|
+
# @return [String] the full name of the card holder
|
|
149
|
+
def name
|
|
150
|
+
[@first_name, @last_name].compact.join(' ')
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def name=(full_name)
|
|
154
|
+
names = full_name.split
|
|
155
|
+
self.last_name = names.pop
|
|
156
|
+
self.first_name = names.join(" ")
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def verification_value?
|
|
160
|
+
!@verification_value.blank?
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Returns a display-friendly version of the card number.
|
|
164
|
+
#
|
|
165
|
+
# All but the last 4 numbers are replaced with an "X", and hyphens are
|
|
166
|
+
# inserted in order to improve legibility.
|
|
167
|
+
#
|
|
168
|
+
# @example
|
|
169
|
+
# credit_card = CreditCard.new(:number => "2132542376824338")
|
|
170
|
+
# credit_card.display_number # "XXXX-XXXX-XXXX-4338"
|
|
171
|
+
#
|
|
172
|
+
# @return [String] a display-friendly version of the card number
|
|
173
|
+
def display_number
|
|
174
|
+
self.class.mask(number)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def first_digits
|
|
178
|
+
self.class.first_digits(number)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def last_digits
|
|
182
|
+
self.class.last_digits(number)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Validates the credit card details.
|
|
186
|
+
#
|
|
187
|
+
# Any validation errors are added to the {#errors} attribute.
|
|
188
|
+
def validate
|
|
189
|
+
validate_essential_attributes
|
|
190
|
+
|
|
191
|
+
# Bogus card is pretty much for testing purposes. Lets just skip these extra tests if its used
|
|
192
|
+
return if type == 'bogus'
|
|
193
|
+
|
|
194
|
+
validate_card_type
|
|
195
|
+
validate_card_number
|
|
196
|
+
validate_verification_value
|
|
197
|
+
validate_switch_or_solo_attributes
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def self.requires_verification_value?
|
|
201
|
+
require_verification_value
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
private
|
|
205
|
+
|
|
206
|
+
def before_validate #:nodoc:
|
|
207
|
+
self.month = month.to_i
|
|
208
|
+
self.year = year.to_i
|
|
209
|
+
self.start_month = start_month.to_i unless start_month.nil?
|
|
210
|
+
self.start_year = start_year.to_i unless start_year.nil?
|
|
211
|
+
self.number = number.to_s.gsub(/[^\d]/, "")
|
|
212
|
+
self.type.downcase! if type.respond_to?(:downcase)
|
|
213
|
+
self.type = self.class.type?(number) if type.blank?
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def validate_card_number #:nodoc:
|
|
217
|
+
if number.blank?
|
|
218
|
+
errors.add :number, "is required"
|
|
219
|
+
elsif !CreditCard.valid_number?(number)
|
|
220
|
+
errors.add :number, "is not a valid credit card number"
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
unless errors.on(:number) || errors.on(:type)
|
|
224
|
+
errors.add :type, "is not the correct card type" unless CreditCard.matching_type?(number, type)
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def validate_card_type #:nodoc:
|
|
229
|
+
errors.add :type, "is required" if type.blank? && number.present?
|
|
230
|
+
errors.add :type, "is invalid" unless type.blank? || CreditCard.card_companies.keys.include?(type)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def validate_essential_attributes #:nodoc:
|
|
234
|
+
errors.add :first_name, "cannot be empty" if @first_name.blank?
|
|
235
|
+
errors.add :last_name, "cannot be empty" if @last_name.blank?
|
|
236
|
+
|
|
237
|
+
if @month.to_i.zero? || @year.to_i.zero?
|
|
238
|
+
errors.add :month, "is required" if @month.to_i.zero?
|
|
239
|
+
errors.add :year, "is required" if @year.to_i.zero?
|
|
240
|
+
else
|
|
241
|
+
errors.add :month, "is not a valid month" unless valid_month?(@month)
|
|
242
|
+
errors.add :year, "expired" if expired?
|
|
243
|
+
errors.add :year, "is not a valid year" unless expired? || valid_expiry_year?(@year)
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def validate_switch_or_solo_attributes #:nodoc:
|
|
248
|
+
if %w[switch solo].include?(type)
|
|
249
|
+
unless valid_month?(@start_month) && valid_start_year?(@start_year) || valid_issue_number?(@issue_number)
|
|
250
|
+
errors.add :start_month, "is invalid" unless valid_month?(@start_month)
|
|
251
|
+
errors.add :start_year, "is invalid" unless valid_start_year?(@start_year)
|
|
252
|
+
errors.add :issue_number, "cannot be empty" unless valid_issue_number?(@issue_number)
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def validate_verification_value #:nodoc:
|
|
258
|
+
if CreditCard.requires_verification_value?
|
|
259
|
+
errors.add :verification_value, "is required" unless verification_value?
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
|
2
|
+
module Billing #:nodoc:
|
|
3
|
+
module CreditCardFormatting
|
|
4
|
+
|
|
5
|
+
# This method is used to format numerical information pertaining to credit cards.
|
|
6
|
+
#
|
|
7
|
+
# format(2005, :two_digits) # => "05"
|
|
8
|
+
# format(05, :four_digits) # => "0005"
|
|
9
|
+
def format(number, option)
|
|
10
|
+
return '' if number.blank?
|
|
11
|
+
|
|
12
|
+
case option
|
|
13
|
+
when :two_digits ; sprintf("%.2i", number)[-2..-1]
|
|
14
|
+
when :four_digits ; sprintf("%.4i", number)[-4..-1]
|
|
15
|
+
else number
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
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
|
+
def valid_issue_number?(number)
|
|
37
|
+
number.to_s =~ /^\d{1,2}$/
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module ClassMethods
|
|
41
|
+
# Returns true if it validates. Optionally, you can pass a card type as an argument and
|
|
42
|
+
# make sure it is of the correct type.
|
|
43
|
+
#
|
|
44
|
+
# References:
|
|
45
|
+
# - http://perl.about.com/compute/perl/library/nosearch/P073000.htm
|
|
46
|
+
# - http://www.beachnet.com/~hstiles/cardtype.html
|
|
47
|
+
def valid_number?(number)
|
|
48
|
+
valid_test_mode_card_number?(number) ||
|
|
49
|
+
valid_card_number_length?(number) &&
|
|
50
|
+
valid_checksum?(number)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Regular expressions for the known card companies.
|
|
54
|
+
#
|
|
55
|
+
# References:
|
|
56
|
+
# - http://en.wikipedia.org/wiki/Credit_card_number
|
|
57
|
+
# - http://www.barclaycardbusiness.co.uk/information_zone/processing/bin_rules.html
|
|
58
|
+
def card_companies
|
|
59
|
+
CARD_COMPANIES
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Returns a string containing the type of card from the list of known information below.
|
|
63
|
+
# Need to check the cards in a particular order, as there is some overlap of the allowable ranges
|
|
64
|
+
#--
|
|
65
|
+
# TODO Refactor this method. We basically need to tighten up the Maestro Regexp.
|
|
66
|
+
#
|
|
67
|
+
# Right now the Maestro regexp overlaps with the MasterCard regexp (IIRC). If we can tighten
|
|
68
|
+
# things up, we can boil this whole thing down to something like...
|
|
69
|
+
#
|
|
70
|
+
# def type?(number)
|
|
71
|
+
# return 'visa' if valid_test_mode_card_number?(number)
|
|
72
|
+
# card_companies.find([nil]) { |type, regexp| number =~ regexp }.first.dup
|
|
73
|
+
# end
|
|
74
|
+
#
|
|
75
|
+
def type?(number)
|
|
76
|
+
return 'bogus' if valid_test_mode_card_number?(number)
|
|
77
|
+
|
|
78
|
+
card_companies.reject { |c,p| c == 'maestro' }.each do |company, pattern|
|
|
79
|
+
return company.dup if number =~ pattern
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
return 'maestro' if number =~ card_companies['maestro']
|
|
83
|
+
|
|
84
|
+
return nil
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def first_digits(number)
|
|
88
|
+
number.to_s.slice(0,6)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def last_digits(number)
|
|
92
|
+
number.to_s.length <= 4 ? number : number.to_s.slice(-4..-1)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def mask(number)
|
|
96
|
+
"XXXX-XXXX-XXXX-#{last_digits(number)}"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Checks to see if the calculated type matches the specified type
|
|
100
|
+
def matching_type?(number, type)
|
|
101
|
+
type?(number) == type
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
private
|
|
105
|
+
|
|
106
|
+
def valid_card_number_length?(number) #:nodoc:
|
|
107
|
+
number.to_s.length >= 12
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def valid_test_mode_card_number?(number) #:nodoc:
|
|
111
|
+
ActiveMerchant::Billing::Base.test? &&
|
|
112
|
+
%w[1 2 3 success failure error].include?(number.to_s)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Checks the validity of a card number by use of the the Luhn Algorithm.
|
|
116
|
+
# Please see http://en.wikipedia.org/wiki/Luhn_algorithm for details.
|
|
117
|
+
def valid_checksum?(number) #:nodoc:
|
|
118
|
+
sum = 0
|
|
119
|
+
for i in 0..number.length
|
|
120
|
+
weight = number[-1 * (i + 2), 1].to_i * (2 - (i % 2))
|
|
121
|
+
sum += (weight < 10) ? weight : weight - 9
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
(number[-1,1].to_i == (10 - sum % 10) % 10)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|