activemerchant 1.117.0 → 1.123.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +217 -0
  3. data/README.md +5 -3
  4. data/lib/active_merchant/billing/check.rb +19 -12
  5. data/lib/active_merchant/billing/credit_card.rb +6 -0
  6. data/lib/active_merchant/billing/credit_card_formatting.rb +1 -0
  7. data/lib/active_merchant/billing/credit_card_methods.rb +96 -22
  8. data/lib/active_merchant/billing/gateways/adyen.rb +38 -21
  9. data/lib/active_merchant/billing/gateways/authorize_net.rb +19 -11
  10. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +4 -0
  11. data/lib/active_merchant/billing/gateways/blue_pay.rb +29 -0
  12. data/lib/active_merchant/billing/gateways/blue_snap.rb +5 -3
  13. data/lib/active_merchant/billing/gateways/braintree_blue.rb +58 -8
  14. data/lib/active_merchant/billing/gateways/cashnet.rb +7 -2
  15. data/lib/active_merchant/billing/gateways/checkout_v2.rb +31 -0
  16. data/lib/active_merchant/billing/gateways/credorax.rb +16 -9
  17. data/lib/active_merchant/billing/gateways/cyber_source.rb +67 -9
  18. data/lib/active_merchant/billing/gateways/d_local.rb +1 -1
  19. data/lib/active_merchant/billing/gateways/decidir.rb +29 -3
  20. data/lib/active_merchant/billing/gateways/elavon.rb +110 -26
  21. data/lib/active_merchant/billing/gateways/element.rb +2 -0
  22. data/lib/active_merchant/billing/gateways/eway_rapid.rb +13 -0
  23. data/lib/active_merchant/billing/gateways/firstdata_e4_v27.rb +17 -6
  24. data/lib/active_merchant/billing/gateways/forte.rb +12 -0
  25. data/lib/active_merchant/billing/gateways/global_collect.rb +25 -16
  26. data/lib/active_merchant/billing/gateways/hps.rb +65 -2
  27. data/lib/active_merchant/billing/gateways/kushki.rb +23 -0
  28. data/lib/active_merchant/billing/gateways/litle.rb +9 -4
  29. data/lib/active_merchant/billing/gateways/mercado_pago.rb +5 -4
  30. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +2 -0
  31. data/lib/active_merchant/billing/gateways/moka.rb +277 -0
  32. data/lib/active_merchant/billing/gateways/monei.rb +228 -144
  33. data/lib/active_merchant/billing/gateways/mundipagg.rb +14 -5
  34. data/lib/active_merchant/billing/gateways/netbanx.rb +37 -2
  35. data/lib/active_merchant/billing/gateways/nmi.rb +14 -9
  36. data/lib/active_merchant/billing/gateways/orbital.rb +202 -47
  37. data/lib/active_merchant/billing/gateways/pay_arc.rb +390 -0
  38. data/lib/active_merchant/billing/gateways/pay_trace.rb +404 -0
  39. data/lib/active_merchant/billing/gateways/payeezy.rb +57 -11
  40. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -0
  41. data/lib/active_merchant/billing/gateways/payflow.rb +9 -0
  42. data/lib/active_merchant/billing/gateways/payment_express.rb +10 -5
  43. data/lib/active_merchant/billing/gateways/paymentez.rb +26 -1
  44. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +1 -0
  45. data/lib/active_merchant/billing/gateways/paypal.rb +10 -2
  46. data/lib/active_merchant/billing/gateways/paypal_express.rb +1 -0
  47. data/lib/active_merchant/billing/gateways/paysafe.rb +291 -0
  48. data/lib/active_merchant/billing/gateways/payu_latam.rb +3 -3
  49. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +253 -0
  50. data/lib/active_merchant/billing/gateways/pin.rb +11 -0
  51. data/lib/active_merchant/billing/gateways/qvalent.rb +23 -9
  52. data/lib/active_merchant/billing/gateways/redsys.rb +78 -30
  53. data/lib/active_merchant/billing/gateways/safe_charge.rb +19 -8
  54. data/lib/active_merchant/billing/gateways/spreedly_core.rb +13 -4
  55. data/lib/active_merchant/billing/gateways/stripe.rb +8 -8
  56. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +86 -25
  57. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +1 -1
  58. data/lib/active_merchant/billing/gateways/vpos.rb +220 -0
  59. data/lib/active_merchant/billing/gateways/worldpay.rb +68 -20
  60. data/lib/active_merchant/billing/response.rb +2 -1
  61. data/lib/active_merchant/billing/three_d_secure_eci_mapper.rb +27 -0
  62. data/lib/active_merchant/billing.rb +1 -0
  63. data/lib/active_merchant/version.rb +1 -1
  64. data/lib/certs/cacert.pem +1582 -2431
  65. metadata +10 -3
