activemerchant 1.119.0 → 1.124.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 +216 -1
- data/README.md +4 -2
- data/lib/active_merchant/billing/check.rb +19 -12
- data/lib/active_merchant/billing/credit_card.rb +3 -0
- data/lib/active_merchant/billing/credit_card_formatting.rb +1 -0
- data/lib/active_merchant/billing/credit_card_methods.rb +32 -14
- data/lib/active_merchant/billing/gateways/adyen.rb +94 -25
- 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/card_stream.rb +17 -13
- 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 +9 -2
- data/lib/active_merchant/billing/gateways/decidir.rb +7 -1
- data/lib/active_merchant/billing/gateways/elavon.rb +70 -28
- data/lib/active_merchant/billing/gateways/element.rb +2 -0
- data/lib/active_merchant/billing/gateways/forte.rb +12 -0
- data/lib/active_merchant/billing/gateways/global_collect.rb +24 -10
- data/lib/active_merchant/billing/gateways/hps.rb +55 -1
- data/lib/active_merchant/billing/gateways/kushki.rb +23 -0
- data/lib/active_merchant/billing/gateways/litle.rb +1 -1
- 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/mit.rb +260 -0
- data/lib/active_merchant/billing/gateways/moka.rb +290 -0
- data/lib/active_merchant/billing/gateways/monei.rb +228 -144
- data/lib/active_merchant/billing/gateways/mundipagg.rb +14 -5
- data/lib/active_merchant/billing/gateways/netbanx.rb +26 -2
- data/lib/active_merchant/billing/gateways/nmi.rb +27 -9
- data/lib/active_merchant/billing/gateways/orbital.rb +99 -59
- data/lib/active_merchant/billing/gateways/pay_arc.rb +392 -0
- data/lib/active_merchant/billing/gateways/pay_conex.rb +3 -1
- data/lib/active_merchant/billing/gateways/pay_trace.rb +404 -0
- data/lib/active_merchant/billing/gateways/payeezy.rb +34 -6
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -0
- data/lib/active_merchant/billing/gateways/payflow.rb +21 -4
- data/lib/active_merchant/billing/gateways/payment_express.rb +5 -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 +376 -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/realex.rb +18 -0
- data/lib/active_merchant/billing/gateways/redsys.rb +42 -24
- data/lib/active_merchant/billing/gateways/safe_charge.rb +25 -13
- data/lib/active_merchant/billing/gateways/spreedly_core.rb +13 -4
- data/lib/active_merchant/billing/gateways/stripe.rb +18 -8
- data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +126 -48
- data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +2 -1
- data/lib/active_merchant/billing/gateways/trust_commerce.rb +2 -1
- data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +1 -1
- data/lib/active_merchant/billing/gateways/vpos.rb +220 -0
- data/lib/active_merchant/billing/gateways/worldpay.rb +78 -18
- data/lib/active_merchant/billing/response.rb +4 -0
- data/lib/active_merchant/billing/three_d_secure_eci_mapper.rb +27 -0
- data/lib/active_merchant/billing.rb +1 -0
- data/lib/active_merchant/version.rb +1 -1
- data/lib/certs/cacert.pem +1582 -2431
- metadata +11 -3
@@ -1,10 +1,13 @@
|
|
1
1
|
module ActiveMerchant #:nodoc:
|
2
2
|
module Billing #:nodoc:
|
3
3
|
class GlobalCollectGateway < Gateway
|
4
|
+
class_attribute :preproduction_url
|
5
|
+
|
4
6
|
self.display_name = 'GlobalCollect'
|
5
7
|
self.homepage_url = 'http://www.globalcollect.com/'
|
6
8
|
|
7
9
|
self.test_url = 'https://eu.sandbox.api-ingenico.com'
|
10
|
+
self.preproduction_url = 'https://world.preprod.api-ingenico.com'
|
8
11
|
self.live_url = 'https://api.globalcollect.com'
|
9
12
|
|
10
13
|
self.supported_countries = %w[AD AE AG AI AL AM AO AR AS AT AU AW AX AZ BA BB BD BE BF BG BH BI BJ BL BM BN BO BQ BR BS BT BW BY BZ CA CC CD CF CH CI CK CL CM CN CO CR CU CV CW CX CY CZ DE DJ DK DM DO DZ EC EE EG ER ES ET FI FJ FK FM FO FR GA GB GD GE GF GH GI GL GM GN GP GQ GR GS GT GU GW GY HK HN HR HT HU ID IE IL IM IN IS IT JM JO JP KE KG KH KI KM KN KR KW KY KZ LA LB LC LI LK LR LS LT LU LV MA MC MD ME MF MG MH MK MM MN MO MP MQ MR MS MT MU MV MW MX MY MZ NA NC NE NG NI NL NO NP NR NU NZ OM PA PE PF PG PH PL PN PS PT PW QA RE RO RS RU RW SA SB SC SE SG SH SI SJ SK SL SM SN SR ST SV SZ TC TD TG TH TJ TL TM TN TO TR TT TV TW TZ UA UG US UY UZ VC VE VG VI VN WF WS ZA ZM ZW]
|
@@ -33,7 +36,7 @@ module ActiveMerchant #:nodoc:
|
|
33
36
|
add_creator_info(post, options)
|
34
37
|
add_fraud_fields(post, options)
|
35
38
|
add_external_cardholder_authentication_data(post, options)
|
36
|
-
commit(:authorize, post)
|
39
|
+
commit(:authorize, post, options: options)
|
37
40
|
end
|
38
41
|
|
39
42
|
def capture(money, authorization, options = {})
|
@@ -41,7 +44,7 @@ module ActiveMerchant #:nodoc:
|
|
41
44
|
add_order(post, money, options, capture: true)
|
42
45
|
add_customer_data(post, options)
|
43
46
|
add_creator_info(post, options)
|
44
|
-
commit(:capture, post, authorization)
|
47
|
+
commit(:capture, post, authorization: authorization)
|
45
48
|
end
|
46
49
|
|
47
50
|
def refund(money, authorization, options = {})
|
@@ -49,13 +52,13 @@ module ActiveMerchant #:nodoc:
|
|
49
52
|
add_amount(post, money, options)
|
50
53
|
add_refund_customer_data(post, options)
|
51
54
|
add_creator_info(post, options)
|
52
|
-
commit(:refund, post, authorization)
|
55
|
+
commit(:refund, post, authorization: authorization)
|
53
56
|
end
|
54
57
|
|
55
58
|
def void(authorization, options = {})
|
56
59
|
post = nestable_hash
|
57
60
|
add_creator_info(post, options)
|
58
|
-
commit(:void, post, authorization)
|
61
|
+
commit(:void, post, authorization: authorization)
|
59
62
|
end
|
60
63
|
|
61
64
|
def verify(payment, options = {})
|
@@ -260,6 +263,8 @@ module ActiveMerchant #:nodoc:
|
|
260
263
|
end
|
261
264
|
|
262
265
|
def url(action, authorization)
|
266
|
+
return preproduction_url + uri(action, authorization) if @options[:url_override].to_s == 'preproduction'
|
267
|
+
|
263
268
|
(test? ? test_url : live_url) + uri(action, authorization)
|
264
269
|
end
|
265
270
|
|
@@ -277,9 +282,13 @@ module ActiveMerchant #:nodoc:
|
|
277
282
|
end
|
278
283
|
end
|
279
284
|
|
280
|
-
def
|
285
|
+
def idempotency_key_for_signature(options)
|
286
|
+
"x-gcs-idempotence-key:#{options[:idempotency_key]}" if options[:idempotency_key]
|
287
|
+
end
|
288
|
+
|
289
|
+
def commit(action, post, authorization: nil, options: {})
|
281
290
|
begin
|
282
|
-
raw_response = ssl_post(url(action, authorization), post.to_json, headers(action, post, authorization))
|
291
|
+
raw_response = ssl_post(url(action, authorization), post.to_json, headers(action, post, authorization, options))
|
283
292
|
response = parse(raw_response)
|
284
293
|
rescue ResponseError => e
|
285
294
|
response = parse(e.response.body) if e.response.code.to_i >= 400
|
@@ -306,21 +315,26 @@ module ActiveMerchant #:nodoc:
|
|
306
315
|
}
|
307
316
|
end
|
308
317
|
|
309
|
-
def headers(action, post, authorization = nil)
|
310
|
-
{
|
318
|
+
def headers(action, post, authorization = nil, options = {})
|
319
|
+
headers = {
|
311
320
|
'Content-Type' => content_type,
|
312
|
-
'Authorization' => auth_digest(action, post, authorization),
|
321
|
+
'Authorization' => auth_digest(action, post, authorization, options),
|
313
322
|
'Date' => date
|
314
323
|
}
|
324
|
+
|
325
|
+
headers['X-GCS-Idempotence-Key'] = options[:idempotency_key] if options[:idempotency_key]
|
326
|
+
headers
|
315
327
|
end
|
316
328
|
|
317
|
-
def auth_digest(action, post, authorization = nil)
|
329
|
+
def auth_digest(action, post, authorization = nil, options = {})
|
318
330
|
data = <<~REQUEST
|
319
331
|
POST
|
320
332
|
#{content_type}
|
321
333
|
#{date}
|
334
|
+
#{idempotency_key_for_signature(options)}
|
322
335
|
#{uri(action, authorization)}
|
323
336
|
REQUEST
|
337
|
+
data = data.each_line.reject { |line| line.strip == '' }.join
|
324
338
|
digest = OpenSSL::Digest.new('sha256')
|
325
339
|
key = @options[:secret_api_key]
|
326
340
|
"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
|
@@ -131,6 +134,21 @@ module ActiveMerchant #:nodoc:
|
|
131
134
|
add_descriptor_name(xml, options)
|
132
135
|
add_card_or_token_payment(xml, card_or_token, options)
|
133
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)
|
134
152
|
end
|
135
153
|
end
|
136
154
|
|
@@ -157,7 +175,7 @@ module ActiveMerchant #:nodoc:
|
|
157
175
|
xml.hps :CardHolderAddr, billing_address[:address1] if billing_address[:address1]
|
158
176
|
xml.hps :CardHolderCity, billing_address[:city] if billing_address[:city]
|
159
177
|
xml.hps :CardHolderState, billing_address[:state] if billing_address[:state]
|
160
|
-
xml.hps :CardHolderZip, billing_address[:zip] if billing_address[:zip]
|
178
|
+
xml.hps :CardHolderZip, alphanumeric_zip(billing_address[:zip]) if billing_address[:zip]
|
161
179
|
end
|
162
180
|
end
|
163
181
|
end
|
@@ -265,6 +283,38 @@ module ActiveMerchant #:nodoc:
|
|
265
283
|
end
|
266
284
|
end
|
267
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
|
+
|
268
318
|
def strip_leading_zero(value)
|
269
319
|
return value unless value[0] == '0'
|
270
320
|
|
@@ -389,6 +439,10 @@ module ActiveMerchant #:nodoc:
|
|
389
439
|
@options[:secret_api_key]&.include?('_cert_')
|
390
440
|
end
|
391
441
|
|
442
|
+
def alphanumeric_zip(zip)
|
443
|
+
zip.gsub(/[^0-9a-z]/i, '')
|
444
|
+
end
|
445
|
+
|
392
446
|
ISSUER_MESSAGES = {
|
393
447
|
'13' => 'Must be greater than or equal 0.',
|
394
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',
|
@@ -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,260 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'openssl'
|
3
|
+
require 'digest'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
module ActiveMerchant #:nodoc:
|
7
|
+
module Billing #:nodoc:
|
8
|
+
class MitGateway < Gateway
|
9
|
+
self.live_url = 'https://wpy.mitec.com.mx/ModuloUtilWS/activeCDP.htm'
|
10
|
+
|
11
|
+
self.supported_countries = ['MX']
|
12
|
+
self.default_currency = 'MXN'
|
13
|
+
self.supported_cardtypes = %i[visa master]
|
14
|
+
|
15
|
+
self.homepage_url = 'http://www.centrodepagos.com.mx/'
|
16
|
+
self.display_name = 'MIT Centro de pagos'
|
17
|
+
|
18
|
+
self.money_format = :dollars
|
19
|
+
|
20
|
+
def initialize(options = {})
|
21
|
+
requires!(options, :commerce_id, :user, :api_key, :key_session)
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def purchase(money, payment, options = {})
|
26
|
+
MultiResponse.run do |r|
|
27
|
+
r.process { authorize(money, payment, options) }
|
28
|
+
r.process { capture(money, r.authorization, options) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def cipher_key
|
33
|
+
@options[:key_session]
|
34
|
+
end
|
35
|
+
|
36
|
+
def decrypt(val, keyinhex)
|
37
|
+
# Splits the first 16 bytes (the IV bytes) in array format
|
38
|
+
unpacked = val.unpack('m')
|
39
|
+
iv_base64 = unpacked[0].bytes.slice(0, 16)
|
40
|
+
# Splits the second bytes (the encrypted text bytes) these would be the
|
41
|
+
# original message
|
42
|
+
full_data = unpacked[0].bytes.slice(16, unpacked[0].bytes.length)
|
43
|
+
# Creates the engine
|
44
|
+
engine = OpenSSL::Cipher::AES128.new(:CBC)
|
45
|
+
# Set engine as decrypt mode
|
46
|
+
engine.decrypt
|
47
|
+
# Converts the key from hex to bytes
|
48
|
+
engine.key = [keyinhex].pack('H*')
|
49
|
+
# Converts the ivBase64 array into bytes
|
50
|
+
engine.iv = iv_base64.pack('c*')
|
51
|
+
# Decrypts the texts and returns the original string
|
52
|
+
engine.update(full_data.pack('c*')) + engine.final
|
53
|
+
end
|
54
|
+
|
55
|
+
def encrypt(val, keyinhex)
|
56
|
+
# Creates the engine motor
|
57
|
+
engine = OpenSSL::Cipher::AES128.new(:CBC)
|
58
|
+
# Set engine as encrypt mode
|
59
|
+
engine.encrypt
|
60
|
+
# Converts the key from hex to bytes
|
61
|
+
engine.key = [keyinhex].pack('H*')
|
62
|
+
# Generates a random iv with this settings
|
63
|
+
iv_rand = engine.random_iv
|
64
|
+
# Packs IV as a Base64 string
|
65
|
+
iv_base64 = [iv_rand].pack('m')
|
66
|
+
# Converts the packed key into bytes
|
67
|
+
unpacked = iv_base64.unpack('m')
|
68
|
+
iv = unpacked[0]
|
69
|
+
# Sets the IV into the engine
|
70
|
+
engine.iv = iv
|
71
|
+
# Encrypts the texts and stores the bytes
|
72
|
+
encrypted_bytes = engine.update(val) + engine.final
|
73
|
+
# Concatenates the (a) IV bytes and (b) the encrypted bytes then returns a
|
74
|
+
# base64 representation
|
75
|
+
[iv << encrypted_bytes].pack('m')
|
76
|
+
end
|
77
|
+
|
78
|
+
def authorize(money, payment, options = {})
|
79
|
+
post = {
|
80
|
+
operation: 'Authorize',
|
81
|
+
commerce_id: @options[:commerce_id],
|
82
|
+
user: @options[:user],
|
83
|
+
apikey: @options[:api_key],
|
84
|
+
testMode: (test? ? 'YES' : 'NO')
|
85
|
+
}
|
86
|
+
add_invoice(post, money, options)
|
87
|
+
# Payments contains the card information
|
88
|
+
add_payment(post, payment)
|
89
|
+
add_customer_data(post, options)
|
90
|
+
post[:key_session] = @options[:key_session]
|
91
|
+
|
92
|
+
post_to_json = post.to_json
|
93
|
+
post_to_json_encrypt = encrypt(post_to_json, @options[:key_session])
|
94
|
+
|
95
|
+
final_post = '<authorization>' + post_to_json_encrypt + '</authorization><dataID>' + @options[:user] + '</dataID>'
|
96
|
+
json_post = {}
|
97
|
+
json_post[:payload] = final_post
|
98
|
+
commit('sale', json_post)
|
99
|
+
end
|
100
|
+
|
101
|
+
def capture(money, authorization, options = {})
|
102
|
+
post = {
|
103
|
+
operation: 'Capture',
|
104
|
+
commerce_id: @options[:commerce_id],
|
105
|
+
user: @options[:user],
|
106
|
+
apikey: @options[:api_key],
|
107
|
+
testMode: (test? ? 'YES' : 'NO'),
|
108
|
+
transaction_id: authorization,
|
109
|
+
amount: amount(money)
|
110
|
+
}
|
111
|
+
post[:key_session] = @options[:key_session]
|
112
|
+
|
113
|
+
post_to_json = post.to_json
|
114
|
+
post_to_json_encrypt = encrypt(post_to_json, @options[:key_session])
|
115
|
+
|
116
|
+
final_post = '<capture>' + post_to_json_encrypt + '</capture><dataID>' + @options[:user] + '</dataID>'
|
117
|
+
json_post = {}
|
118
|
+
json_post[:payload] = final_post
|
119
|
+
commit('capture', json_post)
|
120
|
+
end
|
121
|
+
|
122
|
+
def refund(money, authorization, options = {})
|
123
|
+
post = {
|
124
|
+
operation: 'Refund',
|
125
|
+
commerce_id: @options[:commerce_id],
|
126
|
+
user: @options[:user],
|
127
|
+
apikey: @options[:api_key],
|
128
|
+
testMode: (test? ? 'YES' : 'NO'),
|
129
|
+
transaction_id: authorization,
|
130
|
+
auth: authorization,
|
131
|
+
amount: amount(money)
|
132
|
+
}
|
133
|
+
post[:key_session] = @options[:key_session]
|
134
|
+
|
135
|
+
post_to_json = post.to_json
|
136
|
+
post_to_json_encrypt = encrypt(post_to_json, @options[:key_session])
|
137
|
+
|
138
|
+
final_post = '<refund>' + post_to_json_encrypt + '</refund><dataID>' + @options[:user] + '</dataID>'
|
139
|
+
json_post = {}
|
140
|
+
json_post[:payload] = final_post
|
141
|
+
commit('refund', json_post)
|
142
|
+
end
|
143
|
+
|
144
|
+
def supports_scrubbing?
|
145
|
+
true
|
146
|
+
end
|
147
|
+
|
148
|
+
def scrub(transcript)
|
149
|
+
ret_transcript = transcript
|
150
|
+
auth_origin = ret_transcript[/<authorization>(.*?)<\/authorization>/, 1]
|
151
|
+
unless auth_origin.nil?
|
152
|
+
auth_decrypted = decrypt(auth_origin, @options[:key_session])
|
153
|
+
auth_json = JSON.parse(auth_decrypted)
|
154
|
+
auth_json['card'] = '[FILTERED]'
|
155
|
+
auth_json['cvv'] = '[FILTERED]'
|
156
|
+
auth_json['apikey'] = '[FILTERED]'
|
157
|
+
auth_json['key_session'] = '[FILTERED]'
|
158
|
+
auth_to_json = auth_json.to_json
|
159
|
+
auth_tagged = '<authorization>' + auth_to_json + '</authorization>'
|
160
|
+
ret_transcript = ret_transcript.gsub(/<authorization>(.*?)<\/authorization>/, auth_tagged)
|
161
|
+
end
|
162
|
+
|
163
|
+
cap_origin = ret_transcript[/<capture>(.*?)<\/capture>/, 1]
|
164
|
+
unless cap_origin.nil?
|
165
|
+
cap_decrypted = decrypt(cap_origin, @options[:key_session])
|
166
|
+
cap_json = JSON.parse(cap_decrypted)
|
167
|
+
cap_json['apikey'] = '[FILTERED]'
|
168
|
+
cap_json['key_session'] = '[FILTERED]'
|
169
|
+
cap_to_json = cap_json.to_json
|
170
|
+
cap_tagged = '<capture>' + cap_to_json + '</capture>'
|
171
|
+
ret_transcript = ret_transcript.gsub(/<capture>(.*?)<\/capture>/, cap_tagged)
|
172
|
+
end
|
173
|
+
|
174
|
+
ref_origin = ret_transcript[/<refund>(.*?)<\/refund>/, 1]
|
175
|
+
unless ref_origin.nil?
|
176
|
+
ref_decrypted = decrypt(ref_origin, @options[:key_session])
|
177
|
+
ref_json = JSON.parse(ref_decrypted)
|
178
|
+
ref_json['apikey'] = '[FILTERED]'
|
179
|
+
ref_json['key_session'] = '[FILTERED]'
|
180
|
+
ref_to_json = ref_json.to_json
|
181
|
+
ref_tagged = '<refund>' + ref_to_json + '</refund>'
|
182
|
+
ret_transcript = ret_transcript.gsub(/<refund>(.*?)<\/refund>/, ref_tagged)
|
183
|
+
end
|
184
|
+
|
185
|
+
res_origin = ret_transcript[/#{Regexp.escape('reading ')}(.*?)#{Regexp.escape('read')}/m, 1]
|
186
|
+
loop do
|
187
|
+
break if res_origin.nil?
|
188
|
+
|
189
|
+
resp_origin = res_origin[/#{Regexp.escape('"')}(.*?)#{Regexp.escape('"')}/m, 1]
|
190
|
+
resp_decrypted = decrypt(resp_origin, @options[:key_session])
|
191
|
+
ret_transcript[/#{Regexp.escape('reading ')}(.*?)#{Regexp.escape('read')}/m, 1] = resp_decrypted
|
192
|
+
ret_transcript = ret_transcript.sub('reading ', 'response: ')
|
193
|
+
res_origin = ret_transcript[/#{Regexp.escape('reading ')}(.*?)#{Regexp.escape('read')}/m, 1]
|
194
|
+
end
|
195
|
+
|
196
|
+
ret_transcript
|
197
|
+
end
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
def add_customer_data(post, options)
|
202
|
+
post[:email] = options[:email] || 'nadie@mit.test'
|
203
|
+
end
|
204
|
+
|
205
|
+
def add_invoice(post, money, options)
|
206
|
+
post[:amount] = amount(money)
|
207
|
+
post[:currency] = (options[:currency] || currency(money))
|
208
|
+
post[:reference] = options[:order_id]
|
209
|
+
post[:transaction_id] = options[:order_id]
|
210
|
+
end
|
211
|
+
|
212
|
+
def add_payment(post, payment)
|
213
|
+
post[:installments] = 1
|
214
|
+
post[:card] = payment.number
|
215
|
+
post[:expmonth] = payment.month
|
216
|
+
post[:expyear] = payment.year
|
217
|
+
post[:cvv] = payment.verification_value
|
218
|
+
post[:name_client] = [payment.first_name, payment.last_name].join(' ')
|
219
|
+
end
|
220
|
+
|
221
|
+
def commit(action, parameters)
|
222
|
+
json_str = JSON.generate(parameters)
|
223
|
+
cleaned_str = json_str.gsub('\n', '')
|
224
|
+
raw_response = ssl_post(live_url, cleaned_str, { 'Content-type' => 'application/json' })
|
225
|
+
response = JSON.parse(decrypt(raw_response, @options[:key_session]))
|
226
|
+
|
227
|
+
Response.new(
|
228
|
+
success_from(response),
|
229
|
+
message_from(response),
|
230
|
+
response,
|
231
|
+
authorization: authorization_from(response),
|
232
|
+
avs_result: AVSResult.new(code: response['some_avs_response_key']),
|
233
|
+
cvv_result: CVVResult.new(response['some_cvv_response_key']),
|
234
|
+
test: test?,
|
235
|
+
error_code: error_code_from(response)
|
236
|
+
)
|
237
|
+
end
|
238
|
+
|
239
|
+
def success_from(response)
|
240
|
+
response['response'] == 'approved'
|
241
|
+
end
|
242
|
+
|
243
|
+
def message_from(response)
|
244
|
+
response['response']
|
245
|
+
end
|
246
|
+
|
247
|
+
def authorization_from(response)
|
248
|
+
response['reference']
|
249
|
+
end
|
250
|
+
|
251
|
+
def post_data(action, parameters = {})
|
252
|
+
parameters.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&')
|
253
|
+
end
|
254
|
+
|
255
|
+
def error_code_from(response)
|
256
|
+
response['message'].split(' -- ', 2)[0] unless success_from(response)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|