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
@@ -13,7 +13,7 @@ module ActiveMerchant #:nodoc:
13
13
  self.homepage_url = 'https://www.barclaycardsmartpay.com/'
14
14
  self.display_name = 'Barclaycard Smartpay'
15
15
 
16
- API_VERSION = 'v30'
16
+ API_VERSION = 'v40'
17
17
 
18
18
  def initialize(options = {})
19
19
  requires!(options, :company, :merchant, :password)
@@ -37,7 +37,9 @@ module ActiveMerchant #:nodoc:
37
37
  post[:card] = credit_card_hash(creditcard)
38
38
  post[:billingAddress] = billing_address_hash(options) if options[:billing_address]
39
39
  post[:deliveryAddress] = shipping_address_hash(options) if options[:shipping_address]
40
- add_3ds(post, options) if options[:execute_threed]
40
+ post[:shopperStatement] = options[:shopper_statement] if options[:shopper_statement]
41
+
42
+ add_3ds(post, options)
41
43
  commit('authorise', post)
42
44
  end
43
45
 
@@ -186,7 +188,7 @@ module ActiveMerchant #:nodoc:
186
188
  end
187
189
 
188
190
  def parse_avs_code(response)
189
- AVS_MAPPING[response['avsResult'][0..1].strip] if response['avsResult']
191
+ AVS_MAPPING[response['additionalData']['avsResult'][0..1].strip] if response.dig('additionalData', 'avsResult')
190
192
  end
191
193
 
192
194
  def flatten_hash(hash, prefix = nil)
@@ -210,12 +212,18 @@ module ActiveMerchant #:nodoc:
210
212
  end
211
213
 
212
214
  def parse(response)
213
- Hash[
214
- response.split('&').map do |x|
215
- key, val = x.split('=', 2)
216
- [key.split('.').last, CGI.unescape(val)]
215
+ parsed_response = {}
216
+ params = CGI.parse(response)
217
+ params.each do |key, value|
218
+ parsed_key = key.split('.', 2)
219
+ if parsed_key.size > 1
220
+ parsed_response[parsed_key[0]] ||= {}
221
+ parsed_response[parsed_key[0]][parsed_key[1]] = value[0]
222
+ else
223
+ parsed_response[parsed_key[0]] = value[0]
217
224
  end
218
- ]
225
+ end
226
+ parsed_response
219
227
  end
220
228
 
221
229
  def post_data(data)
@@ -343,8 +351,33 @@ module ActiveMerchant #:nodoc:
343
351
  end
344
352
 
345
353
  def add_3ds(post, options)
346
- post[:additionalData] = { executeThreeD: 'true' }
347
- post[:browserInfo] = { userAgent: options[:user_agent], acceptHeader: options[:accept_header] }
354
+ if three_ds_2_options = options[:three_ds_2]
355
+ device_channel = three_ds_2_options[:channel]
356
+ if device_channel == 'app'
357
+ post[:threeDS2RequestData] = { deviceChannel: device_channel }
358
+ else
359
+ add_browser_info(three_ds_2_options[:browser_info], post)
360
+ post[:threeDS2RequestData] = { deviceChannel: device_channel, notificationURL: three_ds_2_options[:notification_url] }
361
+ end
362
+ else
363
+ return unless options[:execute_threed] || options[:threed_dynamic]
364
+ post[:browserInfo] = { userAgent: options[:user_agent], acceptHeader: options[:accept_header] }
365
+ post[:additionalData] = { executeThreeD: 'true' } if options[:execute_threed]
366
+ end
367
+ end
368
+
369
+ def add_browser_info(browser_info, post)
370
+ return unless browser_info
371
+ post[:browserInfo] = {
372
+ acceptHeader: browser_info[:accept_header],
373
+ colorDepth: browser_info[:depth],
374
+ javaEnabled: browser_info[:java],
375
+ language: browser_info[:language],
376
+ screenHeight: browser_info[:height],
377
+ screenWidth: browser_info[:width],
378
+ timeZoneOffset: browser_info[:timezone],
379
+ userAgent: browser_info[:user_agent]
380
+ }
348
381
  end
349
382
  end
350
383
  end
@@ -153,6 +153,8 @@ module ActiveMerchant #:nodoc:
153
153
 