@@ -343,7 +343,7 @@ module ActiveMerchant #:nodoc:
343
343
  parameters[:software] = 'Active Merchant'
344
344
  parameters[:testmode] = (@options[:test] ? 1 : 0) unless parameters.has_key?(:testmode)
345
345
  seed = SecureRandom.hex(32).upcase
346
- hash = Digest::SHA1.hexdigest("#{parameters[:command]}:#{@options[:password]}:#{parameters[:amount]}:#{parameters[:invoice]}:#{seed}")
346
+ hash = Digest::SHA1.hexdigest("#{parameters[:command]}:#{@options[:pin] || @options[:password]}:#{parameters[:amount]}:#{parameters[:invoice]}:#{seed}")
347
347
  parameters[:hash] = "s/#{seed}/#{hash}/n"
348
348
 
349
349
  parameters.collect { |key, value| "UM#{key}=#{CGI.escape(value.to_s)}" }.join('&')
@@ -0,0 +1,220 @@
1
+ require 'digest'
2
+ require 'jwe'
3
+
4
+ module ActiveMerchant #:nodoc:
5
+ module Billing #:nodoc:
6
+ class VposGateway < Gateway
7
+ self.test_url = 'https://vpos.infonet.com.py:8888'
8
+ self.live_url = 'https://vpos.infonet.com.py'
9
+
10
+ self.supported_countries = ['PY']
11
+ self.default_currency = 'PYG'
12
+ self.supported_cardtypes = %i[visa master]
13
+
14
+ self.homepage_url = 'https://comercios.bancard.com.py'
15
+ self.display_name = 'vPOS'
16
+
17
+ self.money_format = :dollars
18
+
19
+ ENDPOINTS = {
20
+ pci_encryption_key: '/vpos/api/0.3/application/encryption-key',
21
+ pay_pci_buy_encrypted: '/vpos/api/0.3/pci/encrypted',
22
+ pci_buy_rollback: '/vpos/api/0.3/pci_buy/rollback',
23
+ refund: '/vpos/api/0.3/refunds'
24
+ }
25
+
26
+ def initialize(options = {})
27
+ requires!(options, :private_key, :public_key)
28
+ @private_key = options[:private_key]
29
+ @public_key = options[:public_key]
30
+ @shop_process_id = options[:shop_process_id] || SecureRandom.random_number(10**15)
31
+ super
32
+ end
33
+
34
+ def purchase(money, payment, options = {})
35
+ commerce = options[:commerce] || @options[:commerce]
36
+ commerce_branch = options[:commerce_branch] || @options[:commerce_branch]
37
+ shop_process_id = options[:shop_process_id] || @shop_process_id
38
+
39
+ token = generate_token(shop_process_id, 'pay_pci', commerce, commerce_branch, amount(money), currency(money))
40
+
41
+ post = {}
42
+ post[:token] = token
43
+ post[:commerce] = commerce.to_s
44
+ post[:commerce_branch] = commerce_branch.to_s
45
+ post[:shop_process_id] = shop_process_id
46
+ post[:number_of_payments] = options[:number_of_payments] || 1
47
+ post[:recursive] = options[:recursive] || false
48
+
49
+ add_invoice(post, money, options)
50
+ add_card_data(post, payment)
51
+ add_customer_data(post, options)
52
+
53
+ commit(:pay_pci_buy_encrypted, post)
54
+ end
55
+
56
+ def void(authorization, options = {})
57
+ _, shop_process_id = authorization.to_s.split('#')
58
+ token = generate_token(shop_process_id, 'rollback', '0.00')
59
+ post = {
60
+ token: token,
61
+ shop_process_id: shop_process_id
62
+ }
63
+ commit(:pci_buy_rollback, post)
64
+ end
65
+
66
+ def credit(money, payment, options = {})
67
+ # Not permitted for foreign cards.
68
+ commerce = options[:commerce] || @options[:commerce]
69
+ commerce_branch = options[:commerce_branch] || @options[:commerce_branch]
70
+
71
+ token = generate_token(@shop_process_id, 'refund', commerce, commerce_branch, amount(money), currency(money))
72
+ post = {}
73
+ post[:token] = token
74
+ post[:commerce] = commerce.to_i
75
+ post[:commerce_branch] = commerce_branch.to_i
76
+ post[:shop_process_id] = @shop_process_id
77
+ add_invoice(post, money, options)
78
+ add_card_data(post, payment)
79
+ add_customer_data(post, options)
80
+ post[:origin_shop_process_id] = options[:original_shop_process_id] if options[:original_shop_process_id]
81
+ commit(:refund, post)
82
+ end
83
+
84
+ def refund(money, authorization, options = {})
85
+ commerce = options[:commerce] || @options[:commerce]
86
+ commerce_branch = options[:commerce_branch] || @options[:commerce_branch]
87
+ shop_process_id = options[:shop_process_id] || @shop_process_id
88
+ _, original_shop_process_id = authorization.to_s.split('#')
89
+
90
+ token = generate_token(shop_process_id, 'refund', commerce, commerce_branch, amount(money), currency(money))
91
+ post = {}
92
+ post[:token] = token
93
+ post[:commerce] = commerce.to_i
94
+ post[:commerce_branch] = commerce_branch.to_i
95
+ post[:shop_process_id] = shop_process_id
96
+ add_invoice(post, money, options)
97
+ add_customer_data(post, options)
98
+ post[:origin_shop_process_id] = original_shop_process_id || options[:original_shop_process_id]
99
+ commit(:refund, post)
100
+ end
101
+
102
+ def supports_scrubbing?
103
+ true
104
+ end
105
+
106
+ def scrub(transcript)
107
+ clean_transcript = remove_invalid_utf_8_byte_sequences(transcript)
108
+ clean_transcript.
109
+ gsub(/(token\\":\\")[.\-\w]+/, '\1[FILTERED]').
110
+ gsub(/(card_encrypted_data\\":\\")[.\-\w]+/, '\1[FILTERED]')
111
+ end
112
+
113
+ def remove_invalid_utf_8_byte_sequences(transcript)
114
+ transcript.encode('UTF-8', 'binary', undef: :replace, replace: '')
115
+ end
116
+
117
+ private
118
+
119
+ # Required to encrypt PAN data.
120
+ def one_time_public_key
121
+ token = generate_token('get_encription_public_key', @public_key)
122
+ response = commit(:pci_encryption_key, token: token)
123
+ OpenSSL::PKey::RSA.new(response.params['encryption_key'])
124
+ end
125
+
126
+ def generate_token(*elements)
127
+ Digest::MD5.hexdigest(@private_key + elements.join)
128
+ end
129
+
130
+ def add_invoice(post, money, options)
131
+ post[:amount] = amount(money)
132
+ post[:currency] = options[:currency] || currency(money)
133
+ end
134
+
135
+ def add_card_data(post, payment)
136
+ card_number = payment.number
137
+ cvv = payment.verification_value
138
+
139
+ payload = { card_number: card_number, 'cvv': cvv }.to_json
140
+
141
+ post[:card_encrypted_data] = JWE.encrypt(payload, one_time_public_key)
142
+ post[:card_month_expiration] = format(payment.month, :two_digits)
143
+ post[:card_year_expiration] = format(payment.year, :two_digits)
144
+ end
145
+
146
+ def add_customer_data(post, options)
147
+ post[:additional_data] = options[:additional_data] || '' # must be passed even if empty
148
+ end
149
+
150
+ def parse(body)
151
+ JSON.parse(body)
152
+ end
153
+
154
+ def commit(action, parameters)
155
+ url = build_request_url(action)
156
+ begin
157
+ response = parse(ssl_post(url, post_data(parameters)))
158
+ rescue ResponseError => response
159
+ # Errors are returned with helpful data,
160
+ # but get filtered out by `ssl_post` because of their HTTP status.
161
+ response = parse(response.response.body)
162
+ end
163
+
164
+ Response.new(
165
+ success_from(response),
166
+ message_from(response),
167
+ response,
168
+ authorization: authorization_from(response),
169
+ avs_result: nil,
170
+ cvv_result: nil,
171
+ test: test?,
172
+ error_code: error_code_from(response)
173
+ )
174
+ end
175
+
176
+ def success_from(response)
177
+ if code = response.dig('confirmation', 'response_code')
178
+ code == '00'
179
+ else
180
+ response['status'] == 'success'
181
+ end
182
+ end
183
+
184
+ def message_from(response)
185
+ %w(confirmation refund).each do |m|
186
+ message =
187
+ response.dig(m, 'extended_response_description') ||
188
+ response.dig(m, 'response_description') ||
189
+ response.dig(m, 'response_details')
190
+ return message if message
191
+ end
192
+ [response.dig('messages', 0, 'key'), response.dig('messages', 0, 'dsc')].join(':')
193
+ end
194
+
195
+ def authorization_from(response)
196
+ response_body = response.dig('confirmation') || response.dig('refund')
197
+ return unless response_body
198
+
199
+ authorization_number = response_body.dig('authorization_number') || response_body.dig('authorization_code')
200
+ shop_process_id = response_body.dig('shop_process_id')
201
+
202
+ "#{authorization_number}##{shop_process_id}"
203
+ end
204
+
205
+ def error_code_from(response)
206
+ response.dig('confirmation', 'response_code') unless success_from(response)
207
+ end
208
+
209
+ def build_request_url(action)
210
+ base_url = (test? ? test_url : live_url)
211
+ base_url + ENDPOINTS[action]
212
+ end
213
+
214
+ def post_data(data)
215
+ { public_key: @public_key,
216
+ operation: data }.compact.to_json
217
+ end
218
+ end
219
+ end
220
+ end
@@ -58,7 +58,7 @@ module ActiveMerchant #:nodoc:
58
58
  def purchase(money, payment_method, options = {})
