activemerchant 1.126.0 → 1.129.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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +241 -0
  3. data/lib/active_merchant/billing/check.rb +40 -8
  4. data/lib/active_merchant/billing/credit_card.rb +28 -1
  5. data/lib/active_merchant/billing/credit_card_methods.rb +79 -23
  6. data/lib/active_merchant/billing/gateways/adyen.rb +67 -8
  7. data/lib/active_merchant/billing/gateways/airwallex.rb +40 -11
  8. data/lib/active_merchant/billing/gateways/alelo.rb +256 -0
  9. data/lib/active_merchant/billing/gateways/authorize_net.rb +21 -4
  10. data/lib/active_merchant/billing/gateways/beanstream.rb +18 -0
  11. data/lib/active_merchant/billing/gateways/blue_snap.rb +22 -1
  12. data/lib/active_merchant/billing/gateways/bogus.rb +4 -0
  13. data/lib/active_merchant/billing/gateways/borgun.rb +56 -16
  14. data/lib/active_merchant/billing/gateways/braintree_blue.rb +64 -17
  15. data/lib/active_merchant/billing/gateways/card_connect.rb +27 -9
  16. data/lib/active_merchant/billing/gateways/card_stream.rb +23 -0
  17. data/lib/active_merchant/billing/gateways/checkout_v2.rb +228 -57
  18. data/lib/active_merchant/billing/gateways/commerce_hub.rb +361 -0
  19. data/lib/active_merchant/billing/gateways/credorax.rb +47 -27
  20. data/lib/active_merchant/billing/gateways/cyber_source/cyber_source_common.rb +36 -0
  21. data/lib/active_merchant/billing/gateways/cyber_source.rb +100 -26
  22. data/lib/active_merchant/billing/gateways/cyber_source_rest.rb +456 -0
  23. data/lib/active_merchant/billing/gateways/d_local.rb +44 -5
  24. data/lib/active_merchant/billing/gateways/decidir.rb +15 -4
  25. data/lib/active_merchant/billing/gateways/ebanx.rb +36 -24
  26. data/lib/active_merchant/billing/gateways/element.rb +21 -1
  27. data/lib/active_merchant/billing/gateways/global_collect.rb +73 -22
  28. data/lib/active_merchant/billing/gateways/ipg.rb +13 -8
  29. data/lib/active_merchant/billing/gateways/iveri.rb +39 -3
  30. data/lib/active_merchant/billing/gateways/kushki.rb +21 -1
  31. data/lib/active_merchant/billing/gateways/litle.rb +25 -5
  32. data/lib/active_merchant/billing/gateways/mastercard.rb +1 -8
  33. data/lib/active_merchant/billing/gateways/mercado_pago.rb +17 -0
  34. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +44 -10
  35. data/lib/active_merchant/billing/gateways/monei.rb +2 -0
  36. data/lib/active_merchant/billing/gateways/moneris.rb +20 -5
  37. data/lib/active_merchant/billing/gateways/mundipagg.rb +3 -0
  38. data/lib/active_merchant/billing/gateways/ogone.rb +35 -7
  39. data/lib/active_merchant/billing/gateways/openpay.rb +20 -3
  40. data/lib/active_merchant/billing/gateways/orbital.rb +43 -22
  41. data/lib/active_merchant/billing/gateways/pay_trace.rb +64 -18
  42. data/lib/active_merchant/billing/gateways/payeezy.rb +59 -4
  43. data/lib/active_merchant/billing/gateways/paymentez.rb +18 -6
  44. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +4 -0
  45. data/lib/active_merchant/billing/gateways/paysafe.rb +22 -14
  46. data/lib/active_merchant/billing/gateways/payu_latam.rb +3 -0
  47. data/lib/active_merchant/billing/gateways/plexo.rb +308 -0
  48. data/lib/active_merchant/billing/gateways/priority.rb +29 -6
  49. data/lib/active_merchant/billing/gateways/rapyd.rb +110 -49
  50. data/lib/active_merchant/billing/gateways/reach.rb +277 -0
  51. data/lib/active_merchant/billing/gateways/redsys.rb +9 -5
  52. data/lib/active_merchant/billing/gateways/sage_pay.rb +1 -1
  53. data/lib/active_merchant/billing/gateways/securion_pay.rb +40 -0
  54. data/lib/active_merchant/billing/gateways/shift4.rb +342 -0
  55. data/lib/active_merchant/billing/gateways/simetrik.rb +28 -22
  56. data/lib/active_merchant/billing/gateways/stripe.rb +21 -1
  57. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +62 -22
  58. data/lib/active_merchant/billing/gateways/tns.rb +2 -5
  59. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +1 -1
  60. data/lib/active_merchant/billing/gateways/trust_commerce.rb +14 -3
  61. data/lib/active_merchant/billing/gateways/vanco.rb +12 -3
  62. data/lib/active_merchant/billing/gateways/visanet_peru.rb +1 -1
  63. data/lib/active_merchant/billing/gateways/vpos.rb +7 -4
  64. data/lib/active_merchant/billing/gateways/wompi.rb +8 -4
  65. data/lib/active_merchant/billing/gateways/worldpay.rb +117 -9
  66. data/lib/active_merchant/billing/response.rb +15 -1
  67. data/lib/active_merchant/connection.rb +0 -2
  68. data/lib/active_merchant/country.rb +1 -0
  69. data/lib/active_merchant/errors.rb +4 -1
  70. data/lib/active_merchant/version.rb +1 -1
  71. metadata +24 -3
