activemerchant 1.93.0 → 1.98.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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +111 -0
  3. data/README.md +3 -0
  4. data/lib/active_merchant/billing/avs_result.rb +4 -5
  5. data/lib/active_merchant/billing/credit_card.rb +6 -0
  6. data/lib/active_merchant/billing/credit_card_methods.rb +67 -4
  7. data/lib/active_merchant/billing/gateway.rb +10 -0
  8. data/lib/active_merchant/billing/gateways/adyen.rb +106 -22
  9. data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +43 -10
  10. data/lib/active_merchant/billing/gateways/beanstream.rb +2 -0
  11. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +3 -0
  12. data/lib/active_merchant/billing/gateways/blue_snap.rb +22 -2
  13. data/lib/active_merchant/billing/gateways/bpoint.rb +4 -4
  14. data/lib/active_merchant/billing/gateways/braintree_blue.rb +56 -9
  15. data/lib/active_merchant/billing/gateways/card_connect.rb +3 -1
  16. data/lib/active_merchant/billing/gateways/cecabank.rb +7 -7
  17. data/lib/active_merchant/billing/gateways/checkout_v2.rb +98 -61
  18. data/lib/active_merchant/billing/gateways/credorax.rb +29 -3
  19. data/lib/active_merchant/billing/gateways/cyber_source.rb +30 -13
  20. data/lib/active_merchant/billing/gateways/d_local.rb +1 -1
  21. data/lib/active_merchant/billing/gateways/decidir.rb +233 -0
  22. data/lib/active_merchant/billing/gateways/elavon.rb +9 -0
  23. data/lib/active_merchant/billing/gateways/epay.rb +13 -2
  24. data/lib/active_merchant/billing/gateways/eway_rapid.rb +42 -12
  25. data/lib/active_merchant/billing/gateways/fat_zebra.rb +6 -0
  26. data/lib/active_merchant/billing/gateways/global_collect.rb +3 -7
  27. data/lib/active_merchant/billing/gateways/hps.rb +46 -1
  28. data/lib/active_merchant/billing/gateways/kushki.rb +1 -1
  29. data/lib/active_merchant/billing/gateways/mercado_pago.rb +1 -1
  30. data/lib/active_merchant/billing/gateways/migs.rb +8 -0
  31. data/lib/active_merchant/billing/gateways/monei.rb +31 -0
  32. data/lib/active_merchant/billing/gateways/mundipagg.rb +3 -2
  33. data/lib/active_merchant/billing/gateways/nab_transact.rb +1 -1
  34. data/lib/active_merchant/billing/gateways/nmi.rb +39 -1
  35. data/lib/active_merchant/billing/gateways/opp.rb +20 -1
  36. data/lib/active_merchant/billing/gateways/orbital.rb +60 -10
  37. data/lib/active_merchant/billing/gateways/payflow.rb +40 -2
  38. data/lib/active_merchant/billing/gateways/paymill.rb +5 -0
  39. data/lib/active_merchant/billing/gateways/paypal.rb +14 -1
  40. data/lib/active_merchant/billing/gateways/payu_latam.rb +6 -2
  41. data/lib/active_merchant/billing/gateways/qvalent.rb +43 -1
  42. data/lib/active_merchant/billing/gateways/realex.rb +32 -9
  43. data/lib/active_merchant/billing/gateways/spreedly_core.rb +43 -29
  44. data/lib/active_merchant/billing/gateways/stripe.rb +54 -9
  45. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +267 -0
  46. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +2 -2
  47. data/lib/active_merchant/billing/gateways/trust_commerce.rb +45 -6
  48. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +8 -5
  49. data/lib/active_merchant/billing/gateways/worldpay.rb +171 -39
  50. data/lib/active_merchant/country.rb +1 -0
  51. data/lib/active_merchant/version.rb +1 -1
  52. metadata +19 -4
