activemerchant 1.121.0 → 1.125.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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +217 -0
  3. data/README.md +1 -1
  4. data/lib/active_merchant/billing/check.rb +13 -19
  5. data/lib/active_merchant/billing/credit_card.rb +13 -0
  6. data/lib/active_merchant/billing/credit_card_formatting.rb +1 -0
  7. data/lib/active_merchant/billing/credit_card_methods.rb +24 -12
  8. data/lib/active_merchant/billing/gateway.rb +1 -1
  9. data/lib/active_merchant/billing/gateways/adyen.rb +75 -27
  10. data/lib/active_merchant/billing/gateways/authorize_net.rb +10 -8
  11. data/lib/active_merchant/billing/gateways/blue_pay.rb +29 -0
  12. data/lib/active_merchant/billing/gateways/blue_snap.rb +2 -2
  13. data/lib/active_merchant/billing/gateways/braintree_blue.rb +6 -3
  14. data/lib/active_merchant/billing/gateways/card_stream.rb +17 -13
  15. data/lib/active_merchant/billing/gateways/cashnet.rb +15 -5
  16. data/lib/active_merchant/billing/gateways/checkout_v2.rb +33 -4
  17. data/lib/active_merchant/billing/gateways/credorax.rb +2 -1
  18. data/lib/active_merchant/billing/gateways/cyber_source.rb +41 -6
  19. data/lib/active_merchant/billing/gateways/d_local.rb +12 -6
  20. data/lib/active_merchant/billing/gateways/decidir.rb +7 -1
  21. data/lib/active_merchant/billing/gateways/decidir_plus.rb +173 -0
  22. data/lib/active_merchant/billing/gateways/ebanx.rb +16 -1
  23. data/lib/active_merchant/billing/gateways/elavon.rb +65 -30
  24. data/lib/active_merchant/billing/gateways/element.rb +22 -2
  25. data/lib/active_merchant/billing/gateways/global_collect.rb +130 -26
  26. data/lib/active_merchant/billing/gateways/ipg.rb +416 -0
  27. data/lib/active_merchant/billing/gateways/kushki.rb +30 -0
  28. data/lib/active_merchant/billing/gateways/mercado_pago.rb +6 -3
  29. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +2 -0
  30. data/lib/active_merchant/billing/gateways/mit.rb +260 -0
  31. data/lib/active_merchant/billing/gateways/moka.rb +290 -0
  32. data/lib/active_merchant/billing/gateways/monei.rb +228 -144
  33. data/lib/active_merchant/billing/gateways/mundipagg.rb +22 -11
  34. data/lib/active_merchant/billing/gateways/nmi.rb +29 -10
  35. data/lib/active_merchant/billing/gateways/orbital.rb +46 -8
  36. data/lib/active_merchant/billing/gateways/pay_arc.rb +392 -0
  37. data/lib/active_merchant/billing/gateways/pay_conex.rb +3 -1
  38. data/lib/active_merchant/billing/gateways/pay_trace.rb +404 -0
  39. data/lib/active_merchant/billing/gateways/payeezy.rb +4 -0
  40. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -0
  41. data/lib/active_merchant/billing/gateways/payflow.rb +21 -4
  42. data/lib/active_merchant/billing/gateways/payment_express.rb +2 -2
  43. data/lib/active_merchant/billing/gateways/paymentez.rb +14 -2
  44. data/lib/active_merchant/billing/gateways/paysafe.rb +412 -0
  45. data/lib/active_merchant/billing/gateways/payu_latam.rb +9 -4
  46. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +3 -3
  47. data/lib/active_merchant/billing/gateways/pin.rb +31 -4
  48. data/lib/active_merchant/billing/gateways/priority.rb +347 -0
  49. data/lib/active_merchant/billing/gateways/realex.rb +18 -0
  50. data/lib/active_merchant/billing/gateways/redsys.rb +35 -32
  51. data/lib/active_merchant/billing/gateways/safe_charge.rb +8 -2
  52. data/lib/active_merchant/billing/gateways/spreedly_core.rb +13 -4
  53. data/lib/active_merchant/billing/gateways/stripe.rb +27 -7
  54. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +115 -39
  55. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +2 -1
  56. data/lib/active_merchant/billing/gateways/trust_commerce.rb +2 -1
  57. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +21 -7
  58. data/lib/active_merchant/billing/gateways/vpos.rb +49 -6
  59. data/lib/active_merchant/billing/gateways/wompi.rb +193 -0
  60. data/lib/active_merchant/billing/gateways/worldpay.rb +226 -62
  61. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +1 -1
  62. data/lib/active_merchant/billing/response.rb +4 -0
  63. data/lib/active_merchant/billing/three_d_secure_eci_mapper.rb +27 -0
  64. data/lib/active_merchant/billing.rb +1 -0
  65. data/lib/active_merchant/version.rb +1 -1
  66. metadata +13 -3
