activemerchant 1.123.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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +206 -0
  3. data/lib/active_merchant/billing/check.rb +5 -8
  4. data/lib/active_merchant/billing/credit_card.rb +10 -0
  5. data/lib/active_merchant/billing/credit_card_methods.rb +18 -3
  6. data/lib/active_merchant/billing/gateway.rb +3 -2
  7. data/lib/active_merchant/billing/gateways/adyen.rb +66 -11
  8. data/lib/active_merchant/billing/gateways/airwallex.rb +341 -0
  9. data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +2 -1
  10. data/lib/active_merchant/billing/gateways/blue_pay.rb +1 -1
  11. data/lib/active_merchant/billing/gateways/blue_snap.rb +31 -21
  12. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +6 -1
  13. data/lib/active_merchant/billing/gateways/braintree/token_nonce.rb +113 -0
  14. data/lib/active_merchant/billing/gateways/braintree_blue.rb +87 -15
  15. data/lib/active_merchant/billing/gateways/card_connect.rb +1 -1
  16. data/lib/active_merchant/billing/gateways/card_stream.rb +17 -13
  17. data/lib/active_merchant/billing/gateways/cashnet.rb +15 -5
  18. data/lib/active_merchant/billing/gateways/checkout_v2.rb +34 -5
  19. data/lib/active_merchant/billing/gateways/credorax.rb +10 -0
  20. data/lib/active_merchant/billing/gateways/cyber_source.rb +24 -36
  21. data/lib/active_merchant/billing/gateways/d_local.rb +61 -6
  22. data/lib/active_merchant/billing/gateways/decidir.rb +17 -1
  23. data/lib/active_merchant/billing/gateways/decidir_plus.rb +344 -0
  24. data/lib/active_merchant/billing/gateways/ebanx.rb +19 -3
  25. data/lib/active_merchant/billing/gateways/elavon.rb +6 -3
  26. data/lib/active_merchant/billing/gateways/element.rb +20 -2
  27. data/lib/active_merchant/billing/gateways/global_collect.rb +137 -32
  28. data/lib/active_merchant/billing/gateways/ipg.rb +415 -0
  29. data/lib/active_merchant/billing/gateways/kushki.rb +7 -0
  30. data/lib/active_merchant/billing/gateways/litle.rb +93 -1
  31. data/lib/active_merchant/billing/gateways/mercado_pago.rb +3 -1
  32. data/lib/active_merchant/billing/gateways/mit.rb +260 -0
  33. data/lib/active_merchant/billing/gateways/moka.rb +24 -11
  34. data/lib/active_merchant/billing/gateways/moneris.rb +35 -8
  35. data/lib/active_merchant/billing/gateways/mundipagg.rb +8 -6
  36. data/lib/active_merchant/billing/gateways/nmi.rb +27 -8
  37. data/lib/active_merchant/billing/gateways/orbital.rb +357 -319
  38. data/lib/active_merchant/billing/gateways/pay_arc.rb +9 -7
  39. data/lib/active_merchant/billing/gateways/pay_conex.rb +3 -1
  40. data/lib/active_merchant/billing/gateways/pay_trace.rb +1 -1
  41. data/lib/active_merchant/billing/gateways/payflow.rb +76 -6
  42. data/lib/active_merchant/billing/gateways/paymentez.rb +35 -9
  43. data/lib/active_merchant/billing/gateways/paysafe.rb +155 -34
  44. data/lib/active_merchant/billing/gateways/payu_latam.rb +31 -16
  45. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +3 -3
  46. data/lib/active_merchant/billing/gateways/pin.rb +31 -4
  47. data/lib/active_merchant/billing/gateways/priority.rb +369 -0
  48. data/lib/active_merchant/billing/gateways/rapyd.rb +258 -0
  49. data/lib/active_merchant/billing/gateways/realex.rb +18 -0
  50. data/lib/active_merchant/billing/gateways/safe_charge.rb +7 -6
  51. data/lib/active_merchant/billing/gateways/simetrik.rb +362 -0
  52. data/lib/active_merchant/billing/gateways/stripe.rb +30 -8
  53. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +175 -72
  54. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +2 -1
  55. data/lib/active_merchant/billing/gateways/trust_commerce.rb +2 -1
  56. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +20 -6
  57. data/lib/active_merchant/billing/gateways/visanet_peru.rb +6 -2
  58. data/lib/active_merchant/billing/gateways/wompi.rb +193 -0
  59. data/lib/active_merchant/billing/gateways/worldpay.rb +196 -64
  60. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +1 -1
  61. data/lib/active_merchant/billing/response.rb +4 -0
  62. data/lib/active_merchant/version.rb +1 -1
  63. metadata +11 -2
