activemerchant 1.100.0 → 1.101.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +10 -0
- data/lib/active_merchant/billing/check.rb +2 -6
- data/lib/active_merchant/billing/credit_card.rb +1 -3
- data/lib/active_merchant/billing/gateway.rb +4 -7
- data/lib/active_merchant/billing/gateways/authorize_net.rb +3 -9
- data/lib/active_merchant/billing/gateways/authorize_net_arb.rb +1 -3
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +4 -12
- data/lib/active_merchant/billing/gateways/axcessms.rb +1 -3
- data/lib/active_merchant/billing/gateways/balanced.rb +12 -11
- data/lib/active_merchant/billing/gateways/blue_pay.rb +2 -6
- data/lib/active_merchant/billing/gateways/blue_snap.rb +1 -3
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +7 -21
- data/lib/active_merchant/billing/gateways/cecabank.rb +1 -3
- data/lib/active_merchant/billing/gateways/checkout.rb +1 -3
- data/lib/active_merchant/billing/gateways/checkout_v2.rb +1 -3
- data/lib/active_merchant/billing/gateways/clearhaus.rb +9 -12
- data/lib/active_merchant/billing/gateways/credorax.rb +1 -1
- data/lib/active_merchant/billing/gateways/culqi.rb +6 -5
- data/lib/active_merchant/billing/gateways/cyber_source.rb +1 -0
- data/lib/active_merchant/billing/gateways/data_cash.rb +1 -3
- data/lib/active_merchant/billing/gateways/decidir.rb +1 -3
- data/lib/active_merchant/billing/gateways/ebanx.rb +1 -3
- data/lib/active_merchant/billing/gateways/elavon.rb +2 -6
- data/lib/active_merchant/billing/gateways/element.rb +1 -3
- data/lib/active_merchant/billing/gateways/eway_managed.rb +6 -5
- data/lib/active_merchant/billing/gateways/fat_zebra.rb +7 -6
- data/lib/active_merchant/billing/gateways/forte.rb +2 -6
- data/lib/active_merchant/billing/gateways/garanti.rb +1 -3
- data/lib/active_merchant/billing/gateways/global_collect.rb +1 -3
- data/lib/active_merchant/billing/gateways/hdfc.rb +1 -3
- data/lib/active_merchant/billing/gateways/hps.rb +6 -5
- data/lib/active_merchant/billing/gateways/inspire.rb +2 -6
- data/lib/active_merchant/billing/gateways/instapay.rb +1 -3
- data/lib/active_merchant/billing/gateways/iridium.rb +1 -3
- data/lib/active_merchant/billing/gateways/iveri.rb +7 -8
- data/lib/active_merchant/billing/gateways/jetpay.rb +3 -9
- data/lib/active_merchant/billing/gateways/jetpay_v2.rb +2 -6
- data/lib/active_merchant/billing/gateways/kushki.rb +7 -8
- data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +6 -5
- data/lib/active_merchant/billing/gateways/merchant_one.rb +1 -3
- data/lib/active_merchant/billing/gateways/merchant_ware.rb +1 -3
- data/lib/active_merchant/billing/gateways/merchant_warrior.rb +1 -3
- data/lib/active_merchant/billing/gateways/mercury.rb +2 -6
- data/lib/active_merchant/billing/gateways/metrics_global.rb +4 -12
- data/lib/active_merchant/billing/gateways/migs.rb +1 -3
- data/lib/active_merchant/billing/gateways/moneris.rb +1 -3
- data/lib/active_merchant/billing/gateways/moneris_us.rb +1 -3
- data/lib/active_merchant/billing/gateways/ncr_secure_pay.rb +1 -3
- data/lib/active_merchant/billing/gateways/netbanx.rb +24 -9
- data/lib/active_merchant/billing/gateways/netbilling.rb +1 -3
- data/lib/active_merchant/billing/gateways/network_merchants.rb +1 -3
- data/lib/active_merchant/billing/gateways/opp.rb +12 -13
- data/lib/active_merchant/billing/gateways/optimal_payment.rb +1 -3
- data/lib/active_merchant/billing/gateways/orbital.rb +8 -11
- data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +1 -3
- data/lib/active_merchant/billing/gateways/pagarme.rb +4 -12
- data/lib/active_merchant/billing/gateways/pago_facil.rb +1 -3
- data/lib/active_merchant/billing/gateways/pay_conex.rb +1 -3
- data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +1 -3
- data/lib/active_merchant/billing/gateways/pay_junction_v2.rb +6 -5
- data/lib/active_merchant/billing/gateways/payflow.rb +2 -6
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -3
- data/lib/active_merchant/billing/gateways/paymentez.rb +1 -3
- data/lib/active_merchant/billing/gateways/paymill.rb +1 -3
- data/lib/active_merchant/billing/gateways/pro_pay.rb +1 -3
- data/lib/active_merchant/billing/gateways/quickbooks.rb +104 -31
- data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +2 -6
- data/lib/active_merchant/billing/gateways/realex.rb +1 -3
- data/lib/active_merchant/billing/gateways/redsys.rb +17 -27
- data/lib/active_merchant/billing/gateways/safe_charge.rb +1 -3
- data/lib/active_merchant/billing/gateways/sage_pay.rb +1 -3
- data/lib/active_merchant/billing/gateways/secure_net.rb +2 -6
- data/lib/active_merchant/billing/gateways/secure_pay.rb +4 -12
- data/lib/active_merchant/billing/gateways/smart_ps.rb +2 -6
- data/lib/active_merchant/billing/gateways/spreedly_core.rb +2 -6
- data/lib/active_merchant/billing/gateways/stripe.rb +3 -9
- data/lib/active_merchant/billing/gateways/telr.rb +2 -6
- data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +6 -5
- data/lib/active_merchant/billing/gateways/transact_pro.rb +1 -3
- data/lib/active_merchant/billing/gateways/trust_commerce.rb +1 -3
- data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +4 -12
- data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +3 -9
- data/lib/active_merchant/billing/gateways/viaklix.rb +2 -6
- data/lib/active_merchant/billing/gateways/wepay.rb +2 -6
- data/lib/active_merchant/billing/gateways/wirecard.rb +2 -6
- data/lib/active_merchant/billing/gateways/worldpay.rb +5 -15
- data/lib/active_merchant/billing/gateways/worldpay_online_payments.rb +2 -6
- data/lib/active_merchant/version.rb +1 -1
- data/lib/support/ssl_verify.rb +1 -3
- metadata +2 -2
@@ -156,9 +156,7 @@ module ActiveMerchant #:nodoc:
|
|
156
156
|
end
|
157
157
|
|
158
158
|
def error_code_from(response)
|
159
|
-
unless success_from(response)
|
160
|
-
response[:msoft_code] || response[:phard_code]
|
161
|
-
end
|
159
|
+
response[:msoft_code] || response[:phard_code] unless success_from(response)
|
162
160
|
end
|
163
161
|
end
|
164
162
|
end
|
@@ -129,9 +129,9 @@ module ActiveMerchant #:nodoc:
|
|
129
129
|
post[:card][:holderName] = credit_card.name
|
130
130
|
post[:card][:cvv] = credit_card.verification_value
|
131
131
|
post[:card][:cardExpiry] = expdate(credit_card)
|
132
|
-
|
133
|
-
|
134
|
-
|
132
|
+
|
133
|
+
post[:authentication] = map_3ds(options[:three_d_secure]) if options[:three_d_secure]
|
134
|
+
post[:card][:billingAddress] = map_address(options[:billing_address]) if options[:billing_address]
|
135
135
|
end
|
136
136
|
|
137
137
|
def add_invoice(post, money, options)
|
@@ -151,6 +151,7 @@ module ActiveMerchant #:nodoc:
|
|
151
151
|
|
152
152
|
post[:currencyCode] = options[:currency] if options[:currency]
|
153
153
|
post[:billingDetails] = map_address(options[:billing_address]) if options[:billing_address]
|
154
|
+
post[:authentication] = map_3ds(options[:three_d_secure]) if options[:three_d_secure]
|
154
155
|
end
|
155
156
|
|
156
157
|
def expdate(credit_card)
|
@@ -179,18 +180,32 @@ module ActiveMerchant #:nodoc:
|
|
179
180
|
mapped
|
180
181
|
end
|
181
182
|
|
183
|
+
def map_3ds(three_d_secure_options)
|
184
|
+
mapped = {
|
185
|
+
:eci => three_d_secure_options[:eci],
|
186
|
+
:cavv => three_d_secure_options[:cavv],
|
187
|
+
:xid => three_d_secure_options[:xid],
|
188
|
+
:threeDResult => three_d_secure_options[:directory_response_status],
|
189
|
+
:threeDSecureVersion => three_d_secure_options[:version],
|
190
|
+
:directoryServerTransactionId => three_d_secure_options[:ds_transaction_id]
|
191
|
+
}
|
192
|
+
|
193
|
+
mapped
|
194
|
+
end
|
195
|
+
|
182
196
|
def parse(body)
|
183
197
|
body.blank? ? {} : JSON.parse(body)
|
184
198
|
end
|
185
199
|
|
186
200
|
def commit(method, uri, parameters)
|
187
201
|
params = parameters.to_json unless parameters.nil?
|
188
|
-
response =
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
202
|
+
response =
|
203
|
+
begin
|
204
|
+
parse(ssl_request(method, get_url(uri), params, headers))
|
205
|
+
rescue ResponseError => e
|
206
|
+
return Response.new(false, 'Invalid Login') if(e.response.code == '401')
|
207
|
+
parse(e.response.body)
|
208
|
+
end
|
194
209
|
|
195
210
|
success = success_from(response)
|
196
211
|
Response.new(
|
@@ -166,9 +166,7 @@ module ActiveMerchant #:nodoc:
|
|
166
166
|
end
|
167
167
|
|
168
168
|
def add_user_data(post, options)
|
169
|
-
if options[:order_id]
|
170
|
-
post[:user_data] = "order_id:#{options[:order_id]}"
|
171
|
-
end
|
169
|
+
post[:user_data] = "order_id:#{options[:order_id]}" if options[:order_id]
|
172
170
|
end
|
173
171
|
|
174
172
|
def add_transaction_id(post, transaction_id)
|
@@ -218,9 +218,7 @@ module ActiveMerchant #:nodoc:
|
|
218
218
|
return nil unless success
|
219
219
|
|
220
220
|
authorization = response['transactionid']
|
221
|
-
if
|
222
|
-
authorization = response['customer_vault_id']
|
223
|
-
end
|
221
|
+
authorization = response['customer_vault_id'] if parameters[:customer_vault] && (authorization.nil? || authorization.empty?)
|
224
222
|
|
225
223
|
authorization
|
226
224
|
end
|
@@ -125,9 +125,7 @@ module ActiveMerchant #:nodoc:
|
|
125
125
|
|
126
126
|
def purchase(money, payment, options={})
|
127
127
|
# debit
|
128
|
-
if payment.is_a?(String)
|
129
|
-
options[:registrationId] = payment
|
130
|
-
end
|
128
|
+
options[:registrationId] = payment if payment.is_a?(String)
|
131
129
|
execute_dbpa(options[:risk_workflow] ? 'PA.CP': 'DB',
|
132
130
|
money, payment, options)
|
133
131
|
end
|
@@ -311,17 +309,18 @@ module ActiveMerchant #:nodoc:
|
|
311
309
|
add_authentication(post)
|
312
310
|
post = flatten_hash(post)
|
313
311
|
|
314
|
-
response =
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
312
|
+
response =
|
313
|
+
begin
|
314
|
+
parse(
|
315
|
+
ssl_post(
|
316
|
+
url,
|
317
|
+
post.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&'),
|
318
|
+
'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8'
|
319
|
+
)
|
320
320
|
)
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
end
|
321
|
+
rescue ResponseError => e
|
322
|
+
parse(e.response.body)
|
323
|
+
end
|
325
324
|
|
326
325
|
success = success_from(response)
|
327
326
|
|
@@ -135,9 +135,7 @@ module ActiveMerchant #:nodoc:
|
|
135
135
|
|
136
136
|
def message_from(response)
|
137
137
|
REXML::XPath.each(response, '//detail') do |detail|
|
138
|
-
if detail.is_a?(REXML::Element) && detail.elements['tag'].text == 'InternalResponseDescription'
|
139
|
-
return detail.elements['value'].text
|
140
|
-
end
|
138
|
+
return detail.elements['value'].text if detail.is_a?(REXML::Element) && detail.elements['tag'].text == 'InternalResponseDescription'
|
141
139
|
end
|
142
140
|
nil
|
143
141
|
end
|
@@ -327,9 +327,7 @@ module ActiveMerchant #:nodoc:
|
|
327
327
|
xml.tag! :CustomerRefNum, options[:customer_ref_num]
|
328
328
|
else
|
329
329
|
if options[:customer_ref_num]
|
330
|
-
if creditcard
|
331
|
-
xml.tag! :CustomerProfileFromOrderInd, USE_CUSTOMER_REF_NUM
|
332
|
-
end
|
330
|
+
xml.tag! :CustomerProfileFromOrderInd, USE_CUSTOMER_REF_NUM if creditcard
|
333
331
|
xml.tag! :CustomerRefNum, options[:customer_ref_num]
|
334
332
|
else
|
335
333
|
xml.tag! :CustomerProfileFromOrderInd, AUTO_GENERATE
|
@@ -457,9 +455,7 @@ module ActiveMerchant #:nodoc:
|
|
457
455
|
# - http://download.chasepaymentech.com/docs/orbital/orbital_gateway_xml_specification.pdf
|
458
456
|
unless creditcard.nil?
|
459
457
|
if creditcard.verification_value?
|
460
|
-
if %w( visa discover ).include?(creditcard.brand)
|
461
|
-
xml.tag! :CardSecValInd, '1'
|
462
|
-
end
|
458
|
+
xml.tag! :CardSecValInd, '1' if %w( visa discover ).include?(creditcard.brand)
|
463
459
|
xml.tag! :CardSecVal, creditcard.verification_value
|
464
460
|
end
|
465
461
|
end
|
@@ -613,11 +609,12 @@ module ActiveMerchant #:nodoc:
|
|
613
609
|
request = ->(url) { parse(ssl_post(url, order, headers)) }
|
614
610
|
|
615
611
|
# Failover URL will be attempted in the event of a connection error
|
616
|
-
response =
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
612
|
+
response =
|
613
|
+
begin
|
614
|
+
request.call(remote_url)
|
615
|
+
rescue ConnectionError
|
616
|
+
request.call(remote_url(:secondary))
|
617
|
+
end
|
621
618
|
|
622
619
|
Response.new(success?(response, message_type), message_from(response), response,
|
623
620
|
{
|
@@ -30,9 +30,7 @@ module ActiveMerchant #:nodoc:
|
|
30
30
|
errors << [:merchant_name, 'is required'] if self.merchant_name.blank?
|
31
31
|
errors << [:merchant_name, 'is required to be 25 bytes or less'] if self.merchant_name.bytesize > 25
|
32
32
|
|
33
|
-
if
|
34
|
-
errors << [:merchant_phone, 'is required to follow "NNN-NNN-NNNN" or "NNN-AAAAAAA" format']
|
35
|
-
end
|
33
|
+
errors << [:merchant_phone, 'is required to follow "NNN-NNN-NNNN" or "NNN-AAAAAAA" format'] if !empty?(self.merchant_phone) && !self.merchant_phone.match(PHONE_FORMAT_1) && !self.merchant_phone.match(PHONE_FORMAT_2)
|
36
34
|
|
37
35
|
[:merchant_email, :merchant_url].each do |attr|
|
38
36
|
unless self.send(attr).blank?
|
@@ -44,26 +44,20 @@ module ActiveMerchant #:nodoc:
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def capture(money, authorization, options={})
|
47
|
-
if authorization.nil?
|
48
|
-
return Response.new(false, 'Não é possível capturar uma transação sem uma prévia autorização.')
|
49
|
-
end
|
47
|
+
return Response.new(false, 'Não é possível capturar uma transação sem uma prévia autorização.') if authorization.nil?
|
50
48
|
|
51
49
|
post = {}
|
52
50
|
commit(:post, "transactions/#{authorization}/capture", post)
|
53
51
|
end
|
54
52
|
|
55
53
|
def refund(money, authorization, options={})
|
56
|
-
if authorization.nil?
|
57
|
-
return Response.new(false, 'Não é possível estornar uma transação sem uma prévia captura.')
|
58
|
-
end
|
54
|
+
return Response.new(false, 'Não é possível estornar uma transação sem uma prévia captura.') if authorization.nil?
|
59
55
|
|
60
56
|
void(authorization, options)
|
61
57
|
end
|
62
58
|
|
63
59
|
def void(authorization, options={})
|
64
|
-
if authorization.nil?
|
65
|
-
return Response.new(false, 'Não é possível estornar uma transação autorizada sem uma prévia autorização.')
|
66
|
-
end
|
60
|
+
return Response.new(false, 'Não é possível estornar uma transação autorizada sem uma prévia autorização.') if authorization.nil?
|
67
61
|
|
68
62
|
post = {}
|
69
63
|
commit(:post, "transactions/#{authorization}/refund", post)
|
@@ -225,9 +219,7 @@ module ActiveMerchant #:nodoc:
|
|
225
219
|
end
|
226
220
|
|
227
221
|
def authorization_from(response)
|
228
|
-
if success_from(response)
|
229
|
-
response['id']
|
230
|
-
end
|
222
|
+
response['id'] if success_from(response)
|
231
223
|
end
|
232
224
|
|
233
225
|
def test?
|
@@ -53,9 +53,7 @@ module ActiveMerchant #:nodoc:
|
|
53
53
|
|
54
54
|
def add_currency(post, money, options)
|
55
55
|
currency = options.fetch(:currency, currency(money))
|
56
|
-
unless currency == self.class.default_currency
|
57
|
-
post[:divisa] = currency
|
58
|
-
end
|
56
|
+
post[:divisa] = currency unless currency == self.class.default_currency
|
59
57
|
end
|
60
58
|
|
61
59
|
def add_payment(post, credit_card)
|
@@ -51,9 +51,7 @@ module ActiveMerchant #:nodoc:
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def credit(money, payment_method, options={})
|
54
|
-
if payment_method.is_a?(String)
|
55
|
-
raise ArgumentError, 'Reference credits are not supported. Please supply the original credit card or use the #refund method.'
|
56
|
-
end
|
54
|
+
raise ArgumentError, 'Reference credits are not supported. Please supply the original credit card or use the #refund method.' if payment_method.is_a?(String)
|
57
55
|
|
58
56
|
post = {}
|
59
57
|
add_auth_purchase_params(post, money, payment_method, options)
|
@@ -255,9 +255,7 @@ module ActiveMerchant #:nodoc:
|
|
255
255
|
response_action = action.gsub(/tx/, 'rx')
|
256
256
|
root = REXML::XPath.first(xml.root, response_action)
|
257
257
|
# we might have gotten an error
|
258
|
-
if root.nil?
|
259
|
-
root = REXML::XPath.first(xml.root, 'errorrx')
|
260
|
-
end
|
258
|
+
root = REXML::XPath.first(xml.root, 'errorrx') if root.nil?
|
261
259
|
root.attributes.each do |name, value|
|
262
260
|
hash[name.to_sym] = value
|
263
261
|
end
|
@@ -111,11 +111,12 @@ module ActiveMerchant #:nodoc:
|
|
111
111
|
end
|
112
112
|
|
113
113
|
def commit(action, params)
|
114
|
-
response =
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
114
|
+
response =
|
115
|
+
begin
|
116
|
+
parse(ssl_invoke(action, params))
|
117
|
+
rescue ResponseError => e
|
118
|
+
parse(e.response.body)
|
119
|
+
end
|
119
120
|
|
120
121
|
success = success_from(response)
|
121
122
|
Response.new(
|
@@ -312,9 +312,7 @@ module ActiveMerchant #:nodoc:
|
|
312
312
|
end
|
313
313
|
|
314
314
|
def build_recurring_request(action, money, options)
|
315
|
-
unless RECURRING_ACTIONS.include?(action)
|
316
|
-
raise StandardError, "Invalid Recurring Profile Action: #{action}"
|
317
|
-
end
|
315
|
+
raise StandardError, "Invalid Recurring Profile Action: #{action}" unless RECURRING_ACTIONS.include?(action)
|
318
316
|
|
319
317
|
xml = Builder::XmlMarkup.new
|
320
318
|
xml.tag! 'RecurringProfiles' do
|
@@ -354,9 +352,7 @@ module ActiveMerchant #:nodoc:
|
|
354
352
|
yield xml
|
355
353
|
end
|
356
354
|
end
|
357
|
-
if action != :add
|
358
|
-
xml.tag! 'ProfileID', options[:profile_id]
|
359
|
-
end
|
355
|
+
xml.tag! 'ProfileID', options[:profile_id] if action != :add
|
360
356
|
if action == :inquiry
|
361
357
|
xml.tag! 'PaymentHistory', (options[:history] ? 'Y' : 'N')
|
362
358
|
end
|
@@ -159,9 +159,7 @@ module ActiveMerchant #:nodoc:
|
|
159
159
|
# REXML::XPath in Ruby 1.8.6 is now unable to match nodes based on their attributes
|
160
160
|
tx_result = root.xpath('.//TransactionResult').first
|
161
161
|
|
162
|
-
if tx_result && tx_result.attributes['Duplicate'].to_s == 'true'
|
163
|
-
response[:duplicate] = true
|
164
|
-
end
|
162
|
+
response[:duplicate] = true if tx_result && tx_result.attributes['Duplicate'].to_s == 'true'
|
165
163
|
|
166
164
|
root.xpath('.//*').each do |node|
|
167
165
|
parse_element(response, node)
|
@@ -276,9 +276,7 @@ module ActiveMerchant #:nodoc:
|
|
276
276
|
return if success_from(response)
|
277
277
|
if response['transaction']
|
278
278
|
detail = response['transaction']['status_detail']
|
279
|
-
if STANDARD_ERROR_CODE_MAPPING.include?(detail)
|
280
|
-
return STANDARD_ERROR_CODE[STANDARD_ERROR_CODE_MAPPING[detail]]
|
281
|
-
end
|
279
|
+
return STANDARD_ERROR_CODE[STANDARD_ERROR_CODE_MAPPING[detail]] if STANDARD_ERROR_CODE_MAPPING.include?(detail)
|
282
280
|
elsif response['error']
|
283
281
|
return STANDARD_ERROR_CODE[:config_error]
|
284
282
|
end
|
@@ -357,9 +357,7 @@ module ActiveMerchant #:nodoc:
|
|
357
357
|
|
358
358
|
def handle_response_correct_parsing
|
359
359
|
@message = parsed['transaction']['processing']['return']['message']
|
360
|
-
if @succeeded = is_ack?
|
361
|
-
@options[:authorization] = parsed['transaction']['identification']['uniqueId']
|
362
|
-
end
|
360
|
+
@options[:authorization] = parsed['transaction']['identification']['uniqueId'] if @succeeded = is_ack?
|
363
361
|
end
|
364
362
|
|
365
363
|
def is_ack?
|
@@ -10,13 +10,10 @@ module ActiveMerchant #:nodoc:
|
|
10
10
|
|
11
11
|
self.homepage_url = 'http://payments.intuit.com'
|
12
12
|
self.display_name = 'QuickBooks Payments'
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
authorize_url: 'https://appcenter.intuit.com/Connect/Begin',
|
18
|
-
access_token_path: '/oauth/v1/get_access_token'
|
19
|
-
}
|
13
|
+
BASE = '/quickbooks/v4/payments'
|
14
|
+
ENDPOINT = "#{BASE}/charges"
|
15
|
+
VOID_ENDPOINT = "#{BASE}/txn-requests"
|
16
|
+
REFRESH_URI = 'https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer'
|
20
17
|
|
21
18
|
# https://developer.intuit.com/docs/0150_payments/0300_developer_guides/error_handling
|
22
19
|
|
@@ -51,7 +48,15 @@ module ActiveMerchant #:nodoc:
|
|
51
48
|
FRAUD_WARNING_CODES = ['PMT-1000', 'PMT-1001', 'PMT-1002', 'PMT-1003']
|
52
49
|
|
53
50
|
def initialize(options = {})
|
54
|
-
|
51
|
+
# Quickbooks is deprecating OAuth 1.0 on December 17, 2019.
|
52
|
+
# OAuth 2.0 requires a client_id, client_secret, access_token, and refresh_token
|
53
|
+
# To maintain backwards compatibility, check for the presence of a refresh_token (only specified for OAuth 2.0)
|
54
|
+
# When present, validate that all OAuth 2.0 options are present
|
55
|
+
if options[:refresh_token]
|
56
|
+
requires!(options, :client_id, :client_secret, :access_token, :refresh_token)
|
57
|
+
else
|
58
|
+
requires!(options, :consumer_key, :consumer_secret, :access_token, :token_secret, :realm)
|
59
|
+
end
|
55
60
|
@options = options
|
56
61
|
super
|
57
62
|
end
|
@@ -62,7 +67,8 @@ module ActiveMerchant #:nodoc:
|
|
62
67
|
add_charge_data(post, payment, options)
|
63
68
|
post[:capture] = 'true'
|
64
69
|
|
65
|
-
commit(ENDPOINT, post)
|
70
|
+
response = commit(ENDPOINT, post)
|
71
|
+
check_token_response(response, ENDPOINT, post)
|
66
72
|
end
|
67
73
|
|
68
74
|
def authorize(money, payment, options = {})
|
@@ -71,24 +77,35 @@ module ActiveMerchant #:nodoc:
|
|
71
77
|
add_charge_data(post, payment, options)
|
72
78
|
post[:capture] = 'false'
|
73
79
|
|
74
|
-
commit(ENDPOINT, post)
|
80
|
+
response = commit(ENDPOINT, post)
|
81
|
+
check_token_response(response, ENDPOINT, post)
|
75
82
|
end
|
76
83
|
|
77
84
|
def capture(money, authorization, options = {})
|
78
85
|
post = {}
|
79
|
-
|
86
|
+
authorization, _ = split_authorization(authorization)
|
80
87
|
post[:amount] = localized_amount(money, currency(money))
|
81
88
|
add_context(post, options)
|
82
89
|
|
83
|
-
commit(capture_uri, post)
|
90
|
+
response = commit(capture_uri(authorization), post)
|
91
|
+
check_token_response(response, capture_uri(authorization), post)
|
84
92
|
end
|
85
93
|
|
86
94
|
def refund(money, authorization, options = {})
|
87
95
|
post = {}
|
88
96
|
post[:amount] = localized_amount(money, currency(money))
|
89
97
|
add_context(post, options)
|
98
|
+
authorization, _ = split_authorization(authorization)
|
90
99
|
|
91
|
-
commit(refund_uri(authorization), post)
|
100
|
+
response = commit(refund_uri(authorization), post)
|
101
|
+
check_token_response(response, refund_uri(authorization), post)
|
102
|
+
end
|
103
|
+
|
104
|
+
def void(authorization, options = {})
|
105
|
+
_, request_id = split_authorization(authorization)
|
106
|
+
|
107
|
+
response = commit(void_uri(request_id))
|
108
|
+
check_token_response(response, void_uri(request_id))
|
92
109
|
end
|
93
110
|
|
94
111
|
def verify(credit_card, options = {})
|
@@ -107,7 +124,12 @@ module ActiveMerchant #:nodoc:
|
|
107
124
|
gsub(%r((oauth_signature=\")[a-zA-Z%0-9]+), '\1[FILTERED]').
|
108
125
|
gsub(%r((oauth_token=\")\w+), '\1[FILTERED]').
|
109
126
|
gsub(%r((number\D+)\d{16}), '\1[FILTERED]').
|
110
|
-
gsub(%r((cvc\D+)\d{3}), '\1[FILTERED]')
|
127
|
+
gsub(%r((cvc\D+)\d{3}), '\1[FILTERED]').
|
128
|
+
gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
|
129
|
+
gsub(%r((access_token\\?":\\?")[\w\-\.]+)i, '\1[FILTERED]').
|
130
|
+
gsub(%r((refresh_token\\?":\\?")\w+), '\1[FILTERED]').
|
131
|
+
gsub(%r((refresh_token=)\w+), '\1[FILTERED]').
|
132
|
+
gsub(%r((Authorization: Bearer )[\w\-\.]+)i, '\1[FILTERED]\2')
|
111
133
|
end
|
112
134
|
|
113
135
|
private
|
@@ -170,30 +192,30 @@ module ActiveMerchant #:nodoc:
|
|
170
192
|
# The QuickBooks API returns HTTP 4xx on failed transactions, which causes a
|
171
193
|
# ResponseError raise, so we have to inspect the response and discern between
|
172
194
|
# a legitimate HTTP error and an actual gateway transactional error.
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
raise ArgumentError, "Invalid HTTP method: #{method}. Valid methods are :post and :get"
|
195
|
+
headers = {}
|
196
|
+
response =
|
197
|
+
begin
|
198
|
+
headers = headers(method, endpoint)
|
199
|
+
method == :post ? ssl_post(endpoint, post_data(body), headers) : ssl_request(:get, endpoint, nil, headers)
|
200
|
+
rescue ResponseError => e
|
201
|
+
extract_response_body_or_raise(e)
|
181
202
|
end
|
182
|
-
rescue ResponseError => e
|
183
|
-
extract_response_body_or_raise(e)
|
184
|
-
end
|
185
203
|
|
186
|
-
response_object(response)
|
204
|
+
response_object(response, headers)
|
187
205
|
end
|
188
206
|
|
189
|
-
def response_object(raw_response)
|
207
|
+
def response_object(raw_response, headers = {})
|
190
208
|
parsed_response = parse(raw_response)
|
191
209
|
|
210
|
+
# Include access_token and refresh_token in params for OAuth 2.0
|
211
|
+
parsed_response['access_token'] = @options[:access_token] if @options[:refresh_token]
|
212
|
+
parsed_response['refresh_token'] = @options[:refresh_token] if @options[:refresh_token]
|
213
|
+
|
192
214
|
Response.new(
|
193
215
|
success?(parsed_response),
|
194
216
|
message_from(parsed_response),
|
195
217
|
parsed_response,
|
196
|
-
authorization: authorization_from(parsed_response),
|
218
|
+
authorization: authorization_from(parsed_response, headers),
|
197
219
|
test: test?,
|
198
220
|
cvv_result: cvv_code_from(parsed_response),
|
199
221
|
error_code: errors_from(parsed_response),
|
@@ -210,6 +232,8 @@ module ActiveMerchant #:nodoc:
|
|
210
232
|
end
|
211
233
|
|
212
234
|
def headers(method, uri)
|
235
|
+
return oauth_v2_headers if @options[:refresh_token]
|
236
|
+
|
213
237
|
raise ArgumentError, "Invalid HTTP method: #{method}. Valid methods are :post and :get" unless [:post, :get].include?(method)
|
214
238
|
request_uri = URI.parse(uri)
|
215
239
|
|
@@ -243,6 +267,42 @@ module ActiveMerchant #:nodoc:
|
|
243
267
|
}
|
244
268
|
end
|
245
269
|
|
270
|
+
def oauth_v2_headers
|
271
|
+
{
|
272
|
+
'Content-Type' => 'application/json',
|
273
|
+
'Request-Id' => generate_unique_id,
|
274
|
+
'Accept' => 'application/json',
|
275
|
+
'Authorization' => "Bearer #{@options[:access_token]}"
|
276
|
+
}
|
277
|
+
end
|
278
|
+
|
279
|
+
def check_token_response(response, endpoint, body = {})
|
280
|
+
return response unless @options[:refresh_token]
|
281
|
+
return response unless response.params['code'] == 'AuthenticationFailed'
|
282
|
+
refresh_access_token
|
283
|
+
commit(endpoint, body)
|
284
|
+
end
|
285
|
+
|
286
|
+
def refresh_access_token
|
287
|
+
post = {}
|
288
|
+
post[:grant_type] = 'refresh_token'
|
289
|
+
post[:refresh_token] = @options[:refresh_token]
|
290
|
+
data = post.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&')
|
291
|
+
|
292
|
+
basic_auth = Base64.strict_encode64("#{@options[:client_id]}:#{@options[:client_secret]}")
|
293
|
+
headers = {
|
294
|
+
'Content-Type' => 'application/x-www-form-urlencoded',
|
295
|
+
'Accept' => 'application/json',
|
296
|
+
'Authorization' => "Basic #{basic_auth}"
|
297
|
+
}
|
298
|
+
|
299
|
+
response = ssl_post(REFRESH_URI, data, headers)
|
300
|
+
response = JSON.parse(response)
|
301
|
+
|
302
|
+
@options[:access_token] = response['access_token'] if response['access_token']
|
303
|
+
@options[:refresh_token] = response['refresh_token'] if response['refresh_token']
|
304
|
+
end
|
305
|
+
|
246
306
|
def cvv_code_from(response)
|
247
307
|
if response['errors'].present?
|
248
308
|
FRAUD_WARNING_CODES.include?(response['errors'].first['code']) ? 'I' : ''
|
@@ -265,8 +325,13 @@ module ActiveMerchant #:nodoc:
|
|
265
325
|
response['errors'].present? ? STANDARD_ERROR_CODE_MAPPING[response['errors'].first['code']] : ''
|
266
326
|
end
|
267
327
|
|
268
|
-
def authorization_from(response)
|
269
|
-
response['id']
|
328
|
+
def authorization_from(response, headers = {})
|
329
|
+
[response['id'], headers['Request-Id']].join('|')
|
330
|
+
end
|
331
|
+
|
332
|
+
def split_authorization(authorization)
|
333
|
+
authorization, request_id = authorization.split('|')
|
334
|
+
[authorization, request_id]
|
270
335
|
end
|
271
336
|
|
272
337
|
def fraud_review_status_from(response)
|
@@ -283,7 +348,15 @@ module ActiveMerchant #:nodoc:
|
|
283
348
|
end
|
284
349
|
|
285
350
|
def refund_uri(authorization)
|
286
|
-
"#{ENDPOINT}/#{CGI.escape(authorization)}/refunds"
|
351
|
+
"#{ENDPOINT}/#{CGI.escape(authorization.to_s)}/refunds"
|
352
|
+
end
|
353
|
+
|
354
|
+
def capture_uri(authorization)
|
355
|
+
"#{ENDPOINT}/#{CGI.escape(authorization.to_s)}/capture"
|
356
|
+
end
|
357
|
+
|
358
|
+
def void_uri(request_id)
|
359
|
+
"#{VOID_ENDPOINT}/#{CGI.escape(request_id.to_s)}/void"
|
287
360
|
end
|
288
361
|
end
|
289
362
|
end
|