59
59
  MultiResponse.run do |r|
60
60
  r.process { authorize(money, payment_method, options) }
61
- r.process { capture(money, r.authorization, options.merge(authorization_validated: true)) }
61
+ r.process { capture(money, r.authorization, options.merge(authorization_validated: true)) } unless options[:skip_capture]
62
62
  end
63
63
  end
64
64
 
@@ -71,7 +71,7 @@ module ActiveMerchant #:nodoc:
71
71
  def capture(money, authorization, options = {})
72
72
  authorization = order_id_from_authorization(authorization.to_s)
73
73
  MultiResponse.run do |r|
74
- r.process { inquire_request(authorization, options, 'AUTHORISED') } unless options[:authorization_validated]
74
+ r.process { inquire_request(authorization, options, 'AUTHORISED', 'CAPTURED') } unless options[:authorization_validated]
75
75
  if r.params
76
76
  authorization_currency = r.params['amount_currency_code']
77
77
  options = options.merge(currency: authorization_currency) if authorization_currency.present?
@@ -90,8 +90,10 @@ module ActiveMerchant #:nodoc:
90
90
 
91
91
  def refund(money, authorization, options = {})
92
92
  authorization = order_id_from_authorization(authorization.to_s)
93
+ success_criteria = %w(CAPTURED SETTLED SETTLED_BY_MERCHANT SENT_FOR_REFUND)
94
+ success_criteria.push('AUTHORIZED') if options[:cancel_or_refund]
93
95
  response = MultiResponse.run do |r|