154
154
  # To match the other stored-value gateways, like TrustCommerce,
155
155
  # store and unstore need to be defined
156
+ #
157
+ # When passing a single-use token the :name option is required
156
158
  def store(payment_method, options = {})
157
159
  post = {}
158
160
  add_address(post, options)
@@ -315,6 +315,9 @@ module ActiveMerchant #:nodoc:
315
315
  post[:operationType] = options[:operationType] || options[:operation] || secure_profile_action(:new)
316
316
  post[:customerCode] = options[:billing_id] || options[:vault_id] || false
317
317
  post[:status] = options[:status]
318
+
319
+ billing_address = options[:billing_address] || options[:address]
320
+ post[:trnCardOwner] = billing_address[:name]
318
321
  end
319
322
 
320
323
  def add_recurring_amount(post, money)
@@ -8,7 +8,7 @@ module ActiveMerchant
8
8
  self.supported_countries = %w(US CA GB AT BE BG HR CY CZ DK EE FI FR DE GR HU IE IT LV LT LU MT NL PL PT RO SK SI ES SE AR BO BR BZ CL CO CR DO EC GF GP GT HN HT MF MQ MX NI PA PE PR PY SV UY VE)
9
9
 
10
10
  self.default_currency = 'USD'
11
- self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club, :maestro]
11
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club, :maestro, :naranja, :cabal]
12
12
 
13
13
  self.homepage_url = 'https://home.bluesnap.com/'
14
14
  self.display_name = 'BlueSnap'
@@ -66,6 +66,8 @@ module ActiveMerchant
66
66
  'business_savings' => 'CORPORATE_SAVINGS'
67
67
  }
68
68
 
69
+ STATE_CODE_COUNTRIES = %w(US CA)
70
+
69
71
  def initialize(options={})
70
72
  requires!(options, :api_username, :api_password)
71
73
  super
@@ -93,6 +95,7 @@ module ActiveMerchant
93
95
  commit(:capture, :put) do |doc|
94
96
  add_authorization(doc, authorization)
95
97
  add_order(doc, options)
98
+ add_amount(doc, money, options) if options[:include_capture_amount] == true
96
99
  end
97
100
  end
98
101
 
@@ -220,6 +223,7 @@ module ActiveMerchant
220
223
  doc.send('merchant-transaction-id', truncate(options[:order_id], 50)) if options[:order_id]
221
224
  doc.send('soft-descriptor', options[:soft_descriptor]) if options[:soft_descriptor]
222
225
  add_description(doc, options[:description]) if options[:description]
226
+ add_3ds(doc, options[:three_d_secure]) if options[:three_d_secure]
223
227
  add_level_3_data(doc, options)
224
228
  end
225
229
 
@@ -228,12 +232,28 @@ module ActiveMerchant
228
232
  return unless address
229
233
 
230
234
  doc.country(address[:country]) if address[:country]
231
- doc.state(address[:state]) if address[:state]
235
+ doc.state(address[:state]) if address[:state] && STATE_CODE_COUNTRIES.include?(address[:country])
232
236
  doc.address(address[:address]) if address[:address]
233
237
  doc.city(address[:city]) if address[:city]
234
238
  doc.zip(address[:zip]) if address[:zip]
235
239
  end
236
240
 
241
+ def add_3ds(doc, three_d_secure_options)
242
+ eci = three_d_secure_options[:eci]
243
+ cavv = three_d_secure_options[:cavv]
244
+ xid = three_d_secure_options[:xid]
245
+ ds_transaction_id = three_d_secure_options[:ds_transaction_id]
246
+ version = three_d_secure_options[:version]
247
+
248
+ doc.send('three-d-secure') do
249
+ doc.eci(eci) if eci
250
+ doc.cavv(cavv) if cavv
251
+ doc.xid(xid) if xid
252
+ doc.send('three-d-secure-version', version) if version
253
+ doc.send('ds-transaction-id', ds_transaction_id) if ds_transaction_id
254
+ end
255
+ end
256
+
237
257
  def add_level_3_data(doc, options)
238
258
  return unless options[:customer_reference_number]
239
259
  doc.send('level-3-data') do
@@ -163,10 +163,10 @@ module ActiveMerchant #:nodoc:
163
163
  xml.send('PaymentType', payment_type)
