activemerchant 1.117.0 → 1.123.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 +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]
|