activemerchant 1.117.0 → 1.123.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +217 -0
- data/README.md +5 -3
- data/lib/active_merchant/billing/check.rb +19 -12
- data/lib/active_merchant/billing/credit_card.rb +6 -0
- data/lib/active_merchant/billing/credit_card_formatting.rb +1 -0
- data/lib/active_merchant/billing/credit_card_methods.rb +96 -22
- data/lib/active_merchant/billing/gateways/adyen.rb +38 -21
- data/lib/active_merchant/billing/gateways/authorize_net.rb +19 -11
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +4 -0
- data/lib/active_merchant/billing/gateways/blue_pay.rb +29 -0
- data/lib/active_merchant/billing/gateways/blue_snap.rb +5 -3
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +58 -8
- data/lib/active_merchant/billing/gateways/cashnet.rb +7 -2
- data/lib/active_merchant/billing/gateways/checkout_v2.rb +31 -0
- data/lib/active_merchant/billing/gateways/credorax.rb +16 -9
- data/lib/active_merchant/billing/gateways/cyber_source.rb +67 -9
- data/lib/active_merchant/billing/gateways/d_local.rb +1 -1
- data/lib/active_merchant/billing/gateways/decidir.rb +29 -3
- data/lib/active_merchant/billing/gateways/elavon.rb +110 -26
- data/lib/active_merchant/billing/gateways/element.rb +2 -0
- data/lib/active_merchant/billing/gateways/eway_rapid.rb +13 -0
- data/lib/active_merchant/billing/gateways/firstdata_e4_v27.rb +17 -6
- data/lib/active_merchant/billing/gateways/forte.rb +12 -0
- data/lib/active_merchant/billing/gateways/global_collect.rb +25 -16
- data/lib/active_merchant/billing/gateways/hps.rb +65 -2
- data/lib/active_merchant/billing/gateways/kushki.rb +23 -0
- data/lib/active_merchant/billing/gateways/litle.rb +9 -4
- data/lib/active_merchant/billing/gateways/mercado_pago.rb +5 -4
- data/lib/active_merchant/billing/gateways/merchant_warrior.rb +2 -0
- data/lib/active_merchant/billing/gateways/moka.rb +277 -0
- data/lib/active_merchant/billing/gateways/monei.rb +228 -144
- data/lib/active_merchant/billing/gateways/mundipagg.rb +14 -5
- data/lib/active_merchant/billing/gateways/netbanx.rb +37 -2
- data/lib/active_merchant/billing/gateways/nmi.rb +14 -9
- data/lib/active_merchant/billing/gateways/orbital.rb +202 -47
- data/lib/active_merchant/billing/gateways/pay_arc.rb +390 -0
- data/lib/active_merchant/billing/gateways/pay_trace.rb +404 -0
- data/lib/active_merchant/billing/gateways/payeezy.rb +57 -11
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -0
- data/lib/active_merchant/billing/gateways/payflow.rb +9 -0
- data/lib/active_merchant/billing/gateways/payment_express.rb +10 -5
- data/lib/active_merchant/billing/gateways/paymentez.rb +26 -1
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +1 -0
- data/lib/active_merchant/billing/gateways/paypal.rb +10 -2
- data/lib/active_merchant/billing/gateways/paypal_express.rb +1 -0
- data/lib/active_merchant/billing/gateways/paysafe.rb +291 -0
- data/lib/active_merchant/billing/gateways/payu_latam.rb +3 -3
- data/lib/active_merchant/billing/gateways/payway_dot_com.rb +253 -0
- data/lib/active_merchant/billing/gateways/pin.rb +11 -0
- data/lib/active_merchant/billing/gateways/qvalent.rb +23 -9
- data/lib/active_merchant/billing/gateways/redsys.rb +78 -30
- data/lib/active_merchant/billing/gateways/safe_charge.rb +19 -8
- data/lib/active_merchant/billing/gateways/spreedly_core.rb +13 -4
- data/lib/active_merchant/billing/gateways/stripe.rb +8 -8
- data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +86 -25
- data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +1 -1
- data/lib/active_merchant/billing/gateways/vpos.rb +220 -0
- data/lib/active_merchant/billing/gateways/worldpay.rb +68 -20
- data/lib/active_merchant/billing/response.rb +2 -1
- data/lib/active_merchant/billing/three_d_secure_eci_mapper.rb +27 -0
- data/lib/active_merchant/billing.rb +1 -0
- data/lib/active_merchant/version.rb +1 -1
- data/lib/certs/cacert.pem +1582 -2431
- metadata +10 -3
@@ -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
|
@@ -210,7 +237,7 @@ module ActiveMerchant #:nodoc:
|
|
210
237
|
def add_details(xml, options)
|
211
238
|
xml.hps :AdditionalTxnFields do
|
212
239
|
xml.hps :Description, options[:description] if options[:description]
|
213
|
-
xml.hps :InvoiceNbr, options[:order_id] if options[:order_id]
|
240
|
+
xml.hps :InvoiceNbr, options[:order_id][0..59] if options[:order_id]
|
214
241
|
xml.hps :CustomerID, options[:customer_id] if options[:customer_id]
|
215
242
|
end
|
216
243
|
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',
|
@@ -5,7 +5,10 @@ module ActiveMerchant #:nodoc:
|
|
5
5
|
class LitleGateway < Gateway
|
6
6
|
SCHEMA_VERSION = '9.14'
|
7
7
|
|
8
|
+
class_attribute :postlive_url
|
9
|
+
|
8
10
|
self.test_url = 'https://www.testvantivcnp.com/sandbox/communicator/online'
|
11
|
+
self.postlive_url = 'https://payments.vantivpostlive.com/vap/communicator/online'
|
9
12
|
self.live_url = 'https://payments.vantivcnp.com/vap/communicator/online'
|
10
13
|
|
11
14
|
self.supported_countries = ['US']
|
@@ -368,9 +371,9 @@ module ActiveMerchant #:nodoc:
|
|
368
371
|
return unless address
|
369
372
|
|
370
373
|
doc.companyName(address[:company]) unless address[:company].blank?
|
371
|
-
doc.addressLine1(address[:address1]) unless address[:address1].blank?
|
372
|
-
doc.addressLine2(address[:address2]) unless address[:address2].blank?
|
373
|
-
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?
|
374
377
|
doc.state(address[:state]) unless address[:state].blank?
|
375
378
|
doc.zip(address[:zip]) unless address[:zip].blank?
|
376
379
|
doc.country(address[:country]) unless address[:country].blank?
|
@@ -491,7 +494,7 @@ module ActiveMerchant #:nodoc:
|
|
491
494
|
attributes = {}
|
492
495
|
attributes[:id] = truncate(options[:id] || options[:order_id], 24)
|
493
496
|
attributes[:reportGroup] = options[:merchant] || 'Default Report Group'
|
494
|
-
attributes[:customerId] = options[:
|
497
|
+
attributes[:customerId] = options[:customer_id]
|
495
498
|
attributes.delete_if { |_key, value| value == nil }
|
496
499
|
attributes
|
497
500
|
end
|
@@ -513,6 +516,8 @@ module ActiveMerchant #:nodoc:
|
|
513
516
|
end
|
514
517
|
|
515
518
|
def url
|
519
|
+
return postlive_url if @options[:url_override].to_s == 'postlive'
|
520
|
+
|
516
521
|
test? ? test_url : live_url
|
517
522
|
end
|
518
523
|
|
@@ -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
|