164
164
  xml.send('TxnType', 'WEB_SHOP')
165
165
  xml.send('BillerCode', options.fetch(:biller_code, ''))
166
- xml.send('MerchantReference', '')
167
- xml.send('CRN1', '')
168
- xml.send('CRN2', '')
169
- xml.send('CRN3', '')
166
+ xml.send('MerchantReference', options[:order_id]) if options[:order_id]
167
+ xml.send('CRN1', options[:crn1]) if options[:crn1]
168
+ xml.send('CRN2', options[:crn2]) if options[:crn2]
169
+ xml.send('CRN3', options[:crn3]) if options[:crn3]
170
170
  xml.send('Amount', amount)
171
171
  end
172
172
 
@@ -419,7 +419,9 @@ module ActiveMerchant #:nodoc:
419
419
  'street: A, zip: N' => 'C',
420
420
  'street: A, zip: U' => 'I',
421
421
  'street: A, zip: I' => 'I',
422
- 'street: A, zip: A' => 'I'
422
+ 'street: A, zip: A' => 'I',
423
+
424
+ 'street: B, zip: B' => 'B'
423
425
  }
424
426
  end
425
427
 
@@ -445,7 +447,6 @@ module ActiveMerchant #:nodoc:
445
447
 
446
448
  def create_transaction(transaction_type, money, credit_card_or_vault_id, options)
447
449
  transaction_params = create_transaction_parameters(money, credit_card_or_vault_id, options)
448
-
449
450
  commit do
450
451
  result = @braintree_gateway.transaction.send(transaction_type, transaction_params)
451
452
  response = Response.new(result.success?, message_from_transaction_result(result), response_params(result), response_options(result))
@@ -565,6 +566,7 @@ module ActiveMerchant #:nodoc:
565
566
  'vault_customer' => vault_customer,
566
567
  'merchant_account_id' => transaction.merchant_account_id,
567
568
  'risk_data' => risk_data,
569
+ 'network_transaction_id' => transaction.network_transaction_id || nil,
568
570
  'processor_response_code' => response_code_from_result(result)
569
571
  }
570
572
  end
@@ -590,6 +592,14 @@ module ActiveMerchant #:nodoc:
590
592
  parameters[:options][:skip_advanced_fraud_checking] = options[:skip_advanced_fraud_checking]
591
593
  end
592
594
 
595
+ if options[:skip_avs]
596
+ parameters[:options][:skip_avs] = options[:skip_avs]
597
+ end
598
+
599
+ if options[:skip_cvv]
600
+ parameters[:options][:skip_cvv] = options[:skip_cvv]
601
+ end
602
+
593
603
  parameters[:custom_fields] = options[:custom_fields]
594
604
  parameters[:device_data] = options[:device_data] if options[:device_data]
595
605
  parameters[:service_fee_amount] = options[:service_fee_amount] if options[:service_fee_amount]
@@ -604,6 +614,7 @@ module ActiveMerchant #:nodoc:
604
614
  end
605
615
 
606
616
  add_payment_method(parameters, credit_card_or_vault_id, options)
617
+ add_stored_credential_data(parameters, credit_card_or_vault_id, options)
607
618
 
608
619
  parameters[:billing] = map_address(options[:billing_address]) if options[:billing_address]
609
620
  parameters[:shipping] = map_address(options[:shipping_address]) if options[:shipping_address]
@@ -619,13 +630,7 @@ module ActiveMerchant #:nodoc:
619
630
  }
620
631
  end
621
632
 
622
- if options[:three_d_secure]
623
- parameters[:three_d_secure_pass_thru] = {
624
- cavv: options[:three_d_secure][:cavv],
625
- eci_flag: options[:three_d_secure][:eci],
626
- xid: options[:three_d_secure][:xid],
627
- }
628
- end
633
+ add_3ds_info(parameters, options[:three_d_secure])
629
634
 
630
635
  parameters[:tax_amount] = options[:tax_amount] if options[:tax_amount]
631
636
  parameters[:tax_exempt] = options[:tax_exempt] if options[:tax_exempt]
@@ -640,6 +645,48 @@ module ActiveMerchant #:nodoc:
640
645
  parameters
641
646
  end
642
647
 