@@ -1,8 +1,8 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
3
  class PaywayDotComGateway < Gateway
4
- self.test_url = 'https://devedgilpayway.net/PaywayWS/Payment/CreditCard'
5
- self.live_url = 'https://edgilpayway.com/PaywayWS/Payment/CreditCard'
4
+ self.test_url = 'https://paywaywsdev.com/PaywayWS/Payment/CreditCard'
5
+ self.live_url = 'https://paywayws.com/PaywayWS/Payment/CreditCard'
6
6
 
7
7
  self.supported_countries = %w[US CA]
8
8
  self.default_currency = 'USD'
@@ -233,7 +233,7 @@ module ActiveMerchant #:nodoc:
233
233
 
234
234
  return response['paywayCode'] + '-' + 'success' if success
235
235
 
236
- response['paywayCode'] + '-' + response['paywayMessage']
236
+ response['paywayCode']
237
237
  end
238
238
 
239
239
  def authorization_from(response)
@@ -6,8 +6,8 @@ module ActiveMerchant #:nodoc:
6
6
 
7
7
  self.default_currency = 'AUD'
8
8
  self.money_format = :cents
9
- self.supported_countries = ['AU']
10
- self.supported_cardtypes = %i[visa master american_express]
9
+ self.supported_countries = %w(AU NZ)
10
+ self.supported_cardtypes = %i[visa master american_express diners_club discover jcb]
11
11
  self.homepage_url = 'http://www.pinpayments.com/'
12
12
  self.display_name = 'Pin Payments'
13
13
 
@@ -46,6 +46,17 @@ module ActiveMerchant #:nodoc:
46
46
  commit(:post, 'customers', post, options)
47
47
  end
48
48
 
49
+ # Unstore a customer and associated credit card.
50
+ def unstore(token)
51
+ customer_token =
52
+ if /cus_/.match?(token)
53
+ get_customer_token(token)
54
+ else
55
+ token
56
+ end
57
+ commit(:delete, "customers/#{CGI.escape(customer_token)}", {}, {})
58
+ end
59
+
49
60
  # Refund a transaction
50
61
  def refund(money, token, options = {})
51
62
  commit(:post, "charges/#{CGI.escape(token)}/refunds", { amount: amount(money) }, options)
@@ -65,6 +76,11 @@ module ActiveMerchant #:nodoc:
65
76
  commit(:put, "charges/#{CGI.escape(token)}/capture", { amount: amount(money) }, options)
66
77
  end
67
78
 
79
+ # Voids a previously authorized charge.
80
+ def void(token, options = {})
81
+ commit(:put, "charges/#{CGI.escape(token)}/void", {}, options)
82
+ end
83
+
68
84
  # Updates the credit card for the customer.
69
85
  def update(token, creditcard, options = {})
70
86
  post = {}
@@ -190,7 +206,9 @@ module ActiveMerchant #:nodoc:
190
206
  body = parse(e.response.body)
191
207
  end
192
208
 
193
- if body['response']
209
+ if body.nil?
210
+ no_content_response
211
+ elsif body['response']
194
212
  success_response(body)
195
213
  elsif body['error']
196
214
  error_response(body)
@@ -220,6 +238,15 @@ module ActiveMerchant #:nodoc:
220
238
  )
221
239
  end
222
240
 