@@ -0,0 +1,456 @@
1
+ require 'active_merchant/billing/gateways/cyber_source/cyber_source_common'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class CyberSourceRestGateway < Gateway
6
+ include ActiveMerchant::Billing::CyberSourceCommon
7
+
8
+ self.test_url = 'https://apitest.cybersource.com'
9
+ self.live_url = 'https://api.cybersource.com'
10
+
11
+ self.supported_countries = ActiveMerchant::Billing::CyberSourceGateway.supported_countries
12
+ self.default_currency = 'USD'
13
+ self.currencies_without_fractions = ActiveMerchant::Billing::CyberSourceGateway.currencies_without_fractions
14
+
15
+ self.supported_cardtypes = %i[visa master american_express discover diners_club jcb maestro elo union_pay cartes_bancaires mada]
16
+
17
+ self.homepage_url = 'http://www.cybersource.com'
18
+ self.display_name = 'Cybersource REST'
19
+
20
+ CREDIT_CARD_CODES = {
21
+ american_express: '003',
22
+ cartes_bancaires: '036',
23
+ dankort: '034',
24
+ diners_club: '005',
25
+ discover: '004',
26
+ elo: '054',
27
+ jcb: '007',
28
+ maestro: '042',
29
+ master: '002',
30
+ unionpay: '062',
31
+ visa: '001'
32
+ }
33
+
34
+ PAYMENT_SOLUTION = {
35
+ apple_pay: '001',
36
+ google_pay: '012'
37
+ }
38
+
39
+ def initialize(options = {})
40
+ requires!(options, :merchant_id, :public_key, :private_key)
41
+ super
42
+ end
43
+
44
+ def purchase(money, payment, options = {})
45
+ authorize(money, payment, options, true)
46
+ end
47
+
48
+ def authorize(money, payment, options = {}, capture = false)
49
+ post = build_auth_request(money, payment, options)
50
+ post[:processingInformation][:capture] = true if capture
51
+
52
+ commit('payments', post, options)
53
+ end
54
+
55
+ def capture(money, authorization, options = {})
56
+ payment = authorization.split('|').first
57
+ post = build_reference_request(money, options)
58
+
59
+ commit("payments/#{payment}/captures", post, options)
60
+ end
61
+
62
+ def refund(money, authorization, options = {})
63
+ payment = authorization.split('|').first
64
+ post = build_reference_request(money, options)
65
+ commit("payments/#{payment}/refunds", post, options)
66
+ end
67
+
68
+ def credit(money, payment, options = {})
69
+ post = build_credit_request(money, payment, options)
70
+ commit('credits', post)
71
+ end
72
+
73
+ def void(authorization, options = {})
74
+ payment, amount = authorization.split('|')
75
+ post = build_void_request(amount)
76
+ commit("payments/#{payment}/reversals", post)
77
+ end
78
+
79
+ def verify(credit_card, options = {})
80
+ amount = eligible_for_zero_auth?(credit_card, options) ? 0 : 100
81
+ MultiResponse.run(:use_first_response) do |r|
82
+ r.process { authorize(amount, credit_card, options) }
83
+ r.process(:ignore_result) { void(r.authorization, options) }
84
+ end
85
+ end
86
+
87
+ def supports_scrubbing?
88
+ true
89
+ end
90
+
91
+ def scrub(transcript)
92
+ transcript.
93
+ gsub(/(\\?"number\\?":\\?")\d+/, '\1[FILTERED]').
94
+ gsub(/(\\?"routingNumber\\?":\\?")\d+/, '\1[FILTERED]').
95
+ gsub(/(\\?"securityCode\\?":\\?")\d+/, '\1[FILTERED]').
96
+ gsub(/(signature=")[^"]*/, '\1[FILTERED]').
97
+ gsub(/(keyid=")[^"]*/, '\1[FILTERED]').
98
+ gsub(/(Digest: SHA-256=)[\w\/\+=]*/, '\1[FILTERED]')
99
+ end
100
+
101
+ private
102
+
103
+ def build_void_request(amount = nil)
104
+ { reversalInformation: { amountDetails: { totalAmount: nil } } }.tap do |post|
105
+ add_reversal_amount(post, amount.to_i) if amount.present?
106
+ end.compact
107
+ end
108
+
109
+ def build_auth_request(amount, payment, options)
110
+ { clientReferenceInformation: {}, paymentInformation: {}, orderInformation: {} }.tap do |post|
111
+ add_customer_id(post, options)
112
+ add_code(post, options)
113
+ add_payment(post, payment, options)
114
+ add_mdd_fields(post, options)
115
+ add_amount(post, amount)
116
+ add_address(post, payment, options[:billing_address], options, :billTo)
117
+ add_address(post, payment, options[:shipping_address], options, :shipTo)
118
+ add_business_rules_data(post, payment, options)
119
+ add_partner_solution_id(post)
120
+ add_stored_credentials(post, payment, options)
121
+ end.compact
122
+ end
123
+
124
+ def build_reference_request(amount, options)
125
+ { clientReferenceInformation: {}, orderInformation: {} }.tap do |post|
126
+ add_code(post, options)
127
+ add_mdd_fields(post, options)
128
+ add_amount(post, amount)
129
+ add_partner_solution_id(post)
130
+ end.compact
131
+ end
132
+
133
+ def build_credit_request(amount, payment, options)
134
+ { clientReferenceInformation: {}, paymentInformation: {}, orderInformation: {} }.tap do |post|
135
+ add_code(post, options)
136
+ add_credit_card(post, payment)
137
+ add_mdd_fields(post, options)
138
+ add_amount(post, amount)
139
+ add_address(post, payment, options[:billing_address], options, :billTo)
140
+ add_merchant_description(post, options)
141
+ end.compact
142
+ end
143
+
144
+ def add_code(post, options)
145
+ return unless options[:order_id].present?
146
+
147
+ post[:clientReferenceInformation][:code] = options[:order_id]
148
+ end
149
+
150
+ def add_customer_id(post, options)
151
+ return unless options[:customer_id].present?
152
+
153
+ post[:paymentInformation][:customer] = { customerId: options[:customer_id] }
154
+ end
155
+
156
+ def add_reversal_amount(post, amount)
157
+ currency = options[:currency] || currency(amount)
158
+
159
+ post[:reversalInformation][:amountDetails] = {
160
+ totalAmount: localized_amount(amount, currency)
161
+ }
162
+ end
163
+
164
+ def add_amount(post, amount)
165
+ currency = options[:currency] || currency(amount)
166
+ post[:orderInformation][:amountDetails] = {
167
+ totalAmount: localized_amount(amount, currency),
168
+ currency: currency
169
+ }
170
+ end
171
+
172
+ def add_ach(post, payment)
173
+ post[:paymentInformation][:bank] = {
174
+ account: {
175
+ type: payment.account_type == 'checking' ? 'C' : 'S',
176
+ number: payment.account_number
177
+ },
178
+ routingNumber: payment.routing_number
179
+ }
180
+ end
181
+
182
+ def add_payment(post, payment, options)
183
+ post[:processingInformation] = {}
184
+ if payment.is_a?(NetworkTokenizationCreditCard)
185
+ add_network_tokenization_card(post, payment, options)
186
+ elsif payment.is_a?(Check)
187
+ add_ach(post, payment)
188
+ else
189
+ add_credit_card(post, payment)
190
+ end
191
+ end
192
+
193
+ def add_network_tokenization_card(post, payment, options)
194
+ post[:processingInformation][:paymentSolution] = PAYMENT_SOLUTION[payment.source]
195
+ post[:processingInformation][:commerceIndicator] = 'internet' unless card_brand(payment) == 'jcb'
196
+
197
+ post[:paymentInformation][:tokenizedCard] = {
198
+ number: payment.number,
199
+ expirationMonth: payment.month,
200
+ expirationYear: payment.year,
201
+ cryptogram: payment.payment_cryptogram,
202
+ transactionType: '1',
203
+ type: CREDIT_CARD_CODES[card_brand(payment).to_sym]
204
+ }
205
+
206
+ if card_brand(payment) == 'master'
207
+ post[:consumerAuthenticationInformation] = {
208
+ ucafAuthenticationData: payment.payment_cryptogram,
209
+ ucafCollectionIndicator: '2'
210
+ }
211
+ else
212
+ post[:consumerAuthenticationInformation] = { cavv: payment.payment_cryptogram }
213
+ end
214
+ end
215
+
216
+ def add_credit_card(post, creditcard)
217
+ post[:paymentInformation][:card] = {
218
+ number: creditcard.number,
219
+ expirationMonth: format(creditcard.month, :two_digits),
220
+ expirationYear: format(creditcard.year, :four_digits),
221
+ securityCode: creditcard.verification_value,
222
+ type: CREDIT_CARD_CODES[card_brand(creditcard).to_sym]
223
+ }
224
+ end
225
+
226
+ def add_address(post, payment_method, address, options, address_type)
227
+ return unless address.present?
228
+
229
+ first_name, last_name = address_names(address[:name], payment_method)
230
+
231
+ post[:orderInformation][address_type] = {
232
+ firstName: first_name,
233
+ lastName: last_name,
234
+ address1: address[:address1],
235
+ address2: address[:address2],
236
+ locality: address[:city],
237
+ administrativeArea: address[:state],
238
+ postalCode: address[:zip],
239
+ country: lookup_country_code(address[:country])&.value,
240
+ email: options[:email].presence || 'null@cybersource.com',
241
+ phoneNumber: address[:phone]
242
+ # merchantTaxID: ship_to ? options[:merchant_tax_id] : nil,
243
+ # company: address[:company],
244
+ # companyTaxID: address[:companyTaxID],
245
+ # ipAddress: options[:ip],
246
+ # driversLicenseNumber: options[:drivers_license_number],
247
+ # driversLicenseState: options[:drivers_license_state],
248
+ }.compact
249
+ end
250
+
251
+ def add_merchant_description(post, options)
252
+ return unless options[:merchant_descriptor_name] || options[:merchant_descriptor_address1] || options[:merchant_descriptor_locality]
253
+
254
+ merchant = post[:merchantInformation][:merchantDescriptor] = {}
255
+ merchant[:name] = options[:merchant_descriptor_name] if options[:merchant_descriptor_name]
256
+ merchant[:address1] = options[:merchant_descriptor_address1] if options[:merchant_descriptor_address1]
257
+ merchant[:locality] = options[:merchant_descriptor_locality] if options[:merchant_descriptor_locality]
258
+ end
259
+
260
+ def add_stored_credentials(post, payment, options)
261
+ return unless stored_credential = options[:stored_credential]
262
+
263
+ options = stored_credential_options(stored_credential, options.fetch(:reason_code, ''))
264
+ post[:processingInformation][:commerceIndicator] = options.fetch(:transaction_type, 'internet')
265
+ stored_credential[:initial_transaction] ? initial_transaction(post, options) : subsequent_transaction(post, options)
266
+ end
267
+
268
+ def stored_credential_options(options, reason_code)
269
+ transaction_type = options[:reason_type]
270
+ transaction_type = 'install' if transaction_type == 'installment'
271
+ initiator = options[:initiator] if options[:initiator]
272
+ initiator = 'customer' if initiator == 'cardholder'
273
+ stored_on_file = options[:reason_type] == 'recurring'
274
+ options.merge({
275
+ transaction_type: transaction_type,
276
+ initiator: initiator,
277
+ reason_code: reason_code,
278
+ stored_on_file: stored_on_file
279
+ })
280
+ end
281
+
282
+ def add_processing_information(initiator, merchant_initiated_transaction_hash = {})
283
+ {
284
+ authorizationOptions: {
285
+ initiator: {
286
+ type: initiator,
287
+ merchantInitiatedTransaction: merchant_initiated_transaction_hash,
288
+ storedCredentialUsed: true
289
+ }
290
+ }
291
+ }.compact
292
+ end
293
+
294
+ def initial_transaction(post, options)
295
+ processing_information = add_processing_information(options[:initiator], {
296
+ reason: options[:reason_code]
297
+ })
298
+
299
+ post[:processingInformation].merge!(processing_information)
300
+ end
301
+
302
+ def subsequent_transaction(post, options)
303
+ network_transaction_id = options[:network_transaction_id] || options.dig(:stored_credential, :network_transaction_id) || ''
304
+ processing_information = add_processing_information(options[:initiator], {
305
+ originalAuthorizedAmount: post.dig(:orderInformation, :amountDetails, :totalAmount),
306
+ previousTransactionID: network_transaction_id,
307
+ reason: options[:reason_code],
308
+ storedCredentialUsed: options[:stored_on_file]
309
+ })
310
+ post[:processingInformation].merge!(processing_information)
311
+ end
312
+
313
+ def network_transaction_id_from(response)
314
+ response.dig('processorInformation', 'networkTransactionId')
315
+ end
316
+
317
+ def url(action)
318
+ "#{(test? ? test_url : live_url)}/pts/v2/#{action}"
319
+ end
320
+
321
+ def host
322
+ URI.parse(url('')).host
323
+ end
324
+
325
+ def parse(body)
326
+ JSON.parse(body)
327
+ end
328
+
329
+ def commit(action, post, options = {})
330
+ add_reconciliation_id(post, options)
331
+ add_sec_code(post, options)
332
+ add_invoice_number(post, options)
333
+ response = parse(ssl_post(url(action), post.to_json, auth_headers(action, post)))
334
+ Response.new(
335
+ success_from(response),
336
+ message_from(response),
337
+ response,
338
+ authorization: authorization_from(response),
339
+ avs_result: AVSResult.new(code: response.dig('processorInformation', 'avs', 'code')),
340
+ # cvv_result: CVVResult.new(response['some_cvv_response_key']),
341
+ network_transaction_id: network_transaction_id_from(response),
342
+ test: test?,
343
+ error_code: error_code_from(response)
344
+ )
345
+ rescue ActiveMerchant::ResponseError => e
346
+ response = e.response.body.present? ? parse(e.response.body) : { 'response' => { 'rmsg' => e.response.msg } }
347
+ message = response.dig('response', 'rmsg') || response.dig('message')
348
+ Response.new(false, message, response, test: test?)
349
+ end
350
+
351
+ def success_from(response)
352
+ %w(AUTHORIZED PENDING REVERSED).include?(response['status'])
353
+ end
354
+
355
+ def message_from(response)
356
+ return response['status'] if success_from(response)
357
+
358
+ response['errorInformation']['message'] || response['message']
359
+ end
360
+
361
+ def authorization_from(response)
362
+ id = response['id']
363
+ has_amount = response['orderInformation'] && response['orderInformation']['amountDetails'] && response['orderInformation']['amountDetails']['authorizedAmount']
364
+ amount = response['orderInformation']['amountDetails']['authorizedAmount'].delete('.') if has_amount
365
+
366
+ return id if amount.blank?
367
+
368
+ [id, amount].join('|')
369
+ end
370
+
371
+ def error_code_from(response)
372
+ response['errorInformation']['reason'] unless success_from(response)
373
+ end
374
+
375
+ # This implementation follows the Cybersource guide on how create the request signature, see:
376
+ # https://developer.cybersource.com/docs/cybs/en-us/payments/developer/all/rest/payments/GenerateHeader/httpSignatureAuthentication.html
377
+ def get_http_signature(resource, digest, http_method = 'post', gmtdatetime = Time.now.httpdate)
378
+ string_to_sign = {
379
+ host: host,
380
+ date: gmtdatetime,
381
+ "(request-target)": "#{http_method} /pts/v2/#{resource}",
382
+ digest: digest,
383
+ "v-c-merchant-id": @options[:merchant_id]
384
+ }.map { |k, v| "#{k}: #{v}" }.join("\n").force_encoding(Encoding::UTF_8)
385
+
386
+ {
387
+ keyid: @options[:public_key],
388
+ algorithm: 'HmacSHA256',
389
+ headers: "host date (request-target)#{digest.present? ? ' digest' : ''} v-c-merchant-id",
390
+ signature: sign_payload(string_to_sign)
391
+ }.map { |k, v| %{#{k}="#{v}"} }.join(', ')
392
+ end
393
+
394
+ def sign_payload(payload)
395
+ decoded_key = Base64.decode64(@options[:private_key])
396
+ Base64.strict_encode64(OpenSSL::HMAC.digest('sha256', decoded_key, payload))
397
+ end
398
+
399
+ def auth_headers(action, post, http_method = 'post')
400
+ digest = "SHA-256=#{Digest::SHA256.base64digest(post.to_json)}" if post.present?
401
+ date = Time.now.httpdate
402
+
403
+ {
404
+ 'Accept' => 'application/hal+json;charset=utf-8',
405
+ 'Content-Type' => 'application/json;charset=utf-8',
406
+ 'V-C-Merchant-Id' => @options[:merchant_id],
407
+ 'Date' => date,
408
+ 'Host' => host,
409
+ 'Signature' => get_http_signature(action, digest, http_method, date),
410
+ 'Digest' => digest
411
+ }
412
+ end
413
+
414
+ def add_business_rules_data(post, payment, options)
415
+ post[:processingInformation][:authorizationOptions] = {}
416
+ unless payment.is_a?(NetworkTokenizationCreditCard)
417
+ post[:processingInformation][:authorizationOptions][:ignoreAvsResult] = 'true' if options[:ignore_avs].to_s == 'true'
418
+ post[:processingInformation][:authorizationOptions][:ignoreCvResult] = 'true' if options[:ignore_cvv].to_s == 'true'
419
+ end
420
+ end
421
+
422
+ def add_mdd_fields(post, options)
423
+ mdd_fields = options.select { |k, v| k.to_s.start_with?('mdd_field') && v.present? }
424
+ return unless mdd_fields.present?
425
+
426
+ post[:merchantDefinedInformation] = mdd_fields.map do |key, value|
427
+ { key: key, value: value }
428
+ end
429
+ end
430
+
431
+ def add_reconciliation_id(post, options)
432
+ return unless options[:reconciliation_id].present?
433
+
434
+ post[:clientReferenceInformation][:reconciliationId] = options[:reconciliation_id]
435
+ end
436
+
437
+ def add_sec_code(post, options)
438
+ return unless options[:sec_code].present?
439
+
440
+ post[:processingInformation][:bankTransferOptions] = { secCode: options[:sec_code] }
441
+ end
442
+
443
+ def add_invoice_number(post, options)
444
+ return unless options[:invoice_number].present?
445
+
446
+ post[:orderInformation][:invoiceDetails] = { invoiceNumber: options[:invoice_number] }
447
+ end
448
+
449
+ def add_partner_solution_id(post)
450
+ return unless application_id
451
+
452
+ post[:clientReferenceInformation][:partner] = { solutionId: application_id }
453
+ end
454
+ end
455
+ end
456
+ end
@@ -4,7 +4,7 @@ module ActiveMerchant #:nodoc:
4
4
  self.test_url = 'https://sandbox.dlocal.com'
5
5
  self.live_url = 'https://api.dlocal.com'
6
6
 
7
- self.supported_countries = %w[AR BD BO BR CL CM CN CO CR DO EC EG GH IN ID KE MY MX MA NG PA PY PE PH SN ZA TR UY VN]
7
+ self.supported_countries = %w[AR BD BO BR CL CM CN CO CR DO EC EG GH GT IN ID JP KE MY MX MA NG PA PY PE PH SN SV TH TR TZ UG UY VN ZA]
8
8
  self.default_currency = 'USD'
9
9
  self.supported_cardtypes = %i[visa master american_express discover jcb diners_club maestro naranja cabal elo alia carnet]
10
10
 
@@ -58,10 +58,21 @@ module ActiveMerchant #:nodoc:
58
58
  authorize(0, credit_card, options.merge(verify: 'true'))
59
59
  end
60
60
 
61
+ def inquire(authorization, options = {})
62
+ post = {}
63
+ post[:payment_id] = authorization
64
+ action = authorization ? 'status' : 'orders'
65
+ commit(action, post, options)
66
+ end
67
+
61
68
  def supports_scrubbing?
62
69
  true
63
70
  end
64
71
 
72
+ def supports_network_tokenization?
73
+ true
74
+ end
75
+
65
76
  def scrub(transcript)
66
77
  transcript.
67
78
  gsub(%r((X-Trans-Key: )\w+), '\1[FILTERED]').
@@ -80,6 +91,7 @@ module ActiveMerchant #:nodoc:
80
91
  add_card(post, card, action, options)
81
92
  add_additional_data(post, options)
82
93
  post[:order_id] = options[:order_id] || generate_unique_id
94
+ post[:original_order_id] = options[:original_order_id] if options[:original_order_id]
83
95
  post[:description] = options[:description] if options[:description]
84
96
  end
85
97
 
@@ -147,11 +159,30 @@ module ActiveMerchant #:nodoc:
147
159
 
148
160
  def add_card(post, card, action, options = {})
149
161
  post[:card] = {}
162
+ if card.is_a?(NetworkTokenizationCreditCard)
163
+ post[:card][:network_token] = card.number
164
+ post[:card][:cryptogram] = card.payment_cryptogram
165
+ post[:card][:eci] = card.eci
166
+ # used case of Network Token: 'CARD_ON_FILE', 'SUBSCRIPTION', 'UNSCHEDULED_CARD_ON_FILE'
167
+ if options.dig(:stored_credential, :reason_type) == 'unscheduled'
168
+ if options.dig(:stored_credential, :initiator) == 'merchant'
169
+ post[:card][:stored_credential_type] = 'UNSCHEDULED_CARD_ON_FILE'
170
+ else
171
+ post[:card][:stored_credential_type] = 'CARD_ON_FILE'
172
+ end
173
+ else
174
+ post[:card][:stored_credential_type] = 'SUBSCRIPTION'
175
+ end
176
+ # required for MC debit recurrent in BR 'USED'(subsecuence Payments) . 'FIRST' an inital payment
177
+ post[:card][:stored_credential_usage] = (options[:stored_credential][:initial_transaction] ? 'FIRST' : 'USED') if options[:stored_credential]
178
+ else
179
+ post[:card][:number] = card.number
180
+ post[:card][:cvv] = card.verification_value
181
+ end
182
+
150
183
  post[:card][:holder_name] = card.name
151
184
  post[:card][:expiration_month] = card.month
152
185
  post[:card][:expiration_year] = card.year
153
- post[:card][:number] = card.number
154
- post[:card][:cvv] = card.verification_value
155
186
  post[:card][:descriptor] = options[:dynamic_descriptor] if options[:dynamic_descriptor]
156
187
  post[:card][:capture] = (action == 'purchase')
157
188
  post[:card][:installments] = options[:installments] if options[:installments]
@@ -170,7 +201,11 @@ module ActiveMerchant #:nodoc:
170
201
  url = url(action, parameters, options)
171
202
  post = post_data(action, parameters)
172
203
  begin
173
- raw = ssl_post(url, post, headers(post, options))
204
+ raw = if %w(status orders).include?(action)
205
+ ssl_get(url, headers(nil, options))
206
+ else
207
+ ssl_post(url, post, headers(post, options))
208
+ end
174
209
  response = parse(raw)
175
210
  rescue ResponseError => e
176
211
  raw = e.response.body
@@ -229,6 +264,10 @@ module ActiveMerchant #:nodoc:
229
264
  'payments'
230
265
  when 'void'
231
266
  "payments/#{parameters[:authorization_id]}/cancel"
267
+ when 'status'
268
+ "payments/#{parameters[:payment_id]}/status"
269
+ when 'orders'
270
+ "orders/#{options[:order_id]}"
232
271
  end
233
272
  end
234
273
 
@@ -242,7 +281,7 @@ module ActiveMerchant #:nodoc:
242
281
  'X-Version' => '2.1',
243
282
  'Authorization' => signature(post, timestamp)
244
283
  }
245
- headers.merge('X-Idempotency-Key' => options[:idempotency_key]) if options[:idempotency_key]
284
+ headers['X-Idempotency-Key'] = options[:idempotency_key] if options[:idempotency_key]
246
285
  headers
247
286
  end
248
287
 
@@ -83,6 +83,11 @@ module ActiveMerchant #:nodoc:
83
83
  commit(:post, "payments/#{authorization}/refunds", post)
84
84
  end
85
85
 
86
+ def inquire(authorization, options = {})
87
+ options[:action] = 'inquire'
88
+ commit(:get, "payments/#{authorization}", nil, options)
89
+ end
90
+
86
91
  def verify(credit_card, options = {})
87
92
  raise ArgumentError, 'Verify is not supported on Decidir gateways unless the preauth_mode option is enabled' unless @options[:preauth_mode]
88
93
 
@@ -267,7 +272,7 @@ module ActiveMerchant #:nodoc:
267
272
  response = parse(raw_response)
268
273
  end
269
274
 
270
- success = success_from(response)
275
+ success = success_from(response, options)
271
276
  Response.new(
272
277
  success,
273
278
  message_from(success, response),
@@ -279,7 +284,7 @@ module ActiveMerchant #:nodoc:
279
284
  end
280
285
 
281
286
  def post_data(parameters = {})
282
- parameters.to_json
287
+ parameters&.to_json
283
288
  end
284
289
 
285
290
  def parse(body)
@@ -311,8 +316,14 @@ module ActiveMerchant #:nodoc:
311
316
  message
312
317
  end
313
318
 
314
- def success_from(response)
315
- response['status'] == 'approved' || response['status'] == 'pre_approved'
319
+ def success_from(response, options)
320
+ status = %w(approved pre_approved)
321
+
322
+ if options[:action] == 'inquire'
323
+ status.include?(response['status']) || response['status'] == 'rejected'
324
+ else
325
+ status.include?(response['status'])
326
+ end
316
327
  end
317
328
 
318
329
  def authorization_from(response)