648
+ def add_3ds_info(parameters, three_d_secure_opts)
649
+ return if empty?(three_d_secure_opts)
650
+ pass_thru = {}
651
+
652
+ pass_thru[:three_d_secure_version] = three_d_secure_opts[:version] if three_d_secure_opts[:version]
653
+ pass_thru[:eci_flag] = three_d_secure_opts[:eci] if three_d_secure_opts[:eci]
654
+ pass_thru[:cavv_algorithm] = three_d_secure_opts[:cavv_algorithm] if three_d_secure_opts[:cavv_algorithm]
655
+ pass_thru[:cavv] = three_d_secure_opts[:cavv] if three_d_secure_opts[:cavv]
656
+ pass_thru[:directory_response] = three_d_secure_opts[:directory_response_status] if three_d_secure_opts[:directory_response_status]
657
+ pass_thru[:authentication_response] = three_d_secure_opts[:authentication_response_status] if three_d_secure_opts[:authentication_response_status]
658
+
659
+ parameters[:three_d_secure_pass_thru] = pass_thru.merge(xid_or_ds_trans_id(three_d_secure_opts))
660
+ end
661
+
662
+ def xid_or_ds_trans_id(three_d_secure_opts)
663
+ if three_d_secure_opts[:version].to_f >= 2
664
+ { ds_transaction_id: three_d_secure_opts[:ds_transaction_id] }
665
+ else
666
+ { xid: three_d_secure_opts[:xid] }
667
+ end
668
+ end
669
+
670
+ def add_stored_credential_data(parameters, credit_card_or_vault_id, options)
671
+ return unless (stored_credential = options[:stored_credential])
672
+ parameters[:external_vault] = {}
673
+ if stored_credential[:initial_transaction]
674
+ parameters[:external_vault][:status] = 'will_vault'
675
+ else
676
+ parameters[:external_vault][:status] = 'vaulted'
677
+ parameters[:external_vault][:previous_network_transaction_id] = stored_credential[:network_transaction_id]
678
+ end
679
+ if stored_credential[:initiator] == 'merchant'
680
+ if stored_credential[:reason_type] == 'installment'
681
+ parameters[:transaction_source] = 'recurring'
682
+ else
683
+ parameters[:transaction_source] = stored_credential[:reason_type]
684
+ end
685
+ else
686
+ parameters[:transaction_source] = ''
687
+ end
688
+ end
689
+
643
690
  def add_payment_method(parameters, credit_card_or_vault_id, options)
644
691
  if credit_card_or_vault_id.is_a?(String) || credit_card_or_vault_id.is_a?(Integer)
645
692
  if options[:payment_method_token]
@@ -68,7 +68,7 @@ module ActiveMerchant #:nodoc:
68
68
  end
69
69
 
70
70
  def require_valid_domain!(options, param)
71
- if options.key?(param)
71
+ if options[param]
72
72
  raise ArgumentError.new('not a valid cardconnect domain') unless /\Dcardconnect.com:\d{1,}\D/ =~ options[param]
73
73
  end
74
74
  end
@@ -233,6 +233,7 @@ module ActiveMerchant #:nodoc:
233
233
  item.each_pair do |k, v|
234
234
  updated.merge!(k.to_s.gsub(/_/, '') => v)
235
235
  end
236
+ updated
236
237
  end
237
238
  end
238
239
  end
@@ -267,6 +268,7 @@ module ActiveMerchant #:nodoc:
267
268
  end
268
269
 
269
270
  def commit(action, parameters, verb: :put, path: '')
271
+ parameters[:frontendid] = application_id
270
272
  parameters[:merchid] = @options[:merchant_id]
271
273
  url = url(action, path)
272
274
  response = parse(ssl_request(verb, url, post_data(parameters), headers))
@@ -1,7 +1,7 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
3
  class CecabankGateway < Gateway
4
- self.test_url = 'http://tpv.ceca.es:8000'
4
+ self.test_url = 'https://tpv.ceca.es'
5
5
  self.live_url = 'https://pgw.ceca.es'
6
6
 
7
7
  self.supported_countries = ['ES']
@@ -13,14 +13,14 @@ module ActiveMerchant #:nodoc:
13
13
 
14
14
  #### CECA's MAGIC NUMBERS
