activemerchant 1.121.0 → 1.125.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 +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'
|