activemerchant 1.118.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 +185 -0
- data/README.md +4 -2
- data/lib/active_merchant/billing/check.rb +19 -12
- data/lib/active_merchant/billing/credit_card.rb +4 -0
- data/lib/active_merchant/billing/credit_card_formatting.rb +1 -0
- data/lib/active_merchant/billing/credit_card_methods.rb +44 -17
- data/lib/active_merchant/billing/gateways/adyen.rb +36 -19
- data/lib/active_merchant/billing/gateways/authorize_net.rb +19 -11
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +3 -0
- 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 +52 -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 +15 -9
- data/lib/active_merchant/billing/gateways/cyber_source.rb +53 -6
- data/lib/active_merchant/billing/gateways/d_local.rb +1 -1
- data/lib/active_merchant/billing/gateways/decidir.rb +15 -3
- data/lib/active_merchant/billing/gateways/elavon.rb +76 -25
- 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/forte.rb +12 -0
- data/lib/active_merchant/billing/gateways/global_collect.rb +25 -16
- data/lib/active_merchant/billing/gateways/hps.rb +64 -1
- data/lib/active_merchant/billing/gateways/kushki.rb +23 -0
- data/lib/active_merchant/billing/gateways/litle.rb +4 -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 +127 -64
- 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 +34 -6
- 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 +5 -0
- 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/qvalent.rb +23 -9
- data/lib/active_merchant/billing/gateways/redsys.rb +44 -25
- data/lib/active_merchant/billing/gateways/safe_charge.rb +20 -11
- 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 +79 -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 +64 -19
- 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
|
@@ -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] = {
|
|
@@ -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]
|
|
@@ -33,7 +33,7 @@ module ActiveMerchant #:nodoc:
|
|
|
33
33
|
add_creator_info(post, options)
|
|
34
34
|
add_fraud_fields(post, options)
|
|
35
35
|
add_external_cardholder_authentication_data(post, options)
|
|
36
|
-
commit(:authorize, post)
|
|
36
|
+
commit(:authorize, post, options: options)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def capture(money, authorization, options = {})
|
|
@@ -41,7 +41,7 @@ module ActiveMerchant #:nodoc:
|
|
|
41
41
|
add_order(post, money, options, capture: true)
|
|
42
42
|
add_customer_data(post, options)
|
|
43
43
|
add_creator_info(post, options)
|
|
44
|
-
commit(:capture, post, authorization)
|
|
44
|
+
commit(:capture, post, authorization: authorization)
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
def refund(money, authorization, options = {})
|
|
@@ -49,13 +49,13 @@ module ActiveMerchant #:nodoc:
|
|
|
49
49
|
add_amount(post, money, options)
|
|
50
50
|
add_refund_customer_data(post, options)
|
|
51
51
|
add_creator_info(post, options)
|
|
52
|
-
commit(:refund, post, authorization)
|
|
52
|
+
commit(:refund, post, authorization: authorization)
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def void(authorization, options = {})
|
|
56
56
|
post = nestable_hash
|
|
57
57
|
add_creator_info(post, options)
|
|
58
|
-
commit(:void, post, authorization)
|
|
58
|
+
commit(:void, post, authorization: authorization)
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def verify(payment, options = {})
|
|
@@ -201,21 +201,21 @@ module ActiveMerchant #:nodoc:
|
|
|
201
201
|
shipping_address = options[:shipping_address]
|
|
202
202
|
if billing_address = options[:billing_address] || options[:address]
|
|
203
203
|
post['order']['customer']['billingAddress'] = {
|
|
204
|
-
'street' => billing_address[:address1],
|
|
205
|
-
'additionalInfo' => billing_address[:address2],
|
|
204
|
+
'street' => truncate(billing_address[:address1], 50),
|
|
205
|
+
'additionalInfo' => truncate(billing_address[:address2], 50),
|
|
206
206
|
'zip' => billing_address[:zip],
|
|
207
207
|
'city' => billing_address[:city],
|
|
208
|
-
'state' => billing_address[:state],
|
|
208
|
+
'state' => truncate(billing_address[:state], 35),
|
|
209
209
|
'countryCode' => billing_address[:country]
|
|
210
210
|
}
|
|
211
211
|
end
|
|
212
212
|
if shipping_address
|
|
213
213
|
post['order']['customer']['shippingAddress'] = {
|
|
214
|
-
'street' => shipping_address[:address1],
|
|
215
|
-
'additionalInfo' => shipping_address[:address2],
|
|
214
|
+
'street' => truncate(shipping_address[:address1], 50),
|
|
215
|
+
'additionalInfo' => truncate(shipping_address[:address2], 50),
|
|
216
216
|
'zip' => shipping_address[:zip],
|
|
217
217
|
'city' => shipping_address[:city],
|
|
218
|
-
'state' => shipping_address[:state],
|
|
218
|
+
'state' => truncate(shipping_address[:state], 35),
|
|
219
219
|
'countryCode' => shipping_address[:country]
|
|
220
220
|
}
|
|
221
221
|
post['order']['customer']['shippingAddress']['name'] = {
|
|
@@ -277,9 +277,13 @@ module ActiveMerchant #:nodoc:
|
|
|
277
277
|
end
|
|
278
278
|
end
|
|
279
279
|
|
|
280
|
-
def
|
|
280
|
+
def idempotency_key_for_signature(options)
|
|
281
|
+
"x-gcs-idempotence-key:#{options[:idempotency_key]}" if options[:idempotency_key]
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def commit(action, post, authorization: nil, options: {})
|
|
281
285
|
begin
|
|
282
|
-
raw_response = ssl_post(url(action, authorization), post.to_json, headers(action, post, authorization))
|
|
286
|
+
raw_response = ssl_post(url(action, authorization), post.to_json, headers(action, post, authorization, options))
|
|
283
287
|
response = parse(raw_response)
|
|
284
288
|
rescue ResponseError => e
|
|
285
289
|
response = parse(e.response.body) if e.response.code.to_i >= 400
|
|
@@ -306,21 +310,26 @@ module ActiveMerchant #:nodoc:
|
|
|
306
310
|
}
|
|
307
311
|
end
|
|
308
312
|
|
|
309
|
-
def headers(action, post, authorization = nil)
|
|
310
|
-
{
|
|
313
|
+
def headers(action, post, authorization = nil, options = {})
|
|
314
|
+
headers = {
|
|
311
315
|
'Content-Type' => content_type,
|
|
312
|
-
'Authorization' => auth_digest(action, post, authorization),
|
|
316
|
+
'Authorization' => auth_digest(action, post, authorization, options),
|
|
313
317
|
'Date' => date
|
|
314
318
|
}
|
|
319
|
+
|
|
320
|
+
headers['X-GCS-Idempotence-Key'] = options[:idempotency_key] if options[:idempotency_key]
|
|
321
|
+
headers
|
|
315
322
|
end
|
|
316
323
|
|
|
317
|
-
def auth_digest(action, post, authorization = nil)
|
|
324
|
+
def auth_digest(action, post, authorization = nil, options = {})
|
|
318
325
|
data = <<~REQUEST
|
|
319
326
|
POST
|
|
320
327
|
#{content_type}
|
|
321
328
|
#{date}
|
|
329
|
+
#{idempotency_key_for_signature(options)}
|
|
322
330
|
#{uri(action, authorization)}
|
|
323
331
|
REQUEST
|
|
332
|
+
data = data.each_line.reject { |line| line.strip == '' }.join
|
|
324
333
|
digest = OpenSSL::Digest.new('sha256')
|
|
325
334
|
key = @options[:secret_api_key]
|
|
326
335
|
"GCS v1HMAC:#{@options[:api_key_id]}:#{Base64.strict_encode64(OpenSSL::HMAC.digest(digest, key, data))}"
|
|
@@ -39,6 +39,7 @@ module ActiveMerchant #:nodoc:
|
|
|
39
39
|
add_descriptor_name(xml, options)
|
|
40
40
|
add_card_or_token_payment(xml, card_or_token, options)
|
|
41
41
|
add_three_d_secure(xml, card_or_token, options)
|
|
42
|
+
add_stored_credentials(xml, options)
|
|
42
43
|
end
|
|
43
44
|
end
|
|
44
45
|
|
|
@@ -52,6 +53,8 @@ module ActiveMerchant #:nodoc:
|
|
|
52
53
|
def purchase(money, payment_method, options = {})
|
|
53
54
|
if payment_method.is_a?(Check)
|
|
54
55
|
commit_check_sale(money, payment_method, options)
|
|
56
|
+
elsif options.dig(:stored_credential, :reason_type) == 'recurring'
|
|
57
|
+
commit_recurring_billing_sale(money, payment_method, options)
|
|
55
58
|
else
|
|
56
59
|
commit_credit_sale(money, payment_method, options)
|
|
57
60
|
end
|
|
@@ -67,6 +70,15 @@ module ActiveMerchant #:nodoc:
|
|
|
67
70
|
end
|
|
68
71
|
end
|
|
69
72
|
|
|
73
|
+
def credit(money, payment_method, options = {})
|
|
74
|
+
commit('CreditReturn') do |xml|
|
|
75
|
+
add_amount(xml, money)
|
|
76
|
+
add_allow_dup(xml)
|
|
77
|
+
add_card_or_token_payment(xml, payment_method, options)
|
|
78
|
+
add_details(xml, options)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
70
82
|
def verify(card_or_token, options = {})
|
|
71
83
|
commit('CreditAccountVerify') do |xml|
|
|
72
84
|
add_card_or_token_customer_data(xml, card_or_token, options)
|
|
@@ -122,6 +134,21 @@ module ActiveMerchant #:nodoc:
|
|
|
122
134
|
add_descriptor_name(xml, options)
|
|
123
135
|
add_card_or_token_payment(xml, card_or_token, options)
|
|
124
136
|
add_three_d_secure(xml, card_or_token, options)
|
|
137
|
+
add_stored_credentials(xml, options)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def commit_recurring_billing_sale(money, card_or_token, options)
|
|
142
|
+
commit('RecurringBilling') do |xml|
|
|
143
|
+
add_amount(xml, money)
|
|
144
|
+
add_allow_dup(xml)
|
|
145
|
+
add_card_or_token_customer_data(xml, card_or_token, options)
|
|
146
|
+
add_details(xml, options)
|
|
147
|
+
add_descriptor_name(xml, options)
|
|
148
|
+
add_card_or_token_payment(xml, card_or_token, options)
|
|
149
|
+
add_three_d_secure(xml, card_or_token, options)
|
|
150
|
+
add_stored_credentials(xml, options)
|
|
151
|
+
add_stored_credentials_for_recurring_billing(xml, options)
|
|
125
152
|
end
|
|
126
153
|
end
|
|
127
154
|
|
|
@@ -148,7 +175,7 @@ module ActiveMerchant #:nodoc:
|
|
|
148
175
|
xml.hps :CardHolderAddr, billing_address[:address1] if billing_address[:address1]
|
|
149
176
|
xml.hps :CardHolderCity, billing_address[:city] if billing_address[:city]
|
|
150
177
|
xml.hps :CardHolderState, billing_address[:state] if billing_address[:state]
|
|
151
|
-
xml.hps :CardHolderZip, billing_address[:zip] if billing_address[:zip]
|
|
178
|
+
xml.hps :CardHolderZip, alphanumeric_zip(billing_address[:zip]) if billing_address[:zip]
|
|
152
179
|
end
|
|
153
180
|
end
|
|
154
181
|
end
|
|
@@ -256,6 +283,38 @@ module ActiveMerchant #:nodoc:
|
|
|
256
283
|
end
|
|
257
284
|
end
|
|
258
285
|
|
|
286
|
+
# We do not currently support installments on this gateway.
|
|
287
|
+
# The HPS gateway treats recurring transactions as a seperate transaction type
|
|
288
|
+
def add_stored_credentials(xml, options)
|
|
289
|
+
return unless options[:stored_credential]
|
|
290
|
+
|
|
291
|
+
xml.hps :CardOnFileData do
|
|
292
|
+
if options[:stored_credential][:initiator] == 'customer'
|
|
293
|
+
xml.hps :CardOnFile, 'C'
|
|
294
|
+
elsif options[:stored_credential][:initiator] == 'merchant'
|
|
295
|
+
xml.hps :CardOnFile, 'M'
|
|
296
|
+
else
|
|
297
|
+
return
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
if options[:stored_credential][:network_transaction_id]
|
|
301
|
+
xml.hps :CardBrandTxnId, options[:stored_credential][:network_transaction_id]
|
|
302
|
+
else
|
|
303
|
+
return
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def add_stored_credentials_for_recurring_billing(xml, options)
|
|
309
|
+
xml.hps :RecurringData do
|
|
310
|
+
if options[:stored_credential][:reason_type] = 'recurring'
|
|
311
|
+
xml.hps :OneTime, 'N'
|
|
312
|
+
else
|
|
313
|
+
xml.hps :OneTime, 'Y'
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
259
318
|
def strip_leading_zero(value)
|
|
260
319
|
return value unless value[0] == '0'
|
|
261
320
|
|
|
@@ -380,6 +439,10 @@ module ActiveMerchant #:nodoc:
|
|
|
380
439
|
@options[:secret_api_key]&.include?('_cert_')
|
|
381
440
|
end
|
|
382
441
|
|
|
442
|
+
def alphanumeric_zip(zip)
|
|
443
|
+
zip.gsub(/[^0-9a-z]/i, '')
|
|
444
|
+
end
|
|
445
|
+
|
|
383
446
|
ISSUER_MESSAGES = {
|
|
384
447
|
'13' => 'Must be greater than or equal 0.',
|
|
385
448
|
'14' => 'The card number is incorrect.',
|
|
@@ -37,6 +37,7 @@ module ActiveMerchant #:nodoc:
|
|
|
37
37
|
post = {}
|
|
38
38
|
post[:ticketNumber] = authorization
|
|
39
39
|
add_invoice(action, post, amount, options)
|
|
40
|
+
add_full_response(post, options)
|
|
40
41
|
|
|
41
42
|
commit(action, post)
|
|
42
43
|
end
|
|
@@ -46,6 +47,7 @@ module ActiveMerchant #:nodoc:
|
|
|
46
47
|
|
|
47
48
|
post = {}
|
|
48
49
|
post[:ticketNumber] = authorization
|
|
50
|
+
add_full_response(post, options)
|
|
49
51
|
|
|
50
52
|
commit(action, post)
|
|
51
53
|
end
|
|
@@ -55,6 +57,7 @@ module ActiveMerchant #:nodoc:
|
|
|
55
57
|
|
|
56
58
|
post = {}
|
|
57
59
|
post[:ticketNumber] = authorization
|
|
60
|
+
add_full_response(post, options)
|
|
58
61
|
|
|
59
62
|
commit(action, post)
|
|
60
63
|
end
|
|
@@ -78,6 +81,7 @@ module ActiveMerchant #:nodoc:
|
|
|
78
81
|
post = {}
|
|
79
82
|
add_invoice(action, post, amount, options)
|
|
80
83
|
add_payment_method(post, payment_method, options)
|
|
84
|
+
add_full_response(post, options)
|
|
81
85
|
|
|
82
86
|
commit(action, post)
|
|
83
87
|
end
|
|
@@ -88,6 +92,8 @@ module ActiveMerchant #:nodoc:
|
|
|
88
92
|
post = {}
|
|
89
93
|
add_reference(post, authorization, options)
|
|
90
94
|
add_invoice(action, post, amount, options)
|
|
95
|
+
add_contact_details(post, options[:contact_details]) if options[:contact_details]
|
|
96
|
+
add_full_response(post, options)
|
|
91
97
|
|
|
92
98
|
commit(action, post)
|
|
93
99
|
end
|
|
@@ -98,6 +104,7 @@ module ActiveMerchant #:nodoc:
|
|
|
98
104
|
post = {}
|
|
99
105
|
add_reference(post, authorization, options)
|
|
100
106
|
add_invoice(action, post, amount, options)
|
|
107
|
+
add_full_response(post, options)
|
|
101
108
|
|
|
102
109
|
commit(action, post)
|
|
103
110
|
end
|
|
@@ -154,6 +161,22 @@ module ActiveMerchant #:nodoc:
|
|
|
154
161
|
post[:token] = authorization
|
|
155
162
|
end
|
|
156
163
|
|
|
164
|
+
def add_contact_details(post, contact_details_options)
|
|
165
|
+
contact_details = {}
|
|
166
|
+
contact_details[:documentType] = contact_details_options[:document_type] if contact_details_options[:document_type]
|
|
167
|
+
contact_details[:documentNumber] = contact_details_options[:document_number] if contact_details_options[:document_number]
|
|
168
|
+
contact_details[:email] = contact_details_options[:email] if contact_details_options[:email]
|
|
169
|
+
contact_details[:firstName] = contact_details_options[:first_name] if contact_details_options[:first_name]
|
|
170
|
+
contact_details[:lastName] = contact_details_options[:last_name] if contact_details_options[:last_name]
|
|
171
|
+
contact_details[:secondLastName] = contact_details_options[:second_last_name] if contact_details_options[:second_last_name]
|
|
172
|
+
contact_details[:phoneNumber] = contact_details_options[:phone_number] if contact_details_options[:phone_number]
|
|
173
|
+
post[:contactDetails] = contact_details
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def add_full_response(post, options)
|
|
177
|
+
post[:fullResponse] = options[:full_response].to_s.casecmp('true').zero? if options[:full_response]
|
|
178
|
+
end
|
|
179
|
+
|
|
157
180
|
ENDPOINT = {
|
|
158
181
|
'tokenize' => 'tokens',
|
|
159
182
|
'charge' => 'charges',
|
|
@@ -371,9 +371,9 @@ module ActiveMerchant #:nodoc:
|
|
|
371
371
|
return unless address
|
|
372
372
|
|
|
373
373
|
doc.companyName(address[:company]) unless address[:company].blank?
|
|
374
|
-
doc.addressLine1(address[:address1]) unless address[:address1].blank?
|
|
375
|
-
doc.addressLine2(address[:address2]) unless address[:address2].blank?
|
|
376
|
-
doc.city(address[:city]) unless address[:city].blank?
|
|
374
|
+
doc.addressLine1(truncate(address[:address1], 35)) unless address[:address1].blank?
|
|
375
|
+
doc.addressLine2(truncate(address[:address2], 35)) unless address[:address2].blank?
|
|
376
|
+
doc.city(truncate(address[:city], 35)) unless address[:city].blank?
|
|
377
377
|
doc.state(address[:state]) unless address[:state].blank?
|
|
378
378
|
doc.zip(address[:zip]) unless address[:zip].blank?
|
|
379
379
|
doc.country(address[:country]) unless address[:country].blank?
|
|
@@ -494,7 +494,7 @@ module ActiveMerchant #:nodoc:
|
|
|
494
494
|
attributes = {}
|
|
495
495
|
attributes[:id] = truncate(options[:id] || options[:order_id], 24)
|
|
496
496
|
attributes[:reportGroup] = options[:merchant] || 'Default Report Group'
|
|
497
|
-
attributes[:customerId] = options[:
|
|
497
|
+
attributes[:customerId] = options[:customer_id]
|
|
498
498
|
attributes.delete_if { |_key, value| value == nil }
|
|
499
499
|
attributes
|
|
500
500
|
end
|
|
@@ -4,7 +4,7 @@ module ActiveMerchant #:nodoc:
|
|
|
4
4
|
self.live_url = self.test_url = 'https://api.mercadopago.com/v1'
|
|
5
5
|
|
|
6
6
|
self.supported_countries = %w[AR BR CL CO MX PE UY]
|
|
7
|
-
self.supported_cardtypes = %i[visa master american_express elo cabal naranja]
|
|
7
|
+
self.supported_cardtypes = %i[visa master american_express elo cabal naranja creditel]
|
|
8
8
|
|
|
9
9
|
self.homepage_url = 'https://www.mercadopago.com/'
|
|
10
10
|
self.display_name = 'Mercado Pago'
|
|
@@ -105,7 +105,7 @@ module ActiveMerchant #:nodoc:
|
|
|
105
105
|
|
|
106
106
|
def authorize_request(money, payment, options = {})
|
|
107
107
|
post = purchase_request(money, payment, options)
|
|
108
|
-
post[:capture] = false
|
|
108
|
+
post[:capture] = options[:capture] || false
|
|
109
109
|
post
|
|
110
110
|
end
|
|
111
111
|
|
|
@@ -129,6 +129,7 @@ module ActiveMerchant #:nodoc:
|
|
|
129
129
|
|
|
130
130
|
def add_additional_data(post, options)
|
|
131
131
|
post[:sponsor_id] = options[:sponsor_id]
|
|
132
|
+
post[:metadata] = options[:metadata] if options[:metadata]
|
|
132
133
|
post[:device_id] = options[:device_id] if options[:device_id]
|
|
133
134
|
post[:additional_info] = {
|
|
134
135
|
ip_address: options[:ip_address]
|
|
@@ -143,7 +144,7 @@ module ActiveMerchant #:nodoc:
|
|
|
143
144
|
email: options[:email],
|
|
144
145
|
first_name: payment.first_name,
|
|
145
146
|
last_name: payment.last_name
|
|
146
|
-
}
|
|
147
|
+
}.merge(options[:payer] || {})
|
|
147
148
|
end
|
|
148
149
|
|
|
149
150
|
def add_address(post, options)
|
|
@@ -191,7 +192,7 @@ module ActiveMerchant #:nodoc:
|
|
|
191
192
|
post[:description] = options[:description]
|
|
192
193
|
post[:installments] = options[:installments] ? options[:installments].to_i : 1
|
|
193
194
|
post[:statement_descriptor] = options[:statement_descriptor] if options[:statement_descriptor]
|
|
194
|
-
post[:external_reference] = options[:order_id] || SecureRandom.hex(16)
|
|
195
|
+
post[:external_reference] = options[:order_id] || options[:external_reference] || SecureRandom.hex(16)
|
|
195
196
|
end
|
|
196
197
|
|
|
197
198
|
def add_payment(post, options)
|
|
@@ -187,6 +187,8 @@ module ActiveMerchant #:nodoc:
|
|
|
187
187
|
def parse(body)
|
|
188
188
|
xml = REXML::Document.new(body)
|
|
189
189
|
|
|
190
|
+
return { response_message: 'Invalid gateway response' } unless xml.root.present?
|
|
191
|
+
|
|
190
192
|
response = {}
|
|
191
193
|
xml.root.elements.to_a.each do |node|
|
|
192
194
|
parse_element(response, node)
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
|
2
|
+
module Billing #:nodoc:
|
|
3
|
+
class MokaGateway < Gateway
|
|
4
|
+
self.test_url = 'https://service.testmoka.com'
|
|
5
|
+
self.live_url = 'https://service.moka.com'
|
|
6
|
+
|
|
7
|
+
self.supported_countries = %w[GB TR US]
|
|
8
|
+
self.default_currency = 'TRY'
|
|
9
|
+
self.money_format = :dollars
|
|
10
|
+
self.supported_cardtypes = %i[visa master american_express discover]
|
|
11
|
+
|
|
12
|
+
self.homepage_url = 'http://developer.moka.com/'
|
|
13
|
+
self.display_name = 'Moka'
|
|
14
|
+
|
|
15
|
+
ERROR_CODE_MAPPING = {
|
|
16
|
+
'000' => 'General error',
|
|
17
|
+
'001' => '3D Not authenticated',
|
|
18
|
+
'002' => 'Limit is insufficient',
|
|
19
|
+
'003' => 'Credit card number format is wrong',
|
|
20
|
+
'004' => 'General decline',
|
|
21
|
+
'005' => 'This process is invalid for the card owner',
|
|
22
|
+
'006' => 'Expiration date is wrong',
|
|
23
|
+
'007' => 'Invalid transaction',
|
|
24
|
+
'008' => 'Connection with the bank not established',
|
|
25
|
+
'009' => 'Undefined error code',
|
|
26
|
+
'010' => 'Bank SSL error',
|
|
27
|
+
'011' => 'Call the bank for the manual authentication',
|
|
28
|
+
'012' => 'Card info is wrong - Kart Number or CVV2',
|
|
29
|
+
'013' => '3D secure is not supported other than Visa MC cards',
|
|
30
|
+
'014' => 'Invalid account number',
|
|
31
|
+
'015' => 'CVV is wrong',
|
|
32
|
+
'016' => 'Authentication process is not present',
|
|
33
|
+
'017' => 'System error',
|
|
34
|
+
'018' => 'Stolen card',
|
|
35
|
+
'019' => 'Lost card',
|
|
36
|
+
'020' => 'Card with limited properties',
|
|
37
|
+
'021' => 'Timeout',
|
|
38
|
+
'022' => 'Invalid merchant',
|
|
39
|
+
'023' => 'False authentication',
|
|
40
|
+
'024' => '3D authorization is successful but the process cannot be completed',
|
|
41
|
+
'025' => '3D authorization failure',
|
|
42
|
+
'026' => 'Either the issuer bank or the card is not enrolled to the 3D process',
|
|
43
|
+
'027' => 'The bank did not allow the process',
|
|
44
|
+
'028' => 'Fraud suspect',
|
|
45
|
+
'029' => 'The card is closed to the e-commerce operations'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
def initialize(options = {})
|
|
49
|
+
requires!(options, :dealer_code, :username, :password)
|
|
50
|
+
super
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def purchase(money, payment, options = {})
|
|
54
|
+
post = {}
|
|
55
|
+
post[:PaymentDealerRequest] = {}
|
|
56
|
+
options[:pre_auth] = 0
|
|
57
|
+
add_auth_purchase(post, money, payment, options)
|
|
58
|
+
|
|
59
|
+
commit('purchase', post)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def authorize(money, payment, options = {})
|
|
63
|
+
post = {}
|
|
64
|
+
post[:PaymentDealerRequest] = {}
|
|
65
|
+
options[:pre_auth] = 1
|
|
66
|
+
add_auth_purchase(post, money, payment, options)
|
|
67
|
+
|
|
68
|
+
commit('authorize', post)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def capture(money, authorization, options = {})
|
|
72
|
+
post = {}
|
|
73
|
+
post[:PaymentDealerRequest] = {}
|
|
74
|
+
add_payment_dealer_authentication(post)
|
|
75
|
+
add_transaction_reference(post, authorization)
|
|
76
|
+
add_additional_transaction_data(post, options)
|
|
77
|
+
|
|
78
|
+
commit('capture', post)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def refund(money, authorization, options = {})
|
|
82
|
+
post = {}
|
|
83
|
+
post[:PaymentDealerRequest] = {}
|
|
84
|
+
add_payment_dealer_authentication(post)
|
|
85
|
+
add_transaction_reference(post, authorization)
|
|
86
|
+
add_additional_transaction_data(post, options)
|
|
87
|
+
add_void_refund_reason(post)
|
|
88
|
+
add_amount(post, money)
|
|
89
|
+
|
|
90
|
+
commit('refund', post)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def void(authorization, options = {})
|
|
94
|
+
post = {}
|
|
95
|
+
post[:PaymentDealerRequest] = {}
|
|
96
|
+
add_payment_dealer_authentication(post)
|
|
97
|
+
add_transaction_reference(post, authorization)
|
|
98
|
+
add_additional_transaction_data(post, options)
|
|
99
|
+
add_void_refund_reason(post)
|
|
100
|
+
|
|
101
|
+
commit('void', post)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def verify(credit_card, options = {})
|
|
105
|
+
MultiResponse.run(:use_first_response) do |r|
|
|
106
|
+
r.process { authorize(100, credit_card, options) }
|
|
107
|
+
r.process(:ignore_result) { void(r.authorization, options) }
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def supports_scrubbing?
|
|
112
|
+
true
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def scrub(transcript)
|
|
116
|
+
transcript.
|
|
117
|
+
gsub(%r(("CardNumber\\?":\\?")[^"]*)i, '\1[FILTERED]').
|
|
118
|
+
gsub(%r(("CvcNumber\\?":\\?")[^"]*)i, '\1[FILTERED]').
|
|
119
|
+
gsub(%r(("DealerCode\\?":\\?"?)[^"?]*)i, '\1[FILTERED]').
|
|
120
|
+
gsub(%r(("Username\\?":\\?")[^"]*)i, '\1[FILTERED]').
|
|
121
|
+
gsub(%r(("Password\\?":\\?")[^"]*)i, '\1[FILTERED]').
|
|
122
|
+
gsub(%r(("CheckKey\\?":\\?")[^"]*)i, '\1[FILTERED]')
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
private
|
|
126
|
+
|
|
127
|
+
def add_auth_purchase(post, money, payment, options)
|
|
128
|
+
add_payment_dealer_authentication(post)
|
|
129
|
+
add_invoice(post, money, options)
|
|
130
|
+
add_payment(post, payment)
|
|
131
|
+
add_additional_auth_purchase_data(post, options)
|
|
132
|
+
add_additional_transaction_data(post, options)
|
|
133
|
+
add_buyer_information(post, payment, options)
|
|
134
|
+
add_basket_product(post, options[:basket_product]) if options[:basket_product]
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def add_payment_dealer_authentication(post)
|
|
138
|
+
post[:PaymentDealerAuthentication] = {
|
|
139
|
+
DealerCode: @options[:dealer_code],
|
|
140
|
+
Username: @options[:username],
|
|
141
|
+
Password: @options[:password],
|
|
142
|
+
CheckKey: check_key
|
|
143
|
+
}
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def check_key
|
|
147
|
+
str = "#{@options[:dealer_code]}MK#{@options[:username]}PD#{@options[:password]}"
|
|
148
|
+
Digest::SHA256.hexdigest(str)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def add_invoice(post, money, options)
|
|
152
|
+
post[:PaymentDealerRequest][:Amount] = amount(money) || 0
|
|
153
|
+
post[:PaymentDealerRequest][:Currency] = options[:currency] || 'TL'
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def add_payment(post, card)
|
|
157
|
+
post[:PaymentDealerRequest][:CardHolderFullName] = card.name
|
|
158
|
+
post[:PaymentDealerRequest][:CardNumber] = card.number
|
|
159
|
+
post[:PaymentDealerRequest][:ExpMonth] = card.month
|
|
160
|
+
post[:PaymentDealerRequest][:ExpYear] = card.year
|
|
161
|
+
post[:PaymentDealerRequest][:CvcNumber] = card.verification_value
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def add_amount(post, money)
|
|
165
|
+
post[:PaymentDealerRequest][:Amount] = money || 0
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def add_additional_auth_purchase_data(post, options)
|
|
169
|
+
post[:PaymentDealerRequest][:IsPreAuth] = options[:pre_auth]
|
|
170
|
+
post[:PaymentDealerRequest][:Description] = options[:order_id] if options[:order_id]
|
|
171
|
+
post[:SubMerchantName] = options[:sub_merchant_name] if options[:sub_merchant_name]
|
|
172
|
+
post[:IsPoolPayment] = options[:is_pool_payment] || 0
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def add_buyer_information(post, card, options)
|
|
176
|
+
obj = {}
|
|
177
|
+
|
|
178
|
+
obj[:BuyerFullName] = card.name || ''
|
|
179
|
+
obj[:BuyerEmail] = options[:email] if options[:email]
|
|
180
|
+
obj[:BuyerAddress] = options[:billing_address][:address1] if options[:billing_address]
|
|
181
|
+
obj[:BuyerGsmNumber] = options[:billing_address][:phone] if options[:billing_address]
|
|
182
|
+
|
|
183
|
+
post[:PaymentDealerRequest][:BuyerInformation] = obj
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def add_basket_product(post, basket_options)
|
|
187
|
+
basket = []
|
|
188
|
+
|
|
189
|
+
basket_options.each do |product|
|
|
190
|
+
obj = {}
|
|
191
|
+
obj[:ProductId] = product[:product_id] if product[:product_id]
|
|
192
|
+
obj[:ProductCode] = product[:product_code] if product[:product_code]
|
|
193
|
+
obj[:UnitPrice] = amount(product[:unit_price]) if product[:unit_price]
|
|
194
|
+
obj[:Quantity] = product[:quantity] if product[:quantity]
|
|
195
|
+
basket << obj
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
post[:PaymentDealerRequest][:BasketProduct] = basket
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def add_additional_transaction_data(post, options)
|
|
202
|
+
post[:PaymentDealerRequest][:ClientIP] = options[:ip] if options[:ip]
|
|
203
|
+
post[:PaymentDealerRequest][:OtherTrxCode] = options[:order_id] if options[:order_id]
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def add_transaction_reference(post, authorization)
|
|
207
|
+
post[:PaymentDealerRequest][:VirtualPosOrderId] = authorization
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def add_void_refund_reason(post)
|
|
211
|
+
post[:PaymentDealerRequest][:VoidRefundReason] = 2
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def commit(action, parameters)
|
|
215
|
+
response = parse(ssl_post(url(action), post_data(parameters), request_headers))
|
|
216
|
+
Response.new(
|
|
217
|
+
success_from(response),
|
|
218
|
+
message_from(response),
|
|
219
|
+
response,
|
|
220
|
+
authorization: authorization_from(response),
|
|
221
|
+
test: test?,
|
|
222
|
+
error_code: error_code_from(response)
|
|
223
|
+
)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def url(action)
|
|
227
|
+
host = (test? ? test_url : live_url)
|
|
228
|
+
endpoint = endpoint(action)
|
|
229
|
+
|
|
230
|
+
"#{host}/PaymentDealer/#{endpoint}"
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def endpoint(action)
|
|
234
|
+
case action
|
|
235
|
+
when 'purchase', 'authorize'
|
|
236
|
+
'DoDirectPayment'
|
|
237
|
+
when 'capture'
|
|
238
|
+
'DoCapture'
|
|
239
|
+
when 'void'
|
|
240
|
+
'DoVoid'
|
|
241
|
+
when 'refund'
|
|
242
|
+
'DoCreateRefundRequest'
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def request_headers
|
|
247
|
+
{ 'Content-Type' => 'application/json' }
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def post_data(parameters = {})
|
|
251
|
+
JSON.generate(parameters)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def parse(body)
|
|
255
|
+
JSON.parse(body)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def success_from(response)
|
|
259
|
+
response.dig('Data', 'IsSuccessful')
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def message_from(response)
|
|
263
|
+
response.dig('Data', 'ResultMessage').presence || response['ResultCode']
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def authorization_from(response)
|
|
267
|
+
response.dig('Data', 'VirtualPosOrderId')
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def error_code_from(response)
|
|
271
|
+
codes = [response['ResultCode'], response.dig('Data', 'ResultCode')].flatten
|
|
272
|
+
codes.reject! { |code| code.blank? || code.casecmp('success').zero? }
|
|
273
|
+
codes.map { |code| ERROR_CODE_MAPPING[code] || code }.join(', ')
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
end
|