activemerchant 1.117.0 → 1.123.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +217 -0
- data/README.md +5 -3
- data/lib/active_merchant/billing/check.rb +19 -12
- data/lib/active_merchant/billing/credit_card.rb +6 -0
- data/lib/active_merchant/billing/credit_card_formatting.rb +1 -0
- data/lib/active_merchant/billing/credit_card_methods.rb +96 -22
- data/lib/active_merchant/billing/gateways/adyen.rb +38 -21
- data/lib/active_merchant/billing/gateways/authorize_net.rb +19 -11
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +4 -0
- data/lib/active_merchant/billing/gateways/blue_pay.rb +29 -0
- data/lib/active_merchant/billing/gateways/blue_snap.rb +5 -3
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +58 -8
- data/lib/active_merchant/billing/gateways/cashnet.rb +7 -2
- data/lib/active_merchant/billing/gateways/checkout_v2.rb +31 -0
- data/lib/active_merchant/billing/gateways/credorax.rb +16 -9
- data/lib/active_merchant/billing/gateways/cyber_source.rb +67 -9
- data/lib/active_merchant/billing/gateways/d_local.rb +1 -1
- data/lib/active_merchant/billing/gateways/decidir.rb +29 -3
- data/lib/active_merchant/billing/gateways/elavon.rb +110 -26
- data/lib/active_merchant/billing/gateways/element.rb +2 -0
- data/lib/active_merchant/billing/gateways/eway_rapid.rb +13 -0
- data/lib/active_merchant/billing/gateways/firstdata_e4_v27.rb +17 -6
- data/lib/active_merchant/billing/gateways/forte.rb +12 -0
- data/lib/active_merchant/billing/gateways/global_collect.rb +25 -16
- data/lib/active_merchant/billing/gateways/hps.rb +65 -2
- data/lib/active_merchant/billing/gateways/kushki.rb +23 -0
- data/lib/active_merchant/billing/gateways/litle.rb +9 -4
- data/lib/active_merchant/billing/gateways/mercado_pago.rb +5 -4
- data/lib/active_merchant/billing/gateways/merchant_warrior.rb +2 -0
- data/lib/active_merchant/billing/gateways/moka.rb +277 -0
- data/lib/active_merchant/billing/gateways/monei.rb +228 -144
- data/lib/active_merchant/billing/gateways/mundipagg.rb +14 -5
- data/lib/active_merchant/billing/gateways/netbanx.rb +37 -2
- data/lib/active_merchant/billing/gateways/nmi.rb +14 -9
- data/lib/active_merchant/billing/gateways/orbital.rb +202 -47
- data/lib/active_merchant/billing/gateways/pay_arc.rb +390 -0
- data/lib/active_merchant/billing/gateways/pay_trace.rb +404 -0
- data/lib/active_merchant/billing/gateways/payeezy.rb +57 -11
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -0
- data/lib/active_merchant/billing/gateways/payflow.rb +9 -0
- data/lib/active_merchant/billing/gateways/payment_express.rb +10 -5
- data/lib/active_merchant/billing/gateways/paymentez.rb +26 -1
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +1 -0
- data/lib/active_merchant/billing/gateways/paypal.rb +10 -2
- data/lib/active_merchant/billing/gateways/paypal_express.rb +1 -0
- data/lib/active_merchant/billing/gateways/paysafe.rb +291 -0
- data/lib/active_merchant/billing/gateways/payu_latam.rb +3 -3
- data/lib/active_merchant/billing/gateways/payway_dot_com.rb +253 -0
- data/lib/active_merchant/billing/gateways/pin.rb +11 -0
- data/lib/active_merchant/billing/gateways/qvalent.rb +23 -9
- data/lib/active_merchant/billing/gateways/redsys.rb +78 -30
- data/lib/active_merchant/billing/gateways/safe_charge.rb +19 -8
- data/lib/active_merchant/billing/gateways/spreedly_core.rb +13 -4
- data/lib/active_merchant/billing/gateways/stripe.rb +8 -8
- data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +86 -25
- data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +1 -1
- data/lib/active_merchant/billing/gateways/vpos.rb +220 -0
- data/lib/active_merchant/billing/gateways/worldpay.rb +68 -20
- data/lib/active_merchant/billing/response.rb +2 -1
- data/lib/active_merchant/billing/three_d_secure_eci_mapper.rb +27 -0
- data/lib/active_merchant/billing.rb +1 -0
- data/lib/active_merchant/version.rb +1 -1
- data/lib/certs/cacert.pem +1582 -2431
- metadata +10 -3
@@ -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
|