johnreitano-activemerchant 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +508 -0
- data/CONTRIBUTORS +134 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +136 -0
- data/gem-public_cert.pem +20 -0
- data/lib/active_merchant/billing/avs_result.rb +98 -0
- data/lib/active_merchant/billing/base.rb +57 -0
- data/lib/active_merchant/billing/check.rb +68 -0
- data/lib/active_merchant/billing/credit_card.rb +159 -0
- data/lib/active_merchant/billing/credit_card_formatting.rb +21 -0
- data/lib/active_merchant/billing/credit_card_methods.rb +125 -0
- data/lib/active_merchant/billing/cvv_result.rb +38 -0
- data/lib/active_merchant/billing/expiry_date.rb +34 -0
- data/lib/active_merchant/billing/gateway.rb +163 -0
- data/lib/active_merchant/billing/gateways/authorize_net.rb +654 -0
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +736 -0
- data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +244 -0
- data/lib/active_merchant/billing/gateways/beanstream.rb +102 -0
- data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
- data/lib/active_merchant/billing/gateways/bogus.rb +98 -0
- data/lib/active_merchant/billing/gateways/braintree.rb +17 -0
- data/lib/active_merchant/billing/gateways/card_stream.rb +230 -0
- data/lib/active_merchant/billing/gateways/cyber_source.rb +594 -0
- data/lib/active_merchant/billing/gateways/data_cash.rb +593 -0
- data/lib/active_merchant/billing/gateways/efsnet.rb +229 -0
- data/lib/active_merchant/billing/gateways/elavon.rb +134 -0
- data/lib/active_merchant/billing/gateways/eway.rb +277 -0
- data/lib/active_merchant/billing/gateways/exact.rb +222 -0
- data/lib/active_merchant/billing/gateways/first_pay.rb +172 -0
- data/lib/active_merchant/billing/gateways/instapay.rb +164 -0
- data/lib/active_merchant/billing/gateways/jetpay.rb +270 -0
- data/lib/active_merchant/billing/gateways/linkpoint.rb +449 -0
- data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +154 -0
- data/lib/active_merchant/billing/gateways/merchant_ware.rb +283 -0
- data/lib/active_merchant/billing/gateways/modern_payments.rb +36 -0
- data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +220 -0
- data/lib/active_merchant/billing/gateways/moneris.rb +205 -0
- data/lib/active_merchant/billing/gateways/net_registry.rb +189 -0
- data/lib/active_merchant/billing/gateways/netbilling.rb +168 -0
- data/lib/active_merchant/billing/gateways/ogone.rb +279 -0
- data/lib/active_merchant/billing/gateways/pay_junction.rb +392 -0
- data/lib/active_merchant/billing/gateways/pay_secure.rb +120 -0
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +207 -0
- data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
- data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
- data/lib/active_merchant/billing/gateways/payflow.rb +236 -0
- data/lib/active_merchant/billing/gateways/payflow_express.rb +138 -0
- data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
- data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
- data/lib/active_merchant/billing/gateways/payment_express.rb +230 -0
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +326 -0
- data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +38 -0
- data/lib/active_merchant/billing/gateways/paypal.rb +121 -0
- data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
- data/lib/active_merchant/billing/gateways/paypal_express.rb +130 -0
- data/lib/active_merchant/billing/gateways/paypal_express_common.rb +20 -0
- data/lib/active_merchant/billing/gateways/plugnpay.rb +292 -0
- data/lib/active_merchant/billing/gateways/psigate.rb +214 -0
- data/lib/active_merchant/billing/gateways/psl_card.rb +304 -0
- data/lib/active_merchant/billing/gateways/quickpay.rb +213 -0
- data/lib/active_merchant/billing/gateways/realex.rb +200 -0
- data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +88 -0
- data/lib/active_merchant/billing/gateways/sage/sage_core.rb +116 -0
- data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +97 -0
- data/lib/active_merchant/billing/gateways/sage.rb +146 -0
- data/lib/active_merchant/billing/gateways/sage_pay.rb +309 -0
- data/lib/active_merchant/billing/gateways/sallie_mae.rb +144 -0
- data/lib/active_merchant/billing/gateways/secure_pay.rb +31 -0
- data/lib/active_merchant/billing/gateways/secure_pay_au.rb +157 -0
- data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +113 -0
- data/lib/active_merchant/billing/gateways/skip_jack.rb +453 -0
- data/lib/active_merchant/billing/gateways/smart_ps.rb +265 -0
- data/lib/active_merchant/billing/gateways/trans_first.rb +127 -0
- data/lib/active_merchant/billing/gateways/transax.rb +25 -0
- data/lib/active_merchant/billing/gateways/trust_commerce.rb +418 -0
- data/lib/active_merchant/billing/gateways/usa_epay.rb +194 -0
- data/lib/active_merchant/billing/gateways/verifi.rb +228 -0
- data/lib/active_merchant/billing/gateways/viaklix.rb +189 -0
- data/lib/active_merchant/billing/gateways/wirecard.rb +318 -0
- data/lib/active_merchant/billing/gateways.rb +18 -0
- data/lib/active_merchant/billing/integrations/action_view_helper.rb +79 -0
- data/lib/active_merchant/billing/integrations/bogus/helper.rb +17 -0
- data/lib/active_merchant/billing/integrations/bogus/notification.rb +11 -0
- data/lib/active_merchant/billing/integrations/bogus/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/bogus.rb +23 -0
- data/lib/active_merchant/billing/integrations/chronopay/helper.rb +120 -0
- data/lib/active_merchant/billing/integrations/chronopay/notification.rb +158 -0
- data/lib/active_merchant/billing/integrations/chronopay/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/chronopay.rb +23 -0
- data/lib/active_merchant/billing/integrations/gestpay/common.rb +42 -0
- data/lib/active_merchant/billing/integrations/gestpay/helper.rb +70 -0
- data/lib/active_merchant/billing/integrations/gestpay/notification.rb +85 -0
- data/lib/active_merchant/billing/integrations/gestpay/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/gestpay.rb +25 -0
- data/lib/active_merchant/billing/integrations/helper.rb +93 -0
- data/lib/active_merchant/billing/integrations/hi_trust/helper.rb +58 -0
- data/lib/active_merchant/billing/integrations/hi_trust/notification.rb +59 -0
- data/lib/active_merchant/billing/integrations/hi_trust/return.rb +67 -0
- data/lib/active_merchant/billing/integrations/hi_trust.rb +27 -0
- data/lib/active_merchant/billing/integrations/nochex/helper.rb +68 -0
- data/lib/active_merchant/billing/integrations/nochex/notification.rb +94 -0
- data/lib/active_merchant/billing/integrations/nochex/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/nochex.rb +88 -0
- data/lib/active_merchant/billing/integrations/notification.rb +62 -0
- data/lib/active_merchant/billing/integrations/paypal/helper.rb +119 -0
- data/lib/active_merchant/billing/integrations/paypal/notification.rb +154 -0
- data/lib/active_merchant/billing/integrations/paypal/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/paypal.rb +39 -0
- data/lib/active_merchant/billing/integrations/quickpay/helper.rb +72 -0
- data/lib/active_merchant/billing/integrations/quickpay/notification.rb +74 -0
- data/lib/active_merchant/billing/integrations/quickpay.rb +17 -0
- data/lib/active_merchant/billing/integrations/return.rb +35 -0
- data/lib/active_merchant/billing/integrations/two_checkout/helper.rb +59 -0
- data/lib/active_merchant/billing/integrations/two_checkout/notification.rb +114 -0
- data/lib/active_merchant/billing/integrations/two_checkout/return.rb +17 -0
- data/lib/active_merchant/billing/integrations/two_checkout.rb +23 -0
- data/lib/active_merchant/billing/integrations.rb +29 -0
- data/lib/active_merchant/billing/response.rb +32 -0
- data/lib/active_merchant/billing.rb +9 -0
- data/lib/active_merchant/lib/connection.rb +170 -0
- data/lib/active_merchant/lib/country.rb +319 -0
- 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 +47 -0
- data/lib/active_merchant/lib/requires_parameters.rb +16 -0
- data/lib/active_merchant/lib/utils.rb +18 -0
- data/lib/active_merchant/lib/validateable.rb +76 -0
- data/lib/active_merchant.rb +46 -0
- data/lib/certs/cacert.pem +7815 -0
- data/lib/support/gateway_support.rb +58 -0
- metadata +218 -0
@@ -0,0 +1,654 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
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/]
|
26
|
+
class AuthorizeNetGateway < Gateway
|
27
|
+
API_VERSION = '3.1'
|
28
|
+
|
29
|
+
class_inheritable_accessor :test_url, :live_url, :arb_test_url, :arb_live_url
|
30
|
+
|
31
|
+
self.test_url = "https://test.authorize.net/gateway/transact.dll"
|
32
|
+
self.live_url = "https://secure.authorize.net/gateway/transact.dll"
|
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
|
+
class_inheritable_accessor :duplicate_window
|
38
|
+
|
39
|
+
APPROVED, DECLINED, ERROR, FRAUD_REVIEW = 1, 2, 3, 4
|
40
|
+
|
41
|
+
RESPONSE_CODE, RESPONSE_REASON_CODE, RESPONSE_REASON_TEXT = 0, 2, 3
|
42
|
+
AVS_RESULT_CODE, TRANSACTION_ID, CARD_CODE_RESPONSE_CODE = 5, 6, 38
|
43
|
+
|
44
|
+
self.supported_countries = ['US']
|
45
|
+
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
46
|
+
self.homepage_url = 'http://www.authorize.net/'
|
47
|
+
self.display_name = 'Authorize.Net'
|
48
|
+
|
49
|
+
CARD_CODE_ERRORS = %w( N S )
|
50
|
+
AVS_ERRORS = %w( A E N R W Z )
|
51
|
+
|
52
|
+
AUTHORIZE_NET_ARB_NAMESPACE = 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'
|
53
|
+
|
54
|
+
RECURRING_ACTIONS = {
|
55
|
+
:create => 'ARBCreateSubscription',
|
56
|
+
:update => 'ARBUpdateSubscription',
|
57
|
+
:cancel => 'ARBCancelSubscription'
|
58
|
+
}
|
59
|
+
|
60
|
+
# Creates a new AuthorizeNetGateway
|
61
|
+
#
|
62
|
+
# The gateway requires that a valid login and password be passed
|
63
|
+
# in the +options+ hash.
|
64
|
+
#
|
65
|
+
# ==== Options
|
66
|
+
#
|
67
|
+
# * <tt>:login</tt> -- The Authorize.Net API Login ID (REQUIRED)
|
68
|
+
# * <tt>:password</tt> -- The Authorize.Net Transaction Key. (REQUIRED)
|
69
|
+
# * <tt>:test</tt> -- +true+ or +false+. If true, perform transactions against the test server.
|
70
|
+
# Otherwise, perform transactions against the production server.
|
71
|
+
def initialize(options = {})
|
72
|
+
requires!(options, :login, :password)
|
73
|
+
@options = options
|
74
|
+
super
|
75
|
+
end
|
76
|
+
|
77
|
+
# Performs an authorization, which reserves the funds on the customer's credit card, but does not
|
78
|
+
# charge the card.
|
79
|
+
#
|
80
|
+
# ==== Parameters
|
81
|
+
#
|
82
|
+
# * <tt>money</tt> -- The amount to be authorized as an Integer value in cents.
|
83
|
+
# * <tt>creditcard</tt> -- The CreditCard details for the transaction.
|
84
|
+
# * <tt>options</tt> -- A hash of optional parameters.
|
85
|
+
def authorize(money, creditcard, options = {})
|
86
|
+
post = {}
|
87
|
+
add_invoice(post, options)
|
88
|
+
add_creditcard(post, creditcard)
|
89
|
+
add_address(post, options)
|
90
|
+
add_customer_data(post, options)
|
91
|
+
add_duplicate_window(post)
|
92
|
+
|
93
|
+
commit('AUTH_ONLY', money, post)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Perform a purchase, which is essentially an authorization and capture in a single operation.
|
97
|
+
#
|
98
|
+
# ==== Parameters
|
99
|
+
#
|
100
|
+
# * <tt>money</tt> -- The amount to be purchased as an Integer value in cents.
|
101
|
+
# * <tt>creditcard</tt> -- The CreditCard details for the transaction.
|
102
|
+
# * <tt>options</tt> -- A hash of optional parameters.
|
103
|
+
def purchase(money, creditcard, options = {})
|
104
|
+
post = {}
|
105
|
+
add_invoice(post, options)
|
106
|
+
add_creditcard(post, creditcard)
|
107
|
+
add_address(post, options)
|
108
|
+
add_customer_data(post, options)
|
109
|
+
add_duplicate_window(post)
|
110
|
+
|
111
|
+
commit('AUTH_CAPTURE', money, post)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Captures the funds from an authorized transaction.
|
115
|
+
#
|
116
|
+
# ==== Parameters
|
117
|
+
#
|
118
|
+
# * <tt>money</tt> -- The amount to be captured as an Integer value in cents.
|
119
|
+
# * <tt>authorization</tt> -- The authorization returned from the previous authorize request.
|
120
|
+
def capture(money, authorization, options = {})
|
121
|
+
post = {:trans_id => authorization}
|
122
|
+
add_customer_data(post, options)
|
123
|
+
commit('PRIOR_AUTH_CAPTURE', money, post)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Void a previous transaction
|
127
|
+
#
|
128
|
+
# ==== Parameters
|
129
|
+
#
|
130
|
+
# * <tt>authorization</tt> - The authorization returned from the previous authorize request.
|
131
|
+
def void(authorization, options = {})
|
132
|
+
post = {:trans_id => authorization}
|
133
|
+
commit('VOID', nil, post)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Credit an account.
|
137
|
+
#
|
138
|
+
# This transaction is also referred to as a Refund and indicates to the gateway that
|
139
|
+
# money should flow from the merchant to the customer.
|
140
|
+
#
|
141
|
+
# ==== Parameters
|
142
|
+
#
|
143
|
+
# * <tt>money</tt> -- The amount to be credited to the customer as an Integer value in cents.
|
144
|
+
# * <tt>identification</tt> -- The ID of the original transaction against which the credit is being issued.
|
145
|
+
# * <tt>options</tt> -- A hash of parameters.
|
146
|
+
#
|
147
|
+
# ==== Options
|
148
|
+
#
|
149
|
+
# * <tt>:card_number</tt> -- The credit card number the credit is being issued to. (REQUIRED)
|
150
|
+
def credit(money, identification, options = {})
|
151
|
+
requires!(options, :card_number)
|
152
|
+
|
153
|
+
post = { :trans_id => identification,
|
154
|
+
:card_num => options[:card_number]
|
155
|
+
}
|
156
|
+
add_invoice(post, options)
|
157
|
+
|
158
|
+
commit('CREDIT', money, post)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Create a recurring payment.
|
162
|
+
#
|
163
|
+
# This transaction creates a new Automated Recurring Billing (ARB) subscription. Your account must have ARB enabled.
|
164
|
+
#
|
165
|
+
# ==== Parameters
|
166
|
+
#
|
167
|
+
# * <tt>money</tt> -- The amount to be charged to the customer at each interval as an Integer value in cents.
|
168
|
+
# * <tt>creditcard</tt> -- The CreditCard details for the transaction.
|
169
|
+
# * <tt>options</tt> -- A hash of parameters.
|
170
|
+
#
|
171
|
+
# ==== Options
|
172
|
+
#
|
173
|
+
# * <tt>:interval</tt> -- A hash containing information about the interval of time between payments. Must
|
174
|
+
# contain the keys <tt>:length</tt> and <tt>:unit</tt>. <tt>:unit</tt> can be either <tt>:months</tt> or <tt>:days</tt>.
|
175
|
+
# If <tt>:unit</tt> is <tt>:months</tt> then <tt>:length</tt> must be an integer between 1 and 12 inclusive.
|
176
|
+
# If <tt>:unit</tt> is <tt>:days</tt> then <tt>:length</tt> must be an integer between 7 and 365 inclusive.
|
177
|
+
# For example, to charge the customer once every three months the hash would be
|
178
|
+
# +:interval => { :unit => :months, :length => 3 }+ (REQUIRED)
|
179
|
+
# * <tt>:duration</tt> -- A hash containing keys for the <tt>:start_date</tt> the subscription begins (also the date the
|
180
|
+
# initial billing occurs) and the total number of billing <tt>:occurences</tt> or payments for the subscription. (REQUIRED)
|
181
|
+
def recurring(money, creditcard, options={})
|
182
|
+
requires!(options, :interval, :duration, :billing_address)
|
183
|
+
requires!(options[:interval], :length, [:unit, :days, :months])
|
184
|
+
requires!(options[:duration], :start_date, :occurrences)
|
185
|
+
requires!(options[:billing_address], :first_name, :last_name)
|
186
|
+
|
187
|
+
options[:credit_card] = creditcard
|
188
|
+
options[:amount] = money
|
189
|
+
|
190
|
+
request = build_recurring_request(:create, options)
|
191
|
+
recurring_commit(:create, request)
|
192
|
+
end
|
193
|
+
|
194
|
+
# Update a recurring payment's details.
|
195
|
+
#
|
196
|
+
# This transaction updates an existing Automated Recurring Billing (ARB) subscription. Your account must have ARB enabled
|
197
|
+
# and the subscription must have already been created previously by calling +recurring()+. The ability to change certain
|
198
|
+
# details about a recurring payment is dependent on transaction history and cannot be determined until after calling
|
199
|
+
# +update_recurring()+. See the ARB XML Guide for such conditions.
|
200
|
+
#
|
201
|
+
# ==== Parameters
|
202
|
+
#
|
203
|
+
# * <tt>options</tt> -- A hash of parameters.
|
204
|
+
#
|
205
|
+
# ==== Options
|
206
|
+
#
|
207
|
+
# * <tt>:subscription_id</tt> -- A string containing the <tt>:subscription_id</tt> of the recurring payment already in place
|
208
|
+
# for a given credit card. (REQUIRED)
|
209
|
+
def update_recurring(options={})
|
210
|
+
requires!(options, :subscription_id)
|
211
|
+
request = build_recurring_request(:update, options)
|
212
|
+
recurring_commit(:update, request)
|
213
|
+
end
|
214
|
+
|
215
|
+
# Cancel a recurring payment.
|
216
|
+
#
|
217
|
+
# This transaction cancels an existing Automated Recurring Billing (ARB) subscription. Your account must have ARB enabled
|
218
|
+
# and the subscription must have already been created previously by calling recurring()
|
219
|
+
#
|
220
|
+
# ==== Parameters
|
221
|
+
#
|
222
|
+
# * <tt>subscription_id</tt> -- A string containing the +subscription_id+ of the recurring payment already in place
|
223
|
+
# for a given credit card. (REQUIRED)
|
224
|
+
def cancel_recurring(subscription_id)
|
225
|
+
request = build_recurring_request(:cancel, :subscription_id => subscription_id)
|
226
|
+
recurring_commit(:cancel, request)
|
227
|
+
end
|
228
|
+
|
229
|
+
private
|
230
|
+
|
231
|
+
def commit(action, money, parameters)
|
232
|
+
parameters[:amount] = amount(money) unless action == 'VOID'
|
233
|
+
|
234
|
+
# Only activate the test_request when the :test option is passed in
|
235
|
+
parameters[:test_request] = @options[:test] ? 'TRUE' : 'FALSE'
|
236
|
+
|
237
|
+
url = test? ? self.test_url : self.live_url
|
238
|
+
data = ssl_post url, post_data(action, parameters)
|
239
|
+
|
240
|
+
response = parse(data)
|
241
|
+
|
242
|
+
message = message_from(response)
|
243
|
+
|
244
|
+
# Return the response. The authorization can be taken out of the transaction_id
|
245
|
+
# Test Mode on/off is something we have to parse from the response text.
|
246
|
+
# It usually looks something like this
|
247
|
+
#
|
248
|
+
# (TESTMODE) Successful Sale
|
249
|
+
test_mode = test? || message =~ /TESTMODE/
|
250
|
+
|
251
|
+
Response.new(success?(response), message, response,
|
252
|
+
:test => test_mode,
|
253
|
+
:authorization => response[:transaction_id],
|
254
|
+
:fraud_review => fraud_review?(response),
|
255
|
+
:avs_result => { :code => response[:avs_result_code] },
|
256
|
+
:cvv_result => response[:card_code]
|
257
|
+
)
|
258
|
+
end
|
259
|
+
|
260
|
+
def success?(response)
|
261
|
+
response[:response_code] == APPROVED
|
262
|
+
end
|
263
|
+
|
264
|
+
def fraud_review?(response)
|
265
|
+
response[:response_code] == FRAUD_REVIEW
|
266
|
+
end
|
267
|
+
|
268
|
+
def parse(body)
|
269
|
+
fields = split(body)
|
270
|
+
|
271
|
+
results = {
|
272
|
+
:response_code => fields[RESPONSE_CODE].to_i,
|
273
|
+
:response_reason_code => fields[RESPONSE_REASON_CODE],
|
274
|
+
:response_reason_text => fields[RESPONSE_REASON_TEXT],
|
275
|
+
:avs_result_code => fields[AVS_RESULT_CODE],
|
276
|
+
:transaction_id => fields[TRANSACTION_ID],
|
277
|
+
:card_code => fields[CARD_CODE_RESPONSE_CODE]
|
278
|
+
}
|
279
|
+
results
|
280
|
+
end
|
281
|
+
|
282
|
+
def post_data(action, parameters = {})
|
283
|
+
post = {}
|
284
|
+
|
285
|
+
post[:version] = API_VERSION
|
286
|
+
post[:login] = @options[:login]
|
287
|
+
post[:tran_key] = @options[:password]
|
288
|
+
post[:relay_response] = "FALSE"
|
289
|
+
post[:type] = action
|
290
|
+
post[:delim_data] = "TRUE"
|
291
|
+
post[:delim_char] = ","
|
292
|
+
post[:encap_char] = "$"
|
293
|
+
|
294
|
+
request = post.merge(parameters).collect { |key, value| "x_#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
295
|
+
request
|
296
|
+
end
|
297
|
+
|
298
|
+
def add_invoice(post, options)
|
299
|
+
post[:invoice_num] = options[:order_id]
|
300
|
+
post[:description] = options[:description]
|
301
|
+
end
|
302
|
+
|
303
|
+
def add_creditcard(post, creditcard)
|
304
|
+
post[:card_num] = creditcard.number
|
305
|
+
post[:card_code] = creditcard.verification_value if creditcard.verification_value?
|
306
|
+
post[:exp_date] = expdate(creditcard)
|
307
|
+
post[:first_name] = creditcard.first_name
|
308
|
+
post[:last_name] = creditcard.last_name
|
309
|
+
end
|
310
|
+
|
311
|
+
def add_customer_data(post, options)
|
312
|
+
if options.has_key? :email
|
313
|
+
post[:email] = options[:email]
|
314
|
+
post[:email_customer] = false
|
315
|
+
end
|
316
|
+
|
317
|
+
if options.has_key? :customer
|
318
|
+
post[:cust_id] = options[:customer]
|
319
|
+
end
|
320
|
+
|
321
|
+
if options.has_key? :ip
|
322
|
+
post[:customer_ip] = options[:ip]
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
# x_duplicate_window won't be sent by default, because sending it changes the response.
|
327
|
+
# "If this field is present in the request with or without a value, an enhanced duplicate transaction response will be sent."
|
328
|
+
# (as of 2008-12-30) http://www.authorize.net/support/AIM_guide_SCC.pdf
|
329
|
+
def add_duplicate_window(post)
|
330
|
+
unless duplicate_window.nil?
|
331
|
+
post[:duplicate_window] = duplicate_window
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def add_address(post, options)
|
336
|
+
if address = options[:billing_address] || options[:address]
|
337
|
+
post[:address] = address[:address1].to_s
|
338
|
+
post[:company] = address[:company].to_s
|
339
|
+
post[:phone] = address[:phone].to_s
|
340
|
+
post[:zip] = address[:zip].to_s
|
341
|
+
post[:city] = address[:city].to_s
|
342
|
+
post[:country] = address[:country].to_s
|
343
|
+
post[:state] = address[:state].blank? ? 'n/a' : address[:state]
|
344
|
+
end
|
345
|
+
|
346
|
+
if address = options[:shipping_address]
|
347
|
+
post[:ship_to_first_name] = address[:first_name].to_s
|
348
|
+
post[:ship_to_last_name] = address[:last_name].to_s
|
349
|
+
post[:ship_to_address] = address[:address1].to_s
|
350
|
+
post[:ship_to_company] = address[:company].to_s
|
351
|
+
post[:ship_to_phone] = address[:phone].to_s
|
352
|
+
post[:ship_to_zip] = address[:zip].to_s
|
353
|
+
post[:ship_to_city] = address[:city].to_s
|
354
|
+
post[:ship_to_country] = address[:country].to_s
|
355
|
+
post[:ship_to_state] = address[:state].blank? ? 'n/a' : address[:state]
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
# Make a ruby type out of the response string
|
360
|
+
def normalize(field)
|
361
|
+
case field
|
362
|
+
when "true" then true
|
363
|
+
when "false" then false
|
364
|
+
when "" then nil
|
365
|
+
when "null" then nil
|
366
|
+
else field
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
def message_from(results)
|
371
|
+
if results[:response_code] == DECLINED
|
372
|
+
return CVVResult.messages[ results[:card_code] ] if CARD_CODE_ERRORS.include?(results[:card_code])
|
373
|
+
return AVSResult.messages[ results[:avs_result_code] ] if AVS_ERRORS.include?(results[:avs_result_code])
|
374
|
+
end
|
375
|
+
|
376
|
+
return results[:response_reason_text].nil? ? '' : results[:response_reason_text][0..-2]
|
377
|
+
end
|
378
|
+
|
379
|
+
def expdate(creditcard)
|
380
|
+
year = sprintf("%.4i", creditcard.year)
|
381
|
+
month = sprintf("%.2i", creditcard.month)
|
382
|
+
|
383
|
+
"#{month}#{year[-2..-1]}"
|
384
|
+
end
|
385
|
+
|
386
|
+
def split(response)
|
387
|
+
response[1..-2].split(/\$,\$/)
|
388
|
+
end
|
389
|
+
|
390
|
+
# ARB
|
391
|
+
|
392
|
+
# Builds recurring billing request
|
393
|
+
def build_recurring_request(action, options = {})
|
394
|
+
unless RECURRING_ACTIONS.include?(action)
|
395
|
+
raise StandardError, "Invalid Automated Recurring Billing Action: #{action}"
|
396
|
+
end
|
397
|
+
|
398
|
+
xml = Builder::XmlMarkup.new(:indent => 2)
|
399
|
+
xml.instruct!(:xml, :version => '1.0', :encoding => 'utf-8')
|
400
|
+
xml.tag!("#{RECURRING_ACTIONS[action]}Request", :xmlns => AUTHORIZE_NET_ARB_NAMESPACE) do
|
401
|
+
add_arb_merchant_authentication(xml)
|
402
|
+
# Merchant-assigned reference ID for the request
|
403
|
+
xml.tag!('refId', options[:ref_id]) if options[:ref_id]
|
404
|
+
send("build_arb_#{action}_subscription_request", xml, options)
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
# Contains the merchant’s payment gateway account authentication information
|
409
|
+
def add_arb_merchant_authentication(xml)
|
410
|
+
xml.tag!('merchantAuthentication') do
|
411
|
+
xml.tag!('name', @options[:login])
|
412
|
+
xml.tag!('transactionKey', @options[:password])
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
# Builds body for ARBCreateSubscriptionRequest
|
417
|
+
def build_arb_create_subscription_request(xml, options)
|
418
|
+
# Subscription
|
419
|
+
add_arb_subscription(xml, options)
|
420
|
+
|
421
|
+
xml.target!
|
422
|
+
end
|
423
|
+
|
424
|
+
# Builds body for ARBUpdateSubscriptionRequest
|
425
|
+
def build_arb_update_subscription_request(xml, options)
|
426
|
+
xml.tag!('subscriptionId', options[:subscription_id])
|
427
|
+
# Adds Subscription
|
428
|
+
add_arb_subscription(xml, options)
|
429
|
+
|
430
|
+
xml.target!
|
431
|
+
end
|
432
|
+
|
433
|
+
# Builds body for ARBCancelSubscriptionRequest
|
434
|
+
def build_arb_cancel_subscription_request(xml, options)
|
435
|
+
xml.tag!('subscriptionId', options[:subscription_id])
|
436
|
+
|
437
|
+
xml.target!
|
438
|
+
end
|
439
|
+
|
440
|
+
# Adds subscription information
|
441
|
+
def add_arb_subscription(xml, options)
|
442
|
+
xml.tag!('subscription') do
|
443
|
+
# Merchant-assigned name for the subscription (optional)
|
444
|
+
xml.tag!('name', options[:subscription_name]) if options[:subscription_name]
|
445
|
+
# Contains information about the payment schedule
|
446
|
+
add_arb_payment_schedule(xml, options)
|
447
|
+
# The amount to be billed to the customer
|
448
|
+
# for each payment in the subscription
|
449
|
+
xml.tag!('amount', amount(options[:amount])) if options[:amount]
|
450
|
+
if trial = options[:trial]
|
451
|
+
# The amount to be charged for each payment during a trial period (conditional)
|
452
|
+
xml.tag!('trialAmount', amount(trial[:amount])) if trial[:amount]
|
453
|
+
end
|
454
|
+
# Contains either the customer’s credit card
|
455
|
+
# or bank account payment information
|
456
|
+
add_arb_payment(xml, options)
|
457
|
+
# Contains order information (optional)
|
458
|
+
add_arb_order(xml, options)
|
459
|
+
# Contains information about the customer
|
460
|
+
add_arb_customer(xml, options)
|
461
|
+
# Contains the customer's billing address information
|
462
|
+
add_arb_address(xml, 'billTo', options[:billing_address])
|
463
|
+
# Contains the customer's shipping address information (optional)
|
464
|
+
add_arb_address(xml, 'shipTo', options[:shipping_address])
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
# Adds information about the interval of time between payments
|
469
|
+
def add_arb_interval(xml, options)
|
470
|
+
interval = options[:interval]
|
471
|
+
return unless interval
|
472
|
+
xml.tag!('interval') do
|
473
|
+
# The measurement of time, in association with the Interval Unit,
|
474
|
+
# that is used to define the frequency of the billing occurrences
|
475
|
+
xml.tag!('length', interval[:length])
|
476
|
+
# The unit of time, in association with the Interval Length,
|
477
|
+
# between each billing occurrence
|
478
|
+
xml.tag!('unit', interval[:unit].to_s)
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
# Adds information about the subscription duration
|
483
|
+
def add_arb_duration(xml, options)
|
484
|
+
duration = options[:duration]
|
485
|
+
return unless duration
|
486
|
+
# The date the subscription begins
|
487
|
+
# (also the date the initial billing occurs)
|
488
|
+
xml.tag!('startDate', duration[:start_date]) if duration[:start_date]
|
489
|
+
# Number of billing occurrences or payments for the subscription
|
490
|
+
xml.tag!('totalOccurrences', duration[:occurrences]) if duration[:occurrences]
|
491
|
+
end
|
492
|
+
|
493
|
+
def add_arb_payment_schedule(xml, options)
|
494
|
+
return unless options[:interval] || options[:duration]
|
495
|
+
xml.tag!('paymentSchedule') do
|
496
|
+
# Contains information about the interval of time between payments
|
497
|
+
add_arb_interval(xml, options)
|
498
|
+
add_arb_duration(xml, options)
|
499
|
+
if trial = options[:trial]
|
500
|
+
# Number of billing occurrences or payments in the trial period (optional)
|
501
|
+
xml.tag!('trialOccurrences', trial[:occurrences]) if trial[:occurrences]
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
# Adds customer's credit card or bank account payment information
|
507
|
+
def add_arb_payment(xml, options)
|
508
|
+
return unless options[:credit_card] || options[:bank_account]
|
509
|
+
xml.tag!('payment') do
|
510
|
+
# Contains the customer’s credit card information
|
511
|
+
add_arb_credit_card(xml, options)
|
512
|
+
# Contains the customer’s bank account information
|
513
|
+
add_arb_bank_account(xml, options)
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
# Adds customer’s credit card information
|
518
|
+
# Note: This element should only be included
|
519
|
+
# when the payment method is credit card.
|
520
|
+
def add_arb_credit_card(xml, options)
|
521
|
+
credit_card = options[:credit_card]
|
522
|
+
return unless credit_card
|
523
|
+
xml.tag!('creditCard') do
|
524
|
+
# The credit card number used for payment of the subscription
|
525
|
+
xml.tag!('cardNumber', credit_card.number)
|
526
|
+
# The expiration date of the credit card used for the subscription
|
527
|
+
xml.tag!('expirationDate', arb_expdate(credit_card))
|
528
|
+
end
|
529
|
+
end
|
530
|
+
|
531
|
+
# Adds customer’s bank account information
|
532
|
+
# Note: This element should only be included
|
533
|
+
# when the payment method is bank account.
|
534
|
+
def add_arb_bank_account(xml, options)
|
535
|
+
bank_account = options[:bank_account]
|
536
|
+
return unless bank_account
|
537
|
+
xml.tag!('bankAccount') do
|
538
|
+
# The type of bank account used for payment of the subscription
|
539
|
+
xml.tag!('accountType', bank_account[:account_type])
|
540
|
+
# The routing number of the customer’s bank
|
541
|
+
xml.tag!('routingNumber', bank_account[:routing_number])
|
542
|
+
# The bank account number used for payment of the subscription
|
543
|
+
xml.tag!('accountNumber', bank_account[:account_number])
|
544
|
+
# The full name of the individual associated
|
545
|
+
# with the bank account number
|
546
|
+
xml.tag!('nameOfAccount', bank_account[:name_of_account])
|
547
|
+
# The full name of the individual associated
|
548
|
+
# with the bank account number (optional)
|
549
|
+
xml.tag!('bankName', bank_account[:bank_name]) if bank_account[:bank_name]
|
550
|
+
# The type of electronic check transaction used for the subscription
|
551
|
+
xml.tag!('echeckType', bank_account[:echeck_type])
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
# Adds order information (optional)
|
556
|
+
def add_arb_order(xml, options)
|
557
|
+
order = options[:order]
|
558
|
+
return unless order
|
559
|
+
xml.tag!('order') do
|
560
|
+
# Merchant-assigned invoice number for the subscription (optional)
|
561
|
+
xml.tag!('invoiceNumber', order[:invoice_number])
|
562
|
+
# Description of the subscription (optional)
|
563
|
+
xml.tag!('description', order[:description])
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
# Adds information about the customer
|
568
|
+
def add_arb_customer(xml, options)
|
569
|
+
customer = options[:customer]
|
570
|
+
return unless customer
|
571
|
+
xml.tag!('customer') do
|
572
|
+
xml.tag!('type', customer[:type]) if customer[:type]
|
573
|
+
xml.tag!('id', customer[:id]) if customer[:id]
|
574
|
+
xml.tag!('email', customer[:email]) if customer[:email]
|
575
|
+
xml.tag!('phoneNumber', customer[:phone_number]) if customer[:phone_number]
|
576
|
+
xml.tag!('faxNumber', customer[:fax_number]) if customer[:fax_number]
|
577
|
+
add_arb_drivers_license(xml, options)
|
578
|
+
xml.tag!('taxId', customer[:tax_id]) if customer[:tax_id]
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
# Adds the customer's driver's license information (conditional)
|
583
|
+
def add_arb_drivers_license(xml, options)
|
584
|
+
return unless customer = options[:customer]
|
585
|
+
return unless drivers_license = customer[:drivers_license]
|
586
|
+
xml.tag!('driversLicense') do
|
587
|
+
# The customer's driver's license number
|
588
|
+
xml.tag!('number', drivers_license[:number])
|
589
|
+
# The customer's driver's license state
|
590
|
+
xml.tag!('state', drivers_license[:state])
|
591
|
+
# The customer's driver's license date of birth
|
592
|
+
xml.tag!('dateOfBirth', drivers_license[:date_of_birth])
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
# Adds address information
|
597
|
+
def add_arb_address(xml, container_name, address)
|
598
|
+
return if address.blank?
|
599
|
+
xml.tag!(container_name) do
|
600
|
+
xml.tag!('firstName', address[:first_name])
|
601
|
+
xml.tag!('lastName', address[:last_name])
|
602
|
+
xml.tag!('company', address[:company])
|
603
|
+
xml.tag!('address', address[:address1])
|
604
|
+
xml.tag!('city', address[:city])
|
605
|
+
xml.tag!('state', address[:state])
|
606
|
+
xml.tag!('zip', address[:zip])
|
607
|
+
xml.tag!('country', address[:country])
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
def arb_expdate(credit_card)
|
612
|
+
sprintf('%04d-%02d', credit_card.year, credit_card.month)
|
613
|
+
end
|
614
|
+
|
615
|
+
def recurring_commit(action, request)
|
616
|
+
url = test? ? arb_test_url : arb_live_url
|
617
|
+
xml = ssl_post(url, request, "Content-Type" => "text/xml")
|
618
|
+
|
619
|
+
response = recurring_parse(action, xml)
|
620
|
+
|
621
|
+
message = response[:message] || response[:text]
|
622
|
+
test_mode = test? || message =~ /Test Mode/
|
623
|
+
success = response[:result_code] == 'Ok'
|
624
|
+
|
625
|
+
Response.new(success, message, response,
|
626
|
+
:test => test_mode,
|
627
|
+
:authorization => response[:subscription_id]
|
628
|
+
)
|
629
|
+
end
|
630
|
+
|
631
|
+
def recurring_parse(action, xml)
|
632
|
+
response = {}
|
633
|
+
xml = REXML::Document.new(xml)
|
634
|
+
root = REXML::XPath.first(xml, "//#{RECURRING_ACTIONS[action]}Response") ||
|
635
|
+
REXML::XPath.first(xml, "//ErrorResponse")
|
636
|
+
if root
|
637
|
+
root.elements.to_a.each do |node|
|
638
|
+
recurring_parse_element(response, node)
|
639
|
+
end
|
640
|
+
end
|
641
|
+
|
642
|
+
response
|
643
|
+
end
|
644
|
+
|
645
|
+
def recurring_parse_element(response, node)
|
646
|
+
if node.has_elements?
|
647
|
+
node.elements.each{|e| recurring_parse_element(response, e) }
|
648
|
+
else
|
649
|
+
response[node.name.underscore.to_sym] = node.text
|
650
|
+
end
|
651
|
+
end
|
652
|
+
end
|
653
|
+
end
|
654
|
+
end
|