activemerchant 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data.tar.gz.sig +0 -0
- data/CHANGELOG +310 -294
- data/CONTRIBUTERS +13 -0
- data/README +55 -31
- data/Rakefile +21 -13
- data/lib/active_merchant.rb +7 -0
- data/lib/active_merchant/billing/avs_result.rb +95 -0
- data/lib/active_merchant/billing/base.rb +8 -3
- data/lib/active_merchant/billing/check.rb +61 -0
- data/lib/active_merchant/billing/credit_card.rb +104 -80
- data/lib/active_merchant/billing/credit_card_formatting.rb +11 -8
- data/lib/active_merchant/billing/credit_card_methods.rb +76 -32
- data/lib/active_merchant/billing/cvv_result.rb +38 -0
- data/lib/active_merchant/billing/expiry_date.rb +28 -0
- data/lib/active_merchant/billing/gateway.rb +47 -111
- data/lib/active_merchant/billing/gateways/authorize_net.rb +508 -121
- data/lib/active_merchant/billing/gateways/bogus.rb +26 -32
- data/lib/active_merchant/billing/gateways/brain_tree.rb +82 -70
- data/lib/active_merchant/billing/gateways/card_stream.rb +43 -15
- data/lib/active_merchant/billing/gateways/cyber_source.rb +9 -29
- data/lib/active_merchant/billing/gateways/data_cash.rb +18 -38
- data/lib/active_merchant/billing/gateways/efsnet.rb +23 -50
- data/lib/active_merchant/billing/gateways/eway.rb +8 -19
- data/lib/active_merchant/billing/gateways/exact.rb +17 -25
- data/lib/active_merchant/billing/gateways/linkpoint.rb +18 -25
- data/lib/active_merchant/billing/gateways/moneris.rb +9 -39
- data/lib/active_merchant/billing/gateways/net_registry.rb +113 -182
- data/lib/active_merchant/billing/gateways/netbilling.rb +168 -0
- data/lib/active_merchant/billing/gateways/pay_junction.rb +52 -73
- data/lib/active_merchant/billing/gateways/pay_secure.rb +120 -0
- data/lib/active_merchant/billing/gateways/payflow.rb +13 -14
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +55 -37
- data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +4 -0
- data/lib/active_merchant/billing/gateways/payflow_express.rb +2 -4
- data/lib/active_merchant/billing/gateways/payment_express.rb +11 -30
- data/lib/active_merchant/billing/gateways/paypal.rb +3 -14
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +36 -16
- data/lib/active_merchant/billing/gateways/paypal_express.rb +1 -2
- data/lib/active_merchant/billing/gateways/plugnpay.rb +7 -15
- data/lib/active_merchant/billing/gateways/protx.rb +24 -25
- data/lib/active_merchant/billing/gateways/psigate.rb +34 -71
- data/lib/active_merchant/billing/gateways/psl_card.rb +24 -19
- data/lib/active_merchant/billing/gateways/quickpay.rb +10 -24
- data/lib/active_merchant/billing/gateways/realex.rb +7 -19
- data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +113 -0
- data/lib/active_merchant/billing/gateways/skip_jack.rb +437 -0
- data/lib/active_merchant/billing/gateways/trans_first.rb +5 -14
- data/lib/active_merchant/billing/gateways/trust_commerce.rb +100 -39
- data/lib/active_merchant/billing/gateways/usa_epay.rb +60 -55
- data/lib/active_merchant/billing/gateways/verifi.rb +32 -39
- data/lib/active_merchant/billing/gateways/viaklix.rb +31 -37
- data/lib/active_merchant/billing/integrations.rb +2 -0
- data/lib/active_merchant/billing/integrations/bogus.rb +5 -0
- data/lib/active_merchant/billing/integrations/bogus/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/chronopay.rb +5 -0
- data/lib/active_merchant/billing/integrations/chronopay/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/gestpay.rb +5 -0
- data/lib/active_merchant/billing/integrations/gestpay/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/hi_trust.rb +26 -0
- data/lib/active_merchant/billing/integrations/hi_trust/helper.rb +58 -0
- data/lib/active_merchant/billing/integrations/hi_trust/notification.rb +57 -0
- data/lib/active_merchant/billing/integrations/hi_trust/return.rb +67 -0
- data/lib/active_merchant/billing/integrations/nochex.rb +5 -0
- data/lib/active_merchant/billing/integrations/nochex/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/paypal.rb +5 -0
- data/lib/active_merchant/billing/integrations/paypal/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/return.rb +35 -0
- data/lib/active_merchant/billing/integrations/two_checkout.rb +5 -0
- data/lib/active_merchant/billing/integrations/two_checkout/return.rb +17 -0
- data/lib/active_merchant/billing/response.rb +12 -8
- data/lib/active_merchant/lib/posts_data.rb +39 -7
- data/lib/active_merchant/lib/requires_parameters.rb +2 -3
- data/lib/active_merchant/lib/utils.rb +18 -0
- data/lib/active_merchant/lib/validateable.rb +3 -3
- data/lib/support/gateway_support.rb +22 -9
- data/script/destroy +14 -0
- data/script/generate +7 -7
- data/test/fixtures.yml +25 -3
- data/test/remote/gateways/remote_authorize_net_test.rb +145 -0
- data/test/remote/gateways/remote_brain_tree_test.rb +118 -0
- data/test/{remote_tests → remote/gateways}/remote_card_stream_test.rb +56 -68
- data/test/{remote_tests → remote/gateways}/remote_cyber_source_test.rb +21 -32
- data/test/{remote_tests → remote/gateways}/remote_data_cash_test.rb +2 -2
- data/test/{remote_tests → remote/gateways}/remote_efsnet_test.rb +22 -34
- data/test/{remote_tests → remote/gateways}/remote_eway_test.rb +18 -15
- data/test/{remote_tests → remote/gateways}/remote_exact_test.rb +20 -19
- data/test/{remote_tests → remote/gateways}/remote_linkpoint_test.rb +31 -63
- data/test/remote/gateways/remote_moneris_test.rb +82 -0
- data/test/{remote_tests → remote/gateways}/remote_net_registry_test.rb +19 -54
- data/test/remote/gateways/remote_netbilling_test.rb +70 -0
- data/test/{remote_tests → remote/gateways}/remote_pay_junction_test.rb +41 -60
- data/test/remote/gateways/remote_pay_secure_test.rb +39 -0
- data/test/{remote_tests → remote/gateways}/remote_payflow_express_test.rb +2 -2
- data/test/{remote_tests → remote/gateways}/remote_payflow_test.rb +34 -38
- data/test/{remote_tests → remote/gateways}/remote_payflow_uk_test.rb +13 -12
- data/test/{remote_tests → remote/gateways}/remote_payment_express_test.rb +26 -36
- data/test/{remote_tests → remote/gateways}/remote_paypal_express_test.rb +3 -3
- data/test/{remote_tests → remote/gateways}/remote_paypal_test.rb +25 -21
- data/test/{remote_tests → remote/gateways}/remote_plugnpay_test.rb +18 -16
- data/test/{remote_tests → remote/gateways}/remote_protx_test.rb +33 -33
- data/test/remote/gateways/remote_psigate_test.rb +50 -0
- data/test/{remote_tests → remote/gateways}/remote_psl_card_test.rb +27 -26
- data/test/{remote_tests → remote/gateways}/remote_quickpay_test.rb +48 -48
- data/test/{remote_tests → remote/gateways}/remote_realex_test.rb +30 -33
- data/test/remote/gateways/remote_secure_pay_tech_test.rb +37 -0
- data/test/remote/gateways/remote_secure_pay_test.rb +28 -0
- data/test/remote/gateways/remote_skipjack_test.rb +105 -0
- data/test/{remote_tests → remote/gateways}/remote_trans_first_test.rb +7 -10
- data/test/remote/gateways/remote_trust_commerce_test.rb +152 -0
- data/test/{remote_tests → remote/gateways}/remote_usa_epay_test.rb +11 -22
- data/test/{remote_tests → remote/gateways}/remote_verifi_test.rb +27 -27
- data/test/{remote_tests → remote/gateways}/remote_viaklix_test.rb +8 -18
- data/test/{remote_tests → remote/integrations}/remote_gestpay_integration_test.rb +1 -1
- data/test/{remote_tests → remote/integrations}/remote_paypal_integration_test.rb +1 -1
- data/test/test_helper.rb +102 -61
- data/test/unit/avs_result_test.rb +59 -0
- data/test/unit/base_test.rb +33 -39
- data/test/unit/check_test.rb +76 -0
- data/test/unit/credit_card_formatting_test.rb +10 -15
- data/test/unit/credit_card_methods_test.rb +132 -17
- data/test/unit/credit_card_test.rb +157 -228
- data/test/unit/cvv_result_test.rb +33 -0
- data/test/unit/expiry_date_test.rb +21 -0
- data/test/unit/gateways/authorize_net_test.rb +180 -40
- data/test/unit/gateways/bogus_test.rb +2 -3
- data/test/unit/gateways/brain_tree_test.rb +63 -29
- data/test/unit/gateways/card_stream_test.rb +59 -6
- data/test/unit/gateways/cyber_source_test.rb +59 -40
- data/test/unit/gateways/data_cash_test.rb +82 -1
- data/test/unit/gateways/efsnet_test.rb +97 -44
- data/test/unit/gateways/eway_test.rb +55 -42
- data/test/unit/gateways/exact_test.rb +93 -55
- data/test/unit/gateways/gateway_test.rb +7 -0
- data/test/unit/gateways/linkpoint_test.rb +60 -58
- data/test/unit/gateways/moneris_test.rb +67 -76
- data/test/unit/gateways/net_registry_test.rb +351 -419
- data/test/unit/gateways/netbilling_test.rb +54 -0
- data/test/unit/gateways/pay_junction_test.rb +108 -46
- data/test/unit/gateways/pay_secure_test.rb +71 -0
- data/test/unit/gateways/payflow_express_test.rb +0 -8
- data/test/unit/gateways/payflow_test.rb +136 -65
- data/test/unit/gateways/payflow_uk_test.rb +0 -38
- data/test/unit/gateways/payment_express_test.rb +31 -51
- data/test/unit/gateways/paypal_express_test.rb +8 -2
- data/test/unit/gateways/paypal_test.rb +213 -54
- data/test/unit/gateways/plugnpay_test.rb +39 -32
- data/test/unit/gateways/protx_test.rb +45 -33
- data/test/unit/gateways/psigate_test.rb +146 -87
- data/test/unit/gateways/psl_card_test.rb +37 -24
- data/test/unit/gateways/quickpay_test.rb +33 -46
- data/test/unit/gateways/realex_test.rb +32 -31
- data/test/unit/gateways/secure_pay_tech_test.rb +44 -0
- data/test/unit/gateways/secure_pay_test.rb +35 -26
- data/test/unit/gateways/skip_jack_test.rb +125 -0
- data/test/unit/gateways/trans_first_test.rb +24 -37
- data/test/unit/gateways/trust_commerce_test.rb +47 -26
- data/test/unit/gateways/usa_epay_test.rb +52 -41
- data/test/unit/gateways/verifi_test.rb +41 -35
- data/test/unit/gateways/viaklix_test.rb +38 -32
- data/test/unit/generators/test_gateway_generator.rb +46 -0
- data/test/unit/generators/test_generator_helper.rb +20 -0
- data/test/unit/generators/test_integration_generator.rb +53 -0
- data/test/unit/integrations/action_view_helper_test.rb +7 -14
- data/test/unit/integrations/bogus_module_test.rb +4 -0
- data/test/unit/integrations/chronopay_module_test.rb +4 -0
- data/test/unit/integrations/gestpay_module_test.rb +4 -0
- data/test/unit/integrations/helpers/hi_trust_helper_test.rb +16 -0
- data/test/unit/integrations/hi_trust_module_test.rb +13 -0
- data/test/unit/integrations/nochex_module_test.rb +4 -0
- data/test/unit/integrations/notifications/hi_trust_notification_test.rb +38 -0
- data/test/unit/integrations/paypal_module_test.rb +4 -0
- data/test/unit/integrations/returns/chronopay_return_test.rb +11 -0
- data/test/unit/integrations/returns/gestpay_return_test.rb +10 -0
- data/test/unit/integrations/returns/hi_trust_return_test.rb +24 -0
- data/test/unit/integrations/returns/nochex_return_test.rb +10 -0
- data/test/unit/integrations/returns/paypal_return_test.rb +10 -0
- data/test/unit/integrations/returns/return_test.rb +11 -0
- data/test/unit/integrations/returns/two_checkout_return_test.rb +24 -0
- data/test/unit/integrations/two_checkout_module_test.rb +4 -0
- data/test/unit/posts_data_test.rb +86 -0
- data/test/unit/response_test.rb +15 -1
- data/test/unit/utils_test.rb +7 -0
- data/test/unit/validateable_test.rb +10 -6
- metadata +180 -142
- metadata.gz.sig +0 -0
- data/lib/active_merchant/billing/gateways/paypal/api_cert_chain.crt +0 -35
- data/script/generator/base.rb +0 -45
- data/script/generator/generator.rb +0 -24
- data/script/generator/generators/gateway/gateway_generator.rb +0 -14
- data/script/generator/generators/gateway/templates/gateway.rb +0 -73
- data/script/generator/generators/gateway/templates/gateway_test.rb +0 -41
- data/script/generator/generators/gateway/templates/remote_gateway_test.rb +0 -56
- data/script/generator/generators/integration/integration_generator.rb +0 -25
- data/script/generator/generators/integration/templates/helper.rb +0 -34
- data/script/generator/generators/integration/templates/helper_test.rb +0 -54
- data/script/generator/generators/integration/templates/integration.rb +0 -18
- data/script/generator/generators/integration/templates/module_test.rb +0 -9
- data/script/generator/generators/integration/templates/notification.rb +0 -100
- data/script/generator/generators/integration/templates/notification_test.rb +0 -41
- data/script/generator/manifest.rb +0 -20
- data/test/remote_tests/remote_authorize_net_test.rb +0 -113
- data/test/remote_tests/remote_brain_tree_test.rb +0 -78
- data/test/remote_tests/remote_moneris_test.rb +0 -110
- data/test/remote_tests/remote_psigate_test.rb +0 -87
- data/test/remote_tests/remote_secure_pay_test.rb +0 -36
- data/test/remote_tests/remote_trust_commerce_test.rb +0 -136
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
module ActiveMerchant #:nodoc:
|
|
2
2
|
module Billing #:nodoc:
|
|
3
3
|
module CreditCardFormatting
|
|
4
|
-
|
|
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)
|
|
5
10
|
return '' if number.blank?
|
|
6
11
|
|
|
7
|
-
case
|
|
8
|
-
|
|
9
|
-
sprintf("%.
|
|
10
|
-
|
|
11
|
-
sprintf("%.4i", number)[-4..-1]
|
|
12
|
-
else
|
|
13
|
-
number
|
|
12
|
+
case option
|
|
13
|
+
when :two_digits ; sprintf("%.2i", number)[-2..-1]
|
|
14
|
+
when :four_digits ; sprintf("%.4i", number)[-4..-1]
|
|
15
|
+
else number
|
|
14
16
|
end
|
|
15
17
|
end
|
|
18
|
+
|
|
16
19
|
end
|
|
17
20
|
end
|
|
18
21
|
end
|
|
@@ -2,6 +2,21 @@ module ActiveMerchant #:nodoc:
|
|
|
2
2
|
module Billing #:nodoc:
|
|
3
3
|
# Convenience methods that can be included into a custom Credit Card object, such as an ActiveRecord based Credit Card object.
|
|
4
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})\d{12}$/,
|
|
9
|
+
'american_express' => /^3[47]\d{13}$/,
|
|
10
|
+
'diners_club' => /^3(0[0-5]|[68]\d)\d{11}$/,
|
|
11
|
+
'jcb' => /^3528\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[89]\d{11}(\d{2,3})?|670695\d{13})$/
|
|
18
|
+
}
|
|
19
|
+
|
|
5
20
|
def self.included(base)
|
|
6
21
|
base.extend(ClassMethods)
|
|
7
22
|
end
|
|
@@ -23,51 +38,44 @@ module ActiveMerchant #:nodoc:
|
|
|
23
38
|
end
|
|
24
39
|
|
|
25
40
|
module ClassMethods
|
|
26
|
-
# Returns true if it validates. Optionally, you can pass a card type as an argument and
|
|
27
|
-
#
|
|
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:
|
|
28
45
|
# - http://perl.about.com/compute/perl/library/nosearch/P073000.htm
|
|
29
46
|
# - http://www.beachnet.com/~hstiles/cardtype.html
|
|
30
47
|
def valid_number?(number)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
sum = 0
|
|
36
|
-
for i in 0..number.length
|
|
37
|
-
weight = number[-1 * (i + 2), 1].to_i * (2 - (i % 2))
|
|
38
|
-
sum += (weight < 10) ? weight : weight - 9
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
(number[-1,1].to_i == (10 - sum % 10) % 10)
|
|
48
|
+
valid_test_mode_card_number?(number) ||
|
|
49
|
+
valid_card_number_length?(number) &&
|
|
50
|
+
valid_checksum?(number)
|
|
42
51
|
end
|
|
43
52
|
|
|
44
|
-
# Regular expressions for the known card companies
|
|
45
|
-
#
|
|
46
|
-
#
|
|
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
|
|
47
58
|
def card_companies
|
|
48
|
-
|
|
49
|
-
'visa' => /^4\d{12}(\d{3})?$/,
|
|
50
|
-
'master' => /^(5[1-5]\d{4}|677189)\d{10}$/,
|
|
51
|
-
'discover' => /^6011\d{12}$/,
|
|
52
|
-
'american_express' => /^3[47]\d{13}$/,
|
|
53
|
-
'diners_club' => /^3(0[0-5]|[68]\d)\d{11}$/,
|
|
54
|
-
'jcb' => /^3528\d{12}$/,
|
|
55
|
-
'switch' => /^6759\d{12}(\d{2,3})?$/,
|
|
56
|
-
'solo' => /^6767\d{12}(\d{2,3})?$/,
|
|
57
|
-
'dankort' => /^5019\d{12}$/,
|
|
58
|
-
'maestro' => /^(5[06-8]|6\d)\d{14}$/,
|
|
59
|
-
'forbrugsforeningen' => /^600722\d{10}$/,
|
|
60
|
-
'laser' => /^(6304[89]\d{11}(\d{2,3})?|670695\d{12})$/
|
|
61
|
-
}
|
|
59
|
+
CARD_COMPANIES
|
|
62
60
|
end
|
|
63
61
|
|
|
64
62
|
# Returns a string containing the type of card from the list of known information below.
|
|
65
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
|
|
66
74
|
#
|
|
67
75
|
def type?(number)
|
|
68
|
-
return '
|
|
76
|
+
return 'bogus' if valid_test_mode_card_number?(number)
|
|
69
77
|
|
|
70
|
-
card_companies.reject{ |c,p| c == 'maestro' }.each do |company, pattern|
|
|
78
|
+
card_companies.reject { |c,p| c == 'maestro' }.each do |company, pattern|
|
|
71
79
|
return company.dup if number =~ pattern
|
|
72
80
|
end
|
|
73
81
|
|
|
@@ -75,6 +83,42 @@ module ActiveMerchant #:nodoc:
|
|
|
75
83
|
|
|
76
84
|
return nil
|
|
77
85
|
end
|
|
86
|
+
|
|
87
|
+
def last_digits(number)
|
|
88
|
+
number.to_s.slice(-4..-1) if number.to_s.length >= 4
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def mask(number)
|
|
92
|
+
"XXXX-XXXX-XXXX-#{last_digits(number)}" if number.to_s.length >= 4
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Checks to see if the calculated type matches the specified type
|
|
96
|
+
def matching_type?(number, type)
|
|
97
|
+
type?(number) == type
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def valid_card_number_length?(number) #:nodoc:
|
|
103
|
+
number.to_s.length >= 12
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def valid_test_mode_card_number?(number) #:nodoc:
|
|
107
|
+
ActiveMerchant::Billing::Base.test? &&
|
|
108
|
+
%w[1 2 3 success failure error].include?(number.to_s)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Checks the validity of a card number by use of the the Luhn Algorithm.
|
|
112
|
+
# Please see http://en.wikipedia.org/wiki/Luhn_algorithm for details.
|
|
113
|
+
def valid_checksum?(number) #:nodoc:
|
|
114
|
+
sum = 0
|
|
115
|
+
for i in 0..number.length
|
|
116
|
+
weight = number[-1 * (i + 2), 1].to_i * (2 - (i % 2))
|
|
117
|
+
sum += (weight < 10) ? weight : weight - 9
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
(number[-1,1].to_i == (10 - sum % 10) % 10)
|
|
121
|
+
end
|
|
78
122
|
end
|
|
79
123
|
end
|
|
80
124
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module ActiveMerchant
|
|
2
|
+
module Billing
|
|
3
|
+
# Result of the Card Verification Value check
|
|
4
|
+
# http://www.bbbonline.org/eExport/doc/MerchantGuide_cvv2.pdf
|
|
5
|
+
# Check additional codes from cybersource website
|
|
6
|
+
class CVVResult
|
|
7
|
+
|
|
8
|
+
MESSAGES = {
|
|
9
|
+
'D' => 'Suspicious transaction',
|
|
10
|
+
'I' => 'Failed data validation check',
|
|
11
|
+
'M' => 'Match',
|
|
12
|
+
'N' => 'No Match',
|
|
13
|
+
'P' => 'Not Processed',
|
|
14
|
+
'S' => 'Should have been present',
|
|
15
|
+
'U' => 'Issuer unable to process request',
|
|
16
|
+
'X' => 'Card does not support verification'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
def self.messages
|
|
20
|
+
MESSAGES
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
attr_reader :code, :message
|
|
24
|
+
|
|
25
|
+
def initialize(code)
|
|
26
|
+
@code = code.upcase unless code.blank?
|
|
27
|
+
@message = MESSAGES[@code]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_hash
|
|
31
|
+
{
|
|
32
|
+
'code' => code,
|
|
33
|
+
'message' => message
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module ActiveMerchant
|
|
2
|
+
module Billing
|
|
3
|
+
class CreditCard
|
|
4
|
+
class ExpiryDate #:nodoc:
|
|
5
|
+
attr_reader :month, :year
|
|
6
|
+
def initialize(month, year)
|
|
7
|
+
@month = month
|
|
8
|
+
@year = year
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def expired? #:nodoc:
|
|
12
|
+
Time.now > expiration rescue true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def expiration #:nodoc:
|
|
16
|
+
Time.parse("#{month}/#{month_days}/#{year} 23:59:59") rescue Time.at(0)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
def month_days
|
|
21
|
+
mdays = [nil,31,28,31,30,31,30,31,31,30,31,30,31]
|
|
22
|
+
mdays[2] = 29 if Date.leap?(year)
|
|
23
|
+
mdays[month]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require 'net/http'
|
|
2
2
|
require 'net/https'
|
|
3
|
-
require 'digest/md5'
|
|
4
3
|
require 'active_merchant/billing/response'
|
|
5
4
|
|
|
6
5
|
module ActiveMerchant #:nodoc:
|
|
@@ -9,93 +8,69 @@ module ActiveMerchant #:nodoc:
|
|
|
9
8
|
# == Description
|
|
10
9
|
# The Gateway class is the base class for all ActiveMerchant gateway implementations.
|
|
11
10
|
#
|
|
12
|
-
# The list of gateway functions that concrete gateway
|
|
13
|
-
# the following:
|
|
11
|
+
# The standard list of gateway functions that most concrete gateway subclasses implement is:
|
|
14
12
|
#
|
|
15
13
|
# * <tt>purchase(money, creditcard, options = {})</tt>
|
|
16
14
|
# * <tt>authorize(money, creditcard, options = {})</tt>
|
|
17
15
|
# * <tt>capture(money, authorization, options = {})</tt>
|
|
18
16
|
# * <tt>void(identification, options = {})</tt>
|
|
19
17
|
# * <tt>credit(money, identification, options = {})</tt>
|
|
20
|
-
#
|
|
21
|
-
# == Setting Up Your Gateway
|
|
22
|
-
# Aside from the obvious authorization parameters (login and password), you can set up your
|
|
23
|
-
# gateway using numerous options. Be sure to reference your gateway of choice's documentation
|
|
24
|
-
# before overriding it's default values that may be defined.
|
|
25
|
-
#
|
|
26
|
-
# * <tt>Gateway.default_currency</tt>: sets the default currency if none is provided. See
|
|
27
|
-
# http://en.wikipedia.org/wiki/ISO_4217#Active_codes for active currency codes.
|
|
28
|
-
#
|
|
29
|
-
# * <tt>Gateway.supported_countries</tt>: sets the countries of _merchants_ the gateway supports.
|
|
30
|
-
#
|
|
31
|
-
# * <tt>Gateway.supported_cardtypes</tt>: sets the card types supported by the gateway.
|
|
32
|
-
#
|
|
33
|
-
# * <tt>Gateway.homepage_url</tt>: sets the URL at which the gateway may be found.
|
|
34
|
-
#
|
|
35
|
-
# * <tt>Gateway.display_name</tt>: sets the name of the gateway for display purposes, such as generating documentation.
|
|
36
|
-
#
|
|
37
|
-
# * <tt>Gateway.application_id</tt>: This is the application making calls to the gateway. This
|
|
38
|
-
# is useful for things like the Paypal build notation (BN) id fields.
|
|
39
|
-
#
|
|
40
|
-
# * <tt>Gateway.money_format</tt>: this attribute may be set to <tt>:dollars</tt> or
|
|
41
|
-
# <tt>:cents</tt>. Use this to set the expected money format you'll be inputting.
|
|
42
18
|
#
|
|
19
|
+
# Some gateways include features for recurring billing
|
|
20
|
+
#
|
|
21
|
+
# * <tt>recurring(money, creditcard, options = {})</tt>
|
|
22
|
+
#
|
|
23
|
+
# Some gateways also support features for storing credit cards:
|
|
24
|
+
#
|
|
25
|
+
# * <tt>store(creditcard, options = {})</tt>
|
|
26
|
+
# * <tt>unstore(identification, options = {})</tt>
|
|
43
27
|
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
# <tt
|
|
65
|
-
# <tt
|
|
66
|
-
# <tt
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
# requests.
|
|
75
|
-
#
|
|
76
|
-
# As with local tests, first ensure that you are in test mode:
|
|
77
|
-
#
|
|
78
|
-
# ActiveMerchant::Base.mode = :test
|
|
79
|
-
#
|
|
80
|
-
# (See ActiveMerchant::Base for more details.)
|
|
81
|
-
#
|
|
82
|
-
# Test requests may then be made using appropriate parameters provided by your gateway of
|
|
83
|
-
# choice. For instance, the Moneris gateway provides a test MasterCard and Visa number that
|
|
84
|
-
# one may use to process test purchases and authorization requests.
|
|
85
|
-
#
|
|
86
|
-
# Given that these remote tests will take longer to run than local tests, it is recommended
|
|
87
|
-
# that you comment them out, or disable them when not required.
|
|
28
|
+
# === Gateway Options
|
|
29
|
+
# The options hash consists of the following options:
|
|
30
|
+
#
|
|
31
|
+
# * <tt>:order_id</tt> - The order number
|
|
32
|
+
# * <tt>:ip</tt> - The IP address of the customer making the purchase
|
|
33
|
+
# * <tt>:customer</tt> - The name, customer number, or other information that identifies the customer
|
|
34
|
+
# * <tt>:invoice</tt> - The invoice number
|
|
35
|
+
# * <tt>:merchant</tt> - The name or description of the merchant offering the product
|
|
36
|
+
# * <tt>:description</tt> - A description of the transaction
|
|
37
|
+
# * <tt>:email</tt> - The email address of the customer
|
|
38
|
+
# * <tt>:currency</tt> - The currency of the transaction. Only important when you are using a currency that is not the default with a gateway that supports multiple currencies.
|
|
39
|
+
# * <tt>:billing_address</tt> - A hash containing the billing address of the customer.
|
|
40
|
+
# * <tt>:shipping_address</tt> - A hash containing the shipping address of the customer.
|
|
41
|
+
#
|
|
42
|
+
# The <tt>:billing_address</tt>, and <tt>:shipping_address</tt> hashes can have the following keys:
|
|
43
|
+
#
|
|
44
|
+
# * <tt>:name</tt> - The full name of the customer.
|
|
45
|
+
# * <tt>:company</tt> - The company name of the customer.
|
|
46
|
+
# * <tt>:address1</tt> - The primary street address of the customer.
|
|
47
|
+
# * <tt>:address2</tt> - Additional line of address information.
|
|
48
|
+
# * <tt>:city</tt> - The city of the customer.
|
|
49
|
+
# * <tt>:state</tt> - The state of the customer. The 2 digit code for US and Canadian addresses. The full name of the state or province for foreign addresses.
|
|
50
|
+
# * <tt>:country</tt> - The [ISO 3166-1-alpha-2 code](http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm) for the customer.
|
|
51
|
+
# * <tt>:zip</tt> - The zip or postal code of the customer.
|
|
52
|
+
# * <tt>:phone</tt> - The phone number of the customer.
|
|
53
|
+
#
|
|
54
|
+
# == Implmenting new gateways
|
|
55
|
+
#
|
|
56
|
+
# See the {ActiveMerchant Guide to Contributing}[http://code.google.com/p/activemerchant/wiki/Contributing]
|
|
57
|
+
#
|
|
88
58
|
class Gateway
|
|
89
59
|
include PostsData
|
|
90
60
|
include RequiresParameters
|
|
91
61
|
include CreditCardFormatting
|
|
92
|
-
|
|
93
|
-
## Constants
|
|
62
|
+
include Utils
|
|
94
63
|
|
|
95
64
|
DEBIT_CARDS = [ :switch, :solo ]
|
|
96
65
|
|
|
97
|
-
|
|
66
|
+
cattr_reader :implementations
|
|
67
|
+
@@implementations = []
|
|
98
68
|
|
|
69
|
+
def self.inherited(subclass)
|
|
70
|
+
super
|
|
71
|
+
@@implementations << subclass
|
|
72
|
+
end
|
|
73
|
+
|
|
99
74
|
# The format of the amounts used by the gateway
|
|
100
75
|
# :dollars => '12.50'
|
|
101
76
|
# :cents => '1250'
|
|
@@ -128,8 +103,6 @@ module ActiveMerchant #:nodoc:
|
|
|
128
103
|
supported_cardtypes.include?(card_type.to_sym)
|
|
129
104
|
end
|
|
130
105
|
|
|
131
|
-
## Instance Methods
|
|
132
|
-
|
|
133
106
|
# Initialize a new gateway.
|
|
134
107
|
#
|
|
135
108
|
# See the documentation for the gateway you will be using to make sure there are no other
|
|
@@ -148,32 +121,6 @@ module ActiveMerchant #:nodoc:
|
|
|
148
121
|
self.class.name.scan(/\:\:(\w+)Gateway/).flatten.first
|
|
149
122
|
end
|
|
150
123
|
|
|
151
|
-
# This is used to check if our credit card number implies that we are seeking a test
|
|
152
|
-
# Response. Of course, this returns false if we are not in test mode.
|
|
153
|
-
#
|
|
154
|
-
# Recognized values:
|
|
155
|
-
# <tt>1</tt>:: Result will be successful
|
|
156
|
-
# <tt>2</tt>:: Result will be a failure
|
|
157
|
-
# <tt>3</tt>:: Result will raise a miscellaneous error
|
|
158
|
-
#
|
|
159
|
-
# All other values will not be recognized.
|
|
160
|
-
#--
|
|
161
|
-
# TODO Refactor this method. It's kind of on the ugly side of things.
|
|
162
|
-
def test_result_from_cc_number(card_number)
|
|
163
|
-
return false unless test?
|
|
164
|
-
|
|
165
|
-
case card_number.to_s
|
|
166
|
-
when '1', 'success'
|
|
167
|
-
Response.new(true, 'Successful test mode response', {:receiptid => '#0001'}, :test => true, :authorization => '5555')
|
|
168
|
-
when '2', 'failure'
|
|
169
|
-
Response.new(false, 'Failed test mode response', {:receiptid => '#0001'}, :test => true)
|
|
170
|
-
when '3', 'error'
|
|
171
|
-
raise Error, 'big bad exception'
|
|
172
|
-
else
|
|
173
|
-
false
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
|
|
177
124
|
# Return a String with the amount in the appropriate format
|
|
178
125
|
#--
|
|
179
126
|
# TODO Refactor this method. It's a tad on the ugly side.
|
|
@@ -201,17 +148,6 @@ module ActiveMerchant #:nodoc:
|
|
|
201
148
|
return false if credit_card.type.blank?
|
|
202
149
|
DEBIT_CARDS.include?(credit_card.type.to_sym)
|
|
203
150
|
end
|
|
204
|
-
|
|
205
|
-
def generate_unique_id
|
|
206
|
-
md5 = Digest::MD5.new
|
|
207
|
-
now = Time.now
|
|
208
|
-
md5 << now.to_s
|
|
209
|
-
md5 << String(now.usec)
|
|
210
|
-
md5 << String(rand(0))
|
|
211
|
-
md5 << String($$)
|
|
212
|
-
md5 << self.class.name
|
|
213
|
-
md5.hexdigest
|
|
214
|
-
end
|
|
215
151
|
end
|
|
216
152
|
end
|
|
217
153
|
end
|
|
@@ -1,168 +1,292 @@
|
|
|
1
1
|
module ActiveMerchant #:nodoc:
|
|
2
2
|
module Billing #:nodoc:
|
|
3
|
-
|
|
3
|
+
# For more information on the Authorize.Net Gateway please visit their {Integration Center}[http://developer.authorize.net/]
|
|
4
|
+
#
|
|
5
|
+
# The login and password are not the username and password you use to
|
|
6
|
+
# login to the Authorize.Net Merchant Interface. Instead, you will
|
|
7
|
+
# use the API Login ID as the login and Transaction Key as the
|
|
8
|
+
# password.
|
|
9
|
+
#
|
|
10
|
+
# ==== How to Get Your API Login ID and Transaction Key
|
|
11
|
+
#
|
|
12
|
+
# 1. Log into the Merchant Interface
|
|
13
|
+
# 2. Select Settings from the Main Menu
|
|
14
|
+
# 3. Click on API Login ID and Transaction Key in the Security section
|
|
15
|
+
# 4. Type in the answer to the secret question configured on setup
|
|
16
|
+
# 5. Click Submit
|
|
17
|
+
#
|
|
18
|
+
# ==== Automated Recurring Billing (ARB)
|
|
19
|
+
#
|
|
20
|
+
# Automated Recurring Billing (ARB) is an optional service for submitting and managing recurring, or subscription-based, transactions.
|
|
21
|
+
#
|
|
22
|
+
# To use recurring, update_recurring, and cancel_recurring ARB must be enabled for your account.
|
|
23
|
+
#
|
|
24
|
+
# Information about ARB is available on the {Authorize.Net website}[http://www.authorize.net/solutions/merchantsolutions/merchantservices/automatedrecurringbilling/].
|
|
25
|
+
# Information about the ARB API is available at the {Authorize.Net Integration Center}[http://developer.authorize.net/]
|
|
4
26
|
class AuthorizeNetGateway < Gateway
|
|
5
27
|
API_VERSION = '3.1'
|
|
6
|
-
|
|
7
|
-
class_inheritable_accessor :test_url, :live_url
|
|
8
|
-
|
|
28
|
+
|
|
29
|
+
class_inheritable_accessor :test_url, :live_url, :arb_test_url, :arb_live_url
|
|
30
|
+
|
|
9
31
|
self.test_url = "https://test.authorize.net/gateway/transact.dll"
|
|
10
32
|
self.live_url = "https://secure.authorize.net/gateway/transact.dll"
|
|
11
|
-
|
|
12
|
-
|
|
33
|
+
|
|
34
|
+
self.arb_test_url = 'https://apitest.authorize.net/xml/v1/request.api'
|
|
35
|
+
self.arb_live_url = 'https://api.authorize.net/xml/v1/request.api'
|
|
36
|
+
|
|
37
|
+
APPROVED, DECLINED, ERROR, FRAUD_REVIEW = 1, 2, 3, 4
|
|
13
38
|
|
|
14
39
|
RESPONSE_CODE, RESPONSE_REASON_CODE, RESPONSE_REASON_TEXT = 0, 2, 3
|
|
15
40
|
AVS_RESULT_CODE, TRANSACTION_ID, CARD_CODE_RESPONSE_CODE = 5, 6, 38
|
|
16
41
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"N" => "Card verification number didn't match",
|
|
22
|
-
"P" => "Card verification number was not processed",
|
|
23
|
-
"S" => "Card verification number should be on card but was not indicated",
|
|
24
|
-
"U" => "Issuer was not certified for card verification"
|
|
25
|
-
}
|
|
42
|
+
self.supported_countries = ['US']
|
|
43
|
+
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
|
44
|
+
self.homepage_url = 'http://www.authorize.net/'
|
|
45
|
+
self.display_name = 'Authorize.Net'
|
|
26
46
|
|
|
47
|
+
CARD_CODE_ERRORS = %w( N S )
|
|
27
48
|
AVS_ERRORS = %w( A E N R W Z )
|
|
28
49
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"P" => "Address verification not applicable for this transaction",
|
|
36
|
-
"R" => "Payment gateway was unavailable or timed out",
|
|
37
|
-
"S" => "Address verification service not supported by issuer",
|
|
38
|
-
"U" => "Address information is unavailable",
|
|
39
|
-
"W" => "9-digit zip/postal code matches billing information, street address does not",
|
|
40
|
-
"X" => "Street address and 9-digit zip/postal code matches billing information",
|
|
41
|
-
"Y" => "Street address and 5-digit zip/postal code matches billing information",
|
|
42
|
-
"Z" => "5-digit zip/postal code matches billing information, street address does not",
|
|
50
|
+
AUTHORIZE_NET_ARB_NAMESPACE = 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'
|
|
51
|
+
|
|
52
|
+
RECURRING_ACTIONS = {
|
|
53
|
+
:create => 'ARBCreateSubscription',
|
|
54
|
+
:update => 'ARBUpdateSubscription',
|
|
55
|
+
:cancel => 'ARBCancelSubscription'
|
|
43
56
|
}
|
|
44
|
-
|
|
45
|
-
# URL
|
|
46
|
-
attr_reader :url
|
|
47
|
-
attr_reader :response
|
|
48
|
-
attr_reader :options
|
|
49
|
-
|
|
50
|
-
self.supported_countries = ['US']
|
|
51
|
-
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
|
52
|
-
self.homepage_url = 'http://www.authorize.net/'
|
|
53
|
-
self.display_name = 'Authorize.net'
|
|
54
57
|
|
|
58
|
+
# Creates a new AuthorizeNetGateway
|
|
59
|
+
#
|
|
60
|
+
# The gateway requires that a valid login and password be passed
|
|
61
|
+
# in the +options+ hash.
|
|
62
|
+
#
|
|
63
|
+
# ==== Options
|
|
64
|
+
#
|
|
65
|
+
# * <tt>:login</tt> -- The Authorize.Net API Login ID (REQUIRED)
|
|
66
|
+
# * <tt>:password</tt> -- The Authorize.Net Transaction Key. (REQUIRED)
|
|
67
|
+
# * <tt>:test</tt> -- +true+ or +false+. If true, perform transactions against the test server.
|
|
68
|
+
# Otherwise, perform transactions against the production server.
|
|
55
69
|
def initialize(options = {})
|
|
56
70
|
requires!(options, :login, :password)
|
|
57
71
|
@options = options
|
|
58
72
|
super
|
|
59
|
-
end
|
|
60
|
-
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Performs an authorization, which reserves the funds on the customer's credit card, but does not
|
|
76
|
+
# charge the card.
|
|
77
|
+
#
|
|
78
|
+
# ==== Parameters
|
|
79
|
+
#
|
|
80
|
+
# * <tt>money</tt> -- The amount to be authorized. Either an Integer value in cents or a Money object.
|
|
81
|
+
# * <tt>creditcard</tt> -- The CreditCard details for the transaction.
|
|
82
|
+
# * <tt>options</tt> -- A hash of optional parameters.
|
|
61
83
|
def authorize(money, creditcard, options = {})
|
|
62
84
|
post = {}
|
|
63
85
|
add_invoice(post, options)
|
|
64
|
-
add_creditcard(post, creditcard)
|
|
65
|
-
add_address(post, options)
|
|
86
|
+
add_creditcard(post, creditcard)
|
|
87
|
+
add_address(post, options)
|
|
66
88
|
add_customer_data(post, options)
|
|
67
|
-
|
|
89
|
+
|
|
68
90
|
commit('AUTH_ONLY', money, post)
|
|
69
91
|
end
|
|
70
|
-
|
|
92
|
+
|
|
93
|
+
# Perform a purchase, which is essentially an authorization and capture in a single operation.
|
|
94
|
+
#
|
|
95
|
+
# ==== Parameters
|
|
96
|
+
#
|
|
97
|
+
# * <tt>money</tt> -- The amount to be purchased. Either an Integer value in cents or a Money object.
|
|
98
|
+
# * <tt>creditcard</tt> -- The CreditCard details for the transaction.
|
|
99
|
+
# * <tt>options</tt> -- A hash of optional parameters.
|
|
71
100
|
def purchase(money, creditcard, options = {})
|
|
72
101
|
post = {}
|
|
73
102
|
add_invoice(post, options)
|
|
74
|
-
add_creditcard(post, creditcard)
|
|
75
|
-
add_address(post, options)
|
|
103
|
+
add_creditcard(post, creditcard)
|
|
104
|
+
add_address(post, options)
|
|
76
105
|
add_customer_data(post, options)
|
|
77
|
-
|
|
106
|
+
|
|
78
107
|
commit('AUTH_CAPTURE', money, post)
|
|
79
|
-
end
|
|
80
|
-
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Captures the funds from an authorized transaction.
|
|
111
|
+
#
|
|
112
|
+
# ==== Parameters
|
|
113
|
+
#
|
|
114
|
+
# * <tt>money</tt> -- The amount to be captured. Either an Integer value in cents or a Money object.
|
|
115
|
+
# * <tt>authorization</tt> -- The authorization returned from the previous authorize request.
|
|
81
116
|
def capture(money, authorization, options = {})
|
|
82
117
|
post = {:trans_id => authorization}
|
|
83
118
|
add_customer_data(post, options)
|
|
84
119
|
commit('PRIOR_AUTH_CAPTURE', money, post)
|
|
85
120
|
end
|
|
86
121
|
|
|
122
|
+
# Void a previous transaction
|
|
123
|
+
#
|
|
124
|
+
# ==== Parameters
|
|
125
|
+
#
|
|
126
|
+
# * <tt>authorization</tt> - The authorization returned from the previous authorize request.
|
|
87
127
|
def void(authorization, options = {})
|
|
88
128
|
post = {:trans_id => authorization}
|
|
89
129
|
commit('VOID', nil, post)
|
|
90
130
|
end
|
|
91
|
-
|
|
131
|
+
|
|
132
|
+
# Credit an account.
|
|
133
|
+
#
|
|
134
|
+
# This transaction is also referred to as a Refund and indicates to the gateway that
|
|
135
|
+
# money should flow from the merchant to the customer.
|
|
136
|
+
#
|
|
137
|
+
# ==== Parameters
|
|
138
|
+
#
|
|
139
|
+
# * <tt>money</tt> -- The amount to be credited to the customer. Either an Integer value in cents or a Money object.
|
|
140
|
+
# * <tt>identification</tt> -- The ID of the original transaction against which the credit is being issued.
|
|
141
|
+
# * <tt>options</tt> -- A hash of parameters.
|
|
142
|
+
#
|
|
143
|
+
# ==== Options
|
|
144
|
+
#
|
|
145
|
+
# * <tt>:card_number</tt> -- The credit card number the credit is being issued to. (REQUIRED)
|
|
92
146
|
def credit(money, identification, options = {})
|
|
93
147
|
requires!(options, :card_number)
|
|
94
|
-
|
|
148
|
+
|
|
95
149
|
post = { :trans_id => identification,
|
|
96
150
|
:card_num => options[:card_number]
|
|
97
151
|
}
|
|
152
|
+
add_invoice(post, options)
|
|
98
153
|
|
|
99
154
|
commit('CREDIT', money, post)
|
|
100
155
|
end
|
|
101
|
-
|
|
102
|
-
|
|
156
|
+
|
|
157
|
+
# Create a recurring payment.
|
|
158
|
+
#
|
|
159
|
+
# This transaction creates a new Automated Recurring Billing (ARB) subscription. Your account must have ARB enabled.
|
|
160
|
+
#
|
|
161
|
+
# ==== Parameters
|
|
162
|
+
#
|
|
163
|
+
# * <tt>money</tt> -- The amount to be charged to the customer at each interval. Either an Integer value in cents or
|
|
164
|
+
# a Money object.
|
|
165
|
+
# * <tt>creditcard</tt> -- The CreditCard details for the transaction.
|
|
166
|
+
# * <tt>options</tt> -- A hash of parameters.
|
|
167
|
+
#
|
|
168
|
+
# ==== Options
|
|
169
|
+
#
|
|
170
|
+
# * <tt>:interval</tt> -- A hash containing information about the interval of time between payments. Must
|
|
171
|
+
# contain the keys <tt>:length</tt> and <tt>:unit</tt>. <tt>:unit</tt> can be either <tt>:months</tt> or <tt>:days</tt>.
|
|
172
|
+
# If <tt>:unit</tt> is <tt>:months</tt> then <tt>:interval</tt> must be an integer between 1 and 12 inclusive.
|
|
173
|
+
# If <tt>:unit</tt> is <tt>:days</tt> then <tt>:interval</tt> must be an integer between 7 and 365 inclusive.
|
|
174
|
+
# For example, to charge the customer once every three months the hash would be
|
|
175
|
+
# +{ :unit => :months, :interval => 3 }+ (REQUIRED)
|
|
176
|
+
# * <tt>:duration</tt> -- A hash containing keys for the <tt>:start_date</tt> the subscription begins (also the date the
|
|
177
|
+
# initial billing occurs) and the total number of billing <tt>:occurences</tt> or payments for the subscription. (REQUIRED)
|
|
178
|
+
def recurring(money, creditcard, options={})
|
|
179
|
+
requires!(options, :interval, :duration, :billing_address)
|
|
180
|
+
requires!(options[:interval], :length, [:unit, :days, :months])
|
|
181
|
+
requires!(options[:duration], :start_date, :occurrences)
|
|
182
|
+
requires!(options[:billing_address], :first_name, :last_name)
|
|
183
|
+
|
|
184
|
+
options[:credit_card] = creditcard
|
|
185
|
+
options[:amount] = money
|
|
186
|
+
|
|
187
|
+
request = build_recurring_request(:create, options)
|
|
188
|
+
recurring_commit(:create, request)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Update a recurring payment's details.
|
|
192
|
+
#
|
|
193
|
+
# This transaction updates an existing Automated Recurring Billing (ARB) subscription. Your account must have ARB enabled
|
|
194
|
+
# and the subscription must have already been created previously by calling +recurring()+. The ability to change certain
|
|
195
|
+
# details about a recurring payment is dependent on transaction history and cannot be determined until after calling
|
|
196
|
+
# +update_recurring()+. See the ARB XML Guide for such conditions.
|
|
197
|
+
#
|
|
198
|
+
# ==== Parameters
|
|
199
|
+
#
|
|
200
|
+
# * <tt>options</tt> -- A hash of parameters.
|
|
201
|
+
#
|
|
202
|
+
# ==== Options
|
|
203
|
+
#
|
|
204
|
+
# * <tt>:subscription_id</tt> -- A string containing the <tt>:subscription_id</tt> of the recurring payment already in place
|
|
205
|
+
# for a given credit card. (REQUIRED)
|
|
206
|
+
def update_recurring(options={})
|
|
207
|
+
requires!(options, :subscription_id)
|
|
208
|
+
request = build_recurring_request(:update, options)
|
|
209
|
+
recurring_commit(:update, request)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Cancel a recurring payment.
|
|
213
|
+
#
|
|
214
|
+
# This transaction cancels an existing Automated Recurring Billing (ARB) subscription. Your account must have ARB enabled
|
|
215
|
+
# and the subscription must have already been created previously by calling recurring()
|
|
216
|
+
#
|
|
217
|
+
# ==== Parameters
|
|
218
|
+
#
|
|
219
|
+
# * <tt>subscription_id</tt> -- A string containing the +subscription_id+ of the recurring payment already in place
|
|
220
|
+
# for a given credit card. (REQUIRED)
|
|
221
|
+
def cancel_recurring(subscription_id)
|
|
222
|
+
request = build_recurring_request(:cancel, :subscription_id => subscription_id)
|
|
223
|
+
recurring_commit(:cancel, request)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
private
|
|
227
|
+
|
|
103
228
|
def commit(action, money, parameters)
|
|
104
|
-
parameters[:amount]
|
|
105
|
-
|
|
229
|
+
parameters[:amount] = amount(money) unless action == 'VOID'
|
|
230
|
+
|
|
106
231
|
# Only activate the test_request when the :test option is passed in
|
|
107
|
-
parameters[:test_request] = @options[:test] ? 'TRUE' : 'FALSE'
|
|
108
|
-
|
|
109
|
-
if result = test_result_from_cc_number(parameters[:card_num])
|
|
110
|
-
return result
|
|
111
|
-
end
|
|
112
|
-
|
|
232
|
+
parameters[:test_request] = @options[:test] ? 'TRUE' : 'FALSE'
|
|
233
|
+
|
|
113
234
|
url = test? ? self.test_url : self.live_url
|
|
114
235
|
data = ssl_post url, post_data(action, parameters)
|
|
115
236
|
|
|
116
|
-
|
|
237
|
+
response = parse(data)
|
|
117
238
|
|
|
118
|
-
|
|
119
|
-
message = message_from(@response)
|
|
239
|
+
message = message_from(response)
|
|
120
240
|
|
|
121
|
-
# Return the response. The authorization can be taken out of the transaction_id
|
|
241
|
+
# Return the response. The authorization can be taken out of the transaction_id
|
|
122
242
|
# Test Mode on/off is something we have to parse from the response text.
|
|
123
243
|
# It usually looks something like this
|
|
124
244
|
#
|
|
125
245
|
# (TESTMODE) Successful Sale
|
|
126
|
-
#
|
|
127
|
-
|
|
128
246
|
test_mode = test? || message =~ /TESTMODE/
|
|
129
|
-
|
|
130
|
-
Response.new(success, message,
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
247
|
+
|
|
248
|
+
Response.new(success?(response), message, response,
|
|
249
|
+
:test => test_mode,
|
|
250
|
+
:authorization => response[:transaction_id],
|
|
251
|
+
:fraud_review => fraud_review?(response),
|
|
252
|
+
:avs_result => { :code => response[:avs_result_code] },
|
|
253
|
+
:cvv_result => response[:card_code]
|
|
254
|
+
)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def success?(response)
|
|
258
|
+
response[:response_code] == APPROVED
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def fraud_review?(response)
|
|
262
|
+
response[:response_code] == FRAUD_REVIEW
|
|
134
263
|
end
|
|
135
|
-
|
|
264
|
+
|
|
136
265
|
def parse(body)
|
|
137
266
|
fields = split(body)
|
|
138
|
-
|
|
139
|
-
results = {
|
|
267
|
+
|
|
268
|
+
results = {
|
|
140
269
|
:response_code => fields[RESPONSE_CODE].to_i,
|
|
141
270
|
:response_reason_code => fields[RESPONSE_REASON_CODE],
|
|
142
271
|
:response_reason_text => fields[RESPONSE_REASON_TEXT],
|
|
143
272
|
:avs_result_code => fields[AVS_RESULT_CODE],
|
|
144
273
|
:transaction_id => fields[TRANSACTION_ID],
|
|
145
|
-
:card_code => fields[CARD_CODE_RESPONSE_CODE]
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
results[:card_code_message] = CARD_CODE_MESSAGES[results[:card_code]] if results[:card_code]
|
|
150
|
-
results[:avs_message] = AVS_MESSAGES[results[:avs_result_code]] if results[:avs_result_code]
|
|
151
|
-
|
|
274
|
+
:card_code => fields[CARD_CODE_RESPONSE_CODE]
|
|
275
|
+
}
|
|
152
276
|
results
|
|
153
|
-
end
|
|
277
|
+
end
|
|
154
278
|
|
|
155
279
|
def post_data(action, parameters = {})
|
|
156
280
|
post = {}
|
|
157
281
|
|
|
158
|
-
post[:version]
|
|
159
|
-
post[:login]
|
|
160
|
-
post[:tran_key]
|
|
282
|
+
post[:version] = API_VERSION
|
|
283
|
+
post[:login] = @options[:login]
|
|
284
|
+
post[:tran_key] = @options[:password]
|
|
161
285
|
post[:relay_response] = "FALSE"
|
|
162
|
-
post[:type]
|
|
163
|
-
post[:delim_data]
|
|
164
|
-
post[:delim_char]
|
|
165
|
-
post[:encap_char]
|
|
286
|
+
post[:type] = action
|
|
287
|
+
post[:delim_data] = "TRUE"
|
|
288
|
+
post[:delim_char] = ","
|
|
289
|
+
post[:encap_char] = "$"
|
|
166
290
|
|
|
167
291
|
request = post.merge(parameters).collect { |key, value| "x_#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
|
168
292
|
request
|
|
@@ -172,43 +296,43 @@ module ActiveMerchant #:nodoc:
|
|
|
172
296
|
post[:invoice_num] = options[:order_id]
|
|
173
297
|
post[:description] = options[:description]
|
|
174
298
|
end
|
|
175
|
-
|
|
176
|
-
def add_creditcard(post, creditcard)
|
|
177
|
-
post[:card_num]
|
|
178
|
-
post[:card_code]
|
|
179
|
-
post[:exp_date]
|
|
299
|
+
|
|
300
|
+
def add_creditcard(post, creditcard)
|
|
301
|
+
post[:card_num] = creditcard.number
|
|
302
|
+
post[:card_code] = creditcard.verification_value if creditcard.verification_value?
|
|
303
|
+
post[:exp_date] = expdate(creditcard)
|
|
180
304
|
post[:first_name] = creditcard.first_name
|
|
181
305
|
post[:last_name] = creditcard.last_name
|
|
182
306
|
end
|
|
183
|
-
|
|
307
|
+
|
|
184
308
|
def add_customer_data(post, options)
|
|
185
309
|
if options.has_key? :email
|
|
186
310
|
post[:email] = options[:email]
|
|
187
311
|
post[:email_customer] = false
|
|
188
312
|
end
|
|
189
|
-
|
|
313
|
+
|
|
190
314
|
if options.has_key? :customer
|
|
191
315
|
post[:cust_id] = options[:customer]
|
|
192
316
|
end
|
|
193
|
-
|
|
317
|
+
|
|
194
318
|
if options.has_key? :ip
|
|
195
319
|
post[:customer_ip] = options[:ip]
|
|
196
|
-
end
|
|
320
|
+
end
|
|
197
321
|
end
|
|
198
322
|
|
|
199
|
-
def add_address(post, options)
|
|
323
|
+
def add_address(post, options)
|
|
200
324
|
|
|
201
325
|
if address = options[:billing_address] || options[:address]
|
|
202
|
-
post[:address]
|
|
203
|
-
post[:company]
|
|
204
|
-
post[:phone]
|
|
205
|
-
post[:zip]
|
|
206
|
-
post[:city]
|
|
207
|
-
post[:country]
|
|
208
|
-
post[:state]
|
|
209
|
-
end
|
|
210
|
-
end
|
|
211
|
-
|
|
326
|
+
post[:address] = address[:address1].to_s
|
|
327
|
+
post[:company] = address[:company].to_s
|
|
328
|
+
post[:phone] = address[:phone].to_s
|
|
329
|
+
post[:zip] = address[:zip].to_s
|
|
330
|
+
post[:city] = address[:city].to_s
|
|
331
|
+
post[:country] = address[:country].to_s
|
|
332
|
+
post[:state] = address[:state].blank? ? 'n/a' : address[:state]
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
212
336
|
# Make a ruby type out of the response string
|
|
213
337
|
def normalize(field)
|
|
214
338
|
case field
|
|
@@ -217,30 +341,293 @@ module ActiveMerchant #:nodoc:
|
|
|
217
341
|
when "" then nil
|
|
218
342
|
when "null" then nil
|
|
219
343
|
else field
|
|
220
|
-
end
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
def message_from(results)
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def message_from(results)
|
|
224
348
|
if results[:response_code] == DECLINED
|
|
225
|
-
return
|
|
226
|
-
return
|
|
349
|
+
return CVVResult.messages[ results[:card_code] ] if CARD_CODE_ERRORS.include?(results[:card_code])
|
|
350
|
+
return AVSResult.messages[ results[:avs_result_code] ] if AVS_ERRORS.include?(results[:avs_result_code])
|
|
227
351
|
end
|
|
228
|
-
|
|
352
|
+
|
|
229
353
|
return results[:response_reason_text].nil? ? '' : results[:response_reason_text][0..-2]
|
|
230
354
|
end
|
|
231
|
-
|
|
355
|
+
|
|
232
356
|
def expdate(creditcard)
|
|
233
357
|
year = sprintf("%.4i", creditcard.year)
|
|
234
358
|
month = sprintf("%.2i", creditcard.month)
|
|
235
359
|
|
|
236
360
|
"#{month}#{year[-2..-1]}"
|
|
237
361
|
end
|
|
238
|
-
|
|
362
|
+
|
|
239
363
|
def split(response)
|
|
240
364
|
response[1..-2].split(/\$,\$/)
|
|
241
365
|
end
|
|
366
|
+
|
|
367
|
+
# ARB
|
|
368
|
+
|
|
369
|
+
# Builds recurring billing request
|
|
370
|
+
def build_recurring_request(action, options = {})
|
|
371
|
+
unless RECURRING_ACTIONS.include?(action)
|
|
372
|
+
raise StandardError, "Invalid Automated Recurring Billing Action: #{action}"
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
xml = Builder::XmlMarkup.new(:indent => 2)
|
|
376
|
+
xml.instruct!(:xml, :version => '1.0', :encoding => 'utf-8')
|
|
377
|
+
xml.tag!("#{RECURRING_ACTIONS[action]}Request", :xmlns => AUTHORIZE_NET_ARB_NAMESPACE) do
|
|
378
|
+
add_arb_merchant_authentication(xml)
|
|
379
|
+
# Merchant-assigned reference ID for the request
|
|
380
|
+
xml.tag!('refId', options[:ref_id]) if options[:ref_id]
|
|
381
|
+
send("build_arb_#{action}_subscription_request", xml, options)
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
# Contains the merchant’s payment gateway account authentication information
|
|
386
|
+
def add_arb_merchant_authentication(xml)
|
|
387
|
+
xml.tag!('merchantAuthentication') do
|
|
388
|
+
xml.tag!('name', @options[:login])
|
|
389
|
+
xml.tag!('transactionKey', @options[:password])
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
# Builds body for ARBCreateSubscriptionRequest
|
|
394
|
+
def build_arb_create_subscription_request(xml, options)
|
|
395
|
+
# Subscription
|
|
396
|
+
add_arb_subscription(xml, options)
|
|
397
|
+
|
|
398
|
+
xml.target!
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
# Builds body for ARBUpdateSubscriptionRequest
|
|
402
|
+
def build_arb_update_subscription_request(xml, options)
|
|
403
|
+
xml.tag!('subscriptionId', options[:subscription_id])
|
|
404
|
+
# Adds Subscription
|
|
405
|
+
add_arb_subscription(xml, options)
|
|
406
|
+
|
|
407
|
+
xml.target!
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
# Builds body for ARBCancelSubscriptionRequest
|
|
411
|
+
def build_arb_cancel_subscription_request(xml, options)
|
|
412
|
+
xml.tag!('subscriptionId', options[:subscription_id])
|
|
413
|
+
|
|
414
|
+
xml.target!
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
# Adds subscription information
|
|
418
|
+
def add_arb_subscription(xml, options)
|
|
419
|
+
xml.tag!('subscription') do
|
|
420
|
+
# Merchant-assigned name for the subscription (optional)
|
|
421
|
+
xml.tag!('name', options[:subscription_name]) if options[:subscription_name]
|
|
422
|
+
# Contains information about the payment schedule
|
|
423
|
+
add_arb_payment_schedule(xml, options)
|
|
424
|
+
# The amount to be billed to the customer
|
|
425
|
+
# for each payment in the subscription
|
|
426
|
+
xml.tag!('amount', amount(options[:amount])) if options[:amount]
|
|
427
|
+
if trial = options[:trial]
|
|
428
|
+
# The amount to be charged for each payment during a trial period (conditional)
|
|
429
|
+
xml.tag!('trialAmount', amount(trial[:amount])) if trial[:amount]
|
|
430
|
+
end
|
|
431
|
+
# Contains either the customer’s credit card
|
|
432
|
+
# or bank account payment information
|
|
433
|
+
add_arb_payment(xml, options)
|
|
434
|
+
# Contains order information (optional)
|
|
435
|
+
add_arb_order(xml, options)
|
|
436
|
+
# Contains information about the customer
|
|
437
|
+
add_arb_customer(xml, options)
|
|
438
|
+
# Contains the customer's billing address information
|
|
439
|
+
add_arb_address(xml, 'billTo', options[:billing_address])
|
|
440
|
+
# Contains the customer's shipping address information (optional)
|
|
441
|
+
add_arb_address(xml, 'shipTo', options[:shipping_address])
|
|
442
|
+
end
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
# Adds information about the interval of time between payments
|
|
446
|
+
def add_arb_interval(xml, options)
|
|
447
|
+
interval = options[:interval]
|
|
448
|
+
return unless interval
|
|
449
|
+
xml.tag!('interval') do
|
|
450
|
+
# The measurement of time, in association with the Interval Unit,
|
|
451
|
+
# that is used to define the frequency of the billing occurrences
|
|
452
|
+
xml.tag!('length', interval[:length])
|
|
453
|
+
# The unit of time, in association with the Interval Length,
|
|
454
|
+
# between each billing occurrence
|
|
455
|
+
xml.tag!('unit', interval[:unit].to_s)
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
# Adds information about the subscription duration
|
|
460
|
+
def add_arb_duration(xml, options)
|
|
461
|
+
duration = options[:duration]
|
|
462
|
+
return unless duration
|
|
463
|
+
# The date the subscription begins
|
|
464
|
+
# (also the date the initial billing occurs)
|
|
465
|
+
xml.tag!('startDate', duration[:start_date]) if duration[:start_date]
|
|
466
|
+
# Number of billing occurrences or payments for the subscription
|
|
467
|
+
xml.tag!('totalOccurrences', duration[:occurrences]) if duration[:occurrences]
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
def add_arb_payment_schedule(xml, options)
|
|
471
|
+
return unless options[:interval] || options[:duration]
|
|
472
|
+
xml.tag!('paymentSchedule') do
|
|
473
|
+
# Contains information about the interval of time between payments
|
|
474
|
+
add_arb_interval(xml, options)
|
|
475
|
+
add_arb_duration(xml, options)
|
|
476
|
+
if trial = options[:trial]
|
|
477
|
+
# Number of billing occurrences or payments in the trial period (optional)
|
|
478
|
+
xml.tag!('trialOccurrences', trial[:occurrences]) if trial[:occurrences]
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
# Adds customer's credit card or bank account payment information
|
|
484
|
+
def add_arb_payment(xml, options)
|
|
485
|
+
return unless options[:credit_card] || options[:bank_account]
|
|
486
|
+
xml.tag!('payment') do
|
|
487
|
+
# Contains the customer’s credit card information
|
|
488
|
+
add_arb_credit_card(xml, options)
|
|
489
|
+
# Contains the customer’s bank account information
|
|
490
|
+
add_arb_bank_account(xml, options)
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
# Adds customer’s credit card information
|
|
495
|
+
# Note: This element should only be included
|
|
496
|
+
# when the payment method is credit card.
|
|
497
|
+
def add_arb_credit_card(xml, options)
|
|
498
|
+
credit_card = options[:credit_card]
|
|
499
|
+
return unless credit_card
|
|
500
|
+
xml.tag!('creditCard') do
|
|
501
|
+
# The credit card number used for payment of the subscription
|
|
502
|
+
xml.tag!('cardNumber', credit_card.number)
|
|
503
|
+
# The expiration date of the credit card used for the subscription
|
|
504
|
+
xml.tag!('expirationDate', arb_expdate(credit_card))
|
|
505
|
+
end
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
# Adds customer’s bank account information
|
|
509
|
+
# Note: This element should only be included
|
|
510
|
+
# when the payment method is bank account.
|
|
511
|
+
def add_arb_bank_account(xml, options)
|
|
512
|
+
bank_account = options[:bank_account]
|
|
513
|
+
return unless bank_account
|
|
514
|
+
xml.tag!('bankAccount') do
|
|
515
|
+
# The type of bank account used for payment of the subscription
|
|
516
|
+
xml.tag!('accountType', bank_account[:account_type])
|
|
517
|
+
# The routing number of the customer’s bank
|
|
518
|
+
xml.tag!('routingNumber', bank_account[:routing_number])
|
|
519
|
+
# The bank account number used for payment of the subscription
|
|
520
|
+
xml.tag!('accountNumber', bank_account[:account_number])
|
|
521
|
+
# The full name of the individual associated
|
|
522
|
+
# with the bank account number
|
|
523
|
+
xml.tag!('nameOfAccount', bank_account[:name_of_account])
|
|
524
|
+
# The full name of the individual associated
|
|
525
|
+
# with the bank account number (optional)
|
|
526
|
+
xml.tag!('bankName', bank_account[:bank_name]) if bank_account[:bank_name]
|
|
527
|
+
# The type of electronic check transaction used for the subscription
|
|
528
|
+
xml.tag!('echeckType', bank_account[:echeck_type])
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
# Adds order information (optional)
|
|
533
|
+
def add_arb_order(xml, options)
|
|
534
|
+
order = options[:order]
|
|
535
|
+
return unless order
|
|
536
|
+
xml.tag!('order') do
|
|
537
|
+
# Merchant-assigned invoice number for the subscription (optional)
|
|
538
|
+
xml.tag!('invoiceNumber', order[:invoice_number])
|
|
539
|
+
# Description of the subscription (optional)
|
|
540
|
+
xml.tag!('description', order[:description])
|
|
541
|
+
end
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
# Adds information about the customer
|
|
545
|
+
def add_arb_customer(xml, options)
|
|
546
|
+
customer = options[:customer]
|
|
547
|
+
return unless customer
|
|
548
|
+
xml.tag!('customer') do
|
|
549
|
+
xml.tag!('type', customer[:type]) if customer[:type]
|
|
550
|
+
xml.tag!('id', customer[:id]) if customer[:id]
|
|
551
|
+
xml.tag!('email', customer[:email]) if customer[:email]
|
|
552
|
+
xml.tag!('phoneNumber', customer[:phone_number]) if customer[:phone_number]
|
|
553
|
+
xml.tag!('faxNumber', customer[:fax_number]) if customer[:fax_number]
|
|
554
|
+
add_arb_drivers_license(xml, options)
|
|
555
|
+
xml.tag!('taxId', customer[:tax_id]) if customer[:tax_id]
|
|
556
|
+
end
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
# Adds the customer's driver's license information (conditional)
|
|
560
|
+
def add_arb_drivers_license(xml, options)
|
|
561
|
+
return unless customer = options[:customer]
|
|
562
|
+
return unless drivers_license = customer[:drivers_license]
|
|
563
|
+
xml.tag!('driversLicense') do
|
|
564
|
+
# The customer's driver's license number
|
|
565
|
+
xml.tag!('number', drivers_license[:number])
|
|
566
|
+
# The customer's driver's license state
|
|
567
|
+
xml.tag!('state', drivers_license[:state])
|
|
568
|
+
# The customer's driver's license date of birth
|
|
569
|
+
xml.tag!('dateOfBirth', drivers_license[:date_of_birth])
|
|
570
|
+
end
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
# Adds address information
|
|
574
|
+
def add_arb_address(xml, container_name, address)
|
|
575
|
+
return if address.blank?
|
|
576
|
+
xml.tag!(container_name) do
|
|
577
|
+
xml.tag!('firstName', address[:first_name])
|
|
578
|
+
xml.tag!('lastName', address[:last_name])
|
|
579
|
+
xml.tag!('company', address[:company])
|
|
580
|
+
xml.tag!('address', address[:address1])
|
|
581
|
+
xml.tag!('city', address[:city])
|
|
582
|
+
xml.tag!('state', address[:state])
|
|
583
|
+
xml.tag!('zip', address[:zip])
|
|
584
|
+
xml.tag!('country', address[:country])
|
|
585
|
+
end
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
def arb_expdate(credit_card)
|
|
589
|
+
sprintf('%04d-%02d', credit_card.year, credit_card.month)
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
def recurring_commit(action, request)
|
|
593
|
+
url = test? ? arb_test_url : arb_live_url
|
|
594
|
+
xml = ssl_post(url, request, "Content-Type" => "text/xml")
|
|
595
|
+
|
|
596
|
+
response = recurring_parse(action, xml)
|
|
597
|
+
|
|
598
|
+
message = response[:message] || response[:text]
|
|
599
|
+
test_mode = test? || message =~ /Test Mode/
|
|
600
|
+
success = response[:result_code] == 'Ok'
|
|
601
|
+
|
|
602
|
+
Response.new(success, message, response,
|
|
603
|
+
:test => test_mode,
|
|
604
|
+
:authorization => response[:subscription_id]
|
|
605
|
+
)
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
def recurring_parse(action, xml)
|
|
609
|
+
response = {}
|
|
610
|
+
xml = REXML::Document.new(xml)
|
|
611
|
+
root = REXML::XPath.first(xml, "//#{RECURRING_ACTIONS[action]}Response") ||
|
|
612
|
+
REXML::XPath.first(xml, "//ErrorResponse")
|
|
613
|
+
if root
|
|
614
|
+
root.elements.to_a.each do |node|
|
|
615
|
+
recurring_parse_element(response, node)
|
|
616
|
+
end
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
response
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
def recurring_parse_element(response, node)
|
|
623
|
+
if node.has_elements?
|
|
624
|
+
node.elements.each{|e| recurring_parse_element(response, e) }
|
|
625
|
+
else
|
|
626
|
+
response[node.name.underscore.to_sym] = node.text
|
|
627
|
+
end
|
|
628
|
+
end
|
|
242
629
|
end
|
|
243
|
-
|
|
630
|
+
|
|
244
631
|
AuthorizedNetGateway = AuthorizeNetGateway
|
|
245
632
|
end
|
|
246
633
|
end
|