94
- r.process { inquire_request(authorization, options, 'CAPTURED', 'SETTLED', 'SETTLED_BY_MERCHANT') } unless options[:authorization_validated]
96
+ r.process { inquire_request(authorization, options, *success_criteria) } unless options[:authorization_validated]
95
97
  r.process { refund_request(money, authorization, options) }
96
98
  end
97
99
 
@@ -113,8 +115,9 @@ module ActiveMerchant #:nodoc:
113
115
  end
114
116
 
115
117
  def verify(payment_method, options = {})
118
+ amount = (eligible_for_0_auth?(payment_method, options) ? 0 : 100)
116
119
  MultiResponse.run(:use_first_response) do |r|
117
- r.process { authorize(100, payment_method, options) }
120
+ r.process { authorize(amount, payment_method, options) }
118
121
  r.process(:ignore_result) { void(r.authorization, options.merge(authorization_validated: true)) }
119
122
  end
120
123
  end
@@ -128,6 +131,10 @@ module ActiveMerchant #:nodoc:
128
131
  true
129
132
  end
130
133
 
134
+ def supports_network_tokenization?
135
+ true
136
+ end
137
+
131
138
  def scrub(transcript)
132
139
  transcript.
133
140
  gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
@@ -138,11 +145,11 @@ module ActiveMerchant #:nodoc:
138
145
  private
