activemerchant 1.121.0 → 1.125.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 +1 -1
- data/lib/active_merchant/billing/check.rb +13 -19
- data/lib/active_merchant/billing/credit_card.rb +13 -0
- data/lib/active_merchant/billing/credit_card_formatting.rb +1 -0
- data/lib/active_merchant/billing/credit_card_methods.rb +24 -12
- data/lib/active_merchant/billing/gateway.rb +1 -1
- data/lib/active_merchant/billing/gateways/adyen.rb +75 -27
- data/lib/active_merchant/billing/gateways/authorize_net.rb +10 -8
- data/lib/active_merchant/billing/gateways/blue_pay.rb +29 -0
- data/lib/active_merchant/billing/gateways/blue_snap.rb +2 -2
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +6 -3
- data/lib/active_merchant/billing/gateways/card_stream.rb +17 -13
- data/lib/active_merchant/billing/gateways/cashnet.rb +15 -5
- data/lib/active_merchant/billing/gateways/checkout_v2.rb +33 -4
- data/lib/active_merchant/billing/gateways/credorax.rb +2 -1
- data/lib/active_merchant/billing/gateways/cyber_source.rb +41 -6
- data/lib/active_merchant/billing/gateways/d_local.rb +12 -6
- data/lib/active_merchant/billing/gateways/decidir.rb +7 -1
- data/lib/active_merchant/billing/gateways/decidir_plus.rb +173 -0
- data/lib/active_merchant/billing/gateways/ebanx.rb +16 -1
- data/lib/active_merchant/billing/gateways/elavon.rb +65 -30
- data/lib/active_merchant/billing/gateways/element.rb +22 -2
- data/lib/active_merchant/billing/gateways/global_collect.rb +130 -26
- data/lib/active_merchant/billing/gateways/ipg.rb +416 -0
- data/lib/active_merchant/billing/gateways/kushki.rb +30 -0
- data/lib/active_merchant/billing/gateways/mercado_pago.rb +6 -3
- data/lib/active_merchant/billing/gateways/merchant_warrior.rb +2 -0
- data/lib/active_merchant/billing/gateways/mit.rb +260 -0
- data/lib/active_merchant/billing/gateways/moka.rb +290 -0
- data/lib/active_merchant/billing/gateways/monei.rb +228 -144
- data/lib/active_merchant/billing/gateways/mundipagg.rb +22 -11
- data/lib/active_merchant/billing/gateways/nmi.rb +29 -10
- data/lib/active_merchant/billing/gateways/orbital.rb +46 -8
- data/lib/active_merchant/billing/gateways/pay_arc.rb +392 -0
- data/lib/active_merchant/billing/gateways/pay_conex.rb +3 -1
- data/lib/active_merchant/billing/gateways/pay_trace.rb +404 -0
- data/lib/active_merchant/billing/gateways/payeezy.rb +4 -0
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -0
- data/lib/active_merchant/billing/gateways/payflow.rb +21 -4
- data/lib/active_merchant/billing/gateways/payment_express.rb +2 -2
- data/lib/active_merchant/billing/gateways/paymentez.rb +14 -2
- data/lib/active_merchant/billing/gateways/paysafe.rb +412 -0
- data/lib/active_merchant/billing/gateways/payu_latam.rb +9 -4
- data/lib/active_merchant/billing/gateways/payway_dot_com.rb +3 -3
- data/lib/active_merchant/billing/gateways/pin.rb +31 -4
- data/lib/active_merchant/billing/gateways/priority.rb +347 -0
- data/lib/active_merchant/billing/gateways/realex.rb +18 -0
- data/lib/active_merchant/billing/gateways/redsys.rb +35 -32
- data/lib/active_merchant/billing/gateways/safe_charge.rb +8 -2
- data/lib/active_merchant/billing/gateways/spreedly_core.rb +13 -4
- data/lib/active_merchant/billing/gateways/stripe.rb +27 -7
- data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +115 -39
- data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +2 -1
- data/lib/active_merchant/billing/gateways/trust_commerce.rb +2 -1
- data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +21 -7
- data/lib/active_merchant/billing/gateways/vpos.rb +49 -6
- data/lib/active_merchant/billing/gateways/wompi.rb +193 -0
- data/lib/active_merchant/billing/gateways/worldpay.rb +226 -62
- data/lib/active_merchant/billing/network_tokenization_credit_card.rb +1 -1
- data/lib/active_merchant/billing/response.rb +4 -0
- 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
- metadata +13 -3
@@ -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) }
|
@@ -285,6 +289,7 @@ module ActiveMerchant #:nodoc:
|
|
285
289
|
|
286
290
|
def build_auth_request(money, creditcard_or_reference, options)
|
287
291
|
xml = Builder::XmlMarkup.new indent: 2
|
292
|
+
add_customer_id(xml, options)
|
288
293
|
add_payment_method_or_subscription(xml, money, creditcard_or_reference, options)
|
289
294
|
add_threeds_2_ucaf_data(xml, creditcard_or_reference, options)
|
290
295
|
add_decision_manager_fields(xml, options)
|
@@ -300,7 +305,15 @@ module ActiveMerchant #:nodoc:
|
|
300
305
|
add_merchant_description(xml, options)
|
301
306
|
add_sales_slip_number(xml, options)
|
302
307
|
add_airline_data(xml, options)
|
308
|
+
xml.target!
|
309
|
+
end
|
303
310
|
|
311
|
+
def build_adjust_request(money, authorization, options)
|
312
|
+
_, request_id = authorization.split(';')
|
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)
|
304
317
|
xml.target!
|
305
318
|
end
|
306
319
|
|
@@ -334,6 +347,7 @@ module ActiveMerchant #:nodoc:
|
|
334
347
|
|
335
348
|
def build_purchase_request(money, payment_method_or_reference, options)
|
336
349
|
xml = Builder::XmlMarkup.new indent: 2
|
350
|
+
add_customer_id(xml, options)
|
337
351
|
add_payment_method_or_subscription(xml, money, payment_method_or_reference, options)
|
338
352
|
add_threeds_2_ucaf_data(xml, payment_method_or_reference, options)
|
339
353
|
add_decision_manager_fields(xml, options)
|
@@ -472,8 +486,8 @@ module ActiveMerchant #:nodoc:
|
|
472
486
|
|
473
487
|
unless network_tokenization?(payment_method)
|
474
488
|
xml.tag! 'businessRules' do
|
475
|
-
xml.tag!('ignoreAVSResult', 'true') if extract_option(prioritized_options, :ignore_avs)
|
476
|
-
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'
|
477
491
|
end
|
478
492
|
end
|
479
493
|
end
|
@@ -486,6 +500,8 @@ module ActiveMerchant #:nodoc:
|
|
486
500
|
end
|
487
501
|
|
488
502
|
def add_line_item_data(xml, options)
|
503
|
+
return unless options[:line_items]
|
504
|
+
|
489
505
|
options[:line_items].each_with_index do |value, index|
|
490
506
|
xml.tag! 'item', { 'id' => index } do
|
491
507
|
xml.tag! 'unitPrice', localized_amount(value[:declared_value].to_i, options[:currency] || default_currency)
|
@@ -493,6 +509,8 @@ module ActiveMerchant #:nodoc:
|
|
493
509
|
xml.tag! 'productCode', value[:code] || 'shipping_only'
|
494
510
|
xml.tag! 'productName', value[:description]
|
495
511
|
xml.tag! 'productSKU', value[:sku]
|
512
|
+
xml.tag! 'taxAmount', value[:tax_amount] if value[:tax_amount]
|
513
|
+
xml.tag! 'nationalTax', value[:national_tax] if value[:national_tax]
|
496
514
|
end
|
497
515
|
end
|
498
516
|
end
|
@@ -508,13 +526,21 @@ module ActiveMerchant #:nodoc:
|
|
508
526
|
end
|
509
527
|
|
510
528
|
def add_merchant_descriptor(xml, options)
|
511
|
-
return unless options[:merchant_descriptor]
|
529
|
+
return unless options[:merchant_descriptor] || options[:user_po] || options[:taxable]
|
512
530
|
|
513
531
|
xml.tag! 'invoiceHeader' do
|
514
|
-
xml.tag! 'merchantDescriptor', options[:merchant_descriptor]
|
532
|
+
xml.tag! 'merchantDescriptor', options[:merchant_descriptor] if options[:merchant_descriptor]
|
533
|
+
xml.tag! 'userPO', options[:user_po] if options[:user_po]
|
534
|
+
xml.tag! 'taxable', options[:taxable] if options[:taxable]
|
515
535
|
end
|
516
536
|
end
|
517
537
|
|
538
|
+
def add_customer_id(xml, options)
|
539
|
+
return unless options[:customer_id]
|
540
|
+
|
541
|
+
xml.tag! 'customerID', options[:customer_id]
|
542
|
+
end
|
543
|
+
|
518
544
|
def add_merchant_description(xml, options)
|
519
545
|
return unless options[:merchant_descriptor_name] || options[:merchant_descriptor_address1] || options[:merchant_descriptor_locality]
|
520
546
|
|
@@ -585,7 +611,7 @@ module ActiveMerchant #:nodoc:
|
|
585
611
|
xml.tag! 'accountNumber', creditcard.number
|
586
612
|
xml.tag! 'expirationMonth', format(creditcard.month, :two_digits)
|
587
613
|
xml.tag! 'expirationYear', format(creditcard.year, :four_digits)
|
588
|
-
xml.tag!('cvNumber', creditcard.verification_value) unless @options[:ignore_cvv] || creditcard.verification_value.blank?
|
614
|
+
xml.tag!('cvNumber', creditcard.verification_value) unless @options[:ignore_cvv].to_s == 'true' || creditcard.verification_value.blank?
|
589
615
|
xml.tag! 'cardType', @@credit_card_codes[card_brand(creditcard).to_sym]
|
590
616
|
end
|
591
617
|
end
|
@@ -608,11 +634,12 @@ module ActiveMerchant #:nodoc:
|
|
608
634
|
end
|
609
635
|
|
610
636
|
def add_other_tax(xml, options)
|
611
|
-
return unless options[:local_tax_amount] || options[:national_tax_amount]
|
637
|
+
return unless options[:local_tax_amount] || options[:national_tax_amount] || options[:national_tax_indicator]
|
612
638
|
|
613
639
|
xml.tag! 'otherTax' do
|
614
640
|
xml.tag! 'localTaxAmount', options[:local_tax_amount] if options[:local_tax_amount]
|
615
641
|
xml.tag! 'nationalTaxAmount', options[:national_tax_amount] if options[:national_tax_amount]
|
642
|
+
xml.tag! 'nationalTaxIndicator', options[:national_tax_indicator] if options[:national_tax_indicator]
|
616
643
|
end
|
617
644
|
end
|
618
645
|
|
@@ -658,6 +685,13 @@ module ActiveMerchant #:nodoc:
|
|
658
685
|
end
|
659
686
|
end
|
660
687
|
|
688
|
+
def add_incremental_auth_service(xml, authorization, options)
|
689
|
+
xml.tag! 'ccIncrementalAuthService', { 'run' => 'true' } do
|
690
|
+
xml.tag! 'authRequestID', authorization
|
691
|
+
end
|
692
|
+
xml.tag! 'subsequentAuthReason', options[:auth_reason]
|
693
|
+
end
|
694
|
+
|
661
695
|
def add_normalized_threeds_2_data(xml, payment_method, options)
|
662
696
|
threeds_2_options = options[:three_d_secure]
|
663
697
|
cc_brand = card_brand(payment_method).to_sym
|
@@ -860,6 +894,7 @@ module ActiveMerchant #:nodoc:
|
|
860
894
|
else
|
861
895
|
add_address(xml, payment_method_or_reference, options[:billing_address], options)
|
862
896
|
add_address(xml, payment_method_or_reference, options[:shipping_address], options, true)
|
897
|
+
add_line_item_data(xml, options)
|
863
898
|
add_purchase_data(xml, money, true, options)
|
864
899
|
add_installments(xml, options)
|
865
900
|
add_creditcard(xml, payment_method_or_reference)
|
@@ -4,7 +4,7 @@ module ActiveMerchant #:nodoc:
|
|
4
4
|
self.test_url = 'https://sandbox.dlocal.com'
|
5
5
|
self.live_url = 'https://api.dlocal.com'
|
6
6
|
|
7
|
-
self.supported_countries = %w[AR BR CL CO MX PE UY
|
7
|
+
self.supported_countries = %w[AR BD BO BR CL CM CN CO CR DO EC EG GH IN ID KE MY MX MA NG PA PY PE PH SN ZA TR UY VN]
|
8
8
|
self.default_currency = 'USD'
|
9
9
|
self.supported_cardtypes = %i[visa master american_express discover jcb diners_club maestro naranja cabal elo alia carnet]
|
10
10
|
|
@@ -26,6 +26,7 @@ module ActiveMerchant #:nodoc:
|
|
26
26
|
def authorize(money, payment, options = {})
|
27
27
|
post = {}
|
28
28
|
add_auth_purchase_params(post, money, payment, 'authorize', options)
|
29
|
+
post[:card][:verify] = true if options[:verify].to_s == 'true'
|
29
30
|
|
30
31
|
commit('authorize', post, options)
|
31
32
|
end
|
@@ -52,10 +53,7 @@ module ActiveMerchant #:nodoc:
|
|
52
53
|
end
|
53
54
|
|
54
55
|
def verify(credit_card, options = {})
|
55
|
-
|
56
|
-
r.process { authorize(100, credit_card, options) }
|
57
|
-
r.process(:ignore_result) { void(r.authorization, options) }
|
58
|
-
end
|
56
|
+
authorize(0, credit_card, options.merge(verify: 'true'))
|
59
57
|
end
|
60
58
|
|
61
59
|
def supports_scrubbing?
|
@@ -78,6 +76,7 @@ module ActiveMerchant #:nodoc:
|
|
78
76
|
add_country(post, card, options)
|
79
77
|
add_payer(post, card, options)
|
80
78
|
add_card(post, card, action, options)
|
79
|
+
add_additional_data(post, options)
|
81
80
|
post[:order_id] = options[:order_id] || generate_unique_id
|
82
81
|
post[:description] = options[:description] if options[:description]
|
83
82
|
end
|
@@ -87,6 +86,10 @@ module ActiveMerchant #:nodoc:
|
|
87
86
|
post[:currency] = (options[:currency] || currency(money))
|
88
87
|
end
|
89
88
|
|
89
|
+
def add_additional_data(post, options)
|
90
|
+
post[:additional_risk_data] = options[:additional_data]
|
91
|
+
end
|
92
|
+
|
90
93
|
def add_country(post, card, options)
|
91
94
|
return unless address = options[:billing_address] || options[:address]
|
92
95
|
|
@@ -109,6 +112,8 @@ module ActiveMerchant #:nodoc:
|
|
109
112
|
post[:payer][:document] = options[:document] if options[:document]
|
110
113
|
post[:payer][:document2] = options[:document2] if options[:document2]
|
111
114
|
post[:payer][:user_reference] = options[:user_reference] if options[:user_reference]
|
115
|
+
post[:payer][:event_uuid] = options[:device_id] if options[:device_id]
|
116
|
+
post[:payer][:onboarding_ip_address] = options[:ip] if options[:ip]
|
112
117
|
post[:payer][:address] = add_address(post, card, options)
|
113
118
|
end
|
114
119
|
|
@@ -184,7 +189,7 @@ module ActiveMerchant #:nodoc:
|
|
184
189
|
def success_from(action, response)
|
185
190
|
return false unless response['status_code']
|
186
191
|
|
187
|
-
%w[100 200 400 600].include? response['status_code'].to_s
|
192
|
+
%w[100 200 400 600 700].include? response['status_code'].to_s
|
188
193
|
end
|
189
194
|
|
190
195
|
def message_from(action, response)
|
@@ -228,6 +233,7 @@ module ActiveMerchant #:nodoc:
|
|
228
233
|
'X-Date' => timestamp,
|
229
234
|
'X-Login' => @options[:login],
|
230
235
|
'X-Trans-Key' => @options[:trans_key],
|
236
|
+
'X-Version' => '2.1',
|
231
237
|
'Authorization' => signature(post, timestamp)
|
232
238
|
}
|
233
239
|
headers.merge('X-Idempotency-Key' => options[:idempotency_key]) if options[:idempotency_key]
|
@@ -282,7 +282,13 @@ module ActiveMerchant #:nodoc:
|
|
282
282
|
if error = response.dig('status_details', 'error')
|
283
283
|
message = "#{error.dig('reason', 'description')} | #{error['type']}"
|
284
284
|
elsif response['error_type']
|
285
|
-
|
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
|
+
|
286
292
|
message ||= response['error_type']
|
287
293
|
end
|
288
294
|
|
@@ -0,0 +1,173 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
class DecidirPlusGateway < Gateway
|
4
|
+
self.test_url = 'https://developers.decidir.com/api/v2'
|
5
|
+
self.live_url = 'https://live.decidir.com/api/v2'
|
6
|
+
|
7
|
+
self.supported_countries = ['AR']
|
8
|
+
self.default_currency = 'ARS'
|
9
|
+
self.supported_cardtypes = %i[visa master american_express discover]
|
10
|
+
|
11
|
+
self.homepage_url = 'http://decidir.com.ar/home'
|
12
|
+
self.display_name = 'Decidir Plus'
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
requires!(options, :public_key, :private_key)
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
def purchase(money, payment, options = {})
|
20
|
+
post = {}
|
21
|
+
|
22
|
+
add_payment(post, payment, options)
|
23
|
+
add_purchase_data(post, money, payment, options)
|
24
|
+
|
25
|
+
commit(:post, 'payments', post)
|
26
|
+
end
|
27
|
+
|
28
|
+
def refund(money, authorization, options = {})
|
29
|
+
post = {}
|
30
|
+
post[:amount] = money
|
31
|
+
|
32
|
+
commit(:post, "payments/#{add_reference(authorization)}/refunds")
|
33
|
+
end
|
34
|
+
|
35
|
+
def store(payment, options = {})
|
36
|
+
post = {}
|
37
|
+
add_payment(post, payment, options)
|
38
|
+
|
39
|
+
commit(:post, 'tokens', post)
|
40
|
+
end
|
41
|
+
|
42
|
+
def supports_scrubbing?
|
43
|
+
true
|
44
|
+
end
|
45
|
+
|
46
|
+
def scrub(transcript)
|
47
|
+
transcript.
|
48
|
+
gsub(%r((Apikey: )\w+), '\1[FILTERED]').
|
49
|
+
gsub(%r(("card_number\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
|
50
|
+
gsub(%r(("security_code\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]')
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def add_reference(authorization)
|
56
|
+
return unless authorization
|
57
|
+
|
58
|
+
authorization.split('|')[0]
|
59
|
+
end
|
60
|
+
|
61
|
+
def add_payment(post, payment, options = {})
|
62
|
+
if payment.is_a?(String)
|
63
|
+
token, bin = payment.split('|')
|
64
|
+
post[:token] = token
|
65
|
+
post[:bin] = bin
|
66
|
+
else
|
67
|
+
post[:card_number] = payment.number
|
68
|
+
post[:card_expiration_month] = payment.month.to_s.rjust(2, '0')
|
69
|
+
post[:card_expiration_year] = payment.year.to_s[-2..-1]
|
70
|
+
post[:security_code] = payment.verification_value.to_s
|
71
|
+
post[:card_holder_name] = payment.name
|
72
|
+
post[:card_holder_identification] = {}
|
73
|
+
post[:card_holder_identification][:type] = options[:dni]
|
74
|
+
post[:card_holder_identification][:number] = options[:card_holder_identification_number]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def add_purchase_data(post, money, payment, options = {})
|
79
|
+
post[:site_transaction_id] = options[:site_transaction_id] || SecureRandom.hex
|
80
|
+
post[:payment_method_id] = 1
|
81
|
+
post[:amount] = money
|
82
|
+
post[:currency] = options[:currency] || self.default_currency
|
83
|
+
post[:installments] = options[:installments] || 1
|
84
|
+
post[:payment_type] = options[:payment_type] || 'single'
|
85
|
+
add_sub_payments(post, options)
|
86
|
+
end
|
87
|
+
|
88
|
+
def add_sub_payments(post, options)
|
89
|
+
# sub_payments field is required for purchase transactions, even if empty
|
90
|
+
post[:sub_payments] = []
|
91
|
+
|
92
|
+
return unless sub_payments = options[:sub_payments]
|
93
|
+
|
94
|
+
sub_payments.each do |sub_payment|
|
95
|
+
sub_payment_hash = {
|
96
|
+
site_id: sub_payment[:site_id],
|
97
|
+
installments: sub_payment[:installments],
|
98
|
+
amount: sub_payment[:amount]
|
99
|
+
}
|
100
|
+
post[:sub_payments] << sub_payment_hash
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def parse(body)
|
105
|
+
JSON.parse(body)
|
106
|
+
end
|
107
|
+
|
108
|
+
def commit(method, endpoint, parameters = {}, options = {})
|
109
|
+
begin
|
110
|
+
raw_response = ssl_request(method, url(endpoint), post_data(parameters), headers(endpoint))
|
111
|
+
response = parse(raw_response)
|
112
|
+
rescue ResponseError => e
|
113
|
+
raw_response = e.response.body
|
114
|
+
response = parse(raw_response)
|
115
|
+
end
|
116
|
+
|
117
|
+
Response.new(
|
118
|
+
success_from(response),
|
119
|
+
message_from(response),
|
120
|
+
response,
|
121
|
+
authorization: authorization_from(response),
|
122
|
+
avs_result: AVSResult.new(code: response['some_avs_response_key']),
|
123
|
+
cvv_result: CVVResult.new(response['some_cvv_response_key']),
|
124
|
+
test: test?,
|
125
|
+
error_code: error_code_from(response)
|
126
|
+
)
|
127
|
+
end
|
128
|
+
|
129
|
+
def headers(endpoint)
|
130
|
+
{
|
131
|
+
'Content-Type' => 'application/json',
|
132
|
+
'apikey' => endpoint == 'tokens' ? @options[:public_key] : @options[:private_key]
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
def url(action, options = {})
|
137
|
+
base_url = (test? ? test_url : live_url)
|
138
|
+
|
139
|
+
return "#{base_url}/#{action}"
|
140
|
+
end
|
141
|
+
|
142
|
+
def success_from(response)
|
143
|
+
response.dig('status') == 'approved' || response.dig('status') == 'active'
|
144
|
+
end
|
145
|
+
|
146
|
+
def message_from(response)
|
147
|
+
response.dig('status') || error_message(response) || response.dig('message')
|
148
|
+
end
|
149
|
+
|
150
|
+
def authorization_from(response)
|
151
|
+
return nil unless response.dig('id') || response.dig('bin')
|
152
|
+
|
153
|
+
"#{response.dig('id')}|#{response.dig('bin')}"
|
154
|
+
end
|
155
|
+
|
156
|
+
def post_data(parameters = {})
|
157
|
+
parameters.to_json
|
158
|
+
end
|
159
|
+
|
160
|
+
def error_code_from(response)
|
161
|
+
response.dig('error_type') unless success_from(response)
|
162
|
+
end
|
163
|
+
|
164
|
+
def error_message(response)
|
165
|
+
return error_code_from(response) unless validation_errors = response.dig('validation_errors')
|
166
|
+
|
167
|
+
validation_errors = validation_errors[0]
|
168
|
+
|
169
|
+
"#{validation_errors.dig('code')}: #{validation_errors.dig('param')}"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -214,6 +214,7 @@ module ActiveMerchant #:nodoc:
|
|
214
214
|
post[:metadata] = options[:metadata] if options[:metadata]
|
215
215
|
post[:metadata] = {} if post[:metadata].nil?
|
216
216
|
post[:metadata][:merchant_payment_code] = options[:order_id] if options[:order_id]
|
217
|
+
post[:processing_type] = options[:processing_type] if options[:processing_type]
|
217
218
|
end
|
218
219
|
|
219
220
|
def parse(body)
|
@@ -222,7 +223,8 @@ module ActiveMerchant #:nodoc:
|
|
222
223
|
|
223
224
|
def commit(action, parameters)
|
224
225
|
url = url_for((test? ? test_url : live_url), action, parameters)
|
225
|
-
|
226
|
+
|
227
|
+
response = parse(ssl_request(HTTP_METHOD[action], url, post_data(action, parameters), headers(parameters)))
|
226
228
|
|
227
229
|
success = success_from(action, response)
|
228
230
|
|
@@ -236,6 +238,19 @@ module ActiveMerchant #:nodoc:
|
|
236
238
|
)
|
237
239
|
end
|
238
240
|
|
241
|
+
def headers(params)
|
242
|
+
processing_type = params[:processing_type]
|
243
|
+
commit_headers = { 'x-ebanx-client-user-agent': "ActiveMerchant/#{ActiveMerchant::VERSION}" }
|
244
|
+
|
245
|
+
add_processing_type_to_commit_headers(commit_headers, processing_type) if processing_type == 'local'
|
246
|
+
|
247
|
+
commit_headers
|
248
|
+
end
|
249
|
+
|
250
|
+
def add_processing_type_to_commit_headers(commit_headers, processing_type)
|
251
|
+
commit_headers['x-ebanx-api-processing-type'] = processing_type
|
252
|
+
end
|
253
|
+
|
239
254
|
def success_from(action, response)
|
240
255
|
if %i[purchase capture refund].include?(action)
|
241
256
|
response.try(:[], 'payment').try(:[], 'status') == 'CO'
|
@@ -201,8 +201,8 @@ module ActiveMerchant #:nodoc:
|
|
201
201
|
private
|
202
202
|
|
203
203
|
def add_invoice(xml, options)
|
204
|
-
xml.ssl_invoice_number
|
205
|
-
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)
|
206
206
|
end
|
207
207
|
|
208
208
|
def add_approval_code(xml, authorization)
|
@@ -219,8 +219,8 @@ module ActiveMerchant #:nodoc:
|
|
219
219
|
|
220
220
|
add_verification_value(xml, creditcard) if creditcard.verification_value?
|
221
221
|
|
222
|
-
xml.ssl_first_name
|
223
|
-
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)
|
224
224
|
end
|
225
225
|
|
226
226
|
def add_currency(xml, money, options)
|
@@ -240,7 +240,7 @@ module ActiveMerchant #:nodoc:
|
|
240
240
|
end
|
241
241
|
|
242
242
|
def add_customer_email(xml, options)
|
243
|
-
xml.ssl_email
|
243
|
+
xml.ssl_email url_encode_truncate(options[:email], 100) unless empty?(options[:email])
|
244
244
|
end
|
245
245
|
|
246
246
|
def add_salestax(xml, options)
|
@@ -253,27 +253,27 @@ module ActiveMerchant #:nodoc:
|
|
253
253
|
billing_address = options[:billing_address] || options[:address]
|
254
254
|
|
255
255
|
if billing_address
|
256
|
-
xml.ssl_avs_address
|
257
|
-
xml.ssl_address2
|
258
|
-
xml.ssl_avs_zip
|
259
|
-
xml.ssl_city
|
260
|
-
xml.ssl_state
|
261
|
-
xml.ssl_company
|
262
|
-
xml.ssl_phone
|
263
|
-
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)
|
264
264
|
end
|
265
265
|
|
266
266
|
if shipping_address = options[:shipping_address]
|
267
|
-
xml.ssl_ship_to_address1
|
268
|
-
xml.ssl_ship_to_address2
|
269
|
-
xml.ssl_ship_to_city
|
270
|
-
xml.ssl_ship_to_company
|
271
|
-
xml.ssl_ship_to_country
|
272
|
-
xml.ssl_ship_to_first_name
|
273
|
-
xml.ssl_ship_to_last_name
|
274
|
-
xml.ssl_ship_to_phone
|
275
|
-
xml.ssl_ship_to_state
|
276
|
-
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)
|
277
277
|
end
|
278
278
|
end
|
279
279
|
|
@@ -293,9 +293,12 @@ module ActiveMerchant #:nodoc:
|
|
293
293
|
xml.ssl_cardholder_ip options[:ip] if options.has_key?(:ip)
|
294
294
|
end
|
295
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
|
296
297
|
def add_auth_purchase_params(xml, options)
|
297
298
|
xml.ssl_dynamic_dba options[:dba] if options.has_key?(:dba)
|
298
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[:ssl_token]
|
299
302
|
xml.ssl_customer_code options[:customer] if options.has_key?(:customer)
|
300
303
|
xml.ssl_customer_number options[:customer_number] if options.has_key?(:customer_number)
|
301
304
|
xml.ssl_entry_mode entry_mode(options) if entry_mode(options)
|
@@ -367,7 +370,7 @@ module ActiveMerchant #:nodoc:
|
|
367
370
|
|
368
371
|
def merchant_initiated_unscheduled(options)
|
369
372
|
return options[:merchant_initiated_unscheduled] if options[:merchant_initiated_unscheduled]
|
370
|
-
return 'Y' if options.dig(:stored_credential, :initiator) == 'merchant' && options.dig(:stored_credential, :reason_type) == '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'
|
371
374
|
end
|
372
375
|
|
373
376
|
def entry_mode(options)
|
@@ -390,15 +393,17 @@ module ActiveMerchant #:nodoc:
|
|
390
393
|
|
391
394
|
def commit(request)
|
392
395
|
request = "xmldata=#{request}".delete('&')
|
396
|
+
store_action = request.match?('CCGETTOKEN')
|
393
397
|
|
394
398
|
response = parse(ssl_post(test? ? self.test_url : self.live_url, request, headers))
|
399
|
+
response = hash_html_decode(response)
|
395
400
|
|
396
401
|
Response.new(
|
397
402
|
response[:result] == '0',
|
398
403
|
response[:result_message] || response[:errorMessage],
|
399
404
|
response,
|
400
405
|
test: @options[:test] || test?,
|
401
|
-
authorization: authorization_from(response),
|
406
|
+
authorization: authorization_from(response, store_action),
|
402
407
|
error_code: response[:errorCode],
|
403
408
|
avs_result: { code: response[:avs_response] },
|
404
409
|
cvv_result: response[:cvv2_response],
|
@@ -413,7 +418,7 @@ module ActiveMerchant #:nodoc:
|
|
413
418
|
def headers
|
414
419
|
{
|
415
420
|
'Accept' => 'application/xml',
|
416
|
-
'Content-type' => 'application/x-www-form-urlencoded'
|
421
|
+
'Content-type' => 'application/x-www-form-urlencoded;charset=utf8'
|
417
422
|
}
|
418
423
|
end
|
419
424
|
|
@@ -424,16 +429,46 @@ module ActiveMerchant #:nodoc:
|
|
424
429
|
response.deep_transform_keys { |key| key.gsub('ssl_', '').to_sym }
|
425
430
|
end
|
426
431
|
|
427
|
-
def authorization_from(response)
|
432
|
+
def authorization_from(response, store_action)
|
433
|
+
return response[:token] if store_action
|
434
|
+
|
428
435
|
[response[:approval_code], response[:txn_id]].join(';')
|
429
436
|
end
|
430
437
|
|
431
|
-
def
|
438
|
+
def url_encode_truncate(value, size)
|
432
439
|
return nil unless value
|
433
440
|
|
434
|
-
|
441
|
+
encoded = url_encode(value)
|
442
|
+
|
443
|
+
while encoded.length > size
|
444
|
+
value.chop!
|
445
|
+
encoded = url_encode(value)
|
446
|
+
end
|
447
|
+
encoded
|
448
|
+
end
|
449
|
+
|
450
|
+
def url_encode(value)
|
451
|
+
if value.is_a?(String)
|
452
|
+
encoded = CGI.escape(value)
|
453
|
+
encoded = encoded.tr('+', ' ') # don't encode spaces
|
454
|
+
encoded = encoded.gsub('%26', '%26amp;') # account for Elavon's weird '&' handling
|
455
|
+
encoded
|
456
|
+
else
|
457
|
+
value.to_s
|
458
|
+
end
|
459
|
+
end
|
435
460
|
|
436
|
-
|
461
|
+
def hash_html_decode(hash)
|
462
|
+
hash.each do |k, v|
|
463
|
+
if v.is_a?(String)
|
464
|
+
# decode all string params
|
465
|
+
v = v.gsub('&amp;', '&') # account for Elavon's weird '&' handling
|
466
|
+
hash[k] = CGI.unescape_html(v)
|
467
|
+
elsif v.is_a?(Hash)
|
468
|
+
hash_html_decode(v)
|
469
|
+
end
|
470
|
+
end
|
471
|
+
hash
|
437
472
|
end
|
438
473
|
end
|
439
474
|
end
|
@@ -82,6 +82,19 @@ module ActiveMerchant #:nodoc:
|
|
82
82
|
commit('CreditCardReturn', request, money)
|
83
83
|
end
|
84
84
|
|
85
|
+
def credit(money, payment, options = {})
|
86
|
+
request = build_soap_request do |xml|
|
87
|
+
xml.CreditCardCredit(xmlns: 'https://transaction.elementexpress.com') do
|
88
|
+
add_credentials(xml)
|
89
|
+
add_payment_method(xml, payment)
|
90
|
+
add_transaction(xml, money, options)
|
91
|
+
add_terminal(xml, options)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
commit('CreditCardCredit', request, money)
|
96
|
+
end
|
97
|
+
|
85
98
|
def void(authorization, options = {})
|
86
99
|
trans_id, trans_amount = split_authorization(authorization)
|
87
100
|
options.merge!({ trans_id: trans_id, trans_amount: trans_amount, reversal_type: 'Full' })
|
@@ -186,15 +199,22 @@ module ActiveMerchant #:nodoc:
|
|
186
199
|
xml.ReversalType options[:reversal_type] if options[:reversal_type]
|
187
200
|
xml.TransactionID options[:trans_id] if options[:trans_id]
|
188
201
|
xml.TransactionAmount amount(money.to_i) if money
|
189
|
-
xml.MarketCode
|
202
|
+
xml.MarketCode market_code(money, options) if options[:market_code] || money
|
190
203
|
xml.ReferenceNumber options[:order_id] || SecureRandom.hex(20)
|
191
|
-
|
204
|
+
xml.TicketNumber options[:ticket_number] if options[:ticket_number]
|
205
|
+
xml.MerchantSuppliedTransactionId options[:merchant_supplied_transaction_id] if options[:merchant_supplied_transaction_id]
|
192
206
|
xml.PaymentType options[:payment_type] if options[:payment_type]
|
193
207
|
xml.SubmissionType options[:submission_type] if options[:submission_type]
|
194
208
|
xml.DuplicateCheckDisableFlag options[:duplicate_check_disable_flag].to_s == 'true' ? 'True' : 'False' unless options[:duplicate_check_disable_flag].nil?
|
209
|
+
xml.DuplicateOverrideFlag options[:duplicate_override_flag].to_s == 'true' ? 'True' : 'False' unless options[:duplicate_override_flag].nil?
|
210
|
+
xml.MerchantDescriptor options[:merchant_descriptor] if options[:merchant_descriptor]
|
195
211
|
end
|
196
212
|
end
|
197
213
|
|
214
|
+
def market_code(money, options)
|
215
|
+
options[:market_code] || 'Default'
|
216
|
+
end
|
217
|
+
|
198
218
|
def add_terminal(xml, options)
|
199
219
|
xml.terminal do
|
200
220
|
xml.TerminalID options[:terminal_id] || '01'
|