@@ -544,7 +544,7 @@ module ActiveMerchant #:nodoc:
544
544
  end
545
545
  end
546
546
  doc['v1'].addrLn1 billing_address[:address1] if billing_address[:address1]
547
- doc['v1'].addrLn2 billing_address[:address2] if billing_address[:address2]
547
+ doc['v1'].addrLn2 billing_address[:address2] unless billing_address[:address2].blank?
548
548
  doc['v1'].city billing_address[:city] if billing_address[:city]
549
549
  doc['v1'].state billing_address[:state] if billing_address[:state]
550
550
  doc['v1'].zipCode billing_address[:zip] if billing_address[:zip]
@@ -559,7 +559,7 @@ module ActiveMerchant #:nodoc:
559
559
  doc['v1'].ship do
560
560
  doc['v1'].fullName fullname unless fullname.blank?
561
561
  doc['v1'].addrLn1 shipping_address[:address1] if shipping_address[:address1]
562
- doc['v1'].addrLn2 shipping_address[:address2] if shipping_address[:address2]
562
+ doc['v1'].addrLn2 shipping_address[:address2] unless shipping_address[:address2].blank?
563
563
  doc['v1'].city shipping_address[:city] if shipping_address[:city]
564
564
  doc['v1'].state shipping_address[:state] if shipping_address[:state]
565
565
  doc['v1'].zipCode shipping_address[:zip] if shipping_address[:zip]
@@ -104,6 +104,8 @@ module ActiveMerchant #:nodoc:
104
104
  TEST_LOGIN = 'TestMerchant'
105
105
  TEST_PASSWORD = 'password'
106
106
 
107
+ VOIDABLE_ACTIONS = %w(preauth sale postauth credit)
108
+
107
109
  self.money_format = :cents
108
110
  self.supported_cardtypes = [:visa, :master, :discover, :american_express, :diners_club, :jcb]
109
111
  self.supported_countries = ['US']
@@ -157,6 +159,8 @@ module ActiveMerchant #:nodoc:
157
159
  add_customer_data(parameters, options)
158
160
  add_payment_source(parameters, creditcard_or_billing_id)
159
161
  add_addresses(parameters, options)
162
+ add_custom_fields(parameters, options)
163
+
160
164
  commit('preauth', parameters)
161
165
  end
162
166
 
@@ -172,6 +176,8 @@ module ActiveMerchant #:nodoc:
172
176
  add_customer_data(parameters, options)
173
177
  add_payment_source(parameters, creditcard_or_billing_id)
174
178
  add_addresses(parameters, options)
179
+ add_custom_fields(parameters, options)
180
+
175
181
  commit('sale', parameters)
176
182
  end
177
183
 
@@ -179,11 +185,13 @@ module ActiveMerchant #:nodoc:
179
185
  # postauth, we preserve active_merchant's nomenclature of capture() for consistency with the rest of the library. To process
180
186
  # a postauthorization with TC, you need an amount in cents or a money object, and a TC transid.
181
187
  def capture(money, authorization, options = {})
188
+ transaction_id, _ = split_authorization(authorization)
182
189
  parameters = {
183
190
  :amount => amount(money),
184
- :transid => authorization,
191
+ :transid => transaction_id,
185
192
  }
186
193
  add_aggregator(parameters, options)
194
+ add_custom_fields(parameters, options)
187
195
 
188
196
  commit('postauth', parameters)
189
197
  end
@@ -191,11 +199,15 @@ module ActiveMerchant #:nodoc:
191
199
  # refund() allows you to return money to a card that was previously billed. You need to supply the amount, in cents or a money object,
192
200
  # that you want to refund, and a TC transid for the transaction that you are refunding.
193
201
  def refund(money, identification, options = {})
202
+ transaction_id, _ = split_authorization(identification)
203
+
194
204
  parameters = {
195
205
  :amount => amount(money),
196
- :transid => identification
206
+ :transid => transaction_id
197
207
  }
