activemerchant 1.1.0 → 1.2.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 +226 -0
- data/CONTRIBUTERS +52 -0
- data/README +34 -24
- data/Rakefile +152 -0
- data/gem-public_cert.pem +20 -0
- data/init.rb +3 -0
- data/lib/active_merchant.rb +3 -1
- data/lib/active_merchant/billing/credit_card.rb +21 -17
- data/lib/active_merchant/billing/credit_card_methods.rb +17 -19
- data/lib/active_merchant/billing/gateway.rb +160 -44
- data/lib/active_merchant/billing/gateways.rb +2 -15
- data/lib/active_merchant/billing/gateways/authorize_net.rb +21 -21
- data/lib/active_merchant/billing/gateways/bogus.rb +6 -6
- data/lib/active_merchant/billing/gateways/brain_tree.rb +191 -0
- data/lib/active_merchant/billing/gateways/card_stream.rb +207 -0
- data/lib/active_merchant/billing/gateways/cyber_source.rb +402 -0
- data/lib/active_merchant/billing/gateways/data_cash.rb +41 -97
- data/lib/active_merchant/billing/gateways/efsnet.rb +256 -0
- data/lib/active_merchant/billing/gateways/eway.rb +77 -29
- data/lib/active_merchant/billing/gateways/exact.rb +230 -0
- data/lib/active_merchant/billing/gateways/linkpoint.rb +6 -33
- data/lib/active_merchant/billing/gateways/moneris.rb +155 -125
- data/lib/active_merchant/billing/gateways/net_registry.rb +257 -0
- data/lib/active_merchant/billing/gateways/pay_junction.rb +407 -0
- data/lib/active_merchant/billing/gateways/payflow.rb +163 -25
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +56 -38
- data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +10 -1
- data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +9 -0
- data/lib/active_merchant/billing/gateways/payflow_express.rb +36 -11
- data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +6 -0
- data/lib/active_merchant/billing/gateways/payflow_uk.rb +7 -3
- data/lib/active_merchant/billing/gateways/payment_express.rb +261 -0
- data/lib/active_merchant/billing/gateways/paypal.rb +18 -4
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +31 -15
- data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +8 -0
- data/lib/active_merchant/billing/gateways/paypal_express.rb +33 -8
- data/lib/active_merchant/billing/gateways/plugnpay.rb +300 -0
- data/lib/active_merchant/billing/gateways/protx.rb +285 -0
- data/lib/active_merchant/billing/gateways/psigate.rb +13 -12
- data/lib/active_merchant/billing/gateways/psl_card.rb +297 -0
- data/lib/active_merchant/billing/gateways/quickpay.rb +197 -0
- data/lib/active_merchant/billing/gateways/realex.rb +212 -0
- data/lib/active_merchant/billing/gateways/secure_pay.rb +31 -0
- data/lib/active_merchant/billing/gateways/trans_first.rb +136 -0
- data/lib/active_merchant/billing/gateways/trust_commerce.rb +43 -20
- data/lib/active_merchant/billing/gateways/usa_epay.rb +6 -5
- data/lib/active_merchant/billing/gateways/verifi.rb +235 -0
- data/lib/active_merchant/billing/gateways/viaklix.rb +171 -0
- data/lib/active_merchant/billing/integrations/gestpay/helper.rb +0 -2
- data/lib/active_merchant/billing/integrations/helper.rb +8 -1
- data/lib/active_merchant/billing/integrations/nochex.rb +62 -1
- data/lib/active_merchant/billing/integrations/nochex/notification.rb +9 -16
- data/lib/active_merchant/billing/integrations/notification.rb +1 -1
- data/lib/active_merchant/billing/integrations/paypal/helper.rb +59 -46
- data/lib/active_merchant/billing/integrations/paypal/notification.rb +14 -47
- data/lib/active_merchant/lib/error.rb +4 -0
- data/lib/active_merchant/lib/post_data.rb +22 -0
- data/lib/active_merchant/lib/posts_data.rb +23 -5
- data/lib/active_merchant/lib/requires_parameters.rb +2 -2
- data/lib/active_merchant/lib/validateable.rb +1 -1
- data/lib/support/gateway_support.rb +45 -0
- data/lib/tasks/cia.rb +1 -1
- data/script/generate +14 -0
- data/script/generator/base.rb +45 -0
- data/script/generator/generator.rb +24 -0
- data/script/generator/generators/gateway/gateway_generator.rb +14 -0
- data/script/generator/generators/gateway/templates/gateway.rb +73 -0
- data/script/generator/generators/gateway/templates/gateway_test.rb +41 -0
- data/script/generator/generators/gateway/templates/remote_gateway_test.rb +56 -0
- data/script/generator/generators/integration/integration_generator.rb +25 -0
- data/script/generator/generators/integration/templates/helper.rb +34 -0
- data/script/generator/generators/integration/templates/helper_test.rb +54 -0
- data/script/generator/generators/integration/templates/integration.rb +18 -0
- data/script/generator/generators/integration/templates/module_test.rb +9 -0
- data/script/generator/generators/integration/templates/notification.rb +100 -0
- data/script/generator/generators/integration/templates/notification_test.rb +41 -0
- data/script/generator/manifest.rb +20 -0
- data/test/extra/binding_of_caller.rb +80 -0
- data/test/extra/breakpoint.rb +547 -0
- data/test/fixtures.yml +251 -0
- data/test/remote_tests/remote_authorize_net_test.rb +113 -0
- data/test/remote_tests/remote_brain_tree_test.rb +78 -0
- data/test/remote_tests/remote_card_stream_test.rb +160 -0
- data/test/remote_tests/remote_cyber_source_test.rb +130 -0
- data/test/remote_tests/remote_data_cash_test.rb +155 -0
- data/test/remote_tests/remote_efsnet_test.rb +93 -0
- data/test/remote_tests/remote_eway_test.rb +71 -0
- data/test/remote_tests/remote_exact_test.rb +59 -0
- data/test/remote_tests/remote_gestpay_integration_test.rb +37 -0
- data/test/remote_tests/remote_linkpoint_test.rb +144 -0
- data/test/remote_tests/remote_moneris_test.rb +110 -0
- data/test/remote_tests/remote_net_registry_test.rb +120 -0
- data/test/remote_tests/remote_pay_junction_test.rb +162 -0
- data/test/remote_tests/remote_payflow_express_test.rb +50 -0
- data/test/remote_tests/remote_payflow_test.rb +241 -0
- data/test/remote_tests/remote_payflow_uk_test.rb +172 -0
- data/test/remote_tests/remote_payment_express_test.rb +136 -0
- data/test/remote_tests/remote_paypal_express_test.rb +49 -0
- data/test/remote_tests/remote_paypal_integration_test.rb +14 -0
- data/test/remote_tests/remote_paypal_test.rb +163 -0
- data/test/remote_tests/remote_plugnpay_test.rb +70 -0
- data/test/remote_tests/remote_protx_test.rb +184 -0
- data/test/remote_tests/remote_psigate_test.rb +87 -0
- data/test/remote_tests/remote_psl_card_test.rb +105 -0
- data/test/remote_tests/remote_quickpay_test.rb +182 -0
- data/test/remote_tests/remote_realex_test.rb +227 -0
- data/test/remote_tests/remote_secure_pay_test.rb +36 -0
- data/test/remote_tests/remote_trans_first_test.rb +37 -0
- data/test/remote_tests/remote_trust_commerce_test.rb +136 -0
- data/test/remote_tests/remote_usa_epay_test.rb +57 -0
- data/test/remote_tests/remote_verifi_test.rb +107 -0
- data/test/remote_tests/remote_viaklix_test.rb +53 -0
- data/test/test_helper.rb +132 -0
- data/test/unit/base_test.rb +61 -0
- data/test/unit/country_code_test.rb +33 -0
- data/test/unit/country_test.rb +64 -0
- data/test/unit/credit_card_formatting_test.rb +24 -0
- data/test/unit/credit_card_methods_test.rb +37 -0
- data/test/unit/credit_card_test.rb +365 -0
- data/test/unit/gateways/authorize_net_test.rb +140 -0
- data/test/unit/gateways/bogus_test.rb +43 -0
- data/test/unit/gateways/brain_tree_test.rb +77 -0
- data/test/unit/gateways/card_stream_test.rb +37 -0
- data/test/unit/gateways/cyber_source_test.rb +151 -0
- data/test/unit/gateways/data_cash_test.rb +23 -0
- data/test/unit/gateways/efsnet_test.rb +70 -0
- data/test/unit/gateways/eway_test.rb +105 -0
- data/test/unit/gateways/exact_test.rb +118 -0
- data/test/unit/gateways/gateway_test.rb +24 -0
- data/test/unit/gateways/linkpoint_test.rb +165 -0
- data/test/unit/gateways/moneris_test.rb +167 -0
- data/test/unit/gateways/net_registry_test.rb +478 -0
- data/test/unit/gateways/pay_junction_test.rb +61 -0
- data/test/unit/gateways/payflow_express_test.rb +165 -0
- data/test/unit/gateways/payflow_express_uk_test.rb +14 -0
- data/test/unit/gateways/payflow_test.rb +230 -0
- data/test/unit/gateways/payflow_uk_test.rb +68 -0
- data/test/unit/gateways/payment_express_test.rb +215 -0
- data/test/unit/gateways/paypal_express_test.rb +222 -0
- data/test/unit/gateways/paypal_test.rb +241 -0
- data/test/unit/gateways/plugnpay_test.rb +79 -0
- data/test/unit/gateways/protx_test.rb +110 -0
- data/test/unit/gateways/psigate_test.rb +110 -0
- data/test/unit/gateways/psl_card_test.rb +51 -0
- data/test/unit/gateways/quickpay_test.rb +125 -0
- data/test/unit/gateways/realex_test.rb +150 -0
- data/test/unit/gateways/secure_pay_test.rb +78 -0
- data/test/unit/gateways/trans_first_test.rb +125 -0
- data/test/unit/gateways/trust_commerce_test.rb +57 -0
- data/test/unit/gateways/usa_epay_test.rb +117 -0
- data/test/unit/gateways/verifi_test.rb +91 -0
- data/test/unit/gateways/viaklix_test.rb +72 -0
- data/test/unit/integrations/action_view_helper_test.rb +54 -0
- data/test/unit/integrations/bogus_module_test.rb +16 -0
- data/test/unit/integrations/chronopay_module_test.rb +9 -0
- data/test/unit/integrations/gestpay_module_test.rb +10 -0
- data/test/unit/integrations/helpers/bogus_helper_test.rb +28 -0
- data/test/unit/integrations/helpers/chronopay_helper_test.rb +67 -0
- data/test/unit/integrations/helpers/gestpay_helper_test.rb +100 -0
- data/test/unit/integrations/helpers/nochex_helper_test.rb +53 -0
- data/test/unit/integrations/helpers/paypal_helper_test.rb +162 -0
- data/test/unit/integrations/helpers/two_checkout_helper_test.rb +92 -0
- data/test/unit/integrations/nochex_module_test.rb +9 -0
- data/test/unit/integrations/notifications/chronopay_notification_test.rb +66 -0
- data/test/unit/integrations/notifications/gestpay_notification_test.rb +60 -0
- data/test/unit/integrations/notifications/nochex_notification_test.rb +51 -0
- data/test/unit/integrations/notifications/notification_test.rb +41 -0
- data/test/unit/integrations/notifications/paypal_notification_test.rb +85 -0
- data/test/unit/integrations/notifications/two_checkout_notification_test.rb +55 -0
- data/test/unit/integrations/paypal_module_test.rb +24 -0
- data/test/unit/integrations/two_checkout_module_test.rb +9 -0
- data/test/unit/post_data_test.rb +55 -0
- data/test/unit/response_test.rb +14 -0
- data/test/unit/validateable_test.rb +56 -0
- metadata +160 -7
- metadata.gz.sig +0 -0
- data/lib/active_merchant/billing/gateways/payflow/f73e89fd.0 +0 -17
|
@@ -3,10 +3,21 @@ require 'rexml/document'
|
|
|
3
3
|
module ActiveMerchant #:nodoc:
|
|
4
4
|
module Billing #:nodoc:
|
|
5
5
|
|
|
6
|
+
# To learn more about the Moneris gateway, please contact
|
|
7
|
+
# eselectplus@moneris.com for a copy of their integration guide. For
|
|
8
|
+
# information on remote testing, please see "Test Environment Penny Value
|
|
9
|
+
# Response Table", and "Test Environment eFraud (AVS and CVD) Penny
|
|
10
|
+
# Response Values", available at Moneris' {eSelect Plus Documentation
|
|
11
|
+
# Centre}[https://www3.moneris.com/connect/en/documents/index.html].
|
|
6
12
|
class MonerisGateway < Gateway
|
|
7
13
|
attr_reader :url
|
|
8
14
|
attr_reader :response
|
|
9
15
|
attr_reader :options
|
|
16
|
+
|
|
17
|
+
self.supported_countries = ['CA']
|
|
18
|
+
self.supported_cardtypes = [:visa, :master]
|
|
19
|
+
self.homepage_url = 'http://www.moneris.com/'
|
|
20
|
+
self.display_name = 'Moneris'
|
|
10
21
|
|
|
11
22
|
TEST_URL = 'https://esqa.moneris.com/gateway2/servlet/MpgRequest'
|
|
12
23
|
LIVE_URL = 'https://www3.moneris.com/gateway2/servlet/MpgRequest'
|
|
@@ -15,145 +26,167 @@ module ActiveMerchant #:nodoc:
|
|
|
15
26
|
# password is your API Token
|
|
16
27
|
def initialize(options = {})
|
|
17
28
|
requires!(options, :login, :password)
|
|
18
|
-
|
|
19
|
-
@options = {
|
|
20
|
-
:strict_ssl => true,
|
|
21
|
-
:crypt_type => 7
|
|
22
|
-
}.update(options)
|
|
23
|
-
|
|
29
|
+
@options = { :crypt_type => 7 }.update(options)
|
|
24
30
|
@url = test? ? TEST_URL : LIVE_URL
|
|
25
|
-
|
|
26
31
|
super
|
|
27
32
|
end
|
|
28
33
|
|
|
34
|
+
# Referred to as "PreAuth" in the Moneris integration guide, this action
|
|
35
|
+
# verifies and locks funds on a customer's card, which then must be
|
|
36
|
+
# captured at a later date.
|
|
37
|
+
#
|
|
38
|
+
# Pass in +order_id+ and optionally a +customer+ parameter.
|
|
29
39
|
def authorize(money, creditcard, options = {})
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
parameters = {
|
|
33
|
-
:order_id => options[:order_id],
|
|
34
|
-
:cust_id => options[:customer],
|
|
35
|
-
:amount => amount(money),
|
|
36
|
-
:pan => creditcard.number,
|
|
37
|
-
:expdate => expdate(creditcard),
|
|
38
|
-
:crypt_type => options[:crypt_type] || @options[:crypt_type]
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
commit('preauth', parameters)
|
|
40
|
+
debit_commit 'preauth', money, creditcard, options
|
|
42
41
|
end
|
|
43
42
|
|
|
43
|
+
# This action verifies funding on a customer's card, and readies them for
|
|
44
|
+
# deposit in a merchant's account.
|
|
45
|
+
#
|
|
44
46
|
# Pass in <tt>order_id</tt> and optionally a <tt>customer</tt> parameter
|
|
45
47
|
def purchase(money, creditcard, options = {})
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
parameters = {
|
|
49
|
-
:order_id => options[:order_id],
|
|
50
|
-
:cust_id => options[:customer],
|
|
51
|
-
:amount => amount(money),
|
|
52
|
-
:pan => creditcard.number,
|
|
53
|
-
:expdate => expdate(creditcard),
|
|
54
|
-
:crypt_type => options[:crypt_type] || @options[:crypt_type]
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
commit('purchase', parameters)
|
|
48
|
+
debit_commit 'purchase', money, creditcard, options
|
|
58
49
|
end
|
|
59
50
|
|
|
60
|
-
#
|
|
51
|
+
# This method retrieves locked funds from a customer's account (from a
|
|
52
|
+
# PreAuth) and prepares them for deposit in a merchant's account.
|
|
53
|
+
#
|
|
54
|
+
# Note: Moneris requires both the order_id and the transaction number of
|
|
61
55
|
# the original authorization. To maintain the same interface as the other
|
|
62
56
|
# gateways the two numbers are concatenated together with a ; separator as
|
|
63
57
|
# the authorization number returned by authorization
|
|
64
58
|
def capture(money, authorization, options = {})
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
parameters = {
|
|
68
|
-
:txn_number => txn_number,
|
|
69
|
-
:order_id => order_id,
|
|
70
|
-
:comp_amount => amount(money),
|
|
71
|
-
:crypt_type => options[:crypt_type] || @options[:crypt_type]
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
commit('completion', parameters)
|
|
59
|
+
commit 'completion', crediting_params(authorization, :comp_amount => amount(money))
|
|
75
60
|
end
|
|
76
61
|
|
|
62
|
+
# Voiding requires the original transaction ID and order ID of some open
|
|
63
|
+
# transaction. Closed transactions must be refunded. Note that the only
|
|
64
|
+
# methods which may be voided are +capture+ and +purchase+.
|
|
65
|
+
#
|
|
66
|
+
# Concatenate your transaction number and order_id by using a semicolon
|
|
67
|
+
# (';'). This is to keep the Moneris interface consistent with other
|
|
68
|
+
# gateways. (See +capture+ for details.)
|
|
77
69
|
def void(authorization, options = {})
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
parameters = {
|
|
81
|
-
:txn_number => txn_number,
|
|
82
|
-
:order_id => order_id,
|
|
83
|
-
:crypt_type => options[:crypt_type] || @options[:crypt_type]
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
commit('purchasecorrection', parameters)
|
|
70
|
+
commit 'purchasecorrection', crediting_params(authorization)
|
|
87
71
|
end
|
|
88
|
-
|
|
89
|
-
#
|
|
90
|
-
|
|
91
|
-
|
|
72
|
+
|
|
73
|
+
# Performs a refund. This method requires that the original transaction
|
|
74
|
+
# number and order number be included. Concatenate your transaction
|
|
75
|
+
# number and order_id by using a semicolon (';'). This is to keep the
|
|
76
|
+
# Moneris interface consistent with other gateways. (See +capture+ for
|
|
77
|
+
# details.)
|
|
78
|
+
def credit(money, authorization, options = {})
|
|
79
|
+
commit 'refund', crediting_params(authorization, :amount => amount(money))
|
|
92
80
|
end
|
|
93
|
-
|
|
94
|
-
|
|
81
|
+
|
|
82
|
+
private # :nodoc: all
|
|
95
83
|
|
|
96
84
|
def expdate(creditcard)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
85
|
+
sprintf("%.4i", creditcard.year)[-2..-1] + sprintf("%.2i", creditcard.month)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def debit_commit(commit_type, money, creditcard, options)
|
|
89
|
+
requires!(options, :order_id)
|
|
90
|
+
commit(commit_type, debit_params(money, creditcard, options))
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Common params used amongst the +purchase+ and +authorization+ methods
|
|
94
|
+
def debit_params(money, creditcard, options = {})
|
|
95
|
+
{
|
|
96
|
+
:order_id => options[:order_id],
|
|
97
|
+
:cust_id => options[:customer],
|
|
98
|
+
:amount => amount(money),
|
|
99
|
+
:pan => creditcard.number,
|
|
100
|
+
:expdate => expdate(creditcard),
|
|
101
|
+
:crypt_type => options[:crypt_type] || @options[:crypt_type]
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Common params used amongst the +credit+, +void+ and +capture+ methods
|
|
106
|
+
def crediting_params(authorization, options = {})
|
|
107
|
+
{
|
|
108
|
+
:txn_number => split_authorization(authorization).first,
|
|
109
|
+
:order_id => split_authorization(authorization).last,
|
|
110
|
+
:crypt_type => options[:crypt_type] || @options[:crypt_type]
|
|
111
|
+
}.merge(options)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Splits an +authorization+ param and retrives the order id and
|
|
115
|
+
# transaction number in that order.
|
|
116
|
+
def split_authorization(authorization)
|
|
117
|
+
if authorization.nil? || authorization.empty? || authorization !~ /;/
|
|
118
|
+
raise ArgumentError, 'You must include a valid authorization code (e.g. "1234;567")'
|
|
119
|
+
else
|
|
120
|
+
authorization.split(';')
|
|
121
|
+
end
|
|
101
122
|
end
|
|
102
123
|
|
|
103
|
-
def commit(action, parameters)
|
|
124
|
+
def commit(action, parameters = {})
|
|
125
|
+
# TODO This part still needs to be refactored
|
|
104
126
|
if result = test_result_from_cc_number(parameters[:pan])
|
|
105
127
|
return result
|
|
106
128
|
end
|
|
107
129
|
|
|
108
|
-
|
|
109
|
-
@response = parse(data)
|
|
130
|
+
@response = parse(ssl_post(@url, post_data(action, parameters)))
|
|
110
131
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
Response.new(success, message, @response,
|
|
116
|
-
:test => test?,
|
|
117
|
-
:authorization => authorization
|
|
132
|
+
Response.new(successful_response?(response), message_form(response[:message]), @response,
|
|
133
|
+
:test => test?,
|
|
134
|
+
:authorization => authorization_string(response)
|
|
118
135
|
)
|
|
119
136
|
end
|
|
137
|
+
|
|
138
|
+
# Generates a Moneris authorization string of the form 'trans_id;receipt_id'.
|
|
139
|
+
def authorization_string(response = {})
|
|
140
|
+
if response[:trans_id] && response[:receipt_id]
|
|
141
|
+
"#{response[:trans_id]};#{response[:receipt_id]}"
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Tests for a successful response from Moneris' servers
|
|
146
|
+
def successful_response?(response = {})
|
|
147
|
+
response[:response_code] &&
|
|
148
|
+
response[:complete] &&
|
|
149
|
+
(0..49).include?(response[:response_code].to_i)
|
|
150
|
+
end
|
|
120
151
|
|
|
121
|
-
# Parse
|
|
152
|
+
# Parse Moneris' response XML into a convinient Hash.
|
|
153
|
+
#
|
|
154
|
+
# Expected XML format:
|
|
155
|
+
#
|
|
156
|
+
# "<?xml version=\"1.0\"?><response><receipt>".
|
|
157
|
+
# "<ReceiptId>Global Error Receipt</ReceiptId>".
|
|
158
|
+
# "<ReferenceNum>null</ReferenceNum>
|
|
159
|
+
# <ResponseCode>null</ResponseCode>".
|
|
160
|
+
# "<ISO>null</ISO>
|
|
161
|
+
# <AuthCode>null</AuthCode>
|
|
162
|
+
# <TransTime>null</TransTime>".
|
|
163
|
+
# "<TransDate>null</TransDate>
|
|
164
|
+
# <TransType>null</TransType>
|
|
165
|
+
# <Complete>false</Complete>".
|
|
166
|
+
# "<Message>null</Message>
|
|
167
|
+
# <TransAmount>null</TransAmount>".
|
|
168
|
+
# "<CardType>null</CardType>".
|
|
169
|
+
# "<TransID>null</TransID>
|
|
170
|
+
# <TimedOut>null</TimedOut>".
|
|
171
|
+
# "</receipt></response>
|
|
122
172
|
def parse(xml)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
# <TransType>null</TransType>
|
|
132
|
-
# <Complete>false</Complete>".
|
|
133
|
-
# "<Message>null</Message>
|
|
134
|
-
# <TransAmount>null</TransAmount>".
|
|
135
|
-
# "<CardType>null</CardType>".
|
|
136
|
-
# "<TransID>null</TransID>
|
|
137
|
-
# <TimedOut>null</TimedOut>".
|
|
138
|
-
# "</receipt></response>
|
|
139
|
-
|
|
140
|
-
response = {:message => "Global Error Receipt", :complete => false}
|
|
141
|
-
|
|
142
|
-
xml = REXML::Document.new(xml)
|
|
143
|
-
|
|
173
|
+
response = { :message => "Global Error Receipt", :complete => false }
|
|
174
|
+
hashify_xml!(xml, response)
|
|
175
|
+
response
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def hashify_xml!(xml, response)
|
|
179
|
+
xml = REXML::Document.new(xml)
|
|
180
|
+
return if xml.root.nil?
|
|
144
181
|
xml.elements.each('//receipt/*') do |node|
|
|
145
|
-
|
|
146
182
|
response[node.name.underscore.to_sym] = normalize(node.text)
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
response
|
|
151
|
-
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
152
185
|
|
|
153
186
|
def post_data(action, parameters = {})
|
|
154
187
|
xml = REXML::Document.new
|
|
155
188
|
root = xml.add_element("request")
|
|
156
|
-
root.add_element("store_id").text
|
|
189
|
+
root.add_element("store_id").text = options[:login]
|
|
157
190
|
root.add_element("api_token").text = options[:password]
|
|
158
191
|
transaction = root.add_element(action)
|
|
159
192
|
|
|
@@ -170,36 +203,33 @@ module ActiveMerchant #:nodoc:
|
|
|
170
203
|
message.gsub(/[^\w]/, ' ').split.join(" ").capitalize
|
|
171
204
|
end
|
|
172
205
|
|
|
173
|
-
# Make a
|
|
206
|
+
# Make a Ruby type out of the response string
|
|
174
207
|
def normalize(field)
|
|
175
208
|
case field
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
else field
|
|
209
|
+
when "true" then true
|
|
210
|
+
when "false" then false
|
|
211
|
+
when '', "null" then nil
|
|
212
|
+
else field
|
|
181
213
|
end
|
|
182
|
-
end
|
|
183
|
-
|
|
214
|
+
end
|
|
215
|
+
|
|
184
216
|
def actions
|
|
185
|
-
|
|
217
|
+
{
|
|
218
|
+
"purchase" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
|
|
219
|
+
"preauth" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
|
|
220
|
+
"command" => [:order_id],
|
|
221
|
+
"refund" => [:order_id, :amount, :txn_number, :crypt_type],
|
|
222
|
+
"indrefund" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
|
|
223
|
+
"completion" => [:order_id, :comp_amount, :txn_number, :crypt_type],
|
|
224
|
+
"purchasecorrection" => [:order_id, :txn_number, :crypt_type],
|
|
225
|
+
"cavvpurcha" => [:order_id, :cust_id, :amount, :pan, :expdate, :cav],
|
|
226
|
+
"cavvpreaut" => [:order_id, :cust_id, :amount, :pan, :expdate, :cavv],
|
|
227
|
+
"transact" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
|
|
228
|
+
"Batchcloseall" => [],
|
|
229
|
+
"opentotals" => [:ecr_number],
|
|
230
|
+
"batchclose" => [:ecr_number]
|
|
231
|
+
}
|
|
186
232
|
end
|
|
187
|
-
|
|
188
|
-
ACTIONS = {
|
|
189
|
-
"purchase" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
|
|
190
|
-
"preauth" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
|
|
191
|
-
"command" => [:order_id],
|
|
192
|
-
"refund" => [:order_id, :amount, :txn_number, :crypt_type],
|
|
193
|
-
"indrefund" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
|
|
194
|
-
"completion" => [:order_id, :comp_amount, :txn_number, :crypt_type],
|
|
195
|
-
"purchasecorrection" => [:order_id, :txn_number, :crypt_type],
|
|
196
|
-
"cavvpurcha" => [:order_id, :cust_id, :amount, :pan, :expdate, :cav],
|
|
197
|
-
"cavvpreaut" => [:order_id, :cust_id, :amount, :pan, :expdate, :cavv],
|
|
198
|
-
"transact" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
|
|
199
|
-
"Batchcloseall" => [],
|
|
200
|
-
"opentotals" => [:ecr_number],
|
|
201
|
-
"batchclose" => [:ecr_number],
|
|
202
|
-
}
|
|
203
233
|
end
|
|
204
234
|
end
|
|
205
235
|
end
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Gateway for netregistry.com.au.
|
|
3
|
+
#
|
|
4
|
+
# Note that NetRegistry itself uses gateway service providers. At the
|
|
5
|
+
# time of this writing, there are at least two (Quest and Ingenico).
|
|
6
|
+
# This module has only been tested with Quest.
|
|
7
|
+
#
|
|
8
|
+
# Also note that NetRegistry does not offer a test mode, nor does it
|
|
9
|
+
# have support for the authorize/capture/void functionality by default
|
|
10
|
+
# (you may arrange for this as described in "Programming for
|
|
11
|
+
# NetRegistry's E-commerce Gateway." [http://rubyurl.com/hNG]), and no
|
|
12
|
+
# #void functionality is documented. As a result, the #authorize and
|
|
13
|
+
# #capture have not yet been tested through a live gateway, and #void
|
|
14
|
+
# will raise an error.
|
|
15
|
+
#
|
|
16
|
+
# If you have this functionality enabled, please consider contributing
|
|
17
|
+
# to ActiveMerchant by writing tests/code for these methods, and
|
|
18
|
+
# submitting a patch.
|
|
19
|
+
#
|
|
20
|
+
# In addition to the standard ActiveMerchant functionality, the
|
|
21
|
+
# response will contain a 'receipt' parameter
|
|
22
|
+
# (response.params['receipt']) if a receipt was issued by the gateway.
|
|
23
|
+
# Also, a logger may be provided when instantiating the gateway to log
|
|
24
|
+
# all data sent to/from the gateway (with sensitive information
|
|
25
|
+
# hidden).
|
|
26
|
+
#
|
|
27
|
+
module ActiveMerchant
|
|
28
|
+
module Billing
|
|
29
|
+
class NetRegistryGateway < Gateway
|
|
30
|
+
LIVE_URL = 'https://4tknox.au.com/cgi-bin/themerchant.au.com/ecom/external2.pl'
|
|
31
|
+
|
|
32
|
+
self.supported_countries = ['AU']
|
|
33
|
+
|
|
34
|
+
# Note that support for Diners, Amex, and JCB require extra
|
|
35
|
+
# steps in setting up your account, as detailed in
|
|
36
|
+
# "Programming for NetRegistry's E-commerce Gateway."
|
|
37
|
+
# [http://rubyurl.com/hNG]
|
|
38
|
+
self.supported_cardtypes = [:visa, :master, :diners_club, :american_express, :jcb]
|
|
39
|
+
self.display_name = 'NetRegistry'
|
|
40
|
+
self.homepage_url = 'http://www.netregistry.com.au'
|
|
41
|
+
|
|
42
|
+
# Create a new NetRegistry gateway.
|
|
43
|
+
#
|
|
44
|
+
# Options :login and :password must be given.
|
|
45
|
+
#
|
|
46
|
+
def initialize(options = {})
|
|
47
|
+
requires!(options, :login, :password)
|
|
48
|
+
@logger = options[:logger]
|
|
49
|
+
@options = options
|
|
50
|
+
super
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
#
|
|
54
|
+
# A Logger object used to write extra debugging output to. nil
|
|
55
|
+
# for none.
|
|
56
|
+
#
|
|
57
|
+
attr_accessor :logger
|
|
58
|
+
|
|
59
|
+
#
|
|
60
|
+
# Note that #authorize and #capture only work if your account
|
|
61
|
+
# vendor is St George, and if your account has been setup as
|
|
62
|
+
# described in "Programming for NetRegistry's E-commerce
|
|
63
|
+
# Gateway." [http://rubyurl.com/hNG]
|
|
64
|
+
#
|
|
65
|
+
def authorize(money, credit_card, options = {})
|
|
66
|
+
post(options[:description],
|
|
67
|
+
'COMMAND' => 'preauth',
|
|
68
|
+
'AMOUNT' => amount(money),
|
|
69
|
+
'CCNUM' => credit_card.number,
|
|
70
|
+
'CCEXP' => expiry(credit_card))
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
#
|
|
74
|
+
# Note that #authorize and #capture only work if your account
|
|
75
|
+
# vendor is St George, and if your account has been setup as
|
|
76
|
+
# described in "Programming for NetRegistry's E-commerce
|
|
77
|
+
# Gateway." [http://rubyurl.com/hNG]
|
|
78
|
+
#
|
|
79
|
+
def capture(money, authorization, options = {})
|
|
80
|
+
credit_card = options[:credit_card]
|
|
81
|
+
post(options[:description],
|
|
82
|
+
'COMMAND' => 'completion',
|
|
83
|
+
'PREAUTHNUM' => authorization,
|
|
84
|
+
'AMOUNT' => amount(money),
|
|
85
|
+
'CCNUM' => credit_card.number,
|
|
86
|
+
'CCEXP' => expiry(credit_card))
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def purchase(money, credit_card, options = {})
|
|
90
|
+
post(options[:description],
|
|
91
|
+
'COMMAND' => 'purchase',
|
|
92
|
+
'AMOUNT' => amount(money),
|
|
93
|
+
'CCNUM' => credit_card.number,
|
|
94
|
+
'CCEXP' => expiry(credit_card))
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def credit(money, identification, options = {})
|
|
98
|
+
post(options[:description],
|
|
99
|
+
'COMMAND' => 'refund',
|
|
100
|
+
'AMOUNT' => amount(money),
|
|
101
|
+
'TXNREF' => identification)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Specific to NetRegistry.
|
|
105
|
+
#
|
|
106
|
+
# Run a 'status' command. This lets you view the status of a
|
|
107
|
+
# completed transaction.
|
|
108
|
+
#
|
|
109
|
+
def status(identification)
|
|
110
|
+
post(options[:description],
|
|
111
|
+
'COMMAND' => 'status',
|
|
112
|
+
'TXNREF' => identification)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
private # -----------------------------------------------------
|
|
116
|
+
|
|
117
|
+
#
|
|
118
|
+
# Return the expiry for the given creditcard in the required
|
|
119
|
+
# format for a command.
|
|
120
|
+
#
|
|
121
|
+
def expiry(credit_card)
|
|
122
|
+
month = format(credit_card.month, :two_digits)
|
|
123
|
+
year = format(credit_card.year , :two_digits)
|
|
124
|
+
"#{month}/#{year}"
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
#
|
|
128
|
+
# Post the a request with the given parameters and return the
|
|
129
|
+
# response object.
|
|
130
|
+
#
|
|
131
|
+
# Login and password are added automatically, and the comment is
|
|
132
|
+
# omitted if nil.
|
|
133
|
+
#
|
|
134
|
+
def post(comment, keyvals)
|
|
135
|
+
if result = test_result_from_cc_number(keyvals['CCNUM'])
|
|
136
|
+
return result
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
log "Executing #{keyvals['COMMAND']}:"
|
|
140
|
+
login = @options[:login]
|
|
141
|
+
password = @options[:password]
|
|
142
|
+
|
|
143
|
+
# make query
|
|
144
|
+
keyvals['COMMENT'] = comment if comment
|
|
145
|
+
keyvals['LOGIN'] = "#{login}/#{password}"
|
|
146
|
+
str = URI.encode(keyvals.map{|k,v| "#{k}=#{v}"}.join('&'))
|
|
147
|
+
log " ActiveMerchant/NetRegistry: sending: #{obscure_send_string(str)}"
|
|
148
|
+
|
|
149
|
+
# get gateway response
|
|
150
|
+
text = ssl_post(LIVE_URL, str)
|
|
151
|
+
log " ActiveMerchant/NetRegistry: received:"
|
|
152
|
+
obscure_recv_string(text).each do |line|
|
|
153
|
+
log " #{line}"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# make response object
|
|
157
|
+
response = parse_response(text, keyvals['COMMAND'])
|
|
158
|
+
|
|
159
|
+
return response
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
#
|
|
163
|
+
# Parse the text returned from the gateway into a Response object.
|
|
164
|
+
#
|
|
165
|
+
def parse_response(text, command)
|
|
166
|
+
params = {'original_text' => text}
|
|
167
|
+
sio = StringIO.new(text)
|
|
168
|
+
params['status'] = sio.gets.chomp
|
|
169
|
+
params['rrn'] = sio.gets.chomp
|
|
170
|
+
|
|
171
|
+
if sio.eof?
|
|
172
|
+
# some short errors have nothing else, e.g., "Invalid expiry
|
|
173
|
+
# format"
|
|
174
|
+
message = params.delete('rrn')
|
|
175
|
+
return Response.new(false, message, params)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# parse receipt
|
|
179
|
+
receipt = ''
|
|
180
|
+
while (line = sio.gets)
|
|
181
|
+
break if line.strip == '.'
|
|
182
|
+
receipt << line
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# parse params
|
|
186
|
+
while (line = sio.gets)
|
|
187
|
+
line.chomp!
|
|
188
|
+
key, val = line.split(/=/, 2)
|
|
189
|
+
params[key] = val
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
params['receipt'] = receipt
|
|
193
|
+
authorization =
|
|
194
|
+
case command
|
|
195
|
+
when 'purchase'
|
|
196
|
+
params['txn_ref']
|
|
197
|
+
when 'preauth'
|
|
198
|
+
params['transaction_no']
|
|
199
|
+
else
|
|
200
|
+
nil
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
Response.new(params['status'] == 'approved',
|
|
204
|
+
params['response_text'],
|
|
205
|
+
params,
|
|
206
|
+
:authorization => authorization)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
#
|
|
210
|
+
# Log a message if logging is enabled.
|
|
211
|
+
#
|
|
212
|
+
def log(msg)
|
|
213
|
+
logger.info(msg.chomp) unless logger.nil?
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
#
|
|
217
|
+
# Return a copy of the given string (to be sent to the gateway),
|
|
218
|
+
# with sensitive information hidden.
|
|
219
|
+
#
|
|
220
|
+
def obscure_send_string(string)
|
|
221
|
+
string.gsub(/LOGIN=[^&]+/) do |keyval|
|
|
222
|
+
keyval.sub(/[^\/]+\z/){|pass| '*'*pass.size}
|
|
223
|
+
end.gsub(/CCNUM=[^&]+/) do |keyval|
|
|
224
|
+
keyval.sub(/[^=]+\z/){|num| obscure_card_number(num)}
|
|
225
|
+
end.gsub(/CCEXP=[^&]+/) do |keyval|
|
|
226
|
+
keyval.sub(/[^=]+\z/){|num| obscure_card_expiry(num)}
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
#
|
|
231
|
+
# Return a copy of the given string (received from the gateway),
|
|
232
|
+
# with sensitive information hidden.
|
|
233
|
+
#
|
|
234
|
+
def obscure_recv_string(string)
|
|
235
|
+
string.
|
|
236
|
+
gsub(/(card_(?:no|number)=)(.*)$/){$1 << obscure_card_number($2)}.
|
|
237
|
+
gsub(/(card_expiry=)(.*)$/){$1 << obscure_card_expiry($2)}
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
#
|
|
241
|
+
# Obscure a credit card number.
|
|
242
|
+
#
|
|
243
|
+
def obscure_card_number(number)
|
|
244
|
+
return number if number.size < 4
|
|
245
|
+
number[0...-4] = '*'*(number.size-4)
|
|
246
|
+
return number
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
#
|
|
250
|
+
# Obscure a credit card expiry.
|
|
251
|
+
#
|
|
252
|
+
def obscure_card_expiry(expiry)
|
|
253
|
+
expiry.gsub(/\d/, '*')
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|