15
15
  CECA_NOTIFICATIONS_URL = 'NONE'
16
- CECA_ENCRIPTION = 'SHA1'
16
+ CECA_ENCRIPTION = 'SHA2'
17
17
  CECA_DECIMALS = '2'
18
18
  CECA_MODE = 'SSL'
19
19
  CECA_UI_LESS_LANGUAGE = 'XML'
20
20
  CECA_UI_LESS_LANGUAGE_REFUND = '1'
21
21
  CECA_UI_LESS_REFUND_PAGE = 'anulacion_xml'
22
- CECA_ACTION_REFUND = 'tpvanularparcialmente' # use partial refund's URL to avoid time frame limitations and decision logic on client side
23
- CECA_ACTION_PURCHASE = 'tpv'
22
+ CECA_ACTION_REFUND = 'anulaciones/anularParcial' # use partial refund's URL to avoid time frame limitations and decision logic on client side
23
+ CECA_ACTION_PURCHASE = 'tpv/compra'
24
24
  CECA_CURRENCIES_DICTIONARY = {'EUR' => 978, 'USD' => 840, 'GBP' => 826}
25
25
 
26
26
  # Creates a new CecabankGateway
@@ -168,8 +168,8 @@ module ActiveMerchant #:nodoc:
168
168
  'AcquirerBIN' => options[:acquirer_bin],
169
169
  'TerminalID' => options[:terminal_id]
170
170
  )
171
- url = (test? ? self.test_url : self.live_url) + "/cgi-bin/#{action}"
172
- xml = ssl_post(url, post_data(parameters))
171
+ url = (test? ? self.test_url : self.live_url) + "/tpvweb/#{action}.action"
172
+ xml = ssl_post("#{url}?", post_data(parameters))
173
173
  response = parse(xml)
174
174
  Response.new(
175
175
  response[:success],
@@ -242,7 +242,7 @@ module ActiveMerchant #:nodoc:
242
242
  CECA_NOTIFICATIONS_URL +
243
243
  CECA_NOTIFICATIONS_URL
244
244
  end
245
- Digest::SHA1.hexdigest(signature_fields)
245
+ Digest::SHA2.hexdigest(signature_fields)
246
246
  end
247
247
  end
248
248
  end
@@ -1,22 +1,22 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
3
  class CheckoutV2Gateway < Gateway
4
- self.display_name = 'Checkout.com V2 Gateway'
4
+ self.display_name = 'Checkout.com Unified Payments'
5
5
  self.homepage_url = 'https://www.checkout.com/'
6
- self.live_url = 'https://api2.checkout.com/v2'
7
- self.test_url = 'https://sandbox.checkout.com/api2/v2'
6
+ self.live_url = 'https://api.checkout.com'
7
+ self.test_url = 'https://api.sandbox.checkout.com'
8
8
 
9
9
  self.supported_countries = ['AD', 'AE', 'AT', 'BE', 'BG', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FO', 'FI', 'FR', 'GB', 'GI', 'GL', 'GR', 'HR', 'HU', 'IE', 'IS', 'IL', 'IT', 'LI', 'LT', 'LU', 'LV', 'MC', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SE', 'SI', 'SM', 'SK', 'SJ', 'TR', 'VA']
10
10
  self.default_currency = 'USD'
11
11
  self.money_format = :cents
12
- self.supported_cardtypes = [:visa, :master, :american_express, :diners_club]
12
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :maestro, :discover]
13
13
 
14
- def initialize(options={})
14
+ def initialize(options = {})
15
15
  requires!(options, :secret_key)
16
16
  super
17
17
  end
18
18
 
19
- def purchase(amount, payment_method, options={})
19
+ def purchase(amount, payment_method, options = {})
20
20
  multi = MultiResponse.run do |r|
21
21
  r.process { authorize(amount, payment_method, options) }
22
22
  r.process { capture(amount, r.authorization, options) }
@@ -28,18 +28,19 @@ module ActiveMerchant #:nodoc:
28
28
  response(:purchase, succeeded, merged_params)
29
29
  end
30
30
 
31
- def authorize(amount, payment_method, options={})
31
+ def authorize(amount, payment_method, options = {})
32
32
  post = {}
33
- post[:autoCapture] = 'n'
33
+ post[:capture] = false
34
34
  add_invoice(post, amount, options)