@@ -0,0 +1,404 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class PayTraceGateway < Gateway
4
+ self.test_url = 'https://api.paytrace.com'
5
+ self.live_url = 'https://api.paytrace.com'
6
+
7
+ self.supported_countries = ['US']
8
+ self.default_currency = 'USD'
9
+ self.supported_cardtypes = %i[visa master american_express discover]
10
+
11
+ self.homepage_url = 'https://paytrace.com/'
12
+ self.display_name = 'PayTrace'
13
+
14
+ # Response codes based on API Response Codes found here: https://developers.paytrace.com/support/home#14000041297
15
+ STANDARD_ERROR_CODE_MAPPING = {
16
+ '1' => STANDARD_ERROR_CODE[:error_occurred],
17
+ '102' => STANDARD_ERROR_CODE[:declined],
18
+ '103' => STANDARD_ERROR_CODE[:auto_voided],
19
+ '107' => STANDARD_ERROR_CODE[:unsuccessful_refund],
20
+ '108' => STANDARD_ERROR_CODE[:test_refund],
21
+ '110' => STANDARD_ERROR_CODE[:unsuccessful_void],
22
+ '113' => STANDARD_ERROR_CODE[:unsuccessful_capture]
23
+ }
24
+
25
+ ENDPOINTS = {
26
+ customer_id_sale: 'transactions/sale/by_customer',
27
+ keyed_sale: 'transactions/sale/keyed',
28
+ customer_id_auth: 'transactions/authorization/by_customer',
29
+ keyed_auth: 'transactions/authorization/keyed',
30
+ capture: 'transactions/authorization/capture',
31
+ transaction_refund: 'transactions/refund/for_transaction',
32
+ transaction_void: 'transactions/void',
33
+ store: 'customer/create',
34
+ redact: 'customer/delete',
35
+ level_3_visa: 'level_three/visa',
36
+ level_3_mastercard: 'level_three/mastercard'
37
+ }
38
+
39
+ def initialize(options = {})
40
+ requires!(options, :username, :password, :integrator_id)
41
+ super
42
+ acquire_access_token
43
+ end
44
+
45
+ def purchase(money, payment_or_customer_id, options = {})
46
+ if visa_or_mastercard?(options)
47
+ MultiResponse.run(:use_first_response) do |r|
48
+ endpoint = customer_id?(payment_or_customer_id) ? ENDPOINTS[:customer_id_sale] : ENDPOINTS[:keyed_sale]
49
+
50
+ r.process { commit(endpoint, build_purchase_request(money, payment_or_customer_id, options)) }
51
+ r.process { commit(ENDPOINTS[:"level_3_#{options[:visa_or_mastercard]}"], send_level_3_data(r, options)) }
52
+ end
53
+ else
54
+ post = build_purchase_request(money, payment_or_customer_id, options)
55
+ post[:customer_id] ? endpoint = ENDPOINTS[:customer_id_sale] : endpoint = ENDPOINTS[:keyed_sale]
56
+ response = commit(endpoint, post)
57
+ check_token_response(response, endpoint, post, options)
58
+ end
59
+ end
60
+
61
+ def authorize(money, payment_or_customer_id, options = {})
62
+ post = {}
63
+ add_amount(post, money, options)
64
+ if customer_id?(payment_or_customer_id)
65
+ post[:customer_id] = payment_or_customer_id
66
+ endpoint = ENDPOINTS[:customer_id_auth]
67
+ else
68
+ add_payment(post, payment_or_customer_id)
69
+ add_address(post, payment_or_customer_id, options)
70
+ add_customer_data(post, options)
71
+ endpoint = ENDPOINTS[:keyed_auth]
72
+ end
73
+ response = commit(endpoint, post)
74
+ check_token_response(response, endpoint, post, options)
75
+ end
76
+
77
+ def capture(money, authorization, options = {})
78
+ if visa_or_mastercard?(options)
79
+ MultiResponse.run do |r|
80
+ r.process { commit(ENDPOINTS[:capture], build_capture_request(money, authorization, options)) }
81
+ r.process { commit(ENDPOINTS[:"level_3_#{options[:visa_or_mastercard]}"], send_level_3_data(r, options)) }
82
+ end
83
+ else
84
+ post = build_capture_request(money, authorization, options)
85
+ response = commit(ENDPOINTS[:capture], post)
86
+ check_token_response(response, ENDPOINTS[:capture], post, options)
87
+ end
88
+ end
89
+
90
+ def refund(money, authorization, options = {})
91
+ # currently only support full and partial refunds of settled transactions via a transaction ID
92
+ post = {}
93
+ add_amount(post, money, options)
94
+ post[:transaction_id] = authorization
95
+ response = commit(ENDPOINTS[:transaction_refund], post)
96
+ check_token_response(response, ENDPOINTS[:transaction_refund], post, options)
97
+ end
98
+
99
+ def void(authorization, options = {})
100
+ post = {}
101
+ post[:transaction_id] = authorization
102
+
103
+ response = commit(ENDPOINTS[:transaction_void], post)
104
+ check_token_response(response, ENDPOINTS[:transaction_void], post, options)
105
+ end
106
+
107
+ def verify(credit_card, options = {})
108
+ authorize(0, credit_card, options)
109
+ end
110
+
111
+ # The customer_IDs that come from storing cards can be used for auth and purchase transaction types
112
+ def store(credit_card, options = {})
113
+ post = {}
114
+ post[:customer_id] = options[:customer_id] || SecureRandom.hex(12)
115
+ add_payment(post, credit_card)
116
+ add_address(post, credit_card, options)
117
+ response = commit(ENDPOINTS[:store], post)
118
+ check_token_response(response, ENDPOINTS[:store], post, options)
119
+ end
120
+
121
+ def unstore(customer_id)
122
+ post = {}
123
+ post[:customer_id] = customer_id
124
+ response = commit(ENDPOINTS[:redact], post)
125
+ check_token_response(response, ENDPOINTS[:redact], post, options)
126
+ end
127
+
128
+ def supports_scrubbing?
129
+ true
130
+ end
131
+
132
+ def scrub(transcript)
133
+ transcript.
134
+ gsub(%r((Authorization: Bearer )[a-zA-Z0-9:_]+), '\1[FILTERED]').
135
+ gsub(%r(("credit_card\\?":{\\?"number\\?":\\?")\d+), '\1[FILTERED]').
136
+ gsub(%r(("cvv\\?":\\?")\d+), '\1[FILTERED]').
137
+ gsub(%r(("username\\?":\\?")\w+@+\w+.+\w+), '\1[FILTERED]').
138
+ gsub(%r(("password\\?":\\?")\w+), '\1[FILTERED]').
139
+ gsub(%r(("integrator_id\\?":\\?")\w+), '\1[FILTERED]')
140
+ end
141
+
142
+ def acquire_access_token
143
+ post = {}
144
+ post[:grant_type] = 'password'
145
+ post[:username] = @options[:username]
146
+ post[:password] = @options[:password]
147
+ data = post.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&')
148
+ url = live_url + '/oauth/token'
149
+ oauth_headers = {
150
+ 'Accept' => '*/*',
151
+ 'Content-Type' => 'application/x-www-form-urlencoded'
152
+ }
153
+ response = ssl_post(url, data, oauth_headers)
154
+ json_response = JSON.parse(response)
155
+
156
+ @options[:access_token] = json_response['access_token'] if json_response['access_token']
157
+ response
158
+ end
159
+
160
+ private
161
+
162
+ def build_purchase_request(money, payment_or_customer_id, options)
163
+ post = {}
164
+ add_amount(post, money, options)
165
+ if customer_id?(payment_or_customer_id)
166
+ post[:customer_id] = payment_or_customer_id
167
+ else
168
+ add_payment(post, payment_or_customer_id)
169
+ add_address(post, payment_or_customer_id, options)
170
+ add_customer_data(post, options)
171
+ end
172
+
173
+ post
174
+ end
175
+
176
+ def build_capture_request(money, authorization, options)
177
+ post = {}
178
+ post[:transaction_id] = authorization
179
+ add_amount(post, money, options)
180
+
181
+ post
182
+ end
183
+
184
+ # method can only be used to add level 3 data to any approved and unsettled sale transaction so it is built into the standard purchase workflow above
185
+ def send_level_3_data(response, options)
186
+ post = {}
187
+ post[:transaction_id] = response.authorization
188
+ add_level_3_data(post, options)
189
+
190
+ post
191
+ end
192
+
193
+ def visa_or_mastercard?(options)
194
+ return false unless options[:visa_or_mastercard]
195
+
196
+ options[:visa_or_mastercard] == 'visa' || options[:visa_or_mastercard] == 'mastercard'
197
+ end
198
+
199
+ def customer_id?(payment_or_customer_id)
200
+ payment_or_customer_id.class == String
201
+ end
202
+
203
+ def string_literal_to_boolean(value)
204
+ return value unless value.class == String
205
+
206
+ if value.casecmp('true').zero?
207
+ true
208
+ elsif value.casecmp('false').zero?
209
+ false
210
+ else return nil
211
+ end
212
+ end
213
+
214
+ def add_customer_data(post, options)
215
+ return unless options[:email]
216
+
217
+ post[:email] = options[:email]
218
+ end
219
+
220
+ def add_address(post, creditcard, options)
221
+ return unless options[:billing_address] || options[:address]
222
+
223
+ address = options[:billing_address] || options[:address]
224
+ post[:billing_address] = {}
225
+ post[:billing_address][:name] = creditcard.name
226
+ post[:billing_address][:street_address] = address[:address1]
227
+ post[:billing_address][:city] = address[:city]
228
+ post[:billing_address][:state] = address[:state]
229
+ post[:billing_address][:zip] = address[:zip]
230
+ end
231
+
232
+ def add_amount(post, money, options)
233
+ post[:amount] = amount(money)
234
+ end
235
+
236
+ def add_payment(post, payment)
237
+ post[:credit_card] = {}
238
+ post[:credit_card][:number] = payment.number
239
+ post[:credit_card][:expiration_month] = payment.month
240
+ post[:credit_card][:expiration_year] = payment.year
241
+ end
242
+
243
+ def add_level_3_data(post, options)
244
+ post[:invoice_id] = options[:invoice_id] if options[:invoice_id]
245
+ post[:customer_reference_id] = options[:customer_reference_id] if options[:customer_reference_id]
246
+ post[:tax_amount] = options[:tax_amount].to_i if options[:tax_amount]
247
+ post[:national_tax_amount] = options[:national_tax_amount].to_i if options[:national_tax_amount]
248
+ post[:merchant_tax_id] = options[:merchant_tax_id] if options[:merchant_tax_id]
249
+ post[:customer_tax_id] = options[:customer_tax_id] if options[:customer_tax_id]
250
+ post[:commodity_code] = options[:commodity_code] if options[:commodity_code]
251
+ post[:discount_amount] = options[:discount_amount].to_i if options[:discount_amount]
252
+ post[:freight_amount] = options[:freight_amount].to_i if options[:freight_amount]
253
+ post[:duty_amount] = options[:duty_amount].to_i if options[:duty_amount]
254
+ post[:additional_tax_amount] = options[:additional_tax_amount].to_i if options[:additional_tax_amount]
255
+ post[:additional_tax_rate] = options[:additional_tax_rate].to_i if options[:additional_tax_rate]
256
+
257
+ add_source_address(post, options)
258
+ add_shipping_address(post, options)
259
+ add_line_items(post, options)
260
+ end
261
+
262
+ def add_source_address(post, options)
263
+ return unless source_address = options[:source_address] ||
264
+ options[:billing_address] ||
265
+ options[:address]
266
+
267
+ post[:source_address] = {}
268
+ post[:source_address][:zip] = source_address[:zip] if source_address[:zip]
269
+ end
270
+
271
+ def add_shipping_address(post, options)
272
+ return unless shipping_address = options[:shipping_address]
273
+
274
+ post[:shipping_address] = {}
275
+ post[:shipping_address][:name] = shipping_address[:name] if shipping_address[:name]
276
+ post[:shipping_address][:street_address] = shipping_address[:address1] if shipping_address[:address1]
277
+ post[:shipping_address][:street_address2] = shipping_address[:address2] if shipping_address[:address2]
278
+ post[:shipping_address][:city] = shipping_address[:city] if shipping_address[:city]
279
+ post[:shipping_address][:state] = shipping_address[:state] if shipping_address[:state]
280
+ post[:shipping_address][:zip] = shipping_address[:zip] if shipping_address[:zip]
281
+ post[:shipping_address][:country] = shipping_address[:country] if shipping_address[:country]
282
+ end
283
+
284
+ def add_line_items(post, options)
285
+ return unless options[:line_items]
286
+
287
+ line_items = []
288
+ options[:line_items].each do |li|
289
+ obj = {}
290
+
291
+ obj[:additional_tax_amount] = li[:additional_tax_amount].to_i if li[:additional_tax_amount]
292
+ obj[:additional_tax_included] = string_literal_to_boolean(li[:additional_tax_included]) if li[:additional_tax_included]
293
+ obj[:additional_tax_rate] = li[:additional_tax_rate].to_i if li[:additional_tax_rate]
294
+ obj[:amount] = li[:amount].to_i if li[:amount]
295
+ obj[:commodity_code] = li[:commodity_code] if li[:commodity_code]
296
+ obj[:debit_or_credit] = li[:debit_or_credit] if li[:debit_or_credit]
297
+ obj[:description] = li[:description] if li[:description]
298
+ obj[:discount_amount] = li[:discount_amount].to_i if li[:discount_amount]
299
+ obj[:discount_rate] = li[:discount_rate].to_i if li[:discount_rate]
300
+ obj[:discount_included] = string_literal_to_boolean(li[:discount_included]) if li[:discount_included]
301
+ obj[:merchant_tax_id] = li[:merchant_tax_id] if li[:merchant_tax_id]
302
+ obj[:product_id] = li[:product_id] if li[:product_id]
303
+ obj[:quantity] = li[:quantity] if li[:quantity]
304
+ obj[:transaction_id] = li[:transaction_id] if li[:transaction_id]
305
+ obj[:tax_included] = string_literal_to_boolean(li[:tax_included]) if li[:tax_included]
306
+ obj[:unit_of_measure] = li[:unit_of_measure] if li[:unit_of_measure]
307
+ obj[:unit_cost] = li[:unit_cost].to_i if li[:unit_cost]
308
+
309
+ line_items << obj
310
+ end
311
+ post[:line_items] = line_items
312
+ end
313
+
314
+ def check_token_response(response, endpoint, body = {}, options = {})
315
+ return response unless response.params['error'] == 'invalid_token'
316
+
317
+ acquire_access_token
318
+ commit(endpoint, body)
319
+ end
320
+
321
+ def parse(body)
322
+ JSON.parse(body)
323
+ end
324
+
325
+ def commit(action, parameters)
326
+ base_url = (test? ? test_url : live_url)
327
+ url = base_url + '/v1/' + action
328
+ raw_response = ssl_post(url, post_data(parameters), headers)
329
+ response = parse(raw_response)
330
+ success = success_from(response)
331
+
332
+ Response.new(
333
+ success,
334
+ message_from(success, response),
335
+ response,
336
+ authorization: authorization_from(action, response),
337
+ avs_result: AVSResult.new(code: response['avs_response']),
338
+ cvv_result: response['csc_response'],
339
+ test: test?,
340
+ error_code: success ? nil : error_code_from(response)
341
+ )
342
+ rescue JSON::ParserError
343
+ unparsable_response(raw_response)
344
+ end
345
+
346
+ def unparsable_response(raw_response)
347
+ message = 'Unparsable response received from PayTrace. Please contact PayTrace if you continue to receive this message.'
348
+ message += " (The raw response returned by the API was #{raw_response.inspect})"
349
+ return Response.new(false, message)
350
+ end
351
+
352
+ def headers
353
+ {
354
+ 'Content-type' => 'application/json',
355
+ 'Authorization' => 'Bearer ' + @options[:access_token]
356
+ }
357
+ end
358
+
359
+ def success_from(response)
360
+ response['success']
361
+ end
362
+
363
+ def message_from(success, response)
364
+ return response['status_message'] if success
365
+
366
+ if error = response['errors']
367
+ message = 'Errors-'
368
+ error.each do |k, v|
369
+ message.concat(" code:#{k}, message:#{v}")
370
+ end
371
+ else
372
+ message = response['status_message'].to_s + " #{response['approval_message']}"
373
+ end
374
+
375
+ message
376
+ end
377
+
378
+ # store transactions do not return a transaction_id, but they return a customer_id that will then be used as the third_party_token for the stored payment method
379
+ def authorization_from(action, response)
380
+ if action == ENDPOINTS[:store]
381
+ response['customer_id']
382
+ else
383
+ response['transaction_id']
384
+ end
385
+ end
386
+
387
+ def post_data(parameters = {})
388
+ parameters[:password] = @options[:password]
389
+ parameters[:username] = @options[:username]
390
+ parameters[:integrator_id] = @options[:integrator_id]
391
+
392
+ parameters.to_json
393
+ end
394
+
395
+ def error_code_from(response)
396
+ STANDARD_ERROR_CODE_MAPPING[response['response_code']]
397
+ end
398
+
399
+ def handle_response(response)
400
+ response.body
401
+ end
402
+ end
403
+ end
404
+ end
@@ -75,6 +75,8 @@ module ActiveMerchant
75
75
 
76
76
  add_authorization_info(params, authorization)
77
77
  add_amount(params, (amount || amount_from_authorization(authorization)), options)
78
+ add_soft_descriptors(params, options)
79
+ add_invoice(params, options)
78
80
 
79
81
  commit(params, options)
80
82
  end
@@ -84,6 +86,8 @@ module ActiveMerchant
84
86
 
85
87
  add_amount(params, amount, options)
86
88
  add_payment_method(params, payment_method, options)
89
+ add_soft_descriptors(params, options)
90
+ add_invoice(params, options)
87
91
  commit(params, options)
88
92
  end
89
93
 
@@ -118,6 +118,7 @@ module ActiveMerchant #:nodoc:
118
118
  xml.tag!('Description', options[:description]) unless options[:description].blank?
119
119
  xml.tag!('Comment', options[:comment]) unless options[:comment].blank?
120
120
  xml.tag!('ExtData', 'Name' => 'COMMENT2', 'Value' => options[:comment2]) unless options[:comment2].blank?
121
+ xml.tag!('MerchDescr', options[:merch_descr]) unless options[:merch_descr].blank?
121
122
  xml.tag!(
122
123
  'ExtData',
123
124
  'Name' => 'CAPTURECOMPLETE',
@@ -56,6 +56,10 @@ module ActiveMerchant #:nodoc:
56
56
  end
57
57
  end
58
58
 
59
+ def store(payment, options = {})
60
+ raise ArgumentError, 'Store is not supported on Payflow gateways'
61
+ end
62
+
59
63
  def verify_credentials
60
64
  response = void('0')
61
65
  response.params['result'] != '26'
@@ -141,6 +145,7 @@ module ActiveMerchant #:nodoc:
141
145
  xml.tag! 'FreightAmt', options[:freightamt] unless options[:freightamt].blank?
142
146
  xml.tag! 'DutyAmt', options[:dutyamt] unless options[:dutyamt].blank?
143
147
  xml.tag! 'DiscountAmt', options[:discountamt] unless options[:discountamt].blank?
148
+ xml.tag! 'MerchDescr', options[:merch_descr] unless options[:merch_descr].blank?
144
149
 
145
150
  billing_address = options[:billing_address] || options[:address]
146
151
  add_address(xml, 'BillTo', billing_address, options) if billing_address
@@ -176,6 +181,7 @@ module ActiveMerchant #:nodoc:
176
181
  xml.tag! 'DutyAmt', options[:dutyamt] unless options[:dutyamt].blank?
177
182
  xml.tag! 'DiscountAmt', options[:discountamt] unless options[:discountamt].blank?
178
183
  xml.tag! 'EMail', options[:email] unless options[:email].nil?
184
+ xml.tag! 'MerchDescr', options[:merch_descr] unless options[:merch_descr].blank?
179
185
 
180
186
  billing_address = options[:billing_address] || options[:address]
181
187
  add_address(xml, 'BillTo', billing_address, options) if billing_address
@@ -239,6 +245,7 @@ module ActiveMerchant #:nodoc:
239
245
  xml.tag! 'InvNum', options[:order_id].to_s.gsub(/[^\w.]/, '') unless options[:order_id].blank?
240
246
  xml.tag! 'Description', options[:description] unless options[:description].blank?
241
247
  xml.tag! 'OrderDesc', options[:order_desc] unless options[:order_desc].blank?
248
+ xml.tag! 'MerchDescr', options[:merch_descr] unless options[:merch_descr].blank?
242
249
  xml.tag! 'BillTo' do
243
250
  xml.tag! 'Name', check.name
244
251
  end
@@ -282,18 +289,28 @@ module ActiveMerchant #:nodoc:
282
289
  xml.tag! 'ECI', three_d_secure[:eci] unless three_d_secure[:eci].blank?
283
290
  xml.tag! 'CAVV', three_d_secure[:cavv] unless three_d_secure[:cavv].blank?
284
291
  xml.tag! 'XID', three_d_secure[:xid] unless three_d_secure[:xid].blank?
292
+ xml.tag! 'ThreeDSVersion', three_d_secure[:version] unless three_d_secure[:version].blank?
293
+ xml.tag! 'DSTransactionID', three_d_secure[:ds_transaction_id] unless three_d_secure[:ds_transaction_id].blank?
285
294
  end
286
295
  end
287
296
  end
288
297
 
289
298
  def authentication_status(three_d_secure, xml)
290
- if three_d_secure[:authentication_response_status].present?
291
- xml.tag! 'Status', three_d_secure[:authentication_response_status]
292
- elsif three_d_secure[:directory_response_status].present?
293
- xml.tag! 'Status', three_d_secure[:directory_response_status]
299
+ status = if three_d_secure[:authentication_response_status].present?
300
+ three_d_secure[:authentication_response_status]
301
+ elsif three_d_secure[:directory_response_status].present?
302
+ three_d_secure[:directory_response_status]
303
+ end
304
+ if status.present?
305
+ xml.tag! 'Status', status
306
+ xml.tag! 'AuthenticationStatus', status if version_2_or_newer?(three_d_secure)
294
307
  end
295
308
  end
296
309
 
310
+ def version_2_or_newer?(three_d_secure)
311
+ three_d_secure[:version]&.start_with?('2')
312
+ end
313
+
297
314
  def credit_card_type(credit_card)
298
315
  return '' if card_brand(credit_card).blank?
299
316
 
@@ -86,8 +86,8 @@ module ActiveMerchant #:nodoc:
86
86
  refund(money, identification, options)
87
87
  end
88
88
 
89
- def verify(money, payment_source, options = {})
90
- request = build_purchase_or_authorization_request(money, payment_source, options)
89
+ def verify(payment_source, options = {})
90
+ request = build_purchase_or_authorization_request(100, payment_source, options)
91
91
  commit(:validate, request)
92
92
  end
93
93
 
@@ -9,7 +9,7 @@ module ActiveMerchant #:nodoc:
9
9
 
10
10
  self.supported_countries = %w[MX EC CO BR CL PE]
11
11
  self.default_currency = 'USD'
12
- self.supported_cardtypes = %i[visa master american_express diners_club elo alia olimpica]
12
+ self.supported_cardtypes = %i[visa master american_express diners_club elo alia olimpica discover maestro sodexo carnet unionpay jcb]
13
13
 
14
14
  self.homepage_url = 'https://secure.paymentez.com/'
15
15
  self.display_name = 'Paymentez'
@@ -39,7 +39,14 @@ module ActiveMerchant #:nodoc:
39
39
  'master' => 'mc',
40
40
  'american_express' => 'ax',
41
41
  'diners_club' => 'di',
42
- 'elo' => 'el'
42
+ 'elo' => 'el',
43
+ 'discover' => 'dc',
44
+ 'maestro' => 'ms',
45
+ 'sodexo' => 'sx',
46
+ 'olimpica' => 'ol',
47
+ 'carnet' => 'ct',
48
+ 'unionpay' => 'up',
49
+ 'jcb' => 'jc'
43
50
  }.freeze
44
51
 
45
52
  def initialize(options = {})
@@ -82,6 +89,7 @@ module ActiveMerchant #:nodoc:
82
89
  def refund(money, authorization, options = {})
83
90
  post = { transaction: { id: authorization } }
84
91
  post[:order] = { amount: amount(money).to_f } if money
92
+ add_more_info(post, options)
85
93
 
86
94
  commit_transaction('refund', post)
87
95
  end
@@ -198,6 +206,10 @@ module ActiveMerchant #:nodoc:
198
206
  extra_params[:auth_data] = auth_data
199
207
  end
200
208
 
209
+ def add_more_info(post, options)
210
+ post[:more_info] = options[:more_info] if options[:more_info]
211
+ end
212
+
201
213
  def parse(body)
202
214
  JSON.parse(body)
203
215
  end