139
146
 
140
147
  def authorize_request(money, payment_method, options)
141
- commit('authorize', build_authorization_request(money, payment_method, options), 'AUTHORISED', options)
148
+ commit('authorize', build_authorization_request(money, payment_method, options), 'AUTHORISED', 'CAPTURED', options)
142
149
  end
143
150
 
144
151
  def capture_request(money, authorization, options)
145
- commit('capture', build_capture_request(money, authorization, options), :ok, options)
152
+ commit('capture', build_capture_request(money, authorization, options), 'CAPTURED', :ok, options)
146
153
  end
147
154
 
148
155
  def cancel_request(authorization, options)
@@ -154,7 +161,7 @@ module ActiveMerchant #:nodoc:
154
161
  end
155
162
 
156
163
  def refund_request(money, authorization, options)
157
- commit('refund', build_refund_request(money, authorization, options), :ok, options)
164
+ commit('refund', build_refund_request(money, authorization, options), :ok, 'SENT_FOR_REFUND', options)
158
165
  end
159
166
 
160
167
  def credit_request(money, payment_method, options)
@@ -206,6 +213,7 @@ module ActiveMerchant #:nodoc:
206
213
  end
207
214
  add_payment_method(xml, money, payment_method, options)
208
215
  add_shopper(xml, options)
216
+ add_statement_narrative(xml, options)
209
217
  add_risk_data(xml, options[:risk_data]) if options[:risk_data]
210
218
  add_hcg_additional_data(xml, options) if options[:hcg_additional_data]
211
219
  add_instalments_data(xml, options) if options[:instalments]
@@ -232,13 +240,22 @@ module ActiveMerchant #:nodoc:
232
240
  end
233
241
 
234
242
  def build_void_request(authorization, options)
235
- build_order_modify_request(authorization, &:cancel)
243
+ if options[:cancel_or_refund]
244
+ build_order_modify_request(authorization, &:cancelOrRefund)
245
+ else
246
+ build_order_modify_request(authorization, &:cancel)
247
+ end
236
248
  end
237
249
 
238
250
  def build_refund_request(money, authorization, options)
239
251
  build_order_modify_request(authorization) do |xml|
240
- xml.refund do
241
- add_amount(xml, money, options.merge(debit_credit_indicator: 'credit'))
252
+ if options[:cancel_or_refund]
253
+ # Worldpay docs claim amount must be passed. This causes an error.
254
+ xml.cancelOrRefund # { add_amount(xml, money, options.merge(debit_credit_indicator: 'credit')) }
255
+ else
256
+ xml.refund do
257
+ add_amount(xml, money, options.merge(debit_credit_indicator: 'credit'))
258
+ end
242
259
  end
243
260
  end
244
261
  end
@@ -260,7 +277,10 @@ module ActiveMerchant #:nodoc:
260
277
  end
261
278
 
262
279
  def add_additional_3ds_data(xml, options)
263
- xml.additional3DSData 'dfReferenceId' => options[:session_id]
280
+ additional_data = { 'dfReferenceId' => options[:session_id] }
281
+ additional_data['challengeWindowSize'] = options[:browser_size] if options[:browser_size]
282
+
283
+ xml.additional3DSData additional_data
264
284
  end
265
285
 
266
286
  def add_3ds_exemption(xml, options)
@@ -378,6 +398,8 @@ module ActiveMerchant #:nodoc:
378
398
  add_amount(xml, amount, options)
379
399
  end
380
400
  end