35
- add_payment_method(post, payment_method)
35
+ add_payment_method(post, payment_method, options)
36
36
  add_customer_data(post, options)
37
37
  add_transaction_data(post, options)
38
+ add_3ds(post, options)
38
39
 
39
40
  commit(:authorize, post)
40
41
  end
41
42
 
42
- def capture(amount, authorization, options={})
43
+ def capture(amount, authorization, options = {})
43
44
  post = {}
44
45
  add_invoice(post, amount, options)
45
46
  add_customer_data(post, options)
@@ -47,12 +48,12 @@ module ActiveMerchant #:nodoc:
47
48
  commit(:capture, post, authorization)
48
49
  end
49
50
 
50
- def void(authorization, options={})
51
+ def void(authorization, _options = {})
51
52
  post = {}
52
53
  commit(:void, post, authorization)
53
54
  end
54
55
 
55
- def refund(amount, authorization, options={})
56
+ def refund(amount, authorization, options = {})
56
57
  post = {}
57
58
  add_invoice(post, amount, options)
58
59
  add_customer_data(post, options)
@@ -60,72 +61,102 @@ module ActiveMerchant #:nodoc:
60
61
  commit(:refund, post, authorization)
61
62
  end
62
63
 
63
- def verify(credit_card, options={})
64
+ def verify(credit_card, options = {})
64
65
  MultiResponse.run(:use_first_response) do |r|
65
66
  r.process { authorize(100, credit_card, options) }
66
67
  r.process(:ignore_result) { void(r.authorization, options) }
67
68
  end
68
69
  end
69
70
 
71
+ def verify_payment(authorization, option={})
72
+ commit(:verify_payment, authorization)
73
+ end
74
+
70
75
  def supports_scrubbing?
71
76
  true
72
77
  end
73
78
 
74
79
  def scrub(transcript)
75
80
  transcript.
