activemerchant 1.126.0 → 1.129.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +241 -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 +79 -23
  6. data/lib/active_merchant/billing/gateways/adyen.rb +67 -8
  7. data/lib/active_merchant/billing/gateways/airwallex.rb +40 -11
  8. data/lib/active_merchant/billing/gateways/alelo.rb +256 -0
  9. data/lib/active_merchant/billing/gateways/authorize_net.rb +21 -4
  10. data/lib/active_merchant/billing/gateways/beanstream.rb +18 -0
  11. data/lib/active_merchant/billing/gateways/blue_snap.rb +22 -1
  12. data/lib/active_merchant/billing/gateways/bogus.rb +4 -0
  13. data/lib/active_merchant/billing/gateways/borgun.rb +56 -16
  14. data/lib/active_merchant/billing/gateways/braintree_blue.rb +64 -17
  15. data/lib/active_merchant/billing/gateways/card_connect.rb +27 -9
  16. data/lib/active_merchant/billing/gateways/card_stream.rb +23 -0
  17. data/lib/active_merchant/billing/gateways/checkout_v2.rb +228 -57
  18. data/lib/active_merchant/billing/gateways/commerce_hub.rb +361 -0
  19. data/lib/active_merchant/billing/gateways/credorax.rb +47 -27
  20. data/lib/active_merchant/billing/gateways/cyber_source/cyber_source_common.rb +36 -0
  21. data/lib/active_merchant/billing/gateways/cyber_source.rb +100 -26
  22. data/lib/active_merchant/billing/gateways/cyber_source_rest.rb +456 -0
  23. data/lib/active_merchant/billing/gateways/d_local.rb +44 -5
  24. data/lib/active_merchant/billing/gateways/decidir.rb +15 -4
  25. data/lib/active_merchant/billing/gateways/ebanx.rb +36 -24
  26. data/lib/active_merchant/billing/gateways/element.rb +21 -1
  27. data/lib/active_merchant/billing/gateways/global_collect.rb +73 -22
  28. data/lib/active_merchant/billing/gateways/ipg.rb +13 -8
  29. data/lib/active_merchant/billing/gateways/iveri.rb +39 -3
  30. data/lib/active_merchant/billing/gateways/kushki.rb +21 -1
  31. data/lib/active_merchant/billing/gateways/litle.rb +25 -5
  32. data/lib/active_merchant/billing/gateways/mastercard.rb +1 -8
  33. data/lib/active_merchant/billing/gateways/mercado_pago.rb +17 -0
  34. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +44 -10
  35. data/lib/active_merchant/billing/gateways/monei.rb +2 -0
  36. data/lib/active_merchant/billing/gateways/moneris.rb +20 -5
  37. data/lib/active_merchant/billing/gateways/mundipagg.rb +3 -0
  38. data/lib/active_merchant/billing/gateways/ogone.rb +35 -7
  39. data/lib/active_merchant/billing/gateways/openpay.rb +20 -3
  40. data/lib/active_merchant/billing/gateways/orbital.rb +43 -22
  41. data/lib/active_merchant/billing/gateways/pay_trace.rb +64 -18
  42. data/lib/active_merchant/billing/gateways/payeezy.rb +59 -4
  43. data/lib/active_merchant/billing/gateways/paymentez.rb +18 -6
  44. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +4 -0
  45. data/lib/active_merchant/billing/gateways/paysafe.rb +22 -14
  46. data/lib/active_merchant/billing/gateways/payu_latam.rb +3 -0
  47. data/lib/active_merchant/billing/gateways/plexo.rb +308 -0
  48. data/lib/active_merchant/billing/gateways/priority.rb +29 -6
  49. data/lib/active_merchant/billing/gateways/rapyd.rb +110 -49
  50. data/lib/active_merchant/billing/gateways/reach.rb +277 -0
  51. data/lib/active_merchant/billing/gateways/redsys.rb +9 -5
  52. data/lib/active_merchant/billing/gateways/sage_pay.rb +1 -1
  53. data/lib/active_merchant/billing/gateways/securion_pay.rb +40 -0
  54. data/lib/active_merchant/billing/gateways/shift4.rb +342 -0
  55. data/lib/active_merchant/billing/gateways/simetrik.rb +28 -22
  56. data/lib/active_merchant/billing/gateways/stripe.rb +21 -1
  57. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +62 -22
  58. data/lib/active_merchant/billing/gateways/tns.rb +2 -5
  59. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +1 -1
  60. data/lib/active_merchant/billing/gateways/trust_commerce.rb +14 -3
  61. data/lib/active_merchant/billing/gateways/vanco.rb +12 -3
  62. data/lib/active_merchant/billing/gateways/visanet_peru.rb +1 -1
  63. data/lib/active_merchant/billing/gateways/vpos.rb +7 -4
  64. data/lib/active_merchant/billing/gateways/wompi.rb +8 -4
  65. data/lib/active_merchant/billing/gateways/worldpay.rb +117 -9
  66. data/lib/active_merchant/billing/response.rb +15 -1
  67. data/lib/active_merchant/connection.rb +0 -2
  68. data/lib/active_merchant/country.rb +1 -0
  69. data/lib/active_merchant/errors.rb +4 -1
  70. data/lib/active_merchant/version.rb +1 -1
  71. metadata +24 -3