401
+ elsif options[:payment_type] == :network_token
402
+ add_network_tokenization_card(xml, payment_method)
381
403
  else
382
404
  xml.paymentDetails credit_fund_transfer_attribute(options) do
383
405
  if options[:payment_type] == :token
@@ -396,7 +418,6 @@ module ActiveMerchant #:nodoc:
396
418
  xml.session 'shopperIPAddress' => options[:ip] if options[:ip]
397
419
  xml.session 'id' => options[:session_id] if options[:session_id]
398
420
  end
399
-
400
421
  if three_d_secure = options[:three_d_secure]
401
422
  add_three_d_secure(three_d_secure, xml)
402
423
  end
@@ -404,10 +425,26 @@ module ActiveMerchant #:nodoc:
404
425
  end
405
426
  end
406
427
 
428
+ def add_network_tokenization_card(xml, payment_method)
429
+ xml.paymentDetails do
430
+ xml.tag! 'EMVCO_TOKEN-SSL', 'type' => 'NETWORKTOKEN' do
431
+ xml.tokenNumber payment_method.number
432
+ xml.expiryDate do
433
+ xml.date(
434
+ 'month' => format(payment_method.month, :two_digits),
435
+ 'year' => format(payment_method.year, :four_digits_year)
436
+ )
437
+ end
438
+ xml.cryptogram payment_method.payment_cryptogram
439
+ xml.eciIndicator format(payment_method.eci, :two_digits)
440
+ end
441
+ end
442
+ end
443
+
407
444
  def add_three_d_secure(three_d_secure, xml)
408
445
  xml.info3DSecure do
409
446
  xml.threeDSVersion three_d_secure[:version]
410
- if /^2/.match?(three_d_secure[:version])
447
+ if three_d_secure[:version] && three_d_secure[:ds_transaction_id]
411
448
  xml.dsTransactionId three_d_secure[:ds_transaction_id]
412
449
  else
413
450
  xml.xid three_d_secure[:xid]
@@ -422,11 +459,11 @@ module ActiveMerchant #:nodoc:
422
459
  xml.expiryDate do
423
460
  xml.date(
424
461
  'month' => format(payment_method.month, :two_digits),
425
- 'year' => format(payment_method.year, :four_digits)
462
+ 'year' => format(payment_method.year, :four_digits_year)
426
463
  )
427
464
  end
428
465
 
429
- card_holder_name = options[:execute_threed] && !options[:three_ds_version]&.start_with?('2') ? '3D' : payment_method.name
466
+ card_holder_name = test? && options[:execute_threed] && !options[:three_ds_version]&.start_with?('2') ? '3D' : payment_method.name
430
467
  xml.cardHolderName card_holder_name
431
468
  xml.cvc payment_method.verification_value
432
469
 
@@ -482,6 +519,10 @@ module ActiveMerchant #:nodoc:
482
519
  end
483
520
  end
484
521
 
522
+ def add_statement_narrative(xml, options)
523
+ xml.statementNarrative truncate(options[:statement_narrative], 50) if options[:statement_narrative]
524
+ end
525
+
485
526
  def add_authenticated_shopper_id(xml, options)
486
527
  xml.authenticatedShopperID options[:customer] if options[:customer]
487
528
  end
@@ -535,11 +576,10 @@ module ActiveMerchant #:nodoc:
535
576
 
536
577
  def default_address
537
578
  {
538
- address1: 'N/A',
539
579
  zip: '0000',
580
+ country: 'US',
540
581
  city: 'N/A',
541
- state: 'N/A',
542
- country: 'US'
582
+ address1: 'N/A'
543
583
  }
544
584
  end
545
585
 
@@ -651,7 +691,9 @@ module ActiveMerchant #:nodoc:
651
691
  # - An array of strings if one of many responses could be considered a
652
692
  # success.
653
693
  def success_criteria_success?(raw, success_criteria)
654
- success_criteria.include?(raw[:last_event]) || raw[:ok].present?
694
+ return if raw[:error]
695
+
696
+ raw[:ok].present? || (success_criteria.include?(raw[:last_event]) if raw[:last_event])
655
697
  end
656
698
 
657
699
  def action_success?(action, raw)