76
- gsub(%r((Authorization: )[^\\]*)i, '\1[FILTERED]').
77
- gsub(%r(("number\\":\\")\d+), '\1[FILTERED]').
78
- gsub(%r(("cvv\\":\\")\d+), '\1[FILTERED]')
81
+ gsub(/(Authorization: )[^\\]*/i, '\1[FILTERED]').
82
+ gsub(/("number\\":\\")\d+/, '\1[FILTERED]').
83
+ gsub(/("cvv\\":\\")\d+/, '\1[FILTERED]')
79
84
  end
80
85
 
81
86
  private
82
87
 
83
88
  def add_invoice(post, money, options)
84
- post[:value] = localized_amount(money, options[:currency])
85
- post[:trackId] = options[:order_id]
89
+ post[:amount] = localized_amount(money, options[:currency])
90
+ post[:reference] = options[:order_id]
86
91
  post[:currency] = options[:currency] || currency(money)
87
- post[:descriptor] = {}
88
- post[:descriptor][:name] = options[:descriptor_name] if options[:descriptor_name]
89
- post[:descriptor][:city] = options[:descriptor_city] if options[:descriptor_city]
92
+ if options[:descriptor_name] || options[:descriptor_city]
93
+ post[:billing_descriptor] = {}
94
+ post[:billing_descriptor][:name] = options[:descriptor_name] if options[:descriptor_name]
95
+ post[:billing_descriptor][:city] = options[:descriptor_city] if options[:descriptor_city]
96
+ end
97
+ post[:metadata] = {}
98
+ post[:metadata][:udf5] = application_id || 'ActiveMerchant'
90
99
  end
91
100
 
92
- def add_payment_method(post, payment_method)
93
- post[:card] = {}
94
- post[:card][:name] = payment_method.name
95
- post[:card][:number] = payment_method.number
96
- post[:card][:cvv] = payment_method.verification_value
97
- post[:card][:expiryYear] = format(payment_method.year, :four_digits)
98
- post[:card][:expiryMonth] = format(payment_method.month, :two_digits)
101
+ def add_payment_method(post, payment_method, options)
102
+ post[:source] = {}
103
+ post[:source][:type] = 'card'
104
+ post[:source][:name] = payment_method.name
105
+ post[:source][:number] = payment_method.number
106
+ post[:source][:cvv] = payment_method.verification_value
107
+ post[:source][:expiry_year] = format(payment_method.year, :four_digits)
108
+ post[:source][:expiry_month] = format(payment_method.month, :two_digits)
109
+ post[:source][:stored] = 'true' if options[:card_on_file] == true
99
110
  end
100
111
 
101
112
  def add_customer_data(post, options)
102
- post[:email] = options[:email] || 'unspecified@example.com'
103
- post[:customerIp] = options[:ip] if options[:ip]
113
+ post[:customer] = {}
114
+ post[:customer][:email] = options[:email] || nil
115
+ post[:payment_ip] = options[:ip] if options[:ip]
104
116
  address = options[:billing_address]
105
- if(address && post[:card])
106
- post[:card][:billingDetails] = {}
107
- post[:card][:billingDetails][:addressLine1] = address[:address1]
108
- post[:card][:billingDetails][:addressLine2] = address[:address2]
109
- post[:card][:billingDetails][:city] = address[:city]
110
- post[:card][:billingDetails][:state] = address[:state]
111
- post[:card][:billingDetails][:country] = address[:country]
112
- post[:card][:billingDetails][:postcode] = address[:zip]
113
- post[:card][:billingDetails][:phone] = { number: address[:phone] } unless address[:phone].blank?
117
+ if address && post[:source]
118
+ post[:source][:billing_address] = {}
119
+ post[:source][:billing_address][:address_line1] = address[:address1] unless address[:address1].blank?
120
+ post[:source][:billing_address][:address_line2] = address[:address2] unless address[:address2].blank?
121
+ post[:source][:billing_address][:city] = address[:city] unless address[:city].blank?
122
+ post[:source][:billing_address][:state] = address[:state] unless address[:state].blank?
123
+ post[:source][:billing_address][:country] = address[:country] unless address[:country].blank?
124
+ post[:source][:billing_address][:zip] = address[:zip] unless address[:zip].blank?
114
125
  end
115
126
  end
116
127
 
117
- def add_transaction_data(post, options={})
118
- post[:cardOnFile] = true if options[:card_on_file] == true
119
- post[:transactionIndicator] = options[:transaction_indicator] || 1
120
- post[:previousChargeId] = options[:previous_charge_id] if options[:previous_charge_id]
128
+ def add_transaction_data(post, options = {})
129
+ post[:payment_type] = 'Regular' if options[:transaction_indicator] == 1
130
+ post[:payment_type] = 'Recurring' if options[:transaction_indicator] == 2
131
+ post[:payment_type] = 'MOTO' if options[:transaction_indicator] == 3 || options.dig(:metadata, :manual_entry)
132
+ post[:previous_payment_id] = options[:previous_charge_id] if options[:previous_charge_id]
133
+ end
134
+
135
+ def add_3ds(post, options)
136
+ if options[:three_d_secure] || options[:execute_threed]
137
+ post[:'3ds'] = {}
138
+ post[:'3ds'][:enabled] = true
139
+ post[:success_url] = options[:callback_url] if options[:callback_url]
140
+ post[:failure_url] = options[:callback_url] if options[:callback_url]
141
+ end
142
+
143
+ if options[:three_d_secure]
144
+ post[:'3ds'][:eci] = options[:three_d_secure][:eci] if options[:three_d_secure][:eci]
145
+ post[:'3ds'][:cryptogram] = options[:three_d_secure][:cavv] if options[:three_d_secure][:cavv]
146
+ post[:'3ds'][:version] = options[:three_d_secure][:version] if options[:three_d_secure][:version]
147
+ post[:'3ds'][:xid] = options[:three_d_secure][:ds_transaction_id] || options[:three_d_secure][:xid]
148
+ end
121
149
  end
122
150
 
123
151
  def commit(action, post, authorization = nil)
124
152
  begin
125
- raw_response = ssl_post(url(post, action, authorization), post.to_json, headers)
153
+ raw_response = (action == :verify_payment ? ssl_get("#{base_url}/payments/#{post}", headers) : ssl_post(url(post, action, authorization), post.to_json, headers))
126
154
  response = parse(raw_response)
155
+ if action == :capture && response.key?('_links')
156
+ response['id'] = response['_links']['payment']['href'].split('/')[-1]
157
+ end
127
158
  rescue ResponseError => e
128
- raise unless(e.response.code.to_s =~ /4\d\d/)
159
+ raise unless e.response.code.to_s =~ /4\d\d/
129
160
  response = parse(e.response.body)
130
161
  end
131
162
 
@@ -154,15 +185,21 @@ module ActiveMerchant #:nodoc:
154
185
  def headers
155
186
  {
156
187
  'Authorization' => @options[:secret_key],
157
- 'Content-Type' => 'application/json;charset=UTF-8'
188
+ 'Content-Type' => 'application/json;charset=UTF-8',
158
189
  }
159
190
  end
160
191
 
161
- def url(post, action, authorization)
192
+ def url(_post, action, authorization)
162
193
  if action == :authorize
163
- "#{base_url}/charges/card"
194
+ "#{base_url}/payments"
195
+ elsif action == :capture
196
+ "#{base_url}/payments/#{authorization}/captures"
197
+ elsif action == :refund
198
+ "#{base_url}/payments/#{authorization}/refunds"
199
+ elsif action == :void
200
+ "#{base_url}/payments/#{authorization}/voids"
164
201
  else
165
- "#{base_url}/charges/#{authorization}/#{action}"
202
+ "#{base_url}/payments/#{authorization}/#{action}"
166
203
  end
167
204
  end
168
205
 
@@ -171,33 +208,33 @@ module ActiveMerchant #:nodoc:
171
208
  end
172
209
 
173
210
  def avs_result(response)
174
- response['card'] && response['card']['avsCheck'] ? AVSResult.new(code: response['card']['avsCheck']) : nil
211
+ response['source'] && response['source']['avs_check'] ? AVSResult.new(code: response['source']['avs_check']) : nil
175
212
  end
176
213
 
177
214
  def cvv_result(response)
178
- response['card'] && response['card']['cvvCheck'] ? CVVResult.new(response['card']['cvvCheck']) : nil
215
+ response['source'] && response['source']['cvv_check'] ? CVVResult.new(response['source']['cvv_check']) : nil
179
216
  end
180
217
 
181
218
  def parse(body)
182
219
  JSON.parse(body)
183
220
  rescue JSON::ParserError
184
221
  {
185
- 'message' => 'Invalid JSON response received from CheckoutV2Gateway. Please contact CheckoutV2Gateway if you continue to receive this message.',
222
+ 'message' => 'Invalid JSON response received from Checkout.com Unified Payments Gateway. Please contact Checkout.com if you continue to receive this message.',
186
223
  'raw_response' => scrub(body)
187
224
  }
188
225
  end
189
226
 
190
227
  def success_from(response)
191
- (response['responseCode'] == '10000' && !response['responseMessage'].start_with?('40')) || response['responseCode'] == '10100'
228
+ response['response_summary'] == 'Approved' || response['approved'] == true || !response.key?('response_summary') && response.key?('action_id')
192
229
  end
193
230
 
194
231
  def message_from(succeeded, response)
195
232
  if succeeded
196
233
  'Succeeded'
197
- elsif response['errors']
198
- response['message'] + ': ' + response['errors'].first
234
+ elsif response['error_type']
235
+ response['error_type'] + ': ' + response['error_codes'].first
199
236
  else
200
- response['responseMessage'] || response['message'] || 'Unable to read error message'
237
+ response['response_summary'] || response['response_code'] || 'Unable to read error message'
201
238
  end
202
239
  end
203
240
 
@@ -220,12 +257,12 @@ module ActiveMerchant #:nodoc:
220
257
 
221
258
  def error_code_from(succeeded, response)
222
259
  return if succeeded
223
- if response['errorCode'] && response['errorMessageCodes']
224
- "#{response["errorCode"]}: #{response["errorMessageCodes"].join(", ")}"
225
- elsif response['errorCode']
226
- response['errorCode']
260
+ if response['error_type'] && response['error_codes']
261
+ "#{response['error_type']}: #{response['error_codes'].join(', ')}"
262
+ elsif response['error_type']
263
+ response['error_type']
227
264
  else
228
- STANDARD_ERROR_CODE_MAPPING[response['responseCode']]
265
+ STANDARD_ERROR_CODE_MAPPING[response['response_code']]
229
266
  end
230
267
  end
231
268
  end