241
+ def no_content_response
242
+ Response.new(
243
+ true,
244
+ nil,
245
+ {},
246
+ test: test?
247
+ )
248
+ end
249
+
223
250
  def unparsable_response(raw_response)
224
251
  message = 'Invalid JSON response received from Pin Payments. Please contact support@pinpayments.com if you continue to receive this message.'
225
252
  message += " (The raw response returned by the API was #{raw_response.inspect})"
@@ -235,7 +262,7 @@ module ActiveMerchant #:nodoc:
235
262
  end
236
263
 
237
264
  def parse(body)
238
- JSON.parse(body)
265
+ JSON.parse(body) unless body.nil? || body.length == 0
239
266
  end
240
267
 
241
268
  def post_data(parameters = {})
@@ -0,0 +1,369 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class PriorityGateway < Gateway
4
+ # Sandbox and Production
5
+ self.test_url = 'https://sandbox.api.mxmerchant.com/checkout/v3/payment'
6
+ self.live_url = 'https://api.mxmerchant.com/checkout/v3/payment'
7
+
8
+ class_attribute :test_url_verify, :live_url_verify, :test_auth, :live_auth, :test_env_verify, :live_env_verify, :test_url_batch, :live_url_batch, :test_url_jwt, :live_url_jwt, :merchant
9
+
10
+ # Sandbox and Production - verify card
11
+ self.test_url_verify = 'https://sandbox-api2.mxmerchant.com/merchant/v1/bin'
12
+ self.live_url_verify = 'https://api2.mxmerchant.com/merchant/v1/bin'
13
+
14
+ # Sandbox and Production - check batch status
15
+ self.test_url_batch = 'https://sandbox.api.mxmerchant.com/checkout/v3/batch'
16
+ self.live_url_batch = 'https://api.mxmerchant.com/checkout/v3/batch'
17
+
18
+ # Sandbox and Production - generate jwt for verify card url
19
+ self.test_url_jwt = 'https://sandbox-api2.mxmerchant.com/security/v1/application/merchantId'
20
+ self.live_url_jwt = 'https://api2.mxmerchant.com/security/v1/application/merchantId'
21
+
22
+ self.supported_countries = ['US']
23
+ self.default_currency = 'USD'
24
+ self.supported_cardtypes = %i[visa master american_express discover]
25
+
26
+ self.homepage_url = 'https://mxmerchant.com/'
27
+ self.display_name = 'Priority'
28
+
29
+ def initialize(options = {})
30
+ requires!(options, :merchant_id, :key, :secret)
31
+ super
32
+ end
33
+
34
+ def basic_auth
35
+ Base64.strict_encode64("#{@options[:key]}:#{@options[:secret]}")
36
+ end
37
+
38
+ def request_headers
39
+ {
40
+ 'Content-Type' => 'application/json',
41
+ 'Authorization' => "Basic #{basic_auth}"
42
+ }
43
+ end
44
+
45
+ def request_verify_headers(jwt)
46
+ {
47
+ 'Authorization' => "Bearer #{jwt}"
48
+ }
49
+ end
50
+
51
+ def purchase(amount, credit_card, options = {})
52
+ params = {}
53
+ params['authOnly'] = false
54
+ params['isSettleFunds'] = true
55
+
56
+ add_merchant_id(params)
57
+ add_amount(params, amount, options)
58
+ add_auth_purchase_params(params, credit_card, options)
59
+
60
+ commit('purchase', params: params)
61
+ end
62
+
63
+ def authorize(amount, credit_card, options = {})
64
+ params = {}
65
+ params['authOnly'] = true
66
+ params['isSettleFunds'] = false
67
+
68
+ add_merchant_id(params)
69
+ add_amount(params, amount, options)
70
+ add_auth_purchase_params(params, credit_card, options)
71
+
72
+ commit('purchase', params: params)
73
+ end
74
+
75
+ def refund(amount, authorization, options = {})
76
+ params = {}
77
+ add_merchant_id(params)
78
+ params['paymentToken'] = payment_token(authorization) || options[:payment_token]
79
+
80
+ # refund amounts must be negative
81
+ params['amount'] = ('-' + localized_amount(amount.to_f, options[:currency])).to_f
82
+
83
+ commit('refund', params: params)
84
+ end
85
+
86
+ def capture(amount, authorization, options = {})
87
+ params = {}
88
+ add_merchant_id(params)
89
+ add_amount(params, amount, options)
90
+ params['paymentToken'] = payment_token(authorization) || options[:payment_token]
91
+ params['tenderType'] = options[:tender_type].present? ? options[:tender_type] : 'Card'
92
+
93
+ commit('capture', params: params)
94
+ end
95
+
96
+ def void(authorization, options = {})
97
+ params = {}
98
+
99
+ commit('void', params: params, iid: payment_id(authorization))
100
+ end
101
+
102
+ def verify(credit_card, _options = {})
103
+ jwt = create_jwt.params['jwtToken']
104
+
105
+ commit('verify', card_number: credit_card.number, jwt: jwt)
106
+ end
107
+
108
+ def get_payment_status(batch_id)
109
+ commit('get_payment_status', params: batch_id)
110
+ end
111
+
112
+ def close_batch(batch_id)
113
+ commit('close_batch', params: batch_id)
114
+ end
115
+
116
+ def create_jwt
117
+ commit('create_jwt', params: @options[:merchant_id])
118
+ end
119
+
120
+ def supports_scrubbing?
121
+ true
122
+ end
123
+
124
+ def scrub(transcript)
125
+ transcript.
126
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
127
+ gsub(%r((number\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
128
+ gsub(%r((cvv\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]')
129
+ end
130
+
131
+ private
132
+
133
+ def add_amount(params, amount, options)
134
+ params['amount'] = localized_amount(amount.to_f, options[:currency])
135
+ end
136
+
137
+ def add_merchant_id(params)
138
+ params['merchantId'] = @options[:merchant_id]
139
+ end
140
+
141
+ def add_auth_purchase_params(params, credit_card, options)
142
+ add_replay_id(params, options)
143
+ add_credit_card(params, credit_card, 'purchase', options)
144
+ add_purchases_data(params, options)
145
+ add_shipping_data(params, options)
146
+ add_pos_data(params, options)
147
+ add_additional_data(params, options)
148
+ end
149
+
150
+ def add_replay_id(params, options)
151
+ params['replayId'] = options[:replay_id] if options[:replay_id]
152
+ end
153
+
154
+ def add_credit_card(params, credit_card, action, options)
155
+ return unless credit_card&.is_a?(CreditCard)
156
+
157
+ card_details = {}
158
+ card_details['expiryMonth'] = format(credit_card.month, :two_digits).to_s
159
+ card_details['expiryYear'] = format(credit_card.year, :two_digits).to_s
160
+ card_details['expiryDate'] = exp_date(credit_card)
161
+ card_details['cardType'] = credit_card.brand
162
+ card_details['last4'] = credit_card.last_digits
163
+ card_details['cvv'] = credit_card.verification_value
164
+ card_details['number'] = credit_card.number
165
+ card_details['avsStreet'] = options[:billing_address][:address1] if options[:billing_address]
166
+ card_details['avsZip'] = options[:billing_address][:zip] if options[:billing_address]
167
+
168
+ params['cardAccount'] = card_details
169
+ end
170
+
171
+ def exp_date(credit_card)
172
+ "#{format(credit_card.month, :two_digits)}/#{format(credit_card.year, :two_digits)}"
173
+ end
174
+
175
+ def add_additional_data(params, options)
176
+ params['isAuth'] = options[:is_auth].present? ? options[:is_auth] : 'true'
177
+ params['paymentType'] = options[:payment_type].present? ? options[:payment_type] : 'Sale'
178
+ params['tenderType'] = options[:tender_type].present? ? options[:tender_type] : 'Card'
179
+ params['taxExempt'] = options[:tax_exempt].present? ? options[:tax_exempt] : 'false'
180
+ params['taxAmount'] = options[:tax_amount] if options[:tax_amount]
181
+ params['shouldGetCreditCardLevel'] = options[:should_get_credit_card_level] if options[:should_get_credit_card_level]
182
+ params['source'] = options[:source] if options[:source]
183
+ params['invoice'] = options[:invoice] if options[:invoice]
184
+ end
185
+
186
+ def add_pos_data(params, options)
187
+ pos_data = {}
188
+
189
+ pos_data['cardholderPresence'] = options.dig(:pos_data, :cardholder_presence) || 'Ecom'
190
+ pos_data['deviceAttendance'] = options.dig(:pos_data, :device_attendance) || 'HomePc'
191
+ pos_data['deviceInputCapability'] = options.dig(:pos_data, :device_input_capability) || 'Unknown'
192
+ pos_data['deviceLocation'] = options.dig(:pos_data, :device_location) || 'HomePc'
193
+ pos_data['panCaptureMethod'] = options.dig(:pos_data, :pan_capture_method) || 'Manual'
194
+ pos_data['partialApprovalSupport'] = options.dig(:pos_data, :partial_approval_support) || 'NotSupported'
195
+ pos_data['pinCaptureCapability'] = options.dig(:pos_data, :pin_capture_capability) || 'Incapable'
196
+
197
+ params['posData'] = pos_data
198
+ end
199
+
200
+ def add_purchases_data(params, options)
201
+ return unless options[:purchases]
202
+
203
+ params['purchases'] = []
204
+
205
+ options[:purchases].each do |purchase|
206
+ purchase_object = {}
207
+
208
+ purchase_object['name'] = purchase[:name] if purchase[:name]
209
+ purchase_object['description'] = purchase[:description] if purchase[:description]
210
+ purchase_object['code'] = purchase[:code] if purchase[:code]
211
+ purchase_object['unitOfMeasure'] = purchase[:unit_of_measure] if purchase[:unit_of_measure]
212
+ purchase_object['unitPrice'] = purchase[:unit_price] if purchase[:unit_price]
213
+ purchase_object['quantity'] = purchase[:quantity] if purchase[:quantity]
214
+ purchase_object['taxRate'] = purchase[:tax_rate] if purchase[:tax_rate]
215
+ purchase_object['taxAmount'] = purchase[:tax_amount] if purchase[:tax_amount]
216
+ purchase_object['discountRate'] = purchase[:discount_rate] if purchase[:discount_rate]
217
+ purchase_object['discountAmount'] = purchase[:discount_amount] if purchase[:discount_amount]
218
+ purchase_object['extendedAmount'] = purchase[:extended_amount] if purchase[:extended_amount]
219
+ purchase_object['lineItemId'] = purchase[:line_item_id] if purchase[:line_item_id]
220
+
221
+ params['purchases'].append(purchase_object)
222
+ end
223
+ end
224
+
225
+ def add_shipping_data(params, options)
226
+ params['shipAmount'] = options[:ship_amount] if options[:ship_amount]
227
+
228
+ shipping_country = shipping_country_from(options)
229
+ params['shipToCountry'] = shipping_country if shipping_country
230
+
231
+ shipping_zip = shipping_zip_from(options)
232
+ params['shipToZip'] = shipping_zip if shipping_zip
233
+ end
234
+
235
+ def shipping_country_from(options)
236
+ options[:ship_to_country] || options.dig(:shipping_address, :country) || options.dig(:billing_address, :country)
237
+ end
238
+
239
+ def shipping_zip_from(options)
240
+ options[:ship_to_zip] || options.dig(:shipping_addres, :zip) || options.dig(:billing_address, :zip)
241
+ end
242
+
243
+ def payment_token(authorization)
244
+ return unless authorization
245
+ return authorization unless authorization.include?('|')
246
+
247
+ authorization.split('|').last
248
+ end
249
+
250
+ def payment_id(authorization)
251
+ return unless authorization
252
+ return authorization unless authorization.include?('|')
253
+
254
+ authorization.split('|').first
255
+ end
256
+
257
+ def commit(action, params: '', iid: '', card_number: nil, jwt: '')
258
+ response =
259
+ begin
260
+ case action
261
+ when 'void'
262
+ parse(ssl_request(:delete, url(action, params, ref_number: iid), nil, request_headers))
263
+ when 'verify'
264
+ parse(ssl_get(url(action, params, credit_card_number: card_number), request_verify_headers(jwt)))
265
+ when 'get_payment_status', 'create_jwt'
266
+ parse(ssl_get(url(action, params, ref_number: iid), request_headers))
267
+ when 'close_batch'
268
+ parse(ssl_request(:put, url(action, params, ref_number: iid), nil, request_headers))
269
+ else
270
+ parse(ssl_post(url(action, params), post_data(params), request_headers))
271
+ end
272
+ rescue ResponseError => e
273
+ # currently Priority returns a 404 with no body on certain calls. In those cases we will substitute the response status from response.message
274
+ gateway_response = e.response.body.presence || e.response.message
275
+ parse(gateway_response)
276
+ end
277
+
278
+ success = success_from(response, action)
279
+ Response.new(
280
+ success,
281
+ message_from(response),
282
+ response,
283
+ authorization: success ? authorization_from(response) : nil,
284
+ error_code: success || response == '' ? nil : error_from(response),
285
+ test: test?
286
+ )
287
+ end
288
+
289
+ def url(action, params, ref_number: '', credit_card_number: nil)
290
+ case action
291
+ when 'void'
292
+ base_url + "/#{ref_number}?force=true"
293
+ when 'verify'
294
+ (verify_url + '?search=') + credit_card_number.to_s[0..6]
295
+ when 'get_payment_status', 'close_batch'
296
+ batch_url + "/#{params}"
297
+ when 'create_jwt'
298
+ jwt_url + "/#{params}/token"
299
+ else
300
+ base_url + '?includeCustomerMatches=false&echo=true'
301
+ end
302
+ end
303
+
304
+ def base_url
305
+ test? ? test_url : live_url
306
+ end
307
+
308
+ def verify_url
309
+ test? ? self.test_url_verify : self.live_url_verify
310
+ end
311
+
312
+ def jwt_url
313
+ test? ? self.test_url_jwt : self.live_url_jwt
314
+ end
315
+
316
+ def batch_url
317
+ test? ? self.test_url_batch : self.live_url_batch
318
+ end
319
+
320
+ def handle_response(response)
321
+ case response.code.to_i
322
+ when 204
323
+ { status: 'Success' }.to_json
324
+ when 200...300
325
+ response.body
326
+ else
327
+ raise ResponseError.new(response)
328
+ end
329
+ end
330
+
331
+ def parse(body)
332
+ return {} if body.blank?
333
+
334
+ parsed_response = JSON.parse(body)
335
+ parsed_response.is_a?(String) ? { 'message' => parsed_response } : parsed_response
336
+ rescue JSON::ParserError
337
+ message = 'Invalid JSON response received from Priority Gateway. Please contact Priority Gateway if you continue to receive this message.'
338
+ message += " (The raw response returned by the API was #{body.inspect})"
339
+ {
340
+ 'message' => message
341
+ }
342
+ end
343
+
344
+ def success_from(response, action)
345
+ return !response['bank'].empty? if action == 'verify' && response['bank']
346
+
347
+ %w[Approved Open Success Settled Voided].include?(response['status'])
348
+ end
349
+
350
+ def message_from(response)
351
+ return response['details'][0] if response['details'] && response['details'][0]
352
+
353
+ response['authMessage'] || response['message'] || response['status']
354
+ end
355
+
356
+ def authorization_from(response)
357
+ [response['id'], response['paymentToken']].join('|')
358
+ end
359
+
360
+ def error_from(response)
361
+ response['errorCode'] || response['status']
362
+ end
363
+
364
+ def post_data(params)
365
+ params.to_json
366
+ end
367
+ end
368
+ end
369
+ end