activemerchant 1.117.0 → 1.123.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +217 -0
- data/README.md +5 -3
- data/lib/active_merchant/billing/check.rb +19 -12
- data/lib/active_merchant/billing/credit_card.rb +6 -0
- data/lib/active_merchant/billing/credit_card_formatting.rb +1 -0
- data/lib/active_merchant/billing/credit_card_methods.rb +96 -22
- data/lib/active_merchant/billing/gateways/adyen.rb +38 -21
- data/lib/active_merchant/billing/gateways/authorize_net.rb +19 -11
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +4 -0
- data/lib/active_merchant/billing/gateways/blue_pay.rb +29 -0
- data/lib/active_merchant/billing/gateways/blue_snap.rb +5 -3
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +58 -8
- data/lib/active_merchant/billing/gateways/cashnet.rb +7 -2
- data/lib/active_merchant/billing/gateways/checkout_v2.rb +31 -0
- data/lib/active_merchant/billing/gateways/credorax.rb +16 -9
- data/lib/active_merchant/billing/gateways/cyber_source.rb +67 -9
- data/lib/active_merchant/billing/gateways/d_local.rb +1 -1
- data/lib/active_merchant/billing/gateways/decidir.rb +29 -3
- data/lib/active_merchant/billing/gateways/elavon.rb +110 -26
- data/lib/active_merchant/billing/gateways/element.rb +2 -0
- data/lib/active_merchant/billing/gateways/eway_rapid.rb +13 -0
- data/lib/active_merchant/billing/gateways/firstdata_e4_v27.rb +17 -6
- data/lib/active_merchant/billing/gateways/forte.rb +12 -0
- data/lib/active_merchant/billing/gateways/global_collect.rb +25 -16
- data/lib/active_merchant/billing/gateways/hps.rb +65 -2
- data/lib/active_merchant/billing/gateways/kushki.rb +23 -0
- data/lib/active_merchant/billing/gateways/litle.rb +9 -4
- data/lib/active_merchant/billing/gateways/mercado_pago.rb +5 -4
- data/lib/active_merchant/billing/gateways/merchant_warrior.rb +2 -0
- data/lib/active_merchant/billing/gateways/moka.rb +277 -0
- data/lib/active_merchant/billing/gateways/monei.rb +228 -144
- data/lib/active_merchant/billing/gateways/mundipagg.rb +14 -5
- data/lib/active_merchant/billing/gateways/netbanx.rb +37 -2
- data/lib/active_merchant/billing/gateways/nmi.rb +14 -9
- data/lib/active_merchant/billing/gateways/orbital.rb +202 -47
- data/lib/active_merchant/billing/gateways/pay_arc.rb +390 -0
- data/lib/active_merchant/billing/gateways/pay_trace.rb +404 -0
- data/lib/active_merchant/billing/gateways/payeezy.rb +57 -11
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -0
- data/lib/active_merchant/billing/gateways/payflow.rb +9 -0
- data/lib/active_merchant/billing/gateways/payment_express.rb +10 -5
- data/lib/active_merchant/billing/gateways/paymentez.rb +26 -1
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +1 -0
- data/lib/active_merchant/billing/gateways/paypal.rb +10 -2
- data/lib/active_merchant/billing/gateways/paypal_express.rb +1 -0
- data/lib/active_merchant/billing/gateways/paysafe.rb +291 -0
- data/lib/active_merchant/billing/gateways/payu_latam.rb +3 -3
- data/lib/active_merchant/billing/gateways/payway_dot_com.rb +253 -0
- data/lib/active_merchant/billing/gateways/pin.rb +11 -0
- data/lib/active_merchant/billing/gateways/qvalent.rb +23 -9
- data/lib/active_merchant/billing/gateways/redsys.rb +78 -30
- data/lib/active_merchant/billing/gateways/safe_charge.rb +19 -8
- data/lib/active_merchant/billing/gateways/spreedly_core.rb +13 -4
- data/lib/active_merchant/billing/gateways/stripe.rb +8 -8
- data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +86 -25
- data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +1 -1
- data/lib/active_merchant/billing/gateways/vpos.rb +220 -0
- data/lib/active_merchant/billing/gateways/worldpay.rb +68 -20
- data/lib/active_merchant/billing/response.rb +2 -1
- data/lib/active_merchant/billing/three_d_secure_eci_mapper.rb +27 -0
- data/lib/active_merchant/billing.rb +1 -0
- data/lib/active_merchant/version.rb +1 -1
- data/lib/certs/cacert.pem +1582 -2431
- metadata +10 -3
@@ -25,8 +25,8 @@ module ActiveMerchant #:nodoc:
|
|
25
25
|
self.live_url = 'https://ics2wsa.ic3.com/commerce/1.x/transactionProcessor'
|
26
26
|
|
27
27
|
# Schema files can be found here: https://ics2ws.ic3.com/commerce/1.x/transactionProcessor/
|
28
|
-
TEST_XSD_VERSION = '1.
|
29
|
-
PRODUCTION_XSD_VERSION = '1.
|
28
|
+
TEST_XSD_VERSION = '1.181'
|
29
|
+
PRODUCTION_XSD_VERSION = '1.181'
|
30
30
|
ECI_BRAND_MAPPING = {
|
31
31
|
visa: 'vbv',
|
32
32
|
master: 'spa',
|
@@ -153,6 +153,10 @@ module ActiveMerchant #:nodoc:
|
|
153
153
|
commit(build_refund_request(money, identification, options), :refund, money, options)
|
154
154
|
end
|
155
155
|
|
156
|
+
def adjust(money, authorization, options = {})
|
157
|
+
commit(build_adjust_request(money, authorization, options), :adjust, money, options)
|
158
|
+
end
|
159
|
+
|
156
160
|
def verify(payment, options = {})
|
157
161
|
MultiResponse.run(:use_first_response) do |r|
|
158
162
|
r.process { authorize(100, payment, options) }
|
@@ -256,8 +260,9 @@ module ActiveMerchant #:nodoc:
|
|
256
260
|
|
257
261
|
private
|
258
262
|
|
259
|
-
# Create all address hash key value pairs
|
260
|
-
#
|
263
|
+
# Create all required address hash key value pairs
|
264
|
+
# If a value of nil is received, that value will be passed on to the gateway and will not be replaced with a default value
|
265
|
+
# Billing address fields received without an override value or with an empty string value will be replaced with the default_address values
|
261
266
|
def setup_address_hash(options)
|
262
267
|
default_address = {
|
263
268
|
address1: 'Unspecified',
|
@@ -268,12 +273,23 @@ module ActiveMerchant #:nodoc:
|
|
268
273
|
}
|
269
274
|
|
270
275
|
submitted_address = options[:billing_address] || options[:address] || default_address
|
271
|
-
options[:billing_address] = default_address.merge(submitted_address.symbolize_keys) { |_k, default, submitted|
|
276
|
+
options[:billing_address] = default_address.merge(submitted_address.symbolize_keys) { |_k, default, submitted| check_billing_field_value(default, submitted) }
|
272
277
|
options[:shipping_address] = options[:shipping_address] || {}
|
273
278
|
end
|
274
279
|
|
280
|
+
def check_billing_field_value(default, submitted)
|
281
|
+
if submitted.nil?
|
282
|
+
nil
|
283
|
+
elsif submitted.blank?
|
284
|
+
default
|
285
|
+
else
|
286
|
+
submitted
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
275
290
|
def build_auth_request(money, creditcard_or_reference, options)
|
276
291
|
xml = Builder::XmlMarkup.new indent: 2
|
292
|
+
add_customer_id(xml, options)
|
277
293
|
add_payment_method_or_subscription(xml, money, creditcard_or_reference, options)
|
278
294
|
add_threeds_2_ucaf_data(xml, creditcard_or_reference, options)
|
279
295
|
add_decision_manager_fields(xml, options)
|
@@ -287,7 +303,17 @@ module ActiveMerchant #:nodoc:
|
|
287
303
|
add_partner_solution_id(xml)
|
288
304
|
add_stored_credential_options(xml, options)
|
289
305
|
add_merchant_description(xml, options)
|
306
|
+
add_sales_slip_number(xml, options)
|
307
|
+
add_airline_data(xml, options)
|
308
|
+
xml.target!
|
309
|
+
end
|
310
|
+
|
311
|
+
def build_adjust_request(money, authorization, options)
|
312
|
+
_, request_id = authorization.split(';')
|
290
313
|
|
314
|
+
xml = Builder::XmlMarkup.new indent: 2
|
315
|
+
add_purchase_data(xml, money, true, options)
|
316
|
+
add_incremental_auth_service(xml, request_id, options)
|
291
317
|
xml.target!
|
292
318
|
end
|
293
319
|
|
@@ -321,10 +347,13 @@ module ActiveMerchant #:nodoc:
|
|
321
347
|
|
322
348
|
def build_purchase_request(money, payment_method_or_reference, options)
|
323
349
|
xml = Builder::XmlMarkup.new indent: 2
|
350
|
+
add_customer_id(xml, options)
|
324
351
|
add_payment_method_or_subscription(xml, money, payment_method_or_reference, options)
|
325
352
|
add_threeds_2_ucaf_data(xml, payment_method_or_reference, options)
|
326
353
|
add_decision_manager_fields(xml, options)
|
327
354
|
add_mdd_fields(xml, options)
|
355
|
+
add_sales_slip_number(xml, options)
|
356
|
+
add_airline_data(xml, options)
|
328
357
|
if !payment_method_or_reference.is_a?(String) && card_brand(payment_method_or_reference) == 'check'
|
329
358
|
add_check_service(xml)
|
330
359
|
add_issuer_additional_data(xml, options)
|
@@ -457,8 +486,8 @@ module ActiveMerchant #:nodoc:
|
|
457
486
|
|
458
487
|
unless network_tokenization?(payment_method)
|
459
488
|
xml.tag! 'businessRules' do
|
460
|
-
xml.tag!('ignoreAVSResult', 'true') if extract_option(prioritized_options, :ignore_avs)
|
461
|
-
xml.tag!('ignoreCVResult', 'true') if extract_option(prioritized_options, :ignore_cvv)
|
489
|
+
xml.tag!('ignoreAVSResult', 'true') if extract_option(prioritized_options, :ignore_avs).to_s == 'true'
|
490
|
+
xml.tag!('ignoreCVResult', 'true') if extract_option(prioritized_options, :ignore_cvv).to_s == 'true'
|
462
491
|
end
|
463
492
|
end
|
464
493
|
end
|
@@ -500,6 +529,12 @@ module ActiveMerchant #:nodoc:
|
|
500
529
|
end
|
501
530
|
end
|
502
531
|
|
532
|
+
def add_customer_id(xml, options)
|
533
|
+
return unless options[:customer_id]
|
534
|
+
|
535
|
+
xml.tag! 'customerID', options[:customer_id]
|
536
|
+
end
|
537
|
+
|
503
538
|
def add_merchant_description(xml, options)
|
504
539
|
return unless options[:merchant_descriptor_name] || options[:merchant_descriptor_address1] || options[:merchant_descriptor_locality]
|
505
540
|
|
@@ -512,6 +547,18 @@ module ActiveMerchant #:nodoc:
|
|
512
547
|
end
|
513
548
|
end
|
514
549
|
|
550
|
+
def add_sales_slip_number(xml, options)
|
551
|
+
xml.tag! 'salesSlipNumber', options[:sales_slip_number] if options[:sales_slip_number]
|
552
|
+
end
|
553
|
+
|
554
|
+
def add_airline_data(xml, options)
|
555
|
+
return unless options[:airline_agent_code]
|
556
|
+
|
557
|
+
xml.tag! 'airlineData' do
|
558
|
+
xml.tag! 'agentCode', options[:airline_agent_code]
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
515
562
|
def add_purchase_data(xml, money = 0, include_grand_total = false, options = {})
|
516
563
|
xml.tag! 'purchaseTotals' do
|
517
564
|
xml.tag! 'currency', options[:currency] || currency(money)
|
@@ -521,6 +568,7 @@ module ActiveMerchant #:nodoc:
|
|
521
568
|
|
522
569
|
def add_address(xml, payment_method, address, options, shipTo = false)
|
523
570
|
first_name, last_name = address_names(address[:name], payment_method)
|
571
|
+
bill_to_merchant_tax_id = options[:merchant_tax_id] unless shipTo
|
524
572
|
|
525
573
|
xml.tag! shipTo ? 'shipTo' : 'billTo' do
|
526
574
|
xml.tag! 'firstName', first_name if first_name
|
@@ -538,6 +586,7 @@ module ActiveMerchant #:nodoc:
|
|
538
586
|
xml.tag! 'ipAddress', options[:ip] unless options[:ip].blank? || shipTo
|
539
587
|
xml.tag! 'driversLicenseNumber', options[:drivers_license_number] unless options[:drivers_license_number].blank?
|
540
588
|
xml.tag! 'driversLicenseState', options[:drivers_license_state] unless options[:drivers_license_state].blank?
|
589
|
+
xml.tag! 'merchantTaxID', bill_to_merchant_tax_id unless bill_to_merchant_tax_id.blank?
|
541
590
|
end
|
542
591
|
end
|
543
592
|
|
@@ -556,7 +605,7 @@ module ActiveMerchant #:nodoc:
|
|
556
605
|
xml.tag! 'accountNumber', creditcard.number
|
557
606
|
xml.tag! 'expirationMonth', format(creditcard.month, :two_digits)
|
558
607
|
xml.tag! 'expirationYear', format(creditcard.year, :four_digits)
|
559
|
-
xml.tag!('cvNumber', creditcard.verification_value) unless @options[:ignore_cvv] || creditcard.verification_value.blank?
|
608
|
+
xml.tag!('cvNumber', creditcard.verification_value) unless @options[:ignore_cvv].to_s == 'true' || creditcard.verification_value.blank?
|
560
609
|
xml.tag! 'cardType', @@credit_card_codes[card_brand(creditcard).to_sym]
|
561
610
|
end
|
562
611
|
end
|
@@ -593,7 +642,7 @@ module ActiveMerchant #:nodoc:
|
|
593
642
|
xml.tag! 'merchantDefinedData' do
|
594
643
|
(1..100).each do |each|
|
595
644
|
key = "mdd_field_#{each}".to_sym
|
596
|
-
xml.tag!(
|
645
|
+
xml.tag!('mddField', options[key], 'id' => each) if options[key]
|
597
646
|
end
|
598
647
|
end
|
599
648
|
end
|
@@ -629,6 +678,13 @@ module ActiveMerchant #:nodoc:
|
|
629
678
|
end
|
630
679
|
end
|
631
680
|
|
681
|
+
def add_incremental_auth_service(xml, authorization, options)
|
682
|
+
xml.tag! 'ccIncrementalAuthService', { 'run' => 'true' } do
|
683
|
+
xml.tag! 'authRequestID', authorization
|
684
|
+
end
|
685
|
+
xml.tag! 'subsequentAuthReason', options[:auth_reason]
|
686
|
+
end
|
687
|
+
|
632
688
|
def add_normalized_threeds_2_data(xml, payment_method, options)
|
633
689
|
threeds_2_options = options[:three_d_secure]
|
634
690
|
cc_brand = card_brand(payment_method).to_sym
|
@@ -842,6 +898,8 @@ module ActiveMerchant #:nodoc:
|
|
842
898
|
|
843
899
|
xml.tag! 'installment' do
|
844
900
|
xml.tag! 'totalCount', options[:installment_total_count]
|
901
|
+
xml.tag!('planType', options[:installment_plan_type]) if options[:installment_plan_type]
|
902
|
+
xml.tag!('firstInstallmentDate', options[:first_installment_date]) if options[:first_installment_date]
|
845
903
|
end
|
846
904
|
end
|
847
905
|
|
@@ -6,7 +6,7 @@ module ActiveMerchant #:nodoc:
|
|
6
6
|
|
7
7
|
self.supported_countries = %w[AR BR CL CO MX PE UY TR]
|
8
8
|
self.default_currency = 'USD'
|
9
|
-
self.supported_cardtypes = %i[visa master american_express discover jcb diners_club maestro naranja cabal]
|
9
|
+
self.supported_cardtypes = %i[visa master american_express discover jcb diners_club maestro naranja cabal elo alia carnet]
|
10
10
|
|
11
11
|
self.homepage_url = 'https://dlocal.com/'
|
12
12
|
self.display_name = 'dLocal'
|
@@ -170,6 +170,12 @@ module ActiveMerchant #:nodoc:
|
|
170
170
|
card_data[:security_code] = credit_card.verification_value if credit_card.verification_value?
|
171
171
|
card_data[:card_holder_name] = credit_card.name if credit_card.name
|
172
172
|
|
173
|
+
# the device_unique_id has to be sent in via the card data (as device_unique_identifier) no other fraud detection fields require this
|
174
|
+
if options[:fraud_detection].present?
|
175
|
+
card_data[:fraud_detection] = {} if (options[:fraud_detection][:device_unique_id]).present?
|
176
|
+
card_data[:fraud_detection][:device_unique_identifier] = (options[:fraud_detection][:device_unique_id]) if (options[:fraud_detection][:device_unique_id]).present?
|
177
|
+
end
|
178
|
+
|
173
179
|
# additional data used for Visa transactions
|
174
180
|
card_data[:card_holder_door_number] = options[:card_holder_door_number].to_i if options[:card_holder_door_number]
|
175
181
|
card_data[:card_holder_birthday] = options[:card_holder_birthday] if options[:card_holder_birthday]
|
@@ -210,6 +216,14 @@ module ActiveMerchant #:nodoc:
|
|
210
216
|
hsh[:channel] = options[:channel] if valid_fraud_detection_option?(options[:channel])
|
211
217
|
hsh[:dispatch_method] = options[:dispatch_method] if valid_fraud_detection_option?(options[:dispatch_method])
|
212
218
|
hsh[:csmdds] = options[:csmdds] if valid_fraud_detection_option?(options[:csmdds])
|
219
|
+
hsh[:device_unique_id] = options[:device_unique_id] if valid_fraud_detection_option?(options[:device_unique_id])
|
220
|
+
hsh[:bill_to] = options[:bill_to] if valid_fraud_detection_option?(options[:bill_to])
|
221
|
+
hsh[:purchase_totals] = options[:purchase_totals] if valid_fraud_detection_option?(options[:purchase_totals])
|
222
|
+
hsh[:customer_in_site] = options[:customer_in_site] if valid_fraud_detection_option?(options[:customer_in_site])
|
223
|
+
hsh[:retail_transaction_data] = options[:retail_transaction_data] if valid_fraud_detection_option?(options[:retail_transaction_data])
|
224
|
+
hsh[:ship_to] = options[:ship_to] if valid_fraud_detection_option?(options[:ship_to])
|
225
|
+
hsh[:tax_voucher_required] = options[:tax_voucher_required] if valid_fraud_detection_option?(options[:tax_voucher_required])
|
226
|
+
hsh[:copy_paste_card_data] = options[:copy_paste_card_data] if valid_fraud_detection_option?(options[:copy_paste_card_data])
|
213
227
|
end
|
214
228
|
end
|
215
229
|
|
@@ -268,7 +282,13 @@ module ActiveMerchant #:nodoc:
|
|
268
282
|
if error = response.dig('status_details', 'error')
|
269
283
|
message = "#{error.dig('reason', 'description')} | #{error['type']}"
|
270
284
|
elsif response['error_type']
|
271
|
-
|
285
|
+
if response['validation_errors'].is_a?(Array)
|
286
|
+
message = response['validation_errors'].map { |errors| "#{errors['code']}: #{errors['param']}" }.join(', ')
|
287
|
+
elsif response['validation_errors'].is_a?(Hash)
|
288
|
+
errors = response['validation_errors'].map { |k, v| "#{k}: #{v}" }.join(', ')
|
289
|
+
message = "#{response['error_type']} - #{errors}"
|
290
|
+
end
|
291
|
+
|
272
292
|
message ||= response['error_type']
|
273
293
|
end
|
274
294
|
|
@@ -287,15 +307,21 @@ module ActiveMerchant #:nodoc:
|
|
287
307
|
error_code = nil
|
288
308
|
if error = response.dig('status_details', 'error')
|
289
309
|
code = error.dig('reason', 'id')
|
290
|
-
|
310
|
+
standard_error_code = STANDARD_ERROR_CODE_MAPPING[code]
|
311
|
+
error_code = "#{code}, #{standard_error_code}"
|
291
312
|
error_code ||= error['type']
|
292
313
|
elsif response['error_type']
|
293
314
|
error_code = response['error_type'] if response['validation_errors']
|
294
|
-
elsif
|
315
|
+
elsif response.dig('error', 'validation_errors')
|
316
|
+
error = response.dig('error')
|
295
317
|
validation_errors = error.dig('validation_errors', 0)
|
296
318
|
code = validation_errors['code'] if validation_errors && validation_errors['code']
|
297
319
|
param = validation_errors['param'] if validation_errors && validation_errors['param']
|
298
320
|
error_code = "#{error['error_type']} | #{code} | #{param}" if error['error_type']
|
321
|
+
elsif error = response.dig('error')
|
322
|
+
code = error.dig('reason', 'id')
|
323
|
+
standard_error_code = STANDARD_ERROR_CODE_MAPPING[code]
|
324
|
+
error_code = "#{code}, #{standard_error_code}"
|
299
325
|
end
|
300
326
|
|
301
327
|
error_code || STANDARD_ERROR_CODE[:processing_error]
|
@@ -39,6 +39,7 @@ module ActiveMerchant #:nodoc:
|
|
39
39
|
|
40
40
|
def purchase(money, payment_method, options = {})
|
41
41
|
request = build_xml_request do |xml|
|
42
|
+
xml.ssl_vendor_id @options[:ssl_vendor_id] || options[:ssl_vendor_id]
|
42
43
|
xml.ssl_transaction_type self.actions[:purchase]
|
43
44
|
xml.ssl_amount amount(money)
|
44
45
|
|
@@ -63,6 +64,7 @@ module ActiveMerchant #:nodoc:
|
|
63
64
|
|
64
65
|
def authorize(money, creditcard, options = {})
|
65
66
|
request = build_xml_request do |xml|
|
67
|
+
xml.ssl_vendor_id @options[:ssl_vendor_id] || options[:ssl_vendor_id]
|
66
68
|
xml.ssl_transaction_type self.actions[:authorize]
|
67
69
|
xml.ssl_amount amount(money)
|
68
70
|
|
@@ -82,6 +84,8 @@ module ActiveMerchant #:nodoc:
|
|
82
84
|
|
83
85
|
def capture(money, authorization, options = {})
|
84
86
|
request = build_xml_request do |xml|
|
87
|
+
xml.ssl_vendor_id @options[:ssl_vendor_id] || options[:ssl_vendor_id]
|
88
|
+
|
85
89
|
if options[:credit_card]
|
86
90
|
xml.ssl_transaction_type self.actions[:capture]
|
87
91
|
xml.ssl_amount amount(money)
|
@@ -107,6 +111,7 @@ module ActiveMerchant #:nodoc:
|
|
107
111
|
|
108
112
|
def refund(money, identification, options = {})
|
109
113
|
request = build_xml_request do |xml|
|
114
|
+
xml.ssl_vendor_id @options[:ssl_vendor_id] || options[:ssl_vendor_id]
|
110
115
|
xml.ssl_transaction_type self.actions[:refund]
|
111
116
|
xml.ssl_amount amount(money)
|
112
117
|
add_txn_id(xml, identification)
|
@@ -117,6 +122,7 @@ module ActiveMerchant #:nodoc:
|
|
117
122
|
|
118
123
|
def void(identification, options = {})
|
119
124
|
request = build_xml_request do |xml|
|
125
|
+
xml.ssl_vendor_id @options[:ssl_vendor_id] || options[:ssl_vendor_id]
|
120
126
|
xml.ssl_transaction_type self.actions[:void]
|
121
127
|
|
122
128
|
add_txn_id(xml, identification)
|
@@ -129,6 +135,7 @@ module ActiveMerchant #:nodoc:
|
|
129
135
|
raise ArgumentError, 'Reference credits are not supported. Please supply the original credit card or use the #refund method.' if creditcard.is_a?(String)
|
130
136
|
|
131
137
|
request = build_xml_request do |xml|
|
138
|
+
xml.ssl_vendor_id @options[:ssl_vendor_id] || options[:ssl_vendor_id]
|
132
139
|
xml.ssl_transaction_type self.actions[:credit]
|
133
140
|
xml.ssl_amount amount(money)
|
134
141
|
add_invoice(xml, options)
|
@@ -143,6 +150,7 @@ module ActiveMerchant #:nodoc:
|
|
143
150
|
|
144
151
|
def verify(credit_card, options = {})
|
145
152
|
request = build_xml_request do |xml|
|
153
|
+
xml.ssl_vendor_id @options[:ssl_vendor_id] || options[:ssl_vendor_id]
|
146
154
|
xml.ssl_transaction_type self.actions[:verify]
|
147
155
|
add_creditcard(xml, credit_card)
|
148
156
|
add_address(xml, options)
|
@@ -154,6 +162,7 @@ module ActiveMerchant #:nodoc:
|
|
154
162
|
|
155
163
|
def store(creditcard, options = {})
|
156
164
|
request = build_xml_request do |xml|
|
165
|
+
xml.ssl_vendor_id @options[:ssl_vendor_id] || options[:ssl_vendor_id]
|
157
166
|
xml.ssl_transaction_type self.actions[:store]
|
158
167
|
xml.ssl_add_token 'Y'
|
159
168
|
add_creditcard(xml, creditcard)
|
@@ -167,6 +176,7 @@ module ActiveMerchant #:nodoc:
|
|
167
176
|
|
168
177
|
def update(token, creditcard, options = {})
|
169
178
|
request = build_xml_request do |xml|
|
179
|
+
xml.ssl_vendor_id @options[:ssl_vendor_id] || options[:ssl_vendor_id]
|
170
180
|
xml.ssl_transaction_type self.actions[:update]
|
171
181
|
add_token(xml, token)
|
172
182
|
add_creditcard(xml, creditcard)
|
@@ -191,8 +201,8 @@ module ActiveMerchant #:nodoc:
|
|
191
201
|
private
|
192
202
|
|
193
203
|
def add_invoice(xml, options)
|
194
|
-
xml.ssl_invoice_number
|
195
|
-
xml.ssl_description
|
204
|
+
xml.ssl_invoice_number url_encode_truncate((options[:order_id] || options[:invoice]), 25)
|
205
|
+
xml.ssl_description url_encode_truncate(options[:description], 255)
|
196
206
|
end
|
197
207
|
|
198
208
|
def add_approval_code(xml, authorization)
|
@@ -209,8 +219,8 @@ module ActiveMerchant #:nodoc:
|
|
209
219
|
|
210
220
|
add_verification_value(xml, creditcard) if creditcard.verification_value?
|
211
221
|
|
212
|
-
xml.ssl_first_name
|
213
|
-
xml.ssl_last_name
|
222
|
+
xml.ssl_first_name url_encode_truncate(creditcard.first_name, 20)
|
223
|
+
xml.ssl_last_name url_encode_truncate(creditcard.last_name, 30)
|
214
224
|
end
|
215
225
|
|
216
226
|
def add_currency(xml, money, options)
|
@@ -230,7 +240,7 @@ module ActiveMerchant #:nodoc:
|
|
230
240
|
end
|
231
241
|
|
232
242
|
def add_customer_email(xml, options)
|
233
|
-
xml.ssl_email
|
243
|
+
xml.ssl_email url_encode_truncate(options[:email], 100) unless empty?(options[:email])
|
234
244
|
end
|
235
245
|
|
236
246
|
def add_salestax(xml, options)
|
@@ -243,27 +253,27 @@ module ActiveMerchant #:nodoc:
|
|
243
253
|
billing_address = options[:billing_address] || options[:address]
|
244
254
|
|
245
255
|
if billing_address
|
246
|
-
xml.ssl_avs_address
|
247
|
-
xml.ssl_address2
|
248
|
-
xml.ssl_avs_zip
|
249
|
-
xml.ssl_city
|
250
|
-
xml.ssl_state
|
251
|
-
xml.ssl_company
|
252
|
-
xml.ssl_phone
|
253
|
-
xml.ssl_country
|
256
|
+
xml.ssl_avs_address url_encode_truncate(billing_address[:address1], 30)
|
257
|
+
xml.ssl_address2 url_encode_truncate(billing_address[:address2], 30)
|
258
|
+
xml.ssl_avs_zip url_encode_truncate(billing_address[:zip].to_s.gsub(/[^a-zA-Z0-9]/, ''), 9)
|
259
|
+
xml.ssl_city url_encode_truncate(billing_address[:city], 30)
|
260
|
+
xml.ssl_state url_encode_truncate(billing_address[:state], 10)
|
261
|
+
xml.ssl_company url_encode_truncate(billing_address[:company], 50)
|
262
|
+
xml.ssl_phone url_encode_truncate(billing_address[:phone], 20)
|
263
|
+
xml.ssl_country url_encode_truncate(billing_address[:country], 50)
|
254
264
|
end
|
255
265
|
|
256
266
|
if shipping_address = options[:shipping_address]
|
257
|
-
xml.ssl_ship_to_address1
|
258
|
-
xml.ssl_ship_to_address2
|
259
|
-
xml.ssl_ship_to_city
|
260
|
-
xml.ssl_ship_to_company
|
261
|
-
xml.ssl_ship_to_country
|
262
|
-
xml.ssl_ship_to_first_name
|
263
|
-
xml.ssl_ship_to_last_name
|
264
|
-
xml.ssl_ship_to_phone
|
265
|
-
xml.ssl_ship_to_state
|
266
|
-
xml.ssl_ship_to_zip
|
267
|
+
xml.ssl_ship_to_address1 url_encode_truncate(shipping_address[:address1], 30)
|
268
|
+
xml.ssl_ship_to_address2 url_encode_truncate(shipping_address[:address2], 30)
|
269
|
+
xml.ssl_ship_to_city url_encode_truncate(shipping_address[:city], 30)
|
270
|
+
xml.ssl_ship_to_company url_encode_truncate(shipping_address[:company], 50)
|
271
|
+
xml.ssl_ship_to_country url_encode_truncate(shipping_address[:country], 50)
|
272
|
+
xml.ssl_ship_to_first_name url_encode_truncate(shipping_address[:first_name], 20)
|
273
|
+
xml.ssl_ship_to_last_name url_encode_truncate(shipping_address[:last_name], 30)
|
274
|
+
xml.ssl_ship_to_phone url_encode_truncate(shipping_address[:phone], 10)
|
275
|
+
xml.ssl_ship_to_state url_encode_truncate(shipping_address[:state], 2)
|
276
|
+
xml.ssl_ship_to_zip url_encode_truncate(shipping_address[:zip], 10)
|
267
277
|
end
|
268
278
|
end
|
269
279
|
|
@@ -283,12 +293,17 @@ module ActiveMerchant #:nodoc:
|
|
283
293
|
xml.ssl_cardholder_ip options[:ip] if options.has_key?(:ip)
|
284
294
|
end
|
285
295
|
|
296
|
+
# add_recurring_token is a field that can be sent in to obtain a token from Elavon for use with their tokenization program
|
286
297
|
def add_auth_purchase_params(xml, options)
|
287
298
|
xml.ssl_dynamic_dba options[:dba] if options.has_key?(:dba)
|
288
|
-
xml.ssl_merchant_initiated_unscheduled options
|
299
|
+
xml.ssl_merchant_initiated_unscheduled merchant_initiated_unscheduled(options) if merchant_initiated_unscheduled(options)
|
300
|
+
xml.ssl_add_token options[:add_recurring_token] if options.has_key?(:add_recurring_token)
|
301
|
+
xml.ssl_token options[:ssl_token] if options.has_key?(:ssl_token)
|
289
302
|
xml.ssl_customer_code options[:customer] if options.has_key?(:customer)
|
290
303
|
xml.ssl_customer_number options[:customer_number] if options.has_key?(:customer_number)
|
304
|
+
xml.ssl_entry_mode entry_mode(options) if entry_mode(options)
|
291
305
|
add_custom_fields(xml, options) if options[:custom_fields]
|
306
|
+
add_stored_credential(xml, options) if options[:stored_credential]
|
292
307
|
end
|
293
308
|
|
294
309
|
def add_custom_fields(xml, options)
|
@@ -337,6 +352,32 @@ module ActiveMerchant #:nodoc:
|
|
337
352
|
}
|
338
353
|
end
|
339
354
|
|
355
|
+
def add_stored_credential(xml, options)
|
356
|
+
network_transaction_id = options.dig(:stored_credential, :network_transaction_id)
|
357
|
+
case
|
358
|
+
when network_transaction_id.nil?
|
359
|
+
return
|
360
|
+
when network_transaction_id.to_s.include?('|')
|
361
|
+
oar_data, ps2000_data = options[:stored_credential][:network_transaction_id].split('|')
|
362
|
+
xml.ssl_oar_data oar_data unless oar_data.nil? || oar_data.empty?
|
363
|
+
xml.ssl_ps2000_data ps2000_data unless ps2000_data.nil? || ps2000_data.empty?
|
364
|
+
when network_transaction_id.to_s.length > 22
|
365
|
+
xml.ssl_oar_data options.dig(:stored_credential, :network_transaction_id)
|
366
|
+
else
|
367
|
+
xml.ssl_ps2000_data options.dig(:stored_credential, :network_transaction_id)
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
def merchant_initiated_unscheduled(options)
|
372
|
+
return options[:merchant_initiated_unscheduled] if options[:merchant_initiated_unscheduled]
|
373
|
+
return 'Y' if options.dig(:stored_credential, :initiator) == 'merchant' && options.dig(:stored_credential, :reason_type) == 'unscheduled' || options.dig(:stored_credential, :reason_type) == 'recurring'
|
374
|
+
end
|
375
|
+
|
376
|
+
def entry_mode(options)
|
377
|
+
return options[:entry_mode] if options[:entry_mode]
|
378
|
+
return 12 if options[:stored_credential]
|
379
|
+
end
|
380
|
+
|
340
381
|
def build_xml_request
|
341
382
|
builder = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
|
342
383
|
xml.txn do
|
@@ -352,7 +393,9 @@ module ActiveMerchant #:nodoc:
|
|
352
393
|
|
353
394
|
def commit(request)
|
354
395
|
request = "xmldata=#{request}".delete('&')
|
396
|
+
|
355
397
|
response = parse(ssl_post(test? ? self.test_url : self.live_url, request, headers))
|
398
|
+
response = hash_html_decode(response)
|
356
399
|
|
357
400
|
Response.new(
|
358
401
|
response[:result] == '0',
|
@@ -362,14 +405,19 @@ module ActiveMerchant #:nodoc:
|
|
362
405
|
authorization: authorization_from(response),
|
363
406
|
error_code: response[:errorCode],
|
364
407
|
avs_result: { code: response[:avs_response] },
|
365
|
-
cvv_result: response[:cvv2_response]
|
408
|
+
cvv_result: response[:cvv2_response],
|
409
|
+
network_transaction_id: build_network_transaction_id(response)
|
366
410
|
)
|
367
411
|
end
|
368
412
|
|
413
|
+
def build_network_transaction_id(response)
|
414
|
+
"#{response[:oar_data]}|#{response[:ps2000_data]}"
|
415
|
+
end
|
416
|
+
|
369
417
|
def headers
|
370
418
|
{
|
371
419
|
'Accept' => 'application/xml',
|
372
|
-
'Content-type' => 'application/x-www-form-urlencoded'
|
420
|
+
'Content-type' => 'application/x-www-form-urlencoded;charset=utf8'
|
373
421
|
}
|
374
422
|
end
|
375
423
|
|
@@ -383,6 +431,42 @@ module ActiveMerchant #:nodoc:
|
|
383
431
|
def authorization_from(response)
|
384
432
|
[response[:approval_code], response[:txn_id]].join(';')
|
385
433
|
end
|
434
|
+
|
435
|
+
def url_encode_truncate(value, size)
|
436
|
+
return nil unless value
|
437
|
+
|
438
|
+
encoded = url_encode(value)
|
439
|
+
|
440
|
+
while encoded.length > size
|
441
|
+
value.chop!
|
442
|
+
encoded = url_encode(value)
|
443
|
+
end
|
444
|
+
encoded
|
445
|
+
end
|
446
|
+
|
447
|
+
def url_encode(value)
|
448
|
+
if value.is_a?(String)
|
449
|
+
encoded = CGI.escape(value)
|
450
|
+
encoded = encoded.tr('+', ' ') # don't encode spaces
|
451
|
+
encoded = encoded.gsub('%26', '%26amp;') # account for Elavon's weird '&' handling
|
452
|
+
encoded
|
453
|
+
else
|
454
|
+
value.to_s
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
def hash_html_decode(hash)
|
459
|
+
hash.each do |k, v|
|
460
|
+
if v.is_a?(String)
|
461
|
+
# decode all string params
|
462
|
+
v = v.gsub('&', '&') # account for Elavon's weird '&' handling
|
463
|
+
hash[k] = CGI.unescape_html(v)
|
464
|
+
elsif v.is_a?(Hash)
|
465
|
+
hash_html_decode(v)
|
466
|
+
end
|
467
|
+
end
|
468
|
+
hash
|
469
|
+
end
|
386
470
|
end
|
387
471
|
end
|
388
472
|
end
|
@@ -192,6 +192,8 @@ module ActiveMerchant #:nodoc:
|
|
192
192
|
xml.PaymentType options[:payment_type] if options[:payment_type]
|
193
193
|
xml.SubmissionType options[:submission_type] if options[:submission_type]
|
194
194
|
xml.DuplicateCheckDisableFlag options[:duplicate_check_disable_flag].to_s == 'true' ? 'True' : 'False' unless options[:duplicate_check_disable_flag].nil?
|
195
|
+
xml.DuplicateOverrideFlag options[:duplicate_override_flag].to_s == 'true' ? 'True' : 'False' unless options[:duplicate_override_flag].nil?
|
196
|
+
xml.MerchantDescriptor options[:merchant_descriptor] if options[:merchant_descriptor]
|
195
197
|
end
|
196
198
|
end
|
197
199
|
|
@@ -53,6 +53,7 @@ module ActiveMerchant #:nodoc:
|
|
53
53
|
add_invoice(params, amount, options)
|
54
54
|
add_customer_data(params, options, payment_method)
|
55
55
|
add_credit_card(params, payment_method, options)
|
56
|
+
add_3ds_authenticated_data(params, options) if options[:three_d_secure]
|
56
57
|
params['Method'] = payment_method.respond_to?(:number) ? 'ProcessPayment' : 'TokenPayment'
|
57
58
|
commit(url_for('Transaction'), params)
|
58
59
|
end
|
@@ -197,6 +198,18 @@ module ActiveMerchant #:nodoc:
|
|
197
198
|
params
|
198
199
|
end
|
199
200
|
|
201
|
+
def add_3ds_authenticated_data(params, options)
|
202
|
+
three_d_secure_options = options[:three_d_secure]
|
203
|
+
params['PaymentInstrument'] ||= {} if params['PaymentInstrument'].nil?
|
204
|
+
threed_secure_auth = params['PaymentInstrument']['ThreeDSecureAuth'] = {}
|
205
|
+
threed_secure_auth['Cryptogram'] = three_d_secure_options[:cavv]
|
206
|
+
threed_secure_auth['ECI'] = three_d_secure_options[:eci]
|
207
|
+
threed_secure_auth['XID'] = three_d_secure_options[:xid]
|
208
|
+
threed_secure_auth['AuthStatus'] = three_d_secure_options[:authentication_response_status]
|
209
|
+
threed_secure_auth['dsTransactionId'] = three_d_secure_options[:ds_transaction_id]
|
210
|
+
threed_secure_auth['Version'] = three_d_secure_options[:version]
|
211
|
+
end
|
212
|
+
|
200
213
|
def add_invoice(params, money, options, key = 'Payment')
|
201
214
|
currency_code = options[:currency] || currency(money)
|
202
215
|
params[key] = {
|
@@ -212,8 +212,8 @@ module ActiveMerchant #:nodoc:
|
|
212
212
|
xml.tag! 'Expiry_Date', expdate(credit_card)
|
213
213
|
xml.tag! 'CardHoldersName', credit_card.name
|
214
214
|
xml.tag! 'CardType', card_type(credit_card.brand)
|
215
|
-
xml.tag! 'WalletProviderID', options[:wallet_provider_id] if options[:wallet_provider_id]
|
216
215
|
|
216
|
+
add_wallet_provider_id(xml, credit_card, options)
|
217
217
|
add_credit_card_eci(xml, credit_card, options)
|
218
218
|
add_credit_card_verification_strings(xml, credit_card, options)
|
219
219
|
end
|
@@ -221,10 +221,9 @@ module ActiveMerchant #:nodoc:
|
|
221
221
|
|
222
222
|
def add_credit_card_eci(xml, credit_card, options)
|
223
223
|
eci = if credit_card.is_a?(NetworkTokenizationCreditCard) && credit_card.source == :apple_pay && card_brand(credit_card) == 'discover'
|
224
|
-
#
|
225
|
-
#
|
226
|
-
|
227
|
-
'04'
|
224
|
+
# Payeezy requires an ECI of 5 for apple pay transactions
|
225
|
+
# See: https://support.payeezy.com/hc/en-us/articles/203730589-Ecommerce-Flag-Values
|
226
|
+
'05'
|
228
227
|
else
|
229
228
|
(credit_card.respond_to?(:eci) ? credit_card.eci : nil) || options[:eci] || DEFAULT_ECI
|
230
229
|
end
|
@@ -276,10 +275,22 @@ module ActiveMerchant #:nodoc:
|
|
276
275
|
xml.tag! 'Expiry_Date', expdate(credit_card)
|
277
276
|
xml.tag! 'CardHoldersName', credit_card.name
|
278
277
|
xml.tag! 'CardType', card_type(credit_card.brand)
|
279
|
-
|
278
|
+
|
279
|
+
add_wallet_provider_id(xml, credit_card, options)
|
280
280
|
add_card_authentication_data(xml, options)
|
281
281
|
end
|
282
282
|
|
283
|
+
def add_wallet_provider_id(xml, credit_card, options)
|
284
|
+
provider_id = if options[:wallet_provider_id]
|
285
|
+
options[:wallet_provider_id]
|
286
|
+
elsif credit_card.is_a?(NetworkTokenizationCreditCard) && credit_card.source == :apple_pay
|
287
|
+
# See: https://support.payeezy.com/hc/en-us/articles/206601408-First-Data-Payeezy-Gateway-Web-Service-API-Reference-Guide#3.9
|
288
|
+
4
|
289
|
+
end
|
290
|
+
|
291
|
+
xml.tag! 'WalletProviderID', provider_id if provider_id
|
292
|
+
end
|
293
|
+
|
283
294
|
def add_customer_data(xml, options)
|
284
295
|
xml.tag! 'Customer_Ref', options[:customer] if options[:customer]
|
285
296
|
xml.tag! 'Client_IP', options[:ip] if options[:ip]
|
@@ -28,6 +28,7 @@ module ActiveMerchant #:nodoc:
|
|
28
28
|
add_payment_method(post, payment_method, options)
|
29
29
|
add_billing_address(post, payment_method, options)
|
30
30
|
add_shipping_address(post, options)
|
31
|
+
add_xdata(post, options)
|
31
32
|
post[:action] = 'sale'
|
32
33
|
|
33
34
|
commit(:post, post)
|
@@ -41,6 +42,7 @@ module ActiveMerchant #:nodoc:
|
|
41
42
|
add_payment_method(post, payment_method, options)
|
42
43
|
add_billing_address(post, payment_method, options)
|
43
44
|
add_shipping_address(post, options)
|
45
|
+
add_xdata(post, options)
|
44
46
|
post[:action] = 'authorize'
|
45
47
|
|
46
48
|
commit(:post, post)
|
@@ -122,6 +124,16 @@ module ActiveMerchant #:nodoc:
|
|
122
124
|
post[:service_fee_amount] = options[:service_fee_amount] if options[:service_fee_amount]
|
123
125
|
end
|
124
126
|
|
127
|
+
def add_xdata(post, options)
|
128
|
+
post[:xdata] = {}
|
129
|
+
if xdata = options[:xdata]
|
130
|
+
(1..9).each do |n|
|
131
|
+
field = "xdata_#{n}".to_sym
|
132
|
+
post[:xdata][field] = xdata[field] if xdata[field]
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
125
137
|
def add_billing_address(post, payment, options)
|
126
138
|
post[:billing_address] = {}
|
127
139
|
if address = options[:billing_address] || options[:address]
|