activemerchant 1.125.0 → 1.126.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 +75 -0
- data/lib/active_merchant/billing/credit_card_methods.rb +12 -0
- data/lib/active_merchant/billing/gateway.rb +2 -1
- data/lib/active_merchant/billing/gateways/adyen.rb +7 -4
- data/lib/active_merchant/billing/gateways/airwallex.rb +341 -0
- data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +2 -1
- data/lib/active_merchant/billing/gateways/blue_pay.rb +1 -1
- data/lib/active_merchant/billing/gateways/blue_snap.rb +31 -21
- data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +6 -1
- data/lib/active_merchant/billing/gateways/braintree/token_nonce.rb +113 -0
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +87 -15
- data/lib/active_merchant/billing/gateways/card_connect.rb +1 -1
- data/lib/active_merchant/billing/gateways/checkout_v2.rb +1 -1
- data/lib/active_merchant/billing/gateways/credorax.rb +10 -0
- data/lib/active_merchant/billing/gateways/cyber_source.rb +13 -33
- data/lib/active_merchant/billing/gateways/d_local.rb +49 -0
- data/lib/active_merchant/billing/gateways/decidir.rb +17 -1
- data/lib/active_merchant/billing/gateways/decidir_plus.rb +185 -14
- data/lib/active_merchant/billing/gateways/ebanx.rb +3 -2
- data/lib/active_merchant/billing/gateways/global_collect.rb +26 -16
- data/lib/active_merchant/billing/gateways/ipg.rb +1 -2
- data/lib/active_merchant/billing/gateways/litle.rb +93 -1
- data/lib/active_merchant/billing/gateways/moneris.rb +35 -8
- data/lib/active_merchant/billing/gateways/nmi.rb +12 -7
- data/lib/active_merchant/billing/gateways/orbital.rb +349 -327
- data/lib/active_merchant/billing/gateways/payflow.rb +62 -0
- data/lib/active_merchant/billing/gateways/paymentez.rb +26 -7
- data/lib/active_merchant/billing/gateways/paysafe.rb +15 -15
- data/lib/active_merchant/billing/gateways/payu_latam.rb +25 -15
- data/lib/active_merchant/billing/gateways/priority.rb +158 -136
- data/lib/active_merchant/billing/gateways/rapyd.rb +258 -0
- data/lib/active_merchant/billing/gateways/safe_charge.rb +1 -4
- data/lib/active_merchant/billing/gateways/simetrik.rb +362 -0
- data/lib/active_merchant/billing/gateways/stripe.rb +4 -2
- data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +93 -48
- data/lib/active_merchant/billing/gateways/visanet_peru.rb +6 -2
- data/lib/active_merchant/version.rb +1 -1
- metadata +6 -2
@@ -117,11 +117,11 @@ module ActiveMerchant #:nodoc:
|
|
117
117
|
post[:establishment_name] = options[:establishment_name] if options[:establishment_name]
|
118
118
|
post[:fraud_detection] = add_fraud_detection(options[:fraud_detection]) if options[:fraud_detection].present?
|
119
119
|
post[:site_id] = options[:site_id] if options[:site_id]
|
120
|
-
post[:sub_payments] = []
|
121
120
|
|
122
121
|
add_invoice(post, money, options)
|
123
122
|
add_payment(post, credit_card, options)
|
124
123
|
add_aggregate_data(post, options) if options[:aggregate_data]
|
124
|
+
add_sub_payments(post, options)
|
125
125
|
end
|
126
126
|
|
127
127
|
def add_payment_method_id(credit_card, options)
|
@@ -210,6 +210,22 @@ module ActiveMerchant #:nodoc:
|
|
210
210
|
post[:aggregate_data] = aggregate_data
|
211
211
|
end
|
212
212
|
|
213
|
+
def add_sub_payments(post, options)
|
214
|
+
# sub_payments field is required for purchase transactions, even if empty
|
215
|
+
post[:sub_payments] = []
|
216
|
+
|
217
|
+
return unless sub_payments = options[:sub_payments]
|
218
|
+
|
219
|
+
sub_payments.each do |sub_payment|
|
220
|
+
sub_payment_hash = {
|
221
|
+
site_id: sub_payment[:site_id],
|
222
|
+
installments: sub_payment[:installments].to_i,
|
223
|
+
amount: sub_payment[:amount].to_i
|
224
|
+
}
|
225
|
+
post[:sub_payments] << sub_payment_hash
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
213
229
|
def add_fraud_detection(options = {})
|
214
230
|
{}.tap do |hsh|
|
215
231
|
hsh[:send_to_cs] = options[:send_to_cs] if valid_fraud_detection_option?(options[:send_to_cs]) # true/false
|
@@ -6,7 +6,7 @@ module ActiveMerchant #:nodoc:
|
|
6
6
|
|
7
7
|
self.supported_countries = ['AR']
|
8
8
|
self.default_currency = 'ARS'
|
9
|
-
self.supported_cardtypes = %i[visa master american_express discover]
|
9
|
+
self.supported_cardtypes = %i[visa master american_express discover diners_club naranja cabal]
|
10
10
|
|
11
11
|
self.homepage_url = 'http://decidir.com.ar/home'
|
12
12
|
self.display_name = 'Decidir Plus'
|
@@ -18,20 +18,44 @@ module ActiveMerchant #:nodoc:
|
|
18
18
|
|
19
19
|
def purchase(money, payment, options = {})
|
20
20
|
post = {}
|
21
|
+
build_purchase_authorize_request(post, money, payment, options)
|
21
22
|
|
22
|
-
|
23
|
-
|
23
|
+
commit(:post, 'payments', post)
|
24
|
+
end
|
25
|
+
|
26
|
+
def authorize(money, payment, options = {})
|
27
|
+
post = {}
|
28
|
+
build_purchase_authorize_request(post, money, payment, options)
|
24
29
|
|
25
30
|
commit(:post, 'payments', post)
|
26
31
|
end
|
27
32
|
|
33
|
+
def capture(money, authorization, options = {})
|
34
|
+
post = {}
|
35
|
+
post[:amount] = money
|
36
|
+
|
37
|
+
commit(:put, "payments/#{add_reference(authorization)}", post)
|
38
|
+
end
|
39
|
+
|
28
40
|
def refund(money, authorization, options = {})
|
29
41
|
post = {}
|
30
42
|
post[:amount] = money
|
31
43
|
|
44
|
+
commit(:post, "payments/#{add_reference(authorization)}/refunds", post)
|
45
|
+
end
|
46
|
+
|
47
|
+
def void(authorization, options = {})
|
32
48
|
commit(:post, "payments/#{add_reference(authorization)}/refunds")
|
33
49
|
end
|
34
50
|
|
51
|
+
def verify(credit_card, options = {})
|
52
|
+
MultiResponse.run(:use_first_response) do |r|
|
53
|
+
r.process { store(credit_card, options) }
|
54
|
+
r.process { authorize(100, r.authorization, options) }
|
55
|
+
r.process(:ignore_result) { void(r.authorization, options) }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
35
59
|
def store(payment, options = {})
|
36
60
|
post = {}
|
37
61
|
add_payment(post, payment, options)
|
@@ -39,6 +63,10 @@ module ActiveMerchant #:nodoc:
|
|
39
63
|
commit(:post, 'tokens', post)
|
40
64
|
end
|
41
65
|
|
66
|
+
def unstore(customer_token)
|
67
|
+
commit(:delete, "cardtokens/#{customer_token}")
|
68
|
+
end
|
69
|
+
|
42
70
|
def supports_scrubbing?
|
43
71
|
true
|
44
72
|
end
|
@@ -52,6 +80,13 @@ module ActiveMerchant #:nodoc:
|
|
52
80
|
|
53
81
|
private
|
54
82
|
|
83
|
+
def build_purchase_authorize_request(post, money, payment, options)
|
84
|
+
add_customer_data(post, options)
|
85
|
+
add_payment(post, payment, options)
|
86
|
+
add_purchase_data(post, money, payment, options)
|
87
|
+
add_fraud_detection(post, options)
|
88
|
+
end
|
89
|
+
|
55
90
|
def add_reference(authorization)
|
56
91
|
return unless authorization
|
57
92
|
|
@@ -65,26 +100,64 @@ module ActiveMerchant #:nodoc:
|
|
65
100
|
post[:bin] = bin
|
66
101
|
else
|
67
102
|
post[:card_number] = payment.number
|
68
|
-
post[:card_expiration_month] = payment.month
|
69
|
-
post[:card_expiration_year] = payment.year
|
103
|
+
post[:card_expiration_month] = format(payment.month, :two_digits)
|
104
|
+
post[:card_expiration_year] = format(payment.year, :two_digits)
|
70
105
|
post[:security_code] = payment.verification_value.to_s
|
71
|
-
post[:card_holder_name] = payment.name
|
106
|
+
post[:card_holder_name] = payment.name.empty? ? options[:name_override] : payment.name
|
72
107
|
post[:card_holder_identification] = {}
|
73
|
-
post[:card_holder_identification][:type] = options[:
|
74
|
-
post[:card_holder_identification][:number] = options[:card_holder_identification_number]
|
108
|
+
post[:card_holder_identification][:type] = options[:card_holder_identification_type] if options[:card_holder_identification_type]
|
109
|
+
post[:card_holder_identification][:number] = options[:card_holder_identification_number] if options[:card_holder_identification_number]
|
110
|
+
|
111
|
+
# additional data used for Visa transactions
|
112
|
+
post[:card_holder_door_number] = options[:card_holder_door_number].to_i if options[:card_holder_door_number]
|
113
|
+
post[:card_holder_birthday] = options[:card_holder_birthday] if options[:card_holder_birthday]
|
75
114
|
end
|
76
115
|
end
|
77
116
|
|
117
|
+
def add_customer_data(post, options = {})
|
118
|
+
return unless customer = options[:customer]
|
119
|
+
|
120
|
+
post[:customer] = {}
|
121
|
+
post[:customer][:id] = customer[:id] if customer[:id]
|
122
|
+
post[:customer][:email] = customer[:email] if customer[:email]
|
123
|
+
end
|
124
|
+
|
78
125
|
def add_purchase_data(post, money, payment, options = {})
|
79
126
|
post[:site_transaction_id] = options[:site_transaction_id] || SecureRandom.hex
|
80
|
-
post[:payment_method_id] =
|
127
|
+
post[:payment_method_id] = add_payment_method_id(options)
|
81
128
|
post[:amount] = money
|
82
129
|
post[:currency] = options[:currency] || self.default_currency
|
83
130
|
post[:installments] = options[:installments] || 1
|
84
131
|
post[:payment_type] = options[:payment_type] || 'single'
|
132
|
+
post[:establishment_name] = options[:establishment_name] if options[:establishment_name]
|
133
|
+
|
134
|
+
add_aggregate_data(post, options) if options[:aggregate_data]
|
85
135
|
add_sub_payments(post, options)
|
86
136
|
end
|
87
137
|
|
138
|
+
def add_aggregate_data(post, options)
|
139
|
+
aggregate_data = {}
|
140
|
+
data = options[:aggregate_data]
|
141
|
+
aggregate_data[:indicator] = data[:indicator] if data[:indicator]
|
142
|
+
aggregate_data[:identification_number] = data[:identification_number] if data[:identification_number]
|
143
|
+
aggregate_data[:bill_to_pay] = data[:bill_to_pay] if data[:bill_to_pay]
|
144
|
+
aggregate_data[:bill_to_refund] = data[:bill_to_refund] if data[:bill_to_refund]
|
145
|
+
aggregate_data[:merchant_name] = data[:merchant_name] if data[:merchant_name]
|
146
|
+
aggregate_data[:street] = data[:street] if data[:street]
|
147
|
+
aggregate_data[:number] = data[:number] if data[:number]
|
148
|
+
aggregate_data[:postal_code] = data[:postal_code] if data[:postal_code]
|
149
|
+
aggregate_data[:category] = data[:category] if data[:category]
|
150
|
+
aggregate_data[:channel] = data[:channel] if data[:channel]
|
151
|
+
aggregate_data[:geographic_code] = data[:geographic_code] if data[:geographic_code]
|
152
|
+
aggregate_data[:city] = data[:city] if data[:city]
|
153
|
+
aggregate_data[:merchant_id] = data[:merchant_id] if data[:merchant_id]
|
154
|
+
aggregate_data[:province] = data[:province] if data[:province]
|
155
|
+
aggregate_data[:country] = data[:country] if data[:country]
|
156
|
+
aggregate_data[:merchant_email] = data[:merchant_email] if data[:merchant_email]
|
157
|
+
aggregate_data[:merchant_phone] = data[:merchant_phone] if data[:merchant_phone]
|
158
|
+
post[:aggregate_data] = aggregate_data
|
159
|
+
end
|
160
|
+
|
88
161
|
def add_sub_payments(post, options)
|
89
162
|
# sub_payments field is required for purchase transactions, even if empty
|
90
163
|
post[:sub_payments] = []
|
@@ -94,14 +167,78 @@ module ActiveMerchant #:nodoc:
|
|
94
167
|
sub_payments.each do |sub_payment|
|
95
168
|
sub_payment_hash = {
|
96
169
|
site_id: sub_payment[:site_id],
|
97
|
-
installments: sub_payment[:installments],
|
98
|
-
amount: sub_payment[:amount]
|
170
|
+
installments: sub_payment[:installments].to_i,
|
171
|
+
amount: sub_payment[:amount].to_i
|
99
172
|
}
|
100
173
|
post[:sub_payments] << sub_payment_hash
|
101
174
|
end
|
102
175
|
end
|
103
176
|
|
177
|
+
def add_payment_method_id(options)
|
178
|
+
return options[:payment_method_id].to_i if options[:payment_method_id]
|
179
|
+
|
180
|
+
if options[:debit]
|
181
|
+
case options[:card_brand]
|
182
|
+
when 'visa'
|
183
|
+
31
|
184
|
+
when 'master'
|
185
|
+
105
|
186
|
+
when 'maestro'
|
187
|
+
106
|
188
|
+
when 'cabal'
|
189
|
+
108
|
190
|
+
else
|
191
|
+
31
|
192
|
+
end
|
193
|
+
else
|
194
|
+
case options[:card_brand]
|
195
|
+
when 'visa'
|
196
|
+
1
|
197
|
+
when 'master'
|
198
|
+
104
|
199
|
+
when 'american_express'
|
200
|
+
65
|
201
|
+
when 'american_express_prisma'
|
202
|
+
111
|
203
|
+
when 'cabal'
|
204
|
+
63
|
205
|
+
when 'diners_club'
|
206
|
+
8
|
207
|
+
else
|
208
|
+
1
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def add_fraud_detection(post, options)
|
214
|
+
return unless fraud_detection = options[:fraud_detection]
|
215
|
+
|
216
|
+
{}.tap do |hsh|
|
217
|
+
hsh[:send_to_cs] = fraud_detection[:send_to_cs] == 'true' # true/false
|
218
|
+
hsh[:channel] = fraud_detection[:channel] if fraud_detection[:channel]
|
219
|
+
hsh[:dispatch_method] = fraud_detection[:dispatch_method] if fraud_detection[:dispatch_method]
|
220
|
+
add_csmdds(hsh, fraud_detection)
|
221
|
+
|
222
|
+
post[:fraud_detection] = hsh
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def add_csmdds(hsh, fraud_detection)
|
227
|
+
return unless fraud_detection[:csmdds]
|
228
|
+
|
229
|
+
csmdds_arr = []
|
230
|
+
fraud_detection[:csmdds].each do |csmdds|
|
231
|
+
csmdds_hsh = {}
|
232
|
+
csmdds_hsh[:code] = csmdds[:code].to_i
|
233
|
+
csmdds_hsh[:description] = csmdds[:description]
|
234
|
+
csmdds_arr.append(csmdds_hsh)
|
235
|
+
end
|
236
|
+
hsh[:csmdds] = csmdds_arr unless csmdds_arr.empty?
|
237
|
+
end
|
238
|
+
|
104
239
|
def parse(body)
|
240
|
+
return {} if body.nil?
|
241
|
+
|
105
242
|
JSON.parse(body)
|
106
243
|
end
|
107
244
|
|
@@ -140,11 +277,13 @@ module ActiveMerchant #:nodoc:
|
|
140
277
|
end
|
141
278
|
|
142
279
|
def success_from(response)
|
143
|
-
response.dig('status') == 'approved' || response.dig('status') == 'active'
|
280
|
+
response.dig('status') == 'approved' || response.dig('status') == 'active' || response.dig('status') == 'pre_approved' || response.empty?
|
144
281
|
end
|
145
282
|
|
146
283
|
def message_from(response)
|
147
|
-
|
284
|
+
return '' if response.empty?
|
285
|
+
|
286
|
+
rejected?(response) ? message_from_status_details(response) : response.dig('status') || error_message(response) || response.dig('message')
|
148
287
|
end
|
149
288
|
|
150
289
|
def authorization_from(response)
|
@@ -158,7 +297,24 @@ module ActiveMerchant #:nodoc:
|
|
158
297
|
end
|
159
298
|
|
160
299
|
def error_code_from(response)
|
161
|
-
|
300
|
+
return if success_from(response)
|
301
|
+
|
302
|
+
error_code = nil
|
303
|
+
if error = response.dig('status_details', 'error')
|
304
|
+
error_code = error.dig('reason', 'id') || error['type']
|
305
|
+
elsif response['error_type']
|
306
|
+
error_code = response['error_type']
|
307
|
+
elsif response.dig('error', 'validation_errors')
|
308
|
+
error = response.dig('error')
|
309
|
+
validation_errors = error.dig('validation_errors', 0)
|
310
|
+
code = validation_errors['code'] if validation_errors && validation_errors['code']
|
311
|
+
param = validation_errors['param'] if validation_errors && validation_errors['param']
|
312
|
+
error_code = "#{error['error_type']} | #{code} | #{param}" if error['error_type']
|
313
|
+
elsif error = response.dig('error')
|
314
|
+
error_code = error.dig('reason', 'id')
|
315
|
+
end
|
316
|
+
|
317
|
+
error_code
|
162
318
|
end
|
163
319
|
|
164
320
|
def error_message(response)
|
@@ -168,6 +324,21 @@ module ActiveMerchant #:nodoc:
|
|
168
324
|
|
169
325
|
"#{validation_errors.dig('code')}: #{validation_errors.dig('param')}"
|
170
326
|
end
|
327
|
+
|
328
|
+
def rejected?(response)
|
329
|
+
return response.dig('status') == 'rejected'
|
330
|
+
end
|
331
|
+
|
332
|
+
def message_from_status_details(response)
|
333
|
+
return unless error = response.dig('status_details', 'error')
|
334
|
+
return message_from_fraud_detection(response) if error.dig('type') == 'cybersource_error'
|
335
|
+
|
336
|
+
"#{error.dig('type')}: #{error.dig('reason', 'description')}"
|
337
|
+
end
|
338
|
+
|
339
|
+
def message_from_fraud_detection(response)
|
340
|
+
return error_message(response.dig('fraud_detection', 'status', 'details'))
|
341
|
+
end
|
171
342
|
end
|
172
343
|
end
|
173
344
|
end
|
@@ -42,8 +42,8 @@ module ActiveMerchant #:nodoc:
|
|
42
42
|
'ar' => 100,
|
43
43
|
'co' => 100,
|
44
44
|
'pe' => 300,
|
45
|
-
'mx' =>
|
46
|
-
'cl' =>
|
45
|
+
'mx' => 2000,
|
46
|
+
'cl' => 80000
|
47
47
|
}
|
48
48
|
|
49
49
|
def initialize(options = {})
|
@@ -183,6 +183,7 @@ module ActiveMerchant #:nodoc:
|
|
183
183
|
post[:payment][:currency_code] = (options[:currency] || currency(money))
|
184
184
|
post[:payment][:merchant_payment_code] = Digest::MD5.hexdigest(options[:order_id])
|
185
185
|
post[:payment][:instalments] = options[:instalments] || 1
|
186
|
+
post[:payment][:order_number] = options[:order_id][0..39] if options[:order_id]
|
186
187
|
end
|
187
188
|
|
188
189
|
def add_card_or_token(post, payment)
|
@@ -87,7 +87,9 @@ module ActiveMerchant #:nodoc:
|
|
87
87
|
'master' => '3',
|
88
88
|
'discover' => '128',
|
89
89
|
'jcb' => '125',
|
90
|
-
'diners_club' => '132'
|
90
|
+
'diners_club' => '132',
|
91
|
+
'cabal' => '135',
|
92
|
+
'naranja' => '136'
|
91
93
|
}
|
92
94
|
|
93
95
|
def add_order(post, money, options, capture: false)
|
@@ -248,7 +250,6 @@ module ActiveMerchant #:nodoc:
|
|
248
250
|
month = format(payment.month, :two_digits)
|
249
251
|
expirydate = "#{month}#{year}"
|
250
252
|
pre_authorization = options[:pre_authorization] ? 'PRE_AUTHORIZATION' : 'FINAL_AUTHORIZATION'
|
251
|
-
|
252
253
|
post['cardPaymentMethodSpecificInput'] = {
|
253
254
|
'paymentProductId' => BRAND_MAP[payment.brand],
|
254
255
|
'skipAuthentication' => 'true', # refers to 3DSecure
|
@@ -386,12 +387,12 @@ module ActiveMerchant #:nodoc:
|
|
386
387
|
response = json_error(raw_response)
|
387
388
|
end
|
388
389
|
|
389
|
-
succeeded = success_from(response)
|
390
|
+
succeeded = success_from(action, response)
|
390
391
|
Response.new(
|
391
392
|
succeeded,
|
392
393
|
message_from(succeeded, response),
|
393
394
|
response,
|
394
|
-
authorization: authorization_from(
|
395
|
+
authorization: authorization_from(response),
|
395
396
|
error_code: error_code_from(succeeded, response),
|
396
397
|
test: test?
|
397
398
|
)
|
@@ -400,8 +401,7 @@ module ActiveMerchant #:nodoc:
|
|
400
401
|
def json_error(raw_response)
|
401
402
|
{
|
402
403
|
'error_message' => 'Invalid response received from the Ingenico ePayments (formerly GlobalCollect) API. Please contact Ingenico ePayments if you continue to receive this message.' \
|
403
|
-
" (The raw response returned by the API was #{raw_response.inspect})"
|
404
|
-
'status' => 'REJECTED'
|
404
|
+
" (The raw response returned by the API was #{raw_response.inspect})"
|
405
405
|
}
|
406
406
|
end
|
407
407
|
|
@@ -438,8 +438,24 @@ module ActiveMerchant #:nodoc:
|
|
438
438
|
'application/json'
|
439
439
|
end
|
440
440
|
|
441
|
-
def success_from(response)
|
442
|
-
|
441
|
+
def success_from(action, response)
|
442
|
+
return false if response['errorId'] || response['error_message']
|
443
|
+
|
444
|
+
case action
|
445
|
+
when :authorize
|
446
|
+
response.dig('payment', 'statusOutput', 'isAuthorized')
|
447
|
+
when :capture
|
448
|
+
capture_status = response.dig('status') || response.dig('payment', 'status')
|
449
|
+
%w(CAPTURED CAPTURE_REQUESTED).include?(capture_status)
|
450
|
+
when :void
|
451
|
+
void_response_id = response.dig('cardPaymentMethodSpecificOutput', 'voidResponseId') || response.dig('mobilePaymentMethodSpecificOutput', 'voidResponseId')
|
452
|
+
%w(00 0 8 11).include?(void_response_id) || response.dig('payment', 'status') == 'CANCELLED'
|
453
|
+
when :refund
|
454
|
+
refund_status = response.dig('status') || response.dig('payment', 'status')
|
455
|
+
%w(REFUNDED REFUND_REQUESTED).include?(refund_status)
|
456
|
+
else
|
457
|
+
response['status'] != 'REJECTED'
|
458
|
+
end
|
443
459
|
end
|
444
460
|
|
445
461
|
def message_from(succeeded, response)
|
@@ -456,14 +472,8 @@ module ActiveMerchant #:nodoc:
|
|
456
472
|
end
|
457
473
|
end
|
458
474
|
|
459
|
-
def authorization_from(
|
460
|
-
|
461
|
-
response['id'] || response['payment']['id'] || response['paymentResult']['payment']['id']
|
462
|
-
elsif response['errorId']
|
463
|
-
response['errorId']
|
464
|
-
else
|
465
|
-
'GATEWAY ERROR'
|
466
|
-
end
|
475
|
+
def authorization_from(response)
|
476
|
+
response.dig('id') || response.dig('payment', 'id') || response.dig('paymentResult', 'payment', 'id')
|
467
477
|
end
|
468
478
|
|
469
479
|
def error_code_from(succeeded, response)
|
@@ -4,7 +4,7 @@ module ActiveMerchant #:nodoc:
|
|
4
4
|
self.test_url = 'https://test.ipg-online.com/ipgapi/services'
|
5
5
|
self.live_url = 'https://www5.ipg-online.com'
|
6
6
|
|
7
|
-
self.supported_countries = %w(
|
7
|
+
self.supported_countries = %w(AR)
|
8
8
|
self.default_currency = 'ARS'
|
9
9
|
self.supported_cardtypes = %i[visa master american_express discover]
|
10
10
|
|
@@ -12,7 +12,6 @@ module ActiveMerchant #:nodoc:
|
|
12
12
|
self.display_name = 'IPG'
|
13
13
|
|
14
14
|
CURRENCY_CODES = {
|
15
|
-
'UYU' => '858',
|
16
15
|
'ARS' => '032'
|
17
16
|
}
|
18
17
|
|
@@ -39,6 +39,96 @@ module ActiveMerchant #:nodoc:
|
|
39
39
|
check?(payment_method) ? commit(:echeckSales, request, money) : commit(:sale, request, money)
|
40
40
|
end
|
41
41
|
|
42
|
+
def add_level_two_data(doc, payment_method, options = {})
|
43
|
+
level_2_data = options[:level_2_data]
|
44
|
+
if level_2_data
|
45
|
+
doc.enhancedData do
|
46
|
+
case payment_method.brand
|
47
|
+
when 'visa'
|
48
|
+
doc.salesTax(level_2_data[:sales_tax]) if level_2_data[:sales_tax]
|
49
|
+
when 'master'
|
50
|
+
doc.customerReference(level_2_data[:customer_code]) if level_2_data[:customer_code]
|
51
|
+
doc.salesTax(level_2_data[:total_tax_amount]) if level_2_data[:total_tax_amount]
|
52
|
+
doc.detailTax do
|
53
|
+
doc.taxIncludedInTotal(level_2_data[:tax_included_in_total]) if level_2_data[:tax_included_in_total]
|
54
|
+
doc.taxAmount(level_2_data[:tax_amount]) if level_2_data[:tax_amount]
|
55
|
+
doc.cardAcceptorTaxId(level_2_data[:card_acceptor_tax_id]) if level_2_data[:card_acceptor_tax_id]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_level_three_data(doc, payment_method, options = {})
|
63
|
+
level_3_data = options[:level_3_data]
|
64
|
+
if level_3_data
|
65
|
+
doc.enhancedData do
|
66
|
+
case payment_method.brand
|
67
|
+
when 'visa'
|
68
|
+
add_level_three_information_tags_visa(doc, payment_method, level_3_data)
|
69
|
+
when 'master'
|
70
|
+
add_level_three_information_tags_master(doc, payment_method, level_3_data)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def add_level_three_information_tags_visa(doc, payment_method, level_3_data)
|
77
|
+
doc.discountAmount(level_3_data[:discount_amount]) if level_3_data[:discount_amount]
|
78
|
+
doc.shippingAmount(level_3_data[:shipping_amount]) if level_3_data[:shipping_amount]
|
79
|
+
doc.dutyAmount(level_3_data[:duty_amount]) if level_3_data[:duty_amount]
|
80
|
+
doc.detailTax do
|
81
|
+
doc.taxIncludedInTotal(level_3_data[:tax_included_in_total]) if level_3_data[:tax_included_in_total]
|
82
|
+
doc.taxAmount(level_3_data[:tax_amount]) if level_3_data[:tax_amount]
|
83
|
+
doc.taxRate(level_3_data[:tax_rate]) if level_3_data[:tax_rate]
|
84
|
+
doc.taxTypeIdentifier(level_3_data[:tax_type_identifier]) if level_3_data[:tax_type_identifier]
|
85
|
+
doc.cardAcceptorTaxId(level_3_data[:card_acceptor_tax_id]) if level_3_data[:card_acceptor_tax_id]
|
86
|
+
end
|
87
|
+
add_line_item_information_for_level_three_visa(doc, payment_method, level_3_data)
|
88
|
+
end
|
89
|
+
|
90
|
+
def add_level_three_information_tags_master(doc, payment_method, level_3_data)
|
91
|
+
doc.customerReference :customerReference, level_3_data[:customer_code] if level_3_data[:customer_code]
|
92
|
+
doc.salesTax(level_3_data[:total_tax_amount]) if level_3_data[:total_tax_amount]
|
93
|
+
doc.detailTax do
|
94
|
+
doc.taxIncludedInTotal(level_3_data[:tax_included_in_total]) if level_3_data[:tax_included_in_total]
|
95
|
+
doc.taxAmount(level_3_data[:tax_amount]) if level_3_data[:tax_amount]
|
96
|
+
doc.cardAcceptorTaxId :cardAcceptorTaxId, level_3_data[:card_acceptor_tax_id] if level_3_data[:card_acceptor_tax_id]
|
97
|
+
end
|
98
|
+
doc.lineItemData do
|
99
|
+
level_3_data[:line_items].each do |line_item|
|
100
|
+
doc.itemDescription(line_item[:item_description]) if line_item[:item_description]
|
101
|
+
doc.productCode(line_item[:product_code]) if line_item[:product_code]
|
102
|
+
doc.quantity(line_item[:quantity]) if line_item[:quantity]
|
103
|
+
doc.unitOfMeasure(line_item[:unit_of_measure]) if line_item[:unit_of_measure]
|
104
|
+
doc.lineItemTotal(line_item[:line_item_total]) if line_item[:line_item_total]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def add_line_item_information_for_level_three_visa(doc, payment_method, level_3_data)
|
110
|
+
doc.lineItemData do
|
111
|
+
level_3_data[:line_items].each do |line_item|
|
112
|
+
doc.itemSequenceNumber(line_item[:item_sequence_number]) if line_item[:item_sequence_number]
|
113
|
+
doc.commodityCode(line_item[:commodity_code]) if line_item[:commodity_code]
|
114
|
+
doc.itemDescription(line_item[:item_description]) if line_item[:item_description]
|
115
|
+
doc.productCode(line_item[:product_code]) if line_item[:product_code]
|
116
|
+
doc.quantity(line_item[:quantity]) if line_item[:quantity]
|
117
|
+
doc.unitOfMeasure(line_item[:unit_of_measure]) if line_item[:unit_of_measure]
|
118
|
+
doc.taxAmount(line_item[:tax_amount]) if line_item[:tax_amount]
|
119
|
+
doc.itemDiscountAmount(line_item[:discount_per_line_item]) unless line_item[:discount_per_line_item] < 0
|
120
|
+
doc.unitCost(line_item[:unit_cost]) unless line_item[:unit_cost] < 0
|
121
|
+
doc.detailTax do
|
122
|
+
doc.taxIncludedInTotal(line_item[:tax_included_in_total]) if line_item[:tax_included_in_total]
|
123
|
+
doc.taxAmount(line_item[:tax_amount]) if line_item[:tax_amount]
|
124
|
+
doc.taxRate(line_item[:tax_rate]) if line_item[:tax_rate]
|
125
|
+
doc.taxTypeIdentifier(line_item[:tax_type_identifier]) if line_item[:tax_type_identifier]
|
126
|
+
doc.cardAcceptorTaxId(line_item[:card_acceptor_tax_id]) if line_item[:card_acceptor_tax_id]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
42
132
|
def authorize(money, payment_method, options = {})
|
43
133
|
request = build_xml_request do |doc|
|
44
134
|
add_authentication(doc)
|
@@ -225,6 +315,8 @@ module ActiveMerchant #:nodoc:
|
|
225
315
|
add_payment_method(doc, payment_method, options)
|
226
316
|
add_pos(doc, payment_method)
|
227
317
|
add_descriptor(doc, options)
|
318
|
+
add_level_two_data(doc, payment_method, options)
|
319
|
+
add_level_three_data(doc, payment_method, options)
|
228
320
|
add_merchant_data(doc, options)
|
229
321
|
add_debt_repayment(doc, options)
|
230
322
|
add_stored_credential_params(doc, options)
|
@@ -386,7 +478,7 @@ module ActiveMerchant #:nodoc:
|
|
386
478
|
doc.orderSource(order_source)
|
387
479
|
elsif payment_method.is_a?(NetworkTokenizationCreditCard) && payment_method.source == :apple_pay
|
388
480
|
doc.orderSource('applepay')
|
389
|
-
elsif payment_method.is_a?(NetworkTokenizationCreditCard) && payment_method.source
|
481
|
+
elsif payment_method.is_a?(NetworkTokenizationCreditCard) && %i[google_pay android_pay].include?(payment_method.source)
|
390
482
|
doc.orderSource('androidpay')
|
391
483
|
elsif payment_method.respond_to?(:track_data) && payment_method.track_data.present?
|
392
484
|
doc.orderSource('retail')
|
@@ -50,15 +50,16 @@ module ActiveMerchant #:nodoc:
|
|
50
50
|
post[:order_id] = options[:order_id]
|
51
51
|
post[:address] = options[:billing_address] || options[:address]
|
52
52
|
post[:crypt_type] = options[:crypt_type] || @options[:crypt_type]
|
53
|
+
add_external_mpi_fields(post, options)
|
53
54
|
add_stored_credential(post, options)
|
54
|
-
action = if post[:cavv]
|
55
|
+
action = if post[:cavv] || options[:three_d_secure]
|
55
56
|
'cavv_preauth'
|
56
57
|
elsif post[:data_key].blank?
|
57
58
|
'preauth'
|
58
59
|
else
|
59
60
|
'res_preauth_cc'
|
60
61
|
end
|
61
|
-
commit(action, post)
|
62
|
+
commit(action, post, options)
|
62
63
|
end
|
63
64
|
|
64
65
|
# This action verifies funding on a customer's card and readies them for
|
@@ -73,15 +74,16 @@ module ActiveMerchant #:nodoc:
|
|
73
74
|
post[:order_id] = options[:order_id]
|
74
75
|
post[:address] = options[:billing_address] || options[:address]
|
75
76
|
post[:crypt_type] = options[:crypt_type] || @options[:crypt_type]
|
77
|
+
add_external_mpi_fields(post, options)
|
76
78
|
add_stored_credential(post, options)
|
77
|
-
action = if post[:cavv]
|
79
|
+
action = if post[:cavv] || options[:three_d_secure]
|
78
80
|
'cavv_purchase'
|
79
81
|
elsif post[:data_key].blank?
|
80
82
|
'purchase'
|
81
83
|
else
|
82
84
|
'res_purchase_cc'
|
83
85
|
end
|
84
|
-
commit(action, post)
|
86
|
+
commit(action, post, options)
|
85
87
|
end
|
86
88
|
|
87
89
|
# This method retrieves locked funds from a customer's account (from a
|
@@ -203,6 +205,21 @@ module ActiveMerchant #:nodoc:
|
|
203
205
|
sprintf('%.4i', creditcard.year)[-2..-1] + sprintf('%.2i', creditcard.month)
|
204
206
|
end
|
205
207
|
|
208
|
+
def add_external_mpi_fields(post, options)
|
209
|
+
# See these pages:
|
210
|
+
# https://developer.moneris.com/livedemo/3ds2/cavv_purchase/tool/php
|
211
|
+
# https://developer.moneris.com/livedemo/3ds2/cavv_preauth/guide/php
|
212
|
+
return unless options[:three_d_secure]
|
213
|
+
|
214
|
+
three_d_secure_options = options[:three_d_secure]
|
215
|
+
|
216
|
+
post[:threeds_version] = three_d_secure_options[:version]
|
217
|
+
post[:crypt_type] = three_d_secure_options[:eci]
|
218
|
+
post[:cavv] = three_d_secure_options[:cavv]
|
219
|
+
post[:threeds_server_trans_id] = three_d_secure_options[:three_ds_server_trans_id]
|
220
|
+
post[:ds_trans_id] = three_d_secure_options[:ds_transaction_id]
|
221
|
+
end
|
222
|
+
|
206
223
|
def add_payment_source(post, payment_method, options)
|
207
224
|
if payment_method.is_a?(String)
|
208
225
|
post[:data_key] = payment_method
|
@@ -291,14 +308,16 @@ module ActiveMerchant #:nodoc:
|
|
291
308
|
end
|
292
309
|
end
|
293
310
|
|
294
|
-
def commit(action, parameters = {})
|
311
|
+
def commit(action, parameters = {}, options = {})
|
312
|
+
threed_ds_transaction = options[:three_d_secure].present?
|
313
|
+
|
295
314
|
data = post_data(action, parameters)
|
296
315
|
url = test? ? self.test_url : self.live_url
|
297
316
|
raw = ssl_post(url, data)
|
298
317
|
response = parse(raw)
|
299
318
|
|
300
319
|
Response.new(
|
301
|
-
successful?(response),
|
320
|
+
successful?(action, response, threed_ds_transaction),
|
302
321
|
message_from(response[:message]),
|
303
322
|
response,
|
304
323
|
test: test?,
|
@@ -314,8 +333,16 @@ module ActiveMerchant #:nodoc:
|
|
314
333
|
end
|
315
334
|
|
316
335
|
# Tests for a successful response from Moneris' servers
|
317
|
-
def successful?(response)
|
318
|
-
|
336
|
+
def successful?(action, response, threed_ds_transaction = false)
|
337
|
+
# See 9.4 CAVV Result Codes in https://developer.moneris.com/livedemo/3ds2/reference/guide/php
|
338
|
+
cavv_accepted = if threed_ds_transaction
|
339
|
+
response[:cavv_result_code] && response[:cavv_result_code] == '2'
|
340
|
+
else
|
341
|
+
true
|
342
|
+
end
|
343
|
+
|
344
|
+
cavv_accepted &&
|
345
|
+
response[:response_code] &&
|
319
346
|
response[:complete] &&
|
320
347
|
(0..49).cover?(response[:response_code].to_i)
|
321
348
|
end
|