activemerchant 1.125.0 → 1.129.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +316 -0
  3. data/lib/active_merchant/billing/check.rb +40 -8
  4. data/lib/active_merchant/billing/credit_card.rb +28 -1
  5. data/lib/active_merchant/billing/credit_card_methods.rb +91 -23
  6. data/lib/active_merchant/billing/gateway.rb +2 -1
  7. data/lib/active_merchant/billing/gateways/adyen.rb +74 -12
  8. data/lib/active_merchant/billing/gateways/airwallex.rb +370 -0
  9. data/lib/active_merchant/billing/gateways/alelo.rb +256 -0
  10. data/lib/active_merchant/billing/gateways/authorize_net.rb +21 -4
  11. data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +2 -1
  12. data/lib/active_merchant/billing/gateways/beanstream.rb +18 -0
  13. data/lib/active_merchant/billing/gateways/blue_pay.rb +1 -1
  14. data/lib/active_merchant/billing/gateways/blue_snap.rb +53 -22
  15. data/lib/active_merchant/billing/gateways/bogus.rb +4 -0
  16. data/lib/active_merchant/billing/gateways/borgun.rb +56 -16
  17. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +6 -1
  18. data/lib/active_merchant/billing/gateways/braintree/token_nonce.rb +113 -0
  19. data/lib/active_merchant/billing/gateways/braintree_blue.rb +151 -32
  20. data/lib/active_merchant/billing/gateways/card_connect.rb +28 -10
  21. data/lib/active_merchant/billing/gateways/card_stream.rb +23 -0
  22. data/lib/active_merchant/billing/gateways/checkout_v2.rb +228 -57
  23. data/lib/active_merchant/billing/gateways/commerce_hub.rb +361 -0
  24. data/lib/active_merchant/billing/gateways/credorax.rb +56 -26
  25. data/lib/active_merchant/billing/gateways/cyber_source/cyber_source_common.rb +36 -0
  26. data/lib/active_merchant/billing/gateways/cyber_source.rb +112 -58
  27. data/lib/active_merchant/billing/gateways/cyber_source_rest.rb +456 -0
  28. data/lib/active_merchant/billing/gateways/d_local.rb +93 -5
  29. data/lib/active_merchant/billing/gateways/decidir.rb +32 -5
  30. data/lib/active_merchant/billing/gateways/decidir_plus.rb +185 -14
  31. data/lib/active_merchant/billing/gateways/ebanx.rb +39 -26
  32. data/lib/active_merchant/billing/gateways/element.rb +21 -1
  33. data/lib/active_merchant/billing/gateways/global_collect.rb +98 -37
  34. data/lib/active_merchant/billing/gateways/ipg.rb +14 -10
  35. data/lib/active_merchant/billing/gateways/iveri.rb +39 -3
  36. data/lib/active_merchant/billing/gateways/kushki.rb +21 -1
  37. data/lib/active_merchant/billing/gateways/litle.rb +118 -6
  38. data/lib/active_merchant/billing/gateways/mastercard.rb +1 -8
  39. data/lib/active_merchant/billing/gateways/mercado_pago.rb +17 -0
  40. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +44 -10
  41. data/lib/active_merchant/billing/gateways/monei.rb +2 -0
  42. data/lib/active_merchant/billing/gateways/moneris.rb +55 -13
  43. data/lib/active_merchant/billing/gateways/mundipagg.rb +3 -0
  44. data/lib/active_merchant/billing/gateways/nmi.rb +12 -7
  45. data/lib/active_merchant/billing/gateways/ogone.rb +35 -7
  46. data/lib/active_merchant/billing/gateways/openpay.rb +20 -3
  47. data/lib/active_merchant/billing/gateways/orbital.rb +378 -335
  48. data/lib/active_merchant/billing/gateways/pay_trace.rb +64 -18
  49. data/lib/active_merchant/billing/gateways/payeezy.rb +59 -4
  50. data/lib/active_merchant/billing/gateways/payflow.rb +62 -0
  51. data/lib/active_merchant/billing/gateways/paymentez.rb +44 -13
  52. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +4 -0
  53. data/lib/active_merchant/billing/gateways/paysafe.rb +37 -29
  54. data/lib/active_merchant/billing/gateways/payu_latam.rb +28 -15
  55. data/lib/active_merchant/billing/gateways/plexo.rb +308 -0
  56. data/lib/active_merchant/billing/gateways/priority.rb +185 -140
  57. data/lib/active_merchant/billing/gateways/rapyd.rb +319 -0
  58. data/lib/active_merchant/billing/gateways/reach.rb +277 -0
  59. data/lib/active_merchant/billing/gateways/redsys.rb +9 -5
  60. data/lib/active_merchant/billing/gateways/safe_charge.rb +1 -4
  61. data/lib/active_merchant/billing/gateways/sage_pay.rb +1 -1
  62. data/lib/active_merchant/billing/gateways/securion_pay.rb +40 -0
  63. data/lib/active_merchant/billing/gateways/shift4.rb +342 -0
  64. data/lib/active_merchant/billing/gateways/simetrik.rb +368 -0
  65. data/lib/active_merchant/billing/gateways/stripe.rb +25 -3
  66. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +155 -70
  67. data/lib/active_merchant/billing/gateways/tns.rb +2 -5
  68. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +1 -1
  69. data/lib/active_merchant/billing/gateways/trust_commerce.rb +14 -3
  70. data/lib/active_merchant/billing/gateways/vanco.rb +12 -3
  71. data/lib/active_merchant/billing/gateways/visanet_peru.rb +6 -2
  72. data/lib/active_merchant/billing/gateways/vpos.rb +7 -4
  73. data/lib/active_merchant/billing/gateways/wompi.rb +8 -4
  74. data/lib/active_merchant/billing/gateways/worldpay.rb +117 -9
  75. data/lib/active_merchant/billing/response.rb +15 -1
  76. data/lib/active_merchant/connection.rb +0 -2
  77. data/lib/active_merchant/country.rb +1 -0
  78. data/lib/active_merchant/errors.rb +4 -1
  79. data/lib/active_merchant/version.rb +1 -1
  80. metadata +28 -3