@@ -6,18 +6,12 @@ module ActiveMerchant #:nodoc:
6
6
 
7
7
  self.supported_countries = %w(BR MX CO CL AR PE)
8
8
  self.default_currency = 'USD'
9
- self.supported_cardtypes = %i[visa master american_express discover diners_club]
9
+ self.supported_cardtypes = %i[visa master american_express discover diners_club elo hipercard]
10
10
 
11
11
  self.homepage_url = 'http://www.ebanx.com/'
12
12
  self.display_name = 'EBANX'
13
13
 
14
- CARD_BRAND = {
15
- visa: 'visa',
16
- master: 'master_card',
17
- american_express: 'amex',
18
- discover: 'discover',
19
- diners_club: 'diners'
20
- }
14
+ TAGS = ['Spreedly']
21
15
 
22
16
  URL_MAP = {
23
17
  purchase: 'direct',
@@ -25,7 +19,8 @@ module ActiveMerchant #:nodoc:
25
19
  capture: 'capture',
26
20
  refund: 'refund',
27
21
  void: 'cancel',
28
- store: 'token'
22
+ store: 'token',
23
+ inquire: 'query'
29
24
  }
30
25
 
31
26
  HTTP_METHOD = {
@@ -34,13 +29,14 @@ module ActiveMerchant #:nodoc:
34
29
  capture: :get,
35
30
  refund: :post,
36
31
  void: :get,
37
- store: :post
32
+ store: :post,
33
+ inquire: :get
38
34
  }
39
35
 