208
+
198
209
  add_aggregator(parameters, options)
210
+ add_custom_fields(parameters, options)
199
211
 
200
212
  commit('credit', parameters)
201
213
  end
@@ -214,18 +226,26 @@ module ActiveMerchant #:nodoc:
214
226
  # TrustCommerce to allow for reversal transactions before you can use this
215
227
  # method.
216
228
  #
229
+ # void() is also used to to cancel a capture (postauth), purchase (sale),
230
+ # or refund (credit) or a before it is sent for settlement.
231
+ #
217
232
  # NOTE: AMEX preauth's cannot be reversed. If you want to clear it more
218
233
  # quickly than the automatic expiration (7-10 days), you will have to
219
234
  # capture it and then immediately issue a credit for the same amount
220
235
  # which should clear the customers credit card with 48 hours according to
221
236
  # TC.
222
237
  def void(authorization, options = {})
238
+ transaction_id, original_action = split_authorization(authorization)
239
+ action = (VOIDABLE_ACTIONS - ['preauth']).include?(original_action) ? 'void' : 'reversal'
240
+
223
241
  parameters = {
224
- :transid => authorization,
242
+ :transid => transaction_id,
225
243
  }
244
+
226
245
  add_aggregator(parameters, options)
246
+ add_custom_fields(parameters, options)
227
247
 
228
- commit('reversal', parameters)
248
+ commit(action, parameters)
229
249
  end
230
250
 
231
251
  # recurring() a TrustCommerce account that is activated for Citadel, TrustCommerce's
@@ -284,6 +304,8 @@ module ActiveMerchant #:nodoc:
284
304
 
285
305
  add_creditcard(parameters, creditcard)
286
306
  add_addresses(parameters, options)
307
+ add_custom_fields(parameters, options)
308
+
287
309
  commit('store', parameters)
288
310
  end
289
311
 
@@ -294,6 +316,8 @@ module ActiveMerchant #:nodoc:
294
316
  :billingid => identification,
295
317
  }
296
318
 
319
+ add_custom_fields(parameters, options)
320
+
297
321
  commit('unstore', parameters)
298
322
  end
299
323
 
@@ -311,6 +335,12 @@ module ActiveMerchant #:nodoc:
311
335
 
312
336
  private
313
337
 
338
+ def add_custom_fields(params, options)
339
+ options[:custom_fields]&.each do |key, value|
340
+ params[key.to_sym] = value
341
+ end
342
+ end
343
+
314
344
  def add_aggregator(params, options)
315
345
  if @options[:aggregator_id] || application_id != Gateway.application_id
316
346
  params[:aggregators] = 1
@@ -409,14 +439,14 @@ module ActiveMerchant #:nodoc:
409
439
  TCLink.send(parameters)
410
440
  else
411
441
  parse(ssl_post(self.live_url, post_data(parameters)))
412
- end
442
+ end
413
443
 
414
444
  # to be considered successful, transaction status must be either "approved" or "accepted"
415
445
  success = SUCCESS_TYPES.include?(data['status'])
416
446
  message = message_from(data)
417
447
  Response.new(success, message, data,
418
448
  :test => test?,
419
- :authorization => data['transid'],
449
+ :authorization => authorization_from(action, data),
420
450
  :cvv_result => data['cvv'],
421
451
  :avs_result => { :code => data['avs'] }
422
452
  )
@@ -446,6 +476,15 @@ module ActiveMerchant #:nodoc:
446
476
  end
447
477
  end
448
478
 
479
+ def authorization_from(action, data)
480
+ authorization = data['transid']
481
+ authorization = "#{authorization}|#{action}" if authorization && VOIDABLE_ACTIONS.include?(action)
482
+ authorization
483
+ end
484
+
485
+ def split_authorization(authorization)
486
+ authorization&.split('|')
487
+ end
449
488
  end
450
489
  end
451
490
  end