@@ -714,7 +756,9 @@ module ActiveMerchant #:nodoc:
714
756
 
715
757
  def payment_details_from(payment_method)
716
758
  payment_details = {}
717
- if payment_method.respond_to?(:number)
759
+ if payment_method.is_a?(NetworkTokenizationCreditCard) && payment_method.source == :network_token
760
+ payment_details[:payment_type] = :network_token
761
+ elsif payment_method.respond_to?(:number)
718
762
  payment_details[:payment_type] = :credit
719
763
  else
720
764
  token_details = token_details_from_authorization(payment_method)
@@ -750,6 +794,10 @@ module ActiveMerchant #:nodoc:
750
794
  def card_code_for(payment_method)
751
795
  CARD_CODES[card_brand(payment_method)] || CARD_CODES['unknown']
752
796
  end
797
+
798
+ def eligible_for_0_auth?(payment_method, options = {})
799
+ payment_method.is_a?(CreditCard) && %w(visa master).include?(payment_method.brand) && options[:zero_dollar_auth]
800
+ end
753
801
  end
754
802
  end
755
803
  end
@@ -4,7 +4,7 @@ module ActiveMerchant #:nodoc:
4
4
  end
5
5
 
6
6
  class Response
7
- attr_reader :params, :message, :test, :authorization, :avs_result, :cvv_result, :error_code, :emv_authorization
7
+ attr_reader :params, :message, :test, :authorization, :avs_result, :cvv_result, :error_code, :emv_authorization, :network_transaction_id
8
8
 
9
9
  def success?
10
10
  @success
@@ -25,6 +25,7 @@ module ActiveMerchant #:nodoc:
25
25
  @fraud_review = options[:fraud_review]
26
26
  @error_code = options[:error_code]
27
27
  @emv_authorization = options[:emv_authorization]
28
+ @network_transaction_id = options[:network_transaction_id]
28
29
 
29
30
  @avs_result = if options[:avs_result].kind_of?(AVSResult)
30
31
  options[:avs_result].to_hash
@@ -0,0 +1,27 @@
1
+ module ActiveMerchant
2
+ module Billing
3
+ module ThreeDSecureEciMapper
4
+ NON_THREE_D_SECURE_TRANSACTION = :non_three_d_secure_transaction
5
+ ATTEMPTED_AUTHENTICATION_TRANSACTION = :attempted_authentication_transaction
6
+ FULLY_AUTHENTICATED_TRANSACTION = :fully_authenticated_transaction
7
+
8
+ ECI_00_01_02_MAP = { '00' => NON_THREE_D_SECURE_TRANSACTION, '01' => ATTEMPTED_AUTHENTICATION_TRANSACTION, '02' => FULLY_AUTHENTICATED_TRANSACTION }.freeze
9
+ ECI_05_06_07_MAP = { '05' => FULLY_AUTHENTICATED_TRANSACTION, '06' => ATTEMPTED_AUTHENTICATION_TRANSACTION, '07' => NON_THREE_D_SECURE_TRANSACTION }.freeze
10
+ BRAND_TO_ECI_MAP = {
11
+ american_express: ECI_05_06_07_MAP,
12
+ dankort: ECI_05_06_07_MAP,
13
+ diners_club: ECI_05_06_07_MAP,
14
+ discover: ECI_05_06_07_MAP,
15
+ elo: ECI_05_06_07_MAP,
16
+ jcb: ECI_05_06_07_MAP,
17
+ maestro: ECI_00_01_02_MAP,
18
+ master: ECI_00_01_02_MAP,
19
+ visa: ECI_05_06_07_MAP
20
+ }.freeze
21
+
22
+ def self.map(brand, eci)
23
+ BRAND_TO_ECI_MAP.dig(brand, eci)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -13,3 +13,4 @@ require 'active_merchant/billing/apple_pay_payment_token'
13
13
  require 'active_merchant/billing/response'
14
14
  require 'active_merchant/billing/gateways'
15
15
  require 'active_merchant/billing/gateway'
16
+ require 'active_merchant/billing/three_d_secure_eci_mapper'
@@ -1,3 +1,3 @@
1
1
  module ActiveMerchant
2
- VERSION = '1.117.0'
2
+ VERSION = '1.123.0'
3
3
  end