40
36
  VERIFY_AMOUNT_PER_COUNTRY = {
41
37
  'br' => 100,
42
38
  'ar' => 100,
43
- 'co' => 100,
39
+ 'co' => 50000,
44
40
  'pe' => 300,
45
41
  'mx' => 2000,
46
42
  'cl' => 80000
@@ -57,7 +53,7 @@ module ActiveMerchant #:nodoc:
57
53
  add_operation(post)
58
54
  add_invoice(post, money, options)
59
55
  add_customer_data(post, payment, options)
60
- add_card_or_token(post, payment)
56
+ add_card_or_token(post, payment, options)
61
57
  add_address(post, options)
62
58
  add_customer_responsible_person(post, payment, options)
63
59
  add_additional_data(post, options)
@@ -71,7 +67,7 @@ module ActiveMerchant #:nodoc:
71
67
  add_operation(post)
72
68
  add_invoice(post, money, options)
73
69
  add_customer_data(post, payment, options)
74
- add_card_or_token(post, payment)
70
+ add_card_or_token(post, payment, options)
75
71
  add_address(post, options)
76
72
  add_customer_responsible_person(post, payment, options)
77
73
  add_additional_data(post, options)
@@ -124,6 +120,14 @@ module ActiveMerchant #:nodoc:
124
120
  end
125
121
  end
126
122
 
123
+ def inquire(authorization, options = {})
124
+ post = {}
125
+ add_integration_key(post)
126
+ add_authorization(post, authorization)
127
+
128
+ commit(:inquire, post)
129
+ end
130
+
127
131
  def supports_scrubbing?
128
132
  true
129
133
  end
@@ -151,7 +155,7 @@ module ActiveMerchant #:nodoc:
151
155
 
152
156
  def add_customer_data(post, payment, options)
153
157
  post[:payment][:name] = customer_name(payment, options)
154
- post[:payment][:email] = options[:email] || 'unspecified@example.com'
158
+ post[:payment][:email] = options[:email]
155
159
  post[:payment][:document] = options[:document]
156
160
  post[:payment][:birth_date] = options[:birth_date] if options[:birth_date]
157
161
  end
@@ -186,14 +190,15 @@ module ActiveMerchant #:nodoc:
186
190
  post[:payment][:order_number] = options[:order_id][0..39] if options[:order_id]
187
191
  end
188
192
 
189
- def add_card_or_token(post, payment)
190
- payment, brand = payment.split('|') if payment.is_a?(String)
191
- post[:payment][:payment_type_code] = payment.is_a?(String) ? brand : CARD_BRAND[payment.brand.to_sym]
193
+ def add_card_or_token(post, payment, options)
194
+ payment = payment.split('|')[0] if payment.is_a?(String)
195
+ post[:payment][:payment_type_code] = 'creditcard'
192
196
  post[:payment][:creditcard] = payment_details(payment)
197
+ post[:payment][:creditcard][:soft_descriptor] = options[:soft_descriptor] if options[:soft_descriptor]
193
198
  end
194
199
 
195
200
  def add_payment_details(post, payment)
196
- post[:payment_type_code] = CARD_BRAND[payment.brand.to_sym]
201
+ post[:payment_type_code] = 'creditcard'
197
202
  post[:creditcard] = payment_details(payment)
198
203
  end
199
204
 
@@ -216,6 +221,7 @@ module ActiveMerchant #:nodoc:
216
221
  post[:metadata] = {} if post[:metadata].nil?
217
222
  post[:metadata][:merchant_payment_code] = options[:order_id] if options[:order_id]
218
223
  post[:processing_type] = options[:processing_type] if options[:processing_type]
224
+ post[:payment][:tags] = TAGS
219
225
  end
220
226
 
221
227
  def parse(body)
@@ -253,13 +259,15 @@ module ActiveMerchant #:nodoc:
253
259
  end
254
260
 
255
261
  def success_from(action, response)
262
+ payment_status = response.try(:[], 'payment').try(:[], 'status')
263
+
256
264
  if %i[purchase capture refund].include?(action)
257
- response.try(:[], 'payment').try(:[], 'status') == 'CO'
265
+ payment_status == 'CO'
258
266
  elsif action == :authorize
259
- response.try(:[], 'payment').try(:[], 'status') == 'PE'
267
+ payment_status == 'PE'
260
268
  elsif action == :void
261
- response.try(:[], 'payment').try(:[], 'status') == 'CA'
262
- elsif action == :store
269
+ payment_status == 'CA'
270
+ elsif %i[store inquire].include?(action)
263
271
  response.try(:[], 'status') == 'SUCCESS'
264
272
  else
265
273
  false
@@ -274,7 +282,11 @@ module ActiveMerchant #:nodoc:
274
282
 
275
283
  def authorization_from(action, parameters, response)
276
284
  if action == :store
277
- "#{response.try(:[], 'token')}|#{CARD_BRAND[parameters[:payment_type_code].to_sym]}"
285
+ if success_from(action, response)
286
+ "#{response.try(:[], 'token')}|#{response['payment_type_code']}"
287
+ else
288
+ response.try(:[], 'token')
289
+ end
278
290
  else
279
291
  response.try(:[], 'payment').try(:[], 'hash')
280
292
  end
@@ -294,7 +306,7 @@ module ActiveMerchant #:nodoc:
294
306
  end
295
307
 
296
308
  def requires_http_get(action)
297
- return true if %i[capture void].include?(action)
309
+ return true if %i[capture void inquire].include?(action)
298
310
 
299
311
  false
300
312
  end
@@ -17,6 +17,11 @@ module ActiveMerchant #:nodoc:
17
17
  SERVICE_TEST_URL = 'https://certservices.elementexpress.com/express.asmx'
18
18
  SERVICE_LIVE_URL = 'https://services.elementexpress.com/express.asmx'
19
19
 
20
+ NETWORK_TOKEN_TYPE = {
21
+ apple_pay: '2',
22
+ google_pay: '1'
23
+ }
24
+
20
25
  def initialize(options = {})
21
26
  requires!(options, :account_id, :account_token, :application_id, :acceptor_id, :application_name, :application_version)
22
27
  super
@@ -171,6 +176,8 @@ module ActiveMerchant #:nodoc:
171
176
  add_payment_account_id(xml, payment)
172
177
  elsif payment.is_a?(Check)
173
178
  add_echeck(xml, payment)
179
+ elsif payment.is_a?(NetworkTokenizationCreditCard)
180
+ add_network_tokenization_card(xml, payment)
174
181
  else
175
182
  add_credit_card(xml, payment)
176
183
  end
@@ -200,7 +207,7 @@ module ActiveMerchant #:nodoc:
200
207
  xml.TransactionID options[:trans_id] if options[:trans_id]
201
208
  xml.TransactionAmount amount(money.to_i) if money
202
209
  xml.MarketCode market_code(money, options) if options[:market_code] || money
203
- xml.ReferenceNumber options[:order_id] || SecureRandom.hex(20)
210
+ xml.ReferenceNumber options[:order_id].present? ? options[:order_id][0, 50] : SecureRandom.hex(20)
204
211
  xml.TicketNumber options[:ticket_number] if options[:ticket_number]
205
212
  xml.MerchantSuppliedTransactionId options[:merchant_supplied_transaction_id] if options[:merchant_supplied_transaction_id]
206
213
  xml.PaymentType options[:payment_type] if options[:payment_type]
@@ -246,8 +253,21 @@ module ActiveMerchant #:nodoc:
246
253
  end
247
254
  end
248
255
 
256
+ def add_network_tokenization_card(xml, payment)
257
+ xml.card do
258
+ xml.CardNumber payment.number
259
+ xml.ExpirationMonth format(payment.month, :two_digits)
260
+ xml.ExpirationYear format(payment.year, :two_digits)
261
+ xml.CardholderName "#{payment.first_name} #{payment.last_name}"
262
+ xml.Cryptogram payment.payment_cryptogram
263
+ xml.ElectronicCommerceIndicator payment.eci if payment.eci.present?
264
+ xml.WalletType NETWORK_TOKEN_TYPE.fetch(payment.source, '0')
265
+ end
266
+ end
267
+
249
268
  def add_address(xml, options)
250
269
  if address = options[:billing_address] || options[:address]
270
+ address[:email] ||= options[:email]
251
271
  xml.address do
252
272
  xml.BillingAddress1 address[:address1] if address[:address1]
253
273
  xml.BillingAddress2 address[:address2] if address[:address2]
@@ -36,7 +36,7 @@ module ActiveMerchant #:nodoc:
36
36
  add_creator_info(post, options)
37
37
  add_fraud_fields(post, options)
38
38
  add_external_cardholder_authentication_data(post, options)
39
- commit(:authorize, post, options: options)
39
+ commit(:post, :authorize, post, options: options)
40
40
  end
41
41
 
42
42
  def capture(money, authorization, options = {})
@@ -44,7 +44,7 @@ module ActiveMerchant #:nodoc:
44
44
  add_order(post, money, options, capture: true)
45
45
  add_customer_data(post, options)
46
46
  add_creator_info(post, options)
47
- commit(:capture, post, authorization: authorization)
47
+ commit(:post, :capture, post, authorization: authorization)
48
48
  end
49
49
 
50
50
  def refund(money, authorization, options = {})
@@ -52,13 +52,13 @@ module ActiveMerchant #:nodoc:
52
52
  add_amount(post, money, options)
53
53
  add_refund_customer_data(post, options)
54
54
  add_creator_info(post, options)
55
- commit(:refund, post, authorization: authorization)
55
+ commit(:post, :refund, post, authorization: authorization)
56
56
  end
57
57
 
58
58
  def void(authorization, options = {})
59
59
  post = nestable_hash
60
60
  add_creator_info(post, options)
61
- commit(:void, post, authorization: authorization)
61
+ commit(:post, :void, post, authorization: authorization)
62
62
  end
63
63
 
64
64
  def verify(payment, options = {})
@@ -68,6 +68,10 @@ module ActiveMerchant #:nodoc:
68
68
  end
69
69
  end
70
70
 
71
+ def inquire(authorization, options = {})
72
+ commit(:get, :inquire, nil, authorization: authorization)
73
+ end
74
+
71
75
  def supports_scrubbing?
72
76
  true
73
77
  end
@@ -76,7 +80,10 @@ module ActiveMerchant #:nodoc:
76
80
  transcript.
77
81
  gsub(%r((Authorization: )[^\\]*)i, '\1[FILTERED]').
78
82
  gsub(%r(("cardNumber\\+":\\+")\d+), '\1[FILTERED]').
79
- gsub(%r(("cvv\\+":\\+")\d+), '\1[FILTERED]')
83
+ gsub(%r(("cvv\\+":\\+")\d+), '\1[FILTERED]').
84
+ gsub(%r(("dpan\\+":\\+")\d+), '\1[FILTERED]').
85
+ gsub(%r(("pan\\+":\\+")\d+), '\1[FILTERED]').
86
+ gsub(%r(("cryptogram\\+":\\+"|("cavv\\+" : \\+"))[^\\]*), '\1[FILTERED]')
80
87
  end
81
88
 
82
89
  private
@@ -89,7 +96,9 @@ module ActiveMerchant #:nodoc:
89
96
  'jcb' => '125',
90
97
  'diners_club' => '132',
91
98
  'cabal' => '135',
92
- 'naranja' => '136'
99
+ 'naranja' => '136',
100
+ 'apple_pay': '302',
101
+ 'google_pay': '320'
93
102
  }
94
103
 
95
104
  def add_order(post, money, options, capture: false)
@@ -250,20 +259,58 @@ module ActiveMerchant #:nodoc:
250
259
  month = format(payment.month, :two_digits)
251
260
  expirydate = "#{month}#{year}"
252
261
  pre_authorization = options[:pre_authorization] ? 'PRE_AUTHORIZATION' : 'FINAL_AUTHORIZATION'
253
- post['cardPaymentMethodSpecificInput'] = {
254
- 'paymentProductId' => BRAND_MAP[payment.brand],
262
+ product_id = options[:payment_product_id] || BRAND_MAP[payment.brand]
263
+ specifics_inputs = {
264
+ 'paymentProductId' => product_id,
255
265
  'skipAuthentication' => 'true', # refers to 3DSecure
256
266
  'skipFraudService' => 'true',
257
267
  'authorizationMode' => pre_authorization
258
268
  }
259
- post['cardPaymentMethodSpecificInput']['requiresApproval'] = options[:requires_approval] unless options[:requires_approval].nil?
269
+ specifics_inputs['requiresApproval'] = options[:requires_approval] unless options[:requires_approval].nil?
270
+ if payment.is_a?(NetworkTokenizationCreditCard)
271
+ add_mobile_credit_card(post, payment, options, specifics_inputs, expirydate)
272
+ elsif payment.is_a?(CreditCard)
273
+ options[:google_pay_pan_only] ? add_mobile_credit_card(post, payment, options, specifics_inputs, expirydate) : add_credit_card(post, payment, specifics_inputs, expirydate)
274
+ end
275
+ end
260
276
 
261
- post['cardPaymentMethodSpecificInput']['card'] = {
262
- 'cvv' => payment.verification_value,
263
- 'cardNumber' => payment.number,
264
- 'expiryDate' => expirydate,
265
- 'cardholderName' => payment.name
266
- }
277
+ def add_credit_card(post, payment, specifics_inputs, expirydate)
278
+ post['cardPaymentMethodSpecificInput'] = specifics_inputs.merge({
279
+ 'card' => {
280
+ 'cvv' => payment.verification_value,
281
+ 'cardNumber' => payment.number,
282
+ 'expiryDate' => expirydate,
283
+ 'cardholderName' => payment.name
284
+ }
285
+ })
286
+ end
287
+
288
+ def add_mobile_credit_card(post, payment, options, specifics_inputs, expirydate)
289
+ specifics_inputs['paymentProductId'] = options[:google_pay_pan_only] ? BRAND_MAP[:google_pay] : BRAND_MAP[payment.source]
290
+ post['mobilePaymentMethodSpecificInput'] = specifics_inputs
291
+ add_decrypted_payment_data(post, payment, options, expirydate)
292
+ end
293
+
294
+ def add_decrypted_payment_data(post, payment, options, expirydate)
295
+ if payment.is_a?(NetworkTokenizationCreditCard) && payment.payment_cryptogram
296
+ data = {
297
+ 'cardholderName' => payment.name,
298
+ 'cryptogram' => payment.payment_cryptogram,
299
+ 'eci' => payment.eci,
300
+ 'expiryDate' => expirydate,
301
+ 'dpan' => payment.number
302
+ }
303
+ data['paymentMethod'] = 'TOKENIZED_CARD' if payment.source == :google_pay
304
+ # else case when google payment is an ONLY_PAN, doesn't have cryptogram or eci.
305
+ elsif options[:google_pay_pan_only]
306
+ data = {
307
+ 'cardholderName' => payment.name,
308
+ 'expiryDate' => expirydate,
309
+ 'pan' => payment.number,
310
+ 'paymentMethod' => 'CARD'
311
+ }
312
+ end
313
+ post['mobilePaymentMethodSpecificInput']['decryptedPaymentData'] = data if data
267
314
  end
268
315
 
269
316
  def add_customer_data(post, options, payment = nil)
@@ -370,6 +417,8 @@ module ActiveMerchant #:nodoc:
370
417
  uri + "payments/#{authorization}/refund"
371
418
  when :void
372
419
  uri + "payments/#{authorization}/cancel"
420
+ when :inquire
421
+ uri + "payments/#{authorization}"
373
422
  end
374
423
  end
375
424
 
@@ -377,9 +426,9 @@ module ActiveMerchant #:nodoc:
377
426
  "x-gcs-idempotence-key:#{options[:idempotency_key]}" if options[:idempotency_key]
378
427
  end
379
428
 
380
- def commit(action, post, authorization: nil, options: {})
429
+ def commit(method, action, post, authorization: nil, options: {})
381
430
  begin
382
- raw_response = ssl_post(url(action, authorization), post.to_json, headers(action, post, authorization, options))
431
+ raw_response = ssl_request(method, url(action, authorization), post&.to_json, headers(method, action, post, authorization, options))
383
432
  response = parse(raw_response)
384
433
  rescue ResponseError => e
385
434
  response = parse(e.response.body) if e.response.code.to_i >= 400
@@ -405,10 +454,10 @@ module ActiveMerchant #:nodoc:
405
454
  }
406
455
  end
407
456
 
408
- def headers(action, post, authorization = nil, options = {})
457
+ def headers(method, action, post, authorization = nil, options = {})
409
458
  headers = {
410
459
  'Content-Type' => content_type,
411
- 'Authorization' => auth_digest(action, post, authorization, options),
460
+ 'Authorization' => auth_digest(method, action, post, authorization, options),
412
461
  'Date' => date
413
462
  }
414
463
 
@@ -416,9 +465,9 @@ module ActiveMerchant #:nodoc:
416
465
  headers
417
466
  end
418
467
 
419
- def auth_digest(action, post, authorization = nil, options = {})
468
+ def auth_digest(method, action, post, authorization = nil, options = {})
420
469
  data = <<~REQUEST
421
- POST
470
+ #{method.to_s.upcase}
422
471
  #{content_type}
423
472
  #{date}
424
473
  #{idempotency_key_for_signature(options)}
@@ -431,7 +480,7 @@ module ActiveMerchant #:nodoc:
431
480
  end
432
481
 
433
482
  def date
434
- @date ||= Time.now.strftime('%a, %d %b %Y %H:%M:%S %Z') # Must be same in digest and HTTP header
483
+ @date ||= Time.now.gmtime.strftime('%a, %d %b %Y %H:%M:%S %Z') # Must be same in digest and HTTP header
435
484
  end
436
485
 
437
486
  def content_type
@@ -441,6 +490,8 @@ module ActiveMerchant #:nodoc:
441
490
  def success_from(action, response)
442
491
  return false if response['errorId'] || response['error_message']
443
492
 
493
+ return %w(CAPTURED CAPTURE_REQUESTED).include?(response.dig('payment', 'status')) if response.dig('payment', 'paymentOutput', 'paymentMethod') == 'mobile'
494
+
444
495
  case action
445
496
  when :authorize
446
497
  response.dig('payment', 'statusOutput', 'isAuthorized')
@@ -27,32 +27,32 @@ module ActiveMerchant #:nodoc:
27
27
  def purchase(money, payment, options = {})
28
28
  xml = build_purchase_and_authorize_request(money, payment, options)
29
29
 
30
- commit('sale', xml)
30
+ commit('sale', xml, options)
31
31
  end
32
32
 
33
33
  def authorize(money, payment, options = {})
34
34
  xml = build_purchase_and_authorize_request(money, payment, options)
35
35
 
36
- commit('preAuth', xml)
36
+ commit('preAuth', xml, options)
37
37
  end
38
38
 
39
39
  def capture(money, authorization, options = {})
40
40
  xml = build_capture_and_refund_request(money, authorization, options)
41
41
 
42
- commit('postAuth', xml)
42
+ commit('postAuth', xml, options)
43
43
  end
44
44
 
45
45
  def refund(money, authorization, options = {})
46
46
  xml = build_capture_and_refund_request(money, authorization, options)
47
47
 
48
- commit('return', xml)
48
+ commit('return', xml, options)
49
49
  end
50
50
 
51
51
  def void(authorization, options = {})
52
52
  xml = Builder::XmlMarkup.new(indent: 2)
53
53
  add_transaction_details(xml, options.merge!({ order_id: authorization }))
54
54
 
55
- commit('void', xml)
55
+ commit('void', xml, options)
56
56
  end
57
57
 
58
58
  def store(credit_card, options = {})
@@ -60,7 +60,7 @@ module ActiveMerchant #:nodoc:
60
60
  xml = Builder::XmlMarkup.new(indent: 2)
61
61
  add_storage_item(xml, credit_card, options)
62
62
 
63
- commit('vault', xml)
63
+ commit('vault', xml, options)
64
64
  end
65
65
 
66
66
  def unstore(hosted_data_id)
@@ -343,7 +343,12 @@ module ActiveMerchant #:nodoc:
343
343
  }
344
344
  end
345
345
 
346
- def commit(action, request)
346
+ def override_store_id(options)
347
+ @credentials[:store_id] = options[:store_id] if options[:store_id].present?
348
+ end
349
+
350
+ def commit(action, request, options = {})
351
+ override_store_id(options)
347
352
  url = (test? ? test_url : live_url)
348
353
  soap_request = build_soap_request(action, request)
349
354
  response = parse(ssl_post(url, soap_request, build_header))
@@ -391,7 +396,7 @@ module ActiveMerchant #:nodoc:
391
396
  end
392
397
 
393
398
  def message_from(response)
394
- response[:TransactionResult]
399
+ [response[:TransactionResult], response[:ErrorMessage]&.split(':')&.last&.strip].compact.join(', ')
395
400
  end
396
401
 
397
402
  def authorization_from(action, response)
@@ -3,7 +3,10 @@ require 'nokogiri'
3
3
  module ActiveMerchant #:nodoc:
4
4
  module Billing #:nodoc:
5
5
  class IveriGateway < Gateway
6
+ class_attribute :iveri_url
7
+
6
8
  self.live_url = self.test_url = 'https://portal.nedsecure.co.za/iVeriWebService/Service.asmx'
9
+ self.iveri_url = 'https://portal.host.iveri.com/iVeriWebService/Service.asmx'
7
10
 
8
11
  self.supported_countries = %w[US ZA GB]
9
12
  self.default_currency = 'ZAR'
@@ -91,7 +94,7 @@ module ActiveMerchant #:nodoc:
91
94
  xml[:soap].Envelope 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema', 'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/' do
92
95
  xml[:soap].Body do
93
96
  xml.Execute 'xmlns' => 'http://iveri.com/' do
94
- xml.validateRequest 'true'
97
+ xml.validateRequest('false')
95
98
  xml.protocol 'V_XML'
96
99
  xml.protocolVersion '2.0'
97
100
  xml.request vxml
@@ -118,8 +121,9 @@ module ActiveMerchant #:nodoc:
118
121
  def add_auth_purchase_params(post, money, payment_method, options)
119
122
  add_card_holder_authentication(post, options)
120
123
  add_amount(post, money, options)
121
- add_electronic_commerce_indicator(post, options)
124
+ add_electronic_commerce_indicator(post, options) unless options[:three_d_secure]
122
125
  add_payment_method(post, payment_method, options)
126
+ add_three_ds(post, options)
123
127
  end
124
128
 
125
129
  def add_amount(post, money, options)
@@ -156,7 +160,7 @@ module ActiveMerchant #:nodoc:
156
160
  def commit(post)
157
161
  raw_response =
158
162
  begin
159
- ssl_post(live_url, build_xml_envelope(post), headers(post))
163
+ ssl_post(url, build_xml_envelope(post), headers(post))
160
164
  rescue ActiveMerchant::ResponseError => e
161
165
  e.response.body
162
166
  end
@@ -178,6 +182,10 @@ module ActiveMerchant #:nodoc:
178
182
  test? ? 'Test' : 'Live'
179
183
  end
180
184
 
185
+ def url
186
+ @options[:url_override].to_s == 'iveri' ? iveri_url : live_url
187
+ end
188
+
181
189
  def headers(post)
182
190
  {
183
191
  'Content-Type' => 'text/xml; charset=utf-8',
@@ -249,6 +257,34 @@ module ActiveMerchant #:nodoc:
249
257
  tr('-', '_').
250
258
  downcase
251
259
  end
260
+
261
+ def add_three_ds(post, options)
262
+ return unless three_d_secure = options[:three_d_secure]
263
+
264
+ post.ElectronicCommerceIndicator(formatted_three_ds_eci(three_d_secure[:eci])) if three_d_secure[:eci]
265
+ post.CardHolderAuthenticationID(three_d_secure[:xid]) if three_d_secure[:xid]
266
+ post.CardHolderAuthenticationData(three_d_secure[:cavv]) if three_d_secure[:cavv]
267
+ post.ThreeDSecure_ProtocolVersion(three_d_secure[:version]) if three_d_secure[:version]
268
+ post.ThreeDSecure_DSTransID(three_d_secure[:ds_transaction_id]) if three_d_secure[:ds_transaction_id]
269
+ post.ThreeDSecure_VEResEnrolled(formatted_enrollment(three_d_secure[:enrolled])) if three_d_secure[:enrolled]
270
+ end
271
+
272
+ def formatted_enrollment(val)
273
+ case val
274
+ when 'Y', 'N', 'U' then val
275
+ when true, 'true' then 'Y'
276
+ when false, 'false' then 'N'
277
+ end
278
+ end
279
+
280
+ def formatted_three_ds_eci(val)
281
+ case val
282
+ when '05', '02' then 'ThreeDSecure'
283
+ when '06', '01' then 'ThreeDSecureAttempted'
284
+ when '07' then 'SecureChannel'
285
+ else val
286
+ end
287
+ end
252
288
  end
253
289
  end
254
290
  end
@@ -83,6 +83,8 @@ module ActiveMerchant #:nodoc:
83
83
  add_payment_method(post, payment_method, options)
84
84
  add_full_response(post, options)
85
85
  add_metadata(post, options)
86
+ add_months(post, options)
87
+ add_deferred(post, options)
86
88
 
87
89
  commit(action, post)
88
90
  end
@@ -96,6 +98,8 @@ module ActiveMerchant #:nodoc:
96
98
  add_contact_details(post, options[:contact_details]) if options[:contact_details]
97
99
  add_full_response(post, options)
98
100
  add_metadata(post, options)
101
+ add_months(post, options)
102
+ add_deferred(post, options)
99
103
 
100
104
  commit(action, post)
101
105
  end
@@ -108,6 +112,8 @@ module ActiveMerchant #:nodoc:
108
112
  add_invoice(action, post, amount, options)
109
113
  add_full_response(post, options)
110
114
  add_metadata(post, options)
115
+ add_months(post, options)
116
+ add_deferred(post, options)
111
117
 
112
118
  commit(action, post)
113
119
  end
@@ -140,7 +146,7 @@ module ActiveMerchant #:nodoc:
140
146
  sum[:iva] = amount[:iva].to_f if amount[:iva]
141
147
  sum[:subtotalIva0] = amount[:subtotal_iva_0].to_f if amount[:subtotal_iva_0]
142
148
  sum[:ice] = amount[:ice].to_f if amount[:ice]
143
- if (extra_taxes = amount[:extra_taxes]) && sum[:currency] == 'COP'
149
+ if (extra_taxes = amount[:extra_taxes])
144
150
  sum[:extraTaxes] ||= Hash.new
145
151
  sum[:extraTaxes][:propina] = extra_taxes[:propina].to_f if extra_taxes[:propina]
146
152
  sum[:extraTaxes][:tasaAeroportuaria] = extra_taxes[:tasa_aeroportuaria].to_f if extra_taxes[:tasa_aeroportuaria]
@@ -184,6 +190,20 @@ module ActiveMerchant #:nodoc:
184
190
  post[:metadata] = options[:metadata] if options[:metadata]
185
191
  end
186
192
 
193
+ def add_months(post, options)
194
+ post[:months] = options[:months] if options[:months]
195
+ end
196
+
197
+ def add_deferred(post, options)
198
+ return unless options[:deferred_grace_months] && options[:deferred_credit_type] && options[:deferred_months]
199
+
200
+ post[:deferred] = {
201
+ graceMonths: options[:deferred_grace_months],
202
+ creditType: options[:deferred_credit_type],
203
+ months: options[:deferred_months]
204
+ }
205
+ end
206
+
187
207
  ENDPOINT = {
188
208
  'tokenize' => 'tokens',
189
209
  'charge' => 'charges',