@@ -313,6 +313,9 @@ module ActiveMerchant #:nodoc:
313
313
  def add_extra_parameters(post, options)
314
314
  extra_parameters = {}
315
315
  extra_parameters[:INSTALLMENTS_NUMBER] = options[:installments_number] || 1
316
+ extra_parameters[:EXTRA1] = options[:extra_1] if options[:extra_1]
317
+ extra_parameters[:EXTRA2] = options[:extra_2] if options[:extra_2]
318
+ extra_parameters[:EXTRA3] = options[:extra_3] if options[:extra_3]
316
319
  post[:transaction][:extraParameters] = extra_parameters
317
320
  end
318
321
 
@@ -392,26 +395,36 @@ module ActiveMerchant #:nodoc:
392
395
  def message_from(action, success, response)
393
396
  case action
394
397
  when 'store'
395
- return response['code'] if success
396
-
397
- error_description = response['creditCardToken']['errorDescription'] if response['creditCardToken']
398
- response['error'] || error_description || 'FAILED'
398
+ message_from_store(success, response)
399
399
  when 'verify_credentials'
400
- return 'VERIFIED' if success
401
-
402
- 'FAILED'
400
+ message_from_verify_credentials(success)
403
401
  else
404
- if response['transactionResponse']
405
- response_message = response['transactionResponse']['responseMessage']
402
+ message_from_transaction_response(success, response)
403
+ end
404
+ end
405
+
406
+ def message_from_store(success, response)
407
+ return response['code'] if success
406
408
 
407
- response_code = response['transactionResponse']['responseCode'] || response['transactionResponse']['pendingReason']
409
+ error_description = response['creditCardToken']['errorDescription'] if response['creditCardToken']
410
+ response['error'] || error_description || 'FAILED'
411
+ end
408
412
 
409
- response_message = response_code + ' | ' + response['transactionResponse']['paymentNetworkResponseErrorMessage'] unless response['transactionResponse']['paymentNetworkResponseErrorMessage'].nil?
410
- end
411
- return response_code if success
413
+ def message_from_verify_credentials(success)
414
+ return 'VERIFIED' if success
412
415
 
