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.
- checksums.yaml +4 -4
- data/CHANGELOG +111 -0
- data/README.md +3 -0
- data/lib/active_merchant/billing/avs_result.rb +4 -5
- data/lib/active_merchant/billing/credit_card.rb +6 -0
- data/lib/active_merchant/billing/credit_card_methods.rb +67 -4
- data/lib/active_merchant/billing/gateway.rb +10 -0
- data/lib/active_merchant/billing/gateways/adyen.rb +106 -22
- data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +43 -10
- data/lib/active_merchant/billing/gateways/beanstream.rb +2 -0
- data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +3 -0
- data/lib/active_merchant/billing/gateways/blue_snap.rb +22 -2
- data/lib/active_merchant/billing/gateways/bpoint.rb +4 -4
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +56 -9
- data/lib/active_merchant/billing/gateways/card_connect.rb +3 -1
- data/lib/active_merchant/billing/gateways/cecabank.rb +7 -7
- data/lib/active_merchant/billing/gateways/checkout_v2.rb +98 -61
- data/lib/active_merchant/billing/gateways/credorax.rb +29 -3
- data/lib/active_merchant/billing/gateways/cyber_source.rb +30 -13
- data/lib/active_merchant/billing/gateways/d_local.rb +1 -1
- data/lib/active_merchant/billing/gateways/decidir.rb +233 -0
- data/lib/active_merchant/billing/gateways/elavon.rb +9 -0
- data/lib/active_merchant/billing/gateways/epay.rb +13 -2
- data/lib/active_merchant/billing/gateways/eway_rapid.rb +42 -12
- data/lib/active_merchant/billing/gateways/fat_zebra.rb +6 -0
- data/lib/active_merchant/billing/gateways/global_collect.rb +3 -7
- data/lib/active_merchant/billing/gateways/hps.rb +46 -1
- data/lib/active_merchant/billing/gateways/kushki.rb +1 -1
- data/lib/active_merchant/billing/gateways/mercado_pago.rb +1 -1
- data/lib/active_merchant/billing/gateways/migs.rb +8 -0
- data/lib/active_merchant/billing/gateways/monei.rb +31 -0
- data/lib/active_merchant/billing/gateways/mundipagg.rb +3 -2
- data/lib/active_merchant/billing/gateways/nab_transact.rb +1 -1
- data/lib/active_merchant/billing/gateways/nmi.rb +39 -1
- data/lib/active_merchant/billing/gateways/opp.rb +20 -1
- data/lib/active_merchant/billing/gateways/orbital.rb +60 -10
- data/lib/active_merchant/billing/gateways/payflow.rb +40 -2
- data/lib/active_merchant/billing/gateways/paymill.rb +5 -0
- data/lib/active_merchant/billing/gateways/paypal.rb +14 -1
- data/lib/active_merchant/billing/gateways/payu_latam.rb +6 -2
- data/lib/active_merchant/billing/gateways/qvalent.rb +43 -1
- data/lib/active_merchant/billing/gateways/realex.rb +32 -9
- data/lib/active_merchant/billing/gateways/spreedly_core.rb +43 -29
- data/lib/active_merchant/billing/gateways/stripe.rb +54 -9
- data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +267 -0
- data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +2 -2
- data/lib/active_merchant/billing/gateways/trust_commerce.rb +45 -6
- data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +8 -5
- data/lib/active_merchant/billing/gateways/worldpay.rb +171 -39
- data/lib/active_merchant/country.rb +1 -0
- data/lib/active_merchant/version.rb +1 -1
- 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 = '
|
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
|
-
|
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
|
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
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
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
|
-
|
347
|
-
|
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
|
-
|
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
|
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 = '
|
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 = '
|
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 = '
|
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) + "/
|
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::
|
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
|
4
|
+
self.display_name = 'Checkout.com Unified Payments'
|
5
5
|
self.homepage_url = 'https://www.checkout.com/'
|
6
|
-
self.live_url = 'https://
|
7
|
-
self.test_url = 'https://sandbox.checkout.com
|
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[:
|
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,
|
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(
|
77
|
-
gsub(
|
78
|
-
gsub(
|
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[:
|
85
|
-
post[:
|
89
|
+
post[:amount] = localized_amount(money, options[:currency])
|
90
|
+
post[:reference] = options[:order_id]
|
86
91
|
post[:currency] = options[:currency] || currency(money)
|
87
|
-
|
88
|
-
|
89
|
-
|
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[:
|
94
|
-
post[:
|
95
|
-
post[:
|
96
|
-
post[:
|
97
|
-
post[:
|
98
|
-
post[:
|
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[:
|
103
|
-
post[:
|
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
|
106
|
-
post[:
|
107
|
-
post[:
|
108
|
-
post[:
|
109
|
-
post[:
|
110
|
-
post[:
|
111
|
-
post[:
|
112
|
-
post[:
|
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[:
|
119
|
-
post[:
|
120
|
-
post[:
|
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
|
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'
|
188
|
+
'Content-Type' => 'application/json;charset=UTF-8',
|
158
189
|
}
|
159
190
|
end
|
160
191
|
|
161
|
-
def url(
|
192
|
+
def url(_post, action, authorization)
|
162
193
|
if action == :authorize
|
163
|
-
"#{base_url}/
|
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}/
|
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['
|
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['
|
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
|
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
|
-
|
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['
|
198
|
-
response['
|
234
|
+
elsif response['error_type']
|
235
|
+
response['error_type'] + ': ' + response['error_codes'].first
|
199
236
|
else
|
200
|
-
response['
|
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['
|
224
|
-
"#{response[
|
225
|
-
elsif response['
|
226
|
-
response['
|
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['
|
265
|
+
STANDARD_ERROR_CODE_MAPPING[response['response_code']]
|
229
266
|
end
|
230
267
|
end
|
231
268
|
end
|