@@ -33,9 +33,9 @@ module ActiveMerchant #:nodoc:
33
33
  '10110' => STANDARD_ERROR_CODE[:incorrect_address],
34
34
  '10111' => STANDARD_ERROR_CODE[:incorrect_address],
35
35
  '10127' => STANDARD_ERROR_CODE[:card_declined],
36
- '10128' => STANDARD_ERROR_CODE[:processing_error],
37
- '10132' => STANDARD_ERROR_CODE[:processing_error],
38
- '00043' => STANDARD_ERROR_CODE[:call_issuer]
36
+ '00043' => STANDARD_ERROR_CODE[:call_issuer],
37
+ '10205' => STANDARD_ERROR_CODE[:card_declined],
38
+ '10204' => STANDARD_ERROR_CODE[:pickup_card]
39
39
  }
40
40
 
41
41
  def initialize(options = {})
@@ -318,12 +318,15 @@ module ActiveMerchant #:nodoc:
318
318
  def commit(action, parameters)
319
319
  url = (test? ? self.test_url : self.live_url)
320
320
  response = parse(ssl_post(url, post_data(action, parameters)))
321
- Response.new(response[:status] == 'Approved', message_from(response), response,
321
+ approved = response[:status] == 'Approved'
322
+ error_code = nil
323
+ error_code = (STANDARD_ERROR_CODE_MAPPING[response[:error_code]] || STANDARD_ERROR_CODE[:processing_error]) unless approved
324
+ Response.new(approved, message_from(response), response,
322
325
  :test => test?,
323
326
  :authorization => response[:ref_num],
324
327
  :cvv_result => response[:cvv2_result_code],
325
328
  :avs_result => { :code => response[:avs_result_code] },
326
- :error_code => STANDARD_ERROR_CODE_MAPPING[response[:error_code]]
329
+ :error_code => error_code
327
330
  )
328
331
  end
329
332
 
@@ -7,7 +7,7 @@ module ActiveMerchant #:nodoc:
7
7
  self.default_currency = 'GBP'
8
8
  self.money_format = :cents
9
9
  self.supported_countries = %w(HK GB AU AD AR BE BR CA CH CN CO CR CY CZ DE DK ES FI FR GI GR HU IE IN IT JP LI LU MC MT MY MX NL NO NZ PA PE PL PT SE SG SI SM TR UM VA)
10
- self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :maestro, :elo]
10
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :maestro, :elo, :naranja, :cabal]
11
11
  self.currencies_without_fractions = %w(HUF IDR ISK JPY KRW)
12
12
  self.currencies_with_three_decimal_places = %w(BHD KWD OMR RSD TND)
13
13
  self.homepage_url = 'http://www.worldpay.com/'
@@ -21,7 +21,10 @@ module ActiveMerchant #:nodoc:
21
21
  'jcb' => 'JCB-SSL',
22
22
  'maestro' => 'MAESTRO-SSL',
23
23
  'diners_club' => 'DINERS-SSL',
24
- 'elo' => 'ELO-SSL'
24
+ 'elo' => 'ELO-SSL',
25
+ 'naranja' => 'NARANJA-SSL',
26
+ 'cabal' => 'CABAL-SSL',
27
+ 'unknown' => 'CARD-SSL'
25
28
  }
26
29
 