413
- response_message || response['error'] || response_code || 'FAILED'
414
- end
416
+ 'FAILED'
417
+ end
418
+
419
+ def message_from_transaction_response(success, response)
420
+ response_code = response.dig('transactionResponse', 'responseCode') || response.dig('transactionResponse', 'pendingReason')
421
+ return response_code if success
422
+ return response_code + ' | ' + response.dig('transactionResponse', 'paymentNetworkResponseErrorMessage') if response.dig('transactionResponse', 'paymentNetworkResponseErrorMessage')
423
+ return response.dig('transactionResponse', 'responseMessage') if response.dig('transactionResponse', 'responseMessage')
424
+ return response['error'] if response['error']
425
+ return response_code if response_code
426
+
427
+ 'FAILED'
415
428
  end
416
429
 
417
430
  def authorization_from(action, response)
@@ -0,0 +1,308 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class PlexoGateway < Gateway
4
+ self.test_url = 'https://api.testing.plexo.com.uy/v1/payments'
5
+ self.live_url = 'https://api.plexo.com.uy/v1/payments'
6
+
7
+ self.supported_countries = ['UY']
8
+ self.default_currency = 'UYU'
9
+ self.supported_cardtypes = %i[visa master american_express discover passcard edenred anda tarjeta-d]
10
+
11
+ self.homepage_url = 'https://www.plexo.com.uy'
12
+ self.display_name = 'Plexo'
13
+
14
+ APPENDED_URLS = %w(captures refunds cancellations verify)
15
+ AMOUNT_IN_RESPONSE = %w(authonly purchase /verify)
16
+ APPROVED_STATUS = %w(approved authorized)
17
+
18
+ def initialize(options = {})
19
+ requires!(options, :client_id, :api_key)
20
+ @credentials = options
21
+ super
22
+ end
23
+
24
+ def purchase(money, payment, options = {})
25
+ post = {}
26
+ build_auth_purchase_request(money, post, payment, options)
27
+
28
+ commit('purchase', post, options)
29
+ end
30
+
31
+ def authorize(money, payment, options = {})
32
+ post = {}
33
+ build_auth_purchase_request(money, post, payment, options)
34
+ add_capture_type(post, options)
35
+
36
+ commit('authonly', post, options)
37
+ end
38
+
39
+ def capture(money, authorization, options = {})
40
+ post = {}
41
+ post[:ReferenceId] = options[:reference_id] || generate_unique_id
42
+ post[:Amount] = amount(money)
43
+
44
+ commit("/#{authorization}/captures", post, options)
45
+ end
46
+
47
+ def refund(money, authorization, options = {})
48
+ post = {}
49
+ post[:ReferenceId] = options[:reference_id] || generate_unique_id
50
+ post[:Type] = options[:refund_type] || 'refund'
51
+ post[:Description] = options[:description]
52
+ post[:Reason] = options[:reason]
53
+ post[:Amount] = amount(money)
54
+
55
+ commit("/#{authorization}/refunds", post, options)
56
+ end
57
+
58
+ def void(authorization, options = {})
59
+ post = {}
60
+ post[:ReferenceId] = options[:reference_id] || generate_unique_id
61
+ post[:Description] = options[:description]
62
+ post[:Reason] = options[:reason]
63
+
64
+ commit("/#{authorization}/cancellations", post, options)
65
+ end
66
+
67
+ def verify(credit_card, options = {})
68
+ post = {}
69
+ post[:ReferenceId] = options[:reference_id] || generate_unique_id
70
+ post[:MerchantId] = options[:merchant_id] || @credentials[:merchant_id]
71
+ post[:StatementDescriptor] = options[:statement_descriptor] if options[:statement_descriptor]
72
+ post[:CustomerId] = options[:customer_id] if options[:customer_id]
73
+ money = options[:verify_amount].to_i || 100
74
+
75
+ add_payment_method(post, credit_card, options)
76
+ add_metadata(post, options[:metadata])
77
+ add_amount(money, post, options)
78
+ add_browser_details(post, options)
79
+
80
+ commit('/verify', post, options)
81
+ end
82
+
83
+ def supports_scrubbing?
84
+ true
85
+ end
86
+
87
+ def scrub(transcript)
88
+ transcript.
89
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
90
+ gsub(%r(("Number\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
91
+ gsub(%r(("Cvc\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
92
+ gsub(%r(("InvoiceNumber\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
93
+ gsub(%r(("MerchantId\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]')
94
+ end
95
+
96
+ private
97
+
98
+ def encoded_credentials
99
+ Base64.encode64("#{@credentials[:client_id]}:#{@credentials[:api_key]}").delete("\n")
100
+ end
101
+
102
+ def build_auth_purchase_request(money, post, payment, options)
103
+ post[:ReferenceId] = options[:reference_id] || generate_unique_id
104
+ post[:MerchantId] = options[:merchant_id] || @credentials[:merchant_id]
105
+ post[:Installments] = options[:installments] if options[:installments]
106
+ post[:StatementDescriptor] = options[:statement_descriptor] if options[:statement_descriptor]
107
+ post[:CustomerId] = options[:customer_id] if options[:customer_id]
108
+
109
+ add_payment_method(post, payment, options)
110
+ add_items(post, options[:items])
111
+ add_metadata(post, options[:metadata])
112
+ add_amount(money, post, options)
113
+ add_browser_details(post, options)
114
+ end
115
+
116
+ def header(parameters = {})
117
+ {
118
+ 'Content-Type' => 'application/json',
119
+ 'Authorization' => "Basic #{encoded_credentials}"
120
+ }
121
+ end
122
+
123
+ def add_capture_type(post, options)
124
+ post[:Capture] = {}
125
+ post[:Capture][:Method] = options.dig(:capture_type, :method) || 'manual'
126
+ end
127
+
128
+ def add_items(post, items)
129
+ return unless items&.kind_of?(Array)
130
+
131
+ post[:Items] = []
132
+
133
+ items.each do |option_item|
134
+ item = {}
135
+ item[:ReferenceId] = option_item[:reference_id] || generate_unique_id
136
+ item[:Name] = option_item[:name] if option_item[:name]
137
+ item[:Description] = option_item[:description] if option_item[:description]
138
+ item[:Quantity] = option_item[:quantity] if option_item[:quantity]
139
+ item[:Price] = option_item[:price] if option_item[:price]
140
+ item[:Discount] = option_item[:discount] if option_item[:discount]
141
+
142
+ post[:Items].append(item)
143
+ end
144
+ end
145
+
146
+ def add_metadata(post, metadata)
147
+ return unless metadata&.kind_of?(Hash)
148
+
149
+ metadata.transform_keys! { |key| key.to_s.camelize.to_sym }
150
+ post[:Metadata] = metadata
151
+ end
152
+
153
+ def add_amount(money, post, amount_options)
154
+ post[:Amount] = {}
155
+
156
+ post[:Amount][:Currency] = amount_options[:currency] || self.default_currency
157
+ post[:Amount][:Total] = amount(money)
158
+ post[:Amount][:Details] = {}
159
+ add_amount_details(post[:Amount][:Details], amount_options[:amount_details]) if amount_options[:amount_details]
160
+ end
161
+
162
+ def add_amount_details(amount_details, options)
163
+ return unless options
164
+
165
+ amount_details[:TaxedAmount] = options[:taxed_amount] if options[:taxed_amount]
166
+ amount_details[:TipAmount] = options[:tip_amount] if options[:tip_amount]
167
+ amount_details[:DiscountAmount] = options[:discount_amount] if options[:discount_amount]
168
+ amount_details[:TaxableAmount] = options[:taxable_amount] if options[:taxable_amount]
169
+ add_tax(amount_details, options[:tax])
170
+ end
171
+
172
+ def add_tax(post, tax)
173
+ return unless tax
174
+
175
+ post[:Tax] = {}
176
+ post[:Tax][:Type] = tax[:type] if tax[:type]
177
+ post[:Tax][:Amount] = tax[:amount] if tax[:amount]
178
+ post[:Tax][:Rate] = tax[:rate] if tax[:rate]
179
+ end
180
+
181
+ def add_browser_details(post, browser_details)
182
+ return unless browser_details
183
+
184
+ post[:BrowserDetails] = {}
185
+ post[:BrowserDetails][:DeviceFingerprint] = browser_details[:finger_print] if browser_details[:finger_print]
186
+ post[:BrowserDetails][:IpAddress] = browser_details[:ip] if browser_details[:ip]
187
+ end
188
+
189
+ def add_payment_method(post, payment, options)
190
+ post[:paymentMethod] = {}
191
+
192
+ if payment&.is_a?(CreditCard)
193
+ post[:paymentMethod][:type] = 'card'
194
+ post[:paymentMethod][:Card] = {}
195
+ post[:paymentMethod][:Card][:Number] = payment.number
196
+ post[:paymentMethod][:Card][:ExpMonth] = format(payment.month, :two_digits) if payment.month
197
+ post[:paymentMethod][:Card][:ExpYear] = format(payment.year, :two_digits) if payment.year
198
+ post[:paymentMethod][:Card][:Cvc] = payment.verification_value if payment.verification_value
199
+
200
+ add_card_holder(post[:paymentMethod][:Card], payment, options)
201
+ end
202
+ end
203
+
204
+ def add_card_holder(card, payment, options)
205
+ requires!(options, :email)
206
+
207
+ cardholder = {}
208
+ cardholder[:FirstName] = payment.first_name if payment.first_name
209
+ cardholder[:LastName] = payment.last_name if payment.last_name
210
+ cardholder[:Email] = options[:email]
211
+ cardholder[:Birthdate] = options[:cardholder_birthdate] if options[:cardholder_birthdate]
212
+ cardholder[:Identification] = {}
213
+ cardholder[:Identification][:Type] = options[:identification_type] if options[:identification_type]
214
+ cardholder[:Identification][:Value] = options[:identification_value] if options[:identification_value]
215
+ add_billing_address(cardholder, options)
216
+
217
+ card[:Cardholder] = cardholder
218
+ end
219
+
220
+ def add_billing_address(cardholder, options)
221
+ return unless address = options[:billing_address]
222
+
223
+ cardholder[:BillingAddress] = {}
224
+ cardholder[:BillingAddress][:City] = address[:city]
225
+ cardholder[:BillingAddress][:Country] = address[:country]
226
+ cardholder[:BillingAddress][:Line1] = address[:address1]
227
+ cardholder[:BillingAddress][:Line2] = address[:address2]
228
+ cardholder[:BillingAddress][:PostalCode] = address[:zip]
229
+ cardholder[:BillingAddress][:State] = address[:state]
230
+ end
231
+
232
+ def parse(body)
233
+ return {} if body == ''
234
+
235
+ JSON.parse(body)
236
+ end
237
+
238
+ def build_url(action, base)
239
+ url = base
240
+ url += action if APPENDED_URLS.any? { |key| action.include?(key) }
241
+ url
242
+ end
243
+
244
+ def get_authorization_from_url(url)
245
+ url.split('/')[1]
246
+ end
247
+
248
+ def reorder_amount_fields(response)
249
+ return response unless response['amount']
250
+
251
+ amount_obj = response['amount']
252
+ response['amount'] = amount_obj['total'].to_i if amount_obj['total']
253
+ response['currency'] = amount_obj['currency'] if amount_obj['currency']
254
+ response['amount_details'] = amount_obj['details'] if amount_obj['details']
255
+ response
256
+ end
257
+
258
+ def commit(action, parameters, options = {})
259
+ base_url = (test? ? test_url : live_url)
260
+ url = build_url(action, base_url)
261
+ response = parse(ssl_post(url, parameters.to_json, header(options)))
262
+ response = reorder_amount_fields(response) if AMOUNT_IN_RESPONSE.include?(action)
263
+
264
+ Response.new(
265
+ success_from(response),
266
+ message_from(response),
267
+ response,
268
+ authorization: authorization_from(response, action),
269
+ test: test?,
270
+ error_code: error_code_from(response)
271
+ )
272
+ end
273
+
274
+ def handle_response(response)
275
+ case response.code.to_i
276
+ when 200...300, 400, 401
277
+ response.body
278
+ else
279
+ raise ResponseError.new(response)
280
+ end
281
+ end
282
+
283
+ def success_from(response)
284
+ APPROVED_STATUS.include?(response['status'])
285
+ end
286
+
287
+ def message_from(response)
288
+ response = response['transactions']&.first if response['transactions']&.is_a?(Array)
289
+ response['resultMessage'] || response['message']
290
+ end
291
+
292
+ def authorization_from(response, action = nil)
293
+ if action.include?('captures')
294
+ get_authorization_from_url(action)
295
+ else
296
+ response['id']
297
+ end
298
+ end
299
+
300
+ def error_code_from(response)
301
+ return if success_from(response)
302
+
303
+ response = response['transactions']&.first if response['transactions']&.is_a?(Array)
304
+ response['resultCode'] || response['status']
305
+ end
306
+ end
307
+ end
308
+ end