activemerchant 1.93.0 → 1.98.0

Sign up to get free protection for your applications and to get access to all the features.
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