27
30
  AVS_CODE_MAP = {
@@ -58,10 +61,12 @@ module ActiveMerchant #:nodoc:
58
61
 
59
62
  def authorize(money, payment_method, options = {})
60
63
  requires!(options, :order_id)
61
- authorize_request(money, payment_method, options)
64
+ payment_details = payment_details_from(payment_method)
65
+ authorize_request(money, payment_method, payment_details.merge(options))
62
66
  end
63
67
 
64
68
  def capture(money, authorization, options = {})
69
+ authorization = order_id_from_authorization(authorization.to_s)
65
70
  MultiResponse.run do |r|
66
71
  r.process { inquire_request(authorization, options, 'AUTHORISED') } unless options[:authorization_validated]
67
72
  if r.params
@@ -73,6 +78,7 @@ module ActiveMerchant #:nodoc:
73
78
  end
74
79
 
75
80
  def void(authorization, options = {})
81
+ authorization = order_id_from_authorization(authorization.to_s)
76
82
  MultiResponse.run do |r|
77
83
  r.process { inquire_request(authorization, options, 'AUTHORISED') } unless options[:authorization_validated]
78
84
  r.process { cancel_request(authorization, options) }
@@ -80,6 +86,7 @@ module ActiveMerchant #:nodoc:
80
86
  end
81
87
 
82
88
  def refund(money, authorization, options = {})
89
+ authorization = order_id_from_authorization(authorization.to_s)
83
90
  response = MultiResponse.run do |r|
84
91
  r.process { inquire_request(authorization, options, 'CAPTURED', 'SETTLED', 'SETTLED_BY_MERCHANT') }
85
92
  r.process { refund_request(money, authorization, options) }
@@ -96,16 +103,22 @@ module ActiveMerchant #:nodoc:
96
103
  # and other transactions should be performed on a normal eCom-flagged
97
104
  # merchant ID.
98
105
  def credit(money, payment_method, options = {})
99
- credit_request(money, payment_method, options.merge(:credit => true))
106
+ payment_details = payment_details_from(payment_method)
107
+ credit_request(money, payment_method, payment_details.merge(:credit => true, **options))
100
108
  end
101
109
 
102
- def verify(credit_card, options={})
110
+ def verify(payment_method, options={})
103
111
  MultiResponse.run(:use_first_response) do |r|
104
- r.process { authorize(100, credit_card, options) }
112
+ r.process { authorize(100, payment_method, options) }
105
113
  r.process(:ignore_result) { void(r.authorization, options.merge(:authorization_validated => true)) }
106
114
  end
107
115
  end
108
116
 
117
+ def store(credit_card, options={})
118
+ requires!(options, :customer)
119
+ store_request(credit_card, options)
120
+ end
121
+
109
122
  def supports_scrubbing
110
123
  true
111
124
  end
@@ -143,6 +156,10 @@ module ActiveMerchant #:nodoc:
143
156
  commit('credit', build_authorization_request(money, payment_method, options), :ok, 'SENT_FOR_REFUND', options)
144
157
  end
145
158
 
159
+ def store_request(credit_card, options)
160
+ commit('store', build_store_request(credit_card, options), options)
161
+ end
162
+
146
163
  def build_request
147
164
  xml = Builder::XmlMarkup.new :indent => 2
148
165
  xml.instruct! :xml, :encoding => 'UTF-8'
@@ -183,13 +200,14 @@ module ActiveMerchant #:nodoc:
183
200
  end
184
201
  end
185
202
  add_payment_method(xml, money, payment_method, options)
186
- add_email(xml, options)
203
+ add_shopper(xml, options)
187
204
  if options[:hcg_additional_data]
188
205
  add_hcg_additional_data(xml, options)
189
206
  end
190
207
  if options[:instalments]
191
208
  add_instalments_data(xml, options)
192
209
  end
210
+ add_moto_flag(xml, options) if options.dig(:metadata, :manual_entry)
193
211
  end
194
212
  end
195
213
  end
@@ -223,6 +241,22 @@ module ActiveMerchant #:nodoc:
223
241
  end
224
242
  end
225
243
 
244
+ def build_store_request(credit_card, options)
245
+ build_request do |xml|
246
+ xml.tag! 'submit' do
247
+ xml.tag! 'paymentTokenCreate' do
248
+ add_authenticated_shopper_id(xml, options)
249
+ xml.tag! 'createToken'
250
+ xml.tag! 'paymentInstrument' do
251
+ xml.tag! 'cardDetails' do
252
+ add_card(xml, credit_card, options)
253
+ end
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end
259
+
226
260
  def add_amount(xml, money, options)
227
261
  currency = options[:currency] || currency(money)
228
262
 
@@ -240,7 +274,7 @@ module ActiveMerchant #:nodoc:
240
274
  end
241
275
 
242
276
  def add_payment_method(xml, amount, payment_method, options)
243
- if payment_method.is_a?(String)
277
+ if options[:payment_type] == :pay_as_order
244
278
  if options[:merchant_code]
245
279
  xml.tag! 'payAsOrder', 'orderCode' => payment_method, 'merchantCode' => options[:merchant_code] do
246
280
  add_amount(xml, amount, options)
@@ -252,16 +286,14 @@ module ActiveMerchant #:nodoc:
252
286
  end
253
287
  else
254
288
  xml.tag! 'paymentDetails', credit_fund_transfer_attribute(options) do
255
- xml.tag! CARD_CODES[card_brand(payment_method)] do
256
- xml.tag! 'cardNumber', payment_method.number
257
- xml.tag! 'expiryDate' do
258
- xml.tag! 'date', 'month' => format(payment_method.month, :two_digits), 'year' => format(payment_method.year, :four_digits)
289
+ if options[:payment_type] == :token
290
+ xml.tag! 'TOKEN-SSL', 'tokenScope' => options[:token_scope] do
291
+ xml.tag! 'paymentTokenID', options[:token_id]
292
+ end
293
+ else
294
+ xml.tag! card_code_for(payment_method) do
295
+ add_card(xml, payment_method, options)
259
296
  end
260
-
261
- xml.tag! 'cardHolderName', options[:execute_threed] ? '3D' : payment_method.name
262
- xml.tag! 'cvc', payment_method.verification_value
263
-
264
- add_address(xml, (options[:billing_address] || options[:address]))
265
297
  end
266
298
  add_stored_credential_options(xml, options)
267
299
  if options[:ip] && options[:session_id]
@@ -272,18 +304,37 @@ module ActiveMerchant #:nodoc:
272
304
  end
273
305
 
274
306
  if three_d_secure = options[:three_d_secure]
275
- xml.tag! 'info3DSecure' do
276
- xml.tag! 'threeDSVersion', three_d_secure[:version]
277
- xid_tag = three_d_secure[:version] =~ /^2/ ? 'dsTransactionId' : 'xid'
278
- xml.tag! xid_tag, three_d_secure[:xid]
279
- xml.tag! 'cavv', three_d_secure[:cavv]
280
- xml.tag! 'eci', three_d_secure[:eci]
281
- end
307
+ add_three_d_secure(three_d_secure, xml)
282
308
  end
283
309
  end
284
310
  end
285
311
  end
286
312
 
313
+ def add_three_d_secure(three_d_secure, xml)
314
+ xml.tag! 'info3DSecure' do
315
+ xml.tag! 'threeDSVersion', three_d_secure[:version]
316
+ if three_d_secure[:version] =~ /^2/
317
+ xml.tag! 'dsTransactionId', three_d_secure[:ds_transaction_id]
318
+ else
319
+ xml.tag! 'xid', three_d_secure[:xid]
320
+ end
321
+ xml.tag! 'cavv', three_d_secure[:cavv]
322
+ xml.tag! 'eci', three_d_secure[:eci]
323
+ end
324
+ end
325
+
326
+ def add_card(xml, payment_method, options)
327
+ xml.tag! 'cardNumber', payment_method.number
328
+ xml.tag! 'expiryDate' do
329
+ xml.tag! 'date', 'month' => format(payment_method.month, :two_digits), 'year' => format(payment_method.year, :four_digits)
330
+ end
331
+
332
+ xml.tag! 'cardHolderName', options[:execute_threed] ? '3D' : payment_method.name
333
+ xml.tag! 'cvc', payment_method.verification_value
334
+
335
+ add_address(xml, (options[:billing_address] || options[:address]))
336
+ end
337
+
287
338
  def add_stored_credential_options(xml, options={})
288
339
  if options[:stored_credential]
289
340
  add_stored_credential_using_normalized_fields(xml, options)
@@ -320,10 +371,11 @@ module ActiveMerchant #:nodoc:
320
371
  end
321
372
  end
322
373
 
323
- def add_email(xml, options)
324
- return unless options[:execute_threed] || options[:email]
374
+ def add_shopper(xml, options)
375
+ return unless options[:execute_threed] || options[:email] || options[:customer]
325
376
  xml.tag! 'shopper' do
326
377
  xml.tag! 'shopperEmailAddress', options[:email] if options[:email]
378
+ add_authenticated_shopper_id(xml, options)
327
379
  xml.tag! 'browser' do
328
380
  xml.tag! 'acceptHeader', options[:accept_header]
329
381
  xml.tag! 'userAgentHeader', options[:user_agent]
@@ -331,6 +383,10 @@ module ActiveMerchant #:nodoc:
331
383
  end
332
384
  end
333
385
 
386
+ def add_authenticated_shopper_id(xml, options)
387
+ xml.tag!('authenticatedShopperID', options[:customer]) if options[:customer]
388
+ end
389
+
334
390
  def add_address(xml, address)
335
391
  return unless address
336
392
 
@@ -368,6 +424,10 @@ module ActiveMerchant #:nodoc:
368
424
  end
369
425
  end
370
426
 
427
+ def add_moto_flag(xml, options)
428
+ xml.tag! 'dynamicInteractionType', 'type' => 'MOTO'
429
+ end
430
+
371
431
  def address_with_defaults(address)
372
432
  address ||= {}
373
433
  address.delete_if { |_, v| v.blank? }
@@ -389,14 +449,17 @@ module ActiveMerchant #:nodoc:
389
449
  end
390
450
 
391
451
  def parse_element(raw, node)
452
+ node_name = node.name.underscore
392
453
  node.attributes.each do |k, v|
393
- raw["#{node.name.underscore}_#{k.underscore}".to_sym] = v
454
+ raw["#{node_name}_#{k.underscore}".to_sym] = v
394
455
  end
395
456
  if node.has_elements?
396
- raw[node.name.underscore.to_sym] = true unless node.name.blank?
457
+ raw[node_name.to_sym] = true unless node.name.blank?
397
458
  node.elements.each { |e| parse_element(raw, e) }
459
+ elsif node.children.count > 1
460
+ raw[node_name.to_sym] = node.children.join(' ').strip
398
461
  else
399
- raw[node.name.underscore.to_sym] = node.text unless node.text.nil?
462
+ raw[node_name.to_sym] = node.text unless node.text.nil?
400
463
  end
401
464
  raw
402
465
  end
@@ -419,13 +482,14 @@ module ActiveMerchant #:nodoc:
419
482
  raw[:cookie] = @cookie
420
483
  raw[:session_id] = options[:session_id]
421
484
  end
422
- success, message = success_and_message_from(raw, success_criteria)
485
+ success = success_from(action, raw, success_criteria)
486
+ message = message_from(success, raw, success_criteria)
423
487
 
424
488
  Response.new(
425
489
  success,
426
490
  message,
427
491
  raw,
428
- :authorization => authorization_from(raw),
492
+ :authorization => authorization_from(action, raw, options),
429
493
  :error_code => error_code_from(success, raw),
430
494
  :test => test?,
431
495
  :avs_result => AVSResult.new(code: AVS_CODE_MAP[raw[:avs_result_code_description]]),
@@ -455,19 +519,30 @@ module ActiveMerchant #:nodoc:
455
519
  end
456
520
  end
457
521
 
522
+ def success_from(action, raw, success_criteria)
523
+ success_criteria_success?(raw, success_criteria) || action_success?(action, raw)
524
+ end
525
+
526
+ def message_from(success, raw, success_criteria)
527
+ return 'SUCCESS' if success
528
+ raw[:iso8583_return_code_description] || raw[:error] || required_status_message(raw, success_criteria)
529
+ end
530
+
458
531
  # success_criteria can be:
459
532
  # - a string or an array of strings (if one of many responses)
460
533
  # - An array of strings if one of many responses could be considered a
461
534
  # success.
462
- def success_and_message_from(raw, success_criteria)
463
- success = (success_criteria.include?(raw[:last_event]) || raw[:ok].present?)
464
- if success
465
- message = 'SUCCESS'
535
+ def success_criteria_success?(raw, success_criteria)
536
+ success_criteria.include?(raw[:last_event]) || raw[:ok].present?
537
+ end
538
+
539
+ def action_success?(action, raw)
540
+ case action
541
+ when 'store'
542
+ raw[:token].present?
466
543
  else
467
- message = (raw[:iso8583_return_code_description] || raw[:error] || required_status_message(raw, success_criteria))
544
+ false
468
545
  end
469
-
470
- [ success, message ]
471
546
  end
472
547
 
473
548
  def error_code_from(success, raw)
@@ -482,11 +557,64 @@ module ActiveMerchant #:nodoc:
482
557
  end
483
558
  end
484
559
 
485
- def authorization_from(raw)
560
+ def authorization_from(action, raw, options)
561
+ order_id = order_id_from(raw)
562
+
563
+ case action
564
+ when 'store'
565
+ authorization_from_token_details(
566
+ order_id: order_id,
567
+ token_id: raw[:payment_token_id],
568
+ token_scope: 'shopper',
569
+ customer: options[:customer]
570
+ )
571
+ else
572
+ order_id
573
+ end
574
+ end
575
+
576
+ def order_id_from(raw)
486
577
  pair = raw.detect { |k, v| k.to_s =~ /_order_code$/ }
487
578
  (pair ? pair.last : nil)
488
579
  end
489
580
 
581
+ def authorization_from_token_details(options={})
582
+ [options[:order_id], options[:token_id], options[:token_scope], options[:customer]].join('|')
583
+ end
584
+
585
+ def order_id_from_authorization(authorization)
586
+ token_details_from_authorization(authorization)[:order_id]
587
+ end
588
+
589
+ def token_details_from_authorization(authorization)
590
+ order_id, token_id, token_scope, customer = authorization.split('|')
591
+
592
+ token_details = {}
593
+ token_details[:order_id] = order_id if order_id.present?
594
+ token_details[:token_id] = token_id if token_id.present?
595
+ token_details[:token_scope] = token_scope if token_scope.present?
596
+ token_details[:customer] = customer if customer.present?
597
+
598
+ token_details
599
+ end
600
+
601
+ def payment_details_from(payment_method)
602
+ payment_details = {}
603
+ if payment_method.respond_to?(:number)
604
+ payment_details[:payment_type] = :credit
605
+ else
606
+ token_details = token_details_from_authorization(payment_method)
607
+ payment_details.merge!(token_details)
608
+ if token_details.has_key?(:token_id)
609
+ payment_details[:payment_type] = :token
610
+ else
611
+ payment_details[:payment_type] = :pay_as_order
612
+ end
613
+ end
614
+
615
+ payment_details
616
+ end
617
+
490
618
  def credit_fund_transfer_attribute(options)
491
619
  return unless options[:credit]
492
620
  {'action' => 'REFUND'}
@@ -502,6 +630,10 @@ module ActiveMerchant #:nodoc:
502
630
  return 3 if three_decimal_currency?(currency)
503
631
  return 2
504
632
  end
633
+
634
+ def card_code_for(payment_method)
635
+ CARD_CODES[card_brand(payment_method)] || CARD_CODES['unknown']
636
+ end
505
637
  end
506
638
  end
507
639
  end