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
@@ -286,16 +286,18 @@ module ActiveMerchant #:nodoc:
286
286
  end
287
287
 
288
288
  def add_address(post, options)
289
- post['address_line1'] = options[:billing_address][:address1]
290
- post['address_line2'] = options[:billing_address][:address2]
291
- post['city'] = options[:billing_address][:city]
292
- post['state'] = options[:billing_address][:state]
293
- post['zip'] = options[:billing_address][:zip]
294
- post['country'] = options[:billing_address][:country]
289
+ return unless billing_address = options[:billing_address]
290
+
291
+ post['address_line1'] = billing_address[:address1]
292
+ post['address_line2'] = billing_address[:address2]
293
+ post['city'] = billing_address[:city]
294
+ post['state'] = billing_address[:state]
295
+ post['zip'] = billing_address[:zip]
296
+ post['country'] = billing_address[:country]
295
297
  end
296
298
 
297
299
  def add_phone(post, options)
298
- post['receipt_phone'] = options[:billing_address][:phone] if options[:billing_address][:phone]
300
+ post['phone_number'] = options[:billing_address][:phone] if options.dig(:billing_address, :phone)
299
301
  end
300
302
 
301
303
  def add_money(post, money, options)
@@ -79,7 +79,9 @@ module ActiveMerchant #:nodoc:
79
79
  force_utf8(transcript).
80
80
  gsub(%r((api_accesskey=)\w+), '\1[FILTERED]').
81
81
  gsub(%r((card_number=)\w+), '\1[FILTERED]').
82
- gsub(%r((card_verification=)\w+), '\1[FILTERED]')
82
+ gsub(%r((card_verification=)\w+), '\1[FILTERED]').
83
+ gsub(%r((bank_account_number=)\w+), '\1[FILTERED]').
84
+ gsub(%r((bank_routing_number=)\w+), '\1[FILTERED]')
83
85
  end
84
86
 
85
87
  private
@@ -118,7 +118,7 @@ module ActiveMerchant #:nodoc:
118
118
  check_token_response(response, ENDPOINTS[:store], post, options)
119
119
  end
120
120
 
121
- def redact(customer_id)
121
+ def unstore(customer_id)
122
122
  post = {}
123
123
  post[:customer_id] = customer_id
124
124
  response = commit(ENDPOINTS[:redact], post)
@@ -83,6 +83,7 @@ module ActiveMerchant #:nodoc:
83
83
  options[:name] = credit_card.name if options[:name].blank? && credit_card
84
84
  request = build_recurring_request(options[:profile_id] ? :modify : :add, money, options) do |xml|
85
85
  add_credit_card(xml, credit_card, options) if credit_card
86
+ add_stored_credential(xml, options[:stored_credential])
86
87
  end
87
88
  commit(request, options.merge(request_type: :recurring))
88
89
  end
@@ -158,6 +159,7 @@ module ActiveMerchant #:nodoc:
158
159
  xml.tag! 'ExtData', 'Name' => 'ORIGID', 'Value' => reference
159
160
  end
160
161
  end
162
+ add_stored_credential(xml, options[:stored_credential])
161
163
  end
162
164
  xml.tag! 'ExtData', 'Name' => 'BUTTONSOURCE', 'Value' => application_id unless application_id.blank?
163
165
  end
@@ -190,15 +192,43 @@ module ActiveMerchant #:nodoc:
190
192
  xml.tag! 'TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money)
191
193
  end
192
194
 
195
+ if %i(authorization purchase).include? action
196
+ add_mpi_3ds(xml, options[:three_d_secure]) if options[:three_d_secure]
197
+ end
198
+
193
199
  xml.tag! 'Tender' do
194
200
  add_credit_card(xml, credit_card, options)
195
201
  end
202
+ add_stored_credential(xml, options[:stored_credential])
196
203
  end
197
204
  xml.tag! 'ExtData', 'Name' => 'BUTTONSOURCE', 'Value' => application_id unless application_id.blank?
198
205
  end
199
206
  add_level_two_three_fields(xml.target!, options)
200
207
  end
201
208
 
209
+ def add_mpi_3ds(xml, three_d_secure_options)
210
+ # structure as per https://developer.paypal.com/api/nvp-soap/payflow/3d-secure-mpi/
211
+ authentication_id = three_d_secure_options[:authentication_id]
212
+ authentication_status = three_d_secure_options[:authentication_response_status]
213
+
214
+ eci = three_d_secure_options[:eci]
215
+ cavv = three_d_secure_options[:cavv]
216
+ xid = three_d_secure_options[:xid]
217
+ version = three_d_secure_options[:version]
218
+
219
+ # 3DS2 only
220
+ ds_transaction_id = three_d_secure_options[:ds_transaction_id] if version_2_or_newer?(three_d_secure_options)
221
+
222
+ xml.tag!('ExtData', 'Name' => 'AUTHENTICATION_ID', 'Value' => authentication_id) unless authentication_id.blank?
223
+ xml.tag!('ExtData', 'Name' => 'AUTHENTICATION_STATUS', 'Value' => authentication_status) unless authentication_status.blank?
224
+
225
+ xml.tag!('ExtData', 'Name' => 'CAVV', 'Value' => cavv) unless cavv.blank?
226
+ xml.tag!('ExtData', 'Name' => 'ECI', 'Value' => eci) unless eci.blank?
227
+ xml.tag!('ExtData', 'Name' => 'XID', 'Value' => xid) unless xid.blank?
228
+ xml.tag!('ExtData', 'Name' => 'THREEDSVERSION', 'Value' => version) unless version.blank?
229
+ xml.tag!('ExtData', 'Name' => 'DSTRANSACTIONID', 'Value' => ds_transaction_id) unless ds_transaction_id.blank?
230
+ end
231
+
202
232
  def add_level_two_three_fields(xml_string, options)
203
233
  if options[:level_two_fields] || options[:level_three_fields]
204
234
  xml_doc = Nokogiri::XML.parse(xml_string)
@@ -258,6 +288,7 @@ module ActiveMerchant #:nodoc:
258
288
  xml.tag! 'ABA', check.routing_number
259
289
  end
260
290
  end
291
+ add_stored_credential(xml, options[:stored_credential])
261
292
  end
262
293
  xml.tag! 'ExtData', 'Name' => 'BUTTONSOURCE', 'Value' => application_id unless application_id.blank?
263
294
  end
@@ -278,6 +309,37 @@ module ActiveMerchant #:nodoc:
278
309
  end
279
310
  end
280
311
 
312
+ def add_stored_credential(xml, stored_credential)
313
+ return unless stored_credential
314
+
315
+ xml.tag! 'CardOnFile', add_card_on_file_type(stored_credential)
316
+ xml.tag! 'TxnId', stored_credential[:network_transaction_id] if stored_credential[:network_transaction_id]
317
+ end
318
+
319
+ def card_on_file_initiator(initator)
320
+ case initator
321
+ when 'merchant'
322
+ 'MIT'
323
+ when 'cardholder'
324
+ 'CIT'
325
+ end
326
+ end
327
+
328
+ def card_on_file_reason(stored_credential)
329
+ return 'I' if stored_credential[:initial_transaction] && stored_credential[:reason_type] == 'unscheduled'
330
+
331
+ case stored_credential[:reason_type]
332
+ when 'recurring', 'installment'
333
+ 'R'
334
+ when 'unscheduled'
335
+ 'U'
336
+ end
337
+ end
338
+
339
+ def add_card_on_file_type(stored_credential)
340
+ card_on_file_initiator(stored_credential[:initiator]).to_s + card_on_file_reason(stored_credential).to_s
341
+ end
342
+
281
343
  def add_three_d_secure(options, xml)
282
344
  if options[:three_d_secure]
283
345
  three_d_secure = options[:three_d_secure]
@@ -289,20 +351,28 @@ module ActiveMerchant #:nodoc:
289
351
  xml.tag! 'ECI', three_d_secure[:eci] unless three_d_secure[:eci].blank?
290
352
  xml.tag! 'CAVV', three_d_secure[:cavv] unless three_d_secure[:cavv].blank?
291
353
  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?
354
+ xml.tag! 'ThreeDSVersion', three_d_secure[:version] unless three_d_secure[:version].blank?
355
+ xml.tag! 'DSTransactionID', three_d_secure[:ds_transaction_id] unless three_d_secure[:ds_transaction_id].blank?
294
356
  end
295
357
  end
296
358
  end
297
359
 
298
360
  def authentication_status(three_d_secure, xml)
299
- if three_d_secure[:authentication_response_status].present?
300
- xml.tag! 'Status', three_d_secure[:authentication_response_status]
301
- elsif three_d_secure[:directory_response_status].present?
302
- xml.tag! 'Status', three_d_secure[:directory_response_status]
361
+ status = if three_d_secure[:authentication_response_status].present?
362
+ three_d_secure[:authentication_response_status]
363
+ elsif three_d_secure[:directory_response_status].present?
364
+ three_d_secure[:directory_response_status]
365
+ end
366
+ if status.present?
367
+ xml.tag! 'Status', status
368
+ xml.tag! 'AuthenticationStatus', status if version_2_or_newer?(three_d_secure)
303
369
  end
304
370
  end
305
371
 
372
+ def version_2_or_newer?(three_d_secure)
373
+ three_d_secure[:version]&.start_with?('2')
374
+ end
375
+
306
376
  def credit_card_type(credit_card)
307
377
  return '' if card_brand(credit_card).blank?
308
378
 
@@ -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'
@@ -34,12 +34,21 @@ module ActiveMerchant #:nodoc:
34
34
  28 => :card_declined
35
35
  }.freeze
36
36
 
37
+ SUCCESS_STATUS = ['success', 'pending', 1, 0]
38
+
37
39
  CARD_MAPPING = {
38
40
  'visa' => 'vi',
39
41
  'master' => 'mc',
40
42
  'american_express' => 'ax',
41
43
  'diners_club' => 'di',
42
- 'elo' => 'el'
44
+ 'elo' => 'el',
45
+ 'discover' => 'dc',
46
+ 'maestro' => 'ms',
47
+ 'sodexo' => 'sx',
48
+ 'olimpica' => 'ol',
49
+ 'carnet' => 'ct',
50
+ 'unionpay' => 'up',
51
+ 'jcb' => 'jc'
43
52
  }.freeze
44
53
 
45
54
  def initialize(options = {})
@@ -60,6 +69,8 @@ module ActiveMerchant #:nodoc:
60
69
  end
61
70
 
62
71
  def authorize(money, payment, options = {})
72
+ return purchase(money, payment, options) if options[:otp_flow]
73
+
63
74
  post = {}
64
75
 
65
76
  add_invoice(post, money, options)
@@ -70,13 +81,21 @@ module ActiveMerchant #:nodoc:
70
81
  commit_transaction('authorize', post)
71
82
  end
72
83
 
73
- def capture(money, authorization, _options = {})
84
+ def capture(money, authorization, options = {})
74
85
  post = {
75
86
  transaction: { id: authorization }
76
87
  }
77
- post[:order] = { amount: amount(money).to_f } if money
88
+ verify_flow = options[:type] && options[:value]
89
+
90
+ if verify_flow
91
+ add_customer_data(post, options)
92
+ add_verify_value(post, options)
93
+ elsif money
94
+ post[:order] = { amount: amount(money).to_f }
95
+ end
78
96
 
79
- commit_transaction('capture', post)
97
+ action = verify_flow ? 'verify' : 'capture'
98
+ commit_transaction(action, post)
80
99
  end
81
100
 
82
101
  def refund(money, authorization, options = {})
@@ -132,10 +151,10 @@ module ActiveMerchant #:nodoc:
132
151
  private
133
152
 
134
153
  def add_customer_data(post, options)
135
- requires!(options, :user_id, :email)
154
+ requires!(options, :user_id)
136
155
  post[:user] ||= {}
137
156
  post[:user][:id] = options[:user_id]
138
- post[:user][:email] = options[:email]
157
+ post[:user][:email] = options[:email] if options[:email]
139
158
  post[:user][:ip_address] = options[:ip] if options[:ip]
140
159
  post[:user][:fiscal_number] = options[:fiscal_number] if options[:fiscal_number]
141
160
  if phone = options[:phone] || options.dig(:billing_address, :phone)
@@ -172,6 +191,11 @@ module ActiveMerchant #:nodoc:
172
191
  end
173
192
  end
174
193
 
194
+ def add_verify_value(post, options)
195
+ post[:type] = options[:type] if options[:type]
196
+ post[:value] = options[:value] if options[:value]
197
+ end
198
+
175
199
  def add_extra_params(post, options)
176
200
  extra_params = {}
177
201
  extra_params.merge!(options[:extra_params]) if options[:extra_params]
@@ -255,7 +279,9 @@ module ActiveMerchant #:nodoc:
255
279
  end
256
280
 
257
281
  def success_from(response)
258
- !response.include?('error') && (response['status'] || response['transaction']['status']) == 'success'
282
+ return false if response.include?('error')
283
+
284
+ SUCCESS_STATUS.include?(response['status'] || response['transaction']['status'])
259
285
  end
260
286
 
261
287
  def card_success_from(response)
@@ -271,7 +297,7 @@ module ActiveMerchant #:nodoc:
271
297
  if !success_from(response) && response['error']
272
298
  response['error'] && response['error']['type']
273
299
  else
274
- response['transaction'] && response['transaction']['message']
300
+ (response['transaction'] && response['transaction']['message']) || (response['message'])
275
301
  end
276
302
  end
277
303
 
@@ -4,8 +4,7 @@ module ActiveMerchant #:nodoc:
4
4
  self.test_url = 'https://api.test.paysafe.com'
5
5
  self.live_url = 'https://api.paysafe.com'
6
6
 
7
- self.supported_countries = %w(FR)
8
- self.default_currency = 'EUR'
7
+ self.supported_countries = %w(AL AT BE BA BG CA HR CY CZ DK EE FI FR DE GR HU IS IE IT LV LI LT LU MT ME NL MK NO PL PT RO RS SK SI ES SE CH TR GB US)
9
8
  self.supported_cardtypes = %i[visa master american_express discover]
10
9
 
11
10
  self.homepage_url = 'https://www.paysafe.com/'
@@ -22,8 +21,11 @@ module ActiveMerchant #:nodoc:
22
21
  add_payment(post, payment)
23
22
  add_billing_address(post, options)
24
23
  add_merchant_details(post, options)
24
+ add_airline_travel_details(post, options)
25
25
  add_customer_data(post, payment, options) unless payment.is_a?(String)
26
26
  add_three_d_secure(post, payment, options) if options[:three_d_secure]
27
+ add_stored_credential(post, options) if options[:stored_credential]
28
+ add_split_pay_details(post, options)
27
29
  post[:settleWithAuth] = true
28
30
 
29
31
  commit(:post, 'auths', post, options)
@@ -37,6 +39,7 @@ module ActiveMerchant #:nodoc:
37
39
  add_merchant_details(post, options)
38
40
  add_customer_data(post, payment, options) unless payment.is_a?(String)
39
41
  add_three_d_secure(post, payment, options) if options[:three_d_secure]
42
+ add_stored_credential(post, options) if options[:stored_credential]
40
43
 
41
44
  commit(:post, 'auths', post, options)
42
45
  end
@@ -91,8 +94,8 @@ module ActiveMerchant #:nodoc:
91
94
  commit(:post, 'profiles', post, options)
92
95
  end
93
96
 
94
- def redact(pm_profile_id)
95
- commit_for_redact(:delete, "profiles/#{pm_profile_id}", nil, nil)
97
+ def unstore(pm_profile_id)
98
+ commit(:delete, "profiles/#{get_id_from_store_auth(pm_profile_id)}", nil, nil)
96
99
  end
97
100
 
98
101
  def supports_scrubbing?
@@ -119,9 +122,8 @@ module ActiveMerchant #:nodoc:
119
122
  end
120
123
 
121
124
  def add_billing_address(post, options)
122
- return unless options[:billing_address] || options[:address]
125
+ return unless address = options[:billing_address] || options[:address]
123
126
 
124
- address = options[:billing_address] || options[:address]
125
127
  post[:billingDetails] = {}
126
128
  post[:billingDetails][:street] = address[:address1]
127
129
  post[:billingDetails][:city] = address[:city]
@@ -134,21 +136,19 @@ module ActiveMerchant #:nodoc:
134
136
  # The add_address_for_vaulting method is applicable to the store method, as the APIs address
135
137
  # object is formatted differently from the standard transaction billing address
136
138
  def add_address_for_vaulting(post, options)
137
- return unless options[:billing_address || options[:address]]
139
+ return unless address = options[:billing_address] || options[:address]
138
140
 
139
- address = options[:billing_address] || options[:address]
140
- post[:billingAddress] = {}
141
- post[:billingAddress][:street] = address[:address1]
142
- post[:billingAddress][:city] = address[:city]
143
- post[:billingAddress][:zip] = address[:zip]
144
- post[:billingAddress][:country] = address[:country]
145
- post[:billingAddress][:state] = address[:state] if address[:state]
141
+ post[:card][:billingAddress] = {}
142
+ post[:card][:billingAddress][:street] = address[:address1]
143
+ post[:card][:billingAddress][:street2] = address[:address2]
144
+ post[:card][:billingAddress][:city] = address[:city]
145
+ post[:card][:billingAddress][:zip] = address[:zip]
146
+ post[:card][:billingAddress][:country] = address[:country]
147
+ post[:card][:billingAddress][:state] = address[:state] if address[:state]
146
148
  end
147
149
 
148
150
  # This data is specific to creating a profile at the gateway's vault level
149
151
  def add_profile_data(post, payment, options)
150
- address = options[:billing_address] || options[:address]
151
-
152
152
  post[:firstName] = payment.first_name
153
153
  post[:lastName] = payment.last_name
154
154
  post[:dateOfBirth] = {}
@@ -156,8 +156,13 @@ module ActiveMerchant #:nodoc:
156
156
  post[:dateOfBirth][:month] = options[:date_of_birth][:month]
157
157
  post[:dateOfBirth][:day] = options[:date_of_birth][:day]
158
158
  post[:email] = options[:email] if options[:email]
159
- post[:phone] = (address[:phone] || options[:phone]) if address[:phone] || options[:phone]
160
159
  post[:ip] = options[:ip] if options[:ip]
160
+
161
+ if options[:phone]
162
+ post[:phone] = options[:phone]
163
+ elsif address = options[:billing_address] || options[:address]
164
+ post[:phone] = address[:phone] if address[:phone]
165
+ end
161
166
  end
162
167
 
163
168
  def add_store_data(post, payment, options)
@@ -174,7 +179,7 @@ module ActiveMerchant #:nodoc:
174
179
  def add_payment(post, payment)
175
180
  if payment.is_a?(String)
176
181
  post[:card] = {}
177
- post[:card][:paymentToken] = payment
182
+ post[:card][:paymentToken] = get_pm_from_store_auth(payment)
178
183
  else
179
184
  post[:card] = { cardExpiry: {} }
180
185
  post[:card][:cardNum] = payment.number
@@ -200,10 +205,124 @@ module ActiveMerchant #:nodoc:
200
205
  post[:authentication][:cavv] = three_d_secure[:cavv]
201
206
  post[:authentication][:xid] = three_d_secure[:xid] if three_d_secure[:xid]
202
207
  post[:authentication][:threeDSecureVersion] = three_d_secure[:version]
203
- post[:authentication][:directoryServerTransactionId] = three_d_secure[:ds_transaction_id] unless payment.is_a?(String) || payment.brand != 'mastercard'
208
+ post[:authentication][:directoryServerTransactionId] = three_d_secure[:ds_transaction_id] unless payment.is_a?(String) || !mastercard?(payment)
209
+ end
210
+
211
+ def add_airline_travel_details(post, options)
212
+ return unless options[:airline_travel_details]
213
+
214
+ post[:airlineTravelDetails] = {}
215
+ post[:airlineTravelDetails][:passengerName] = options[:airline_travel_details][:passenger_name] if options[:airline_travel_details][:passenger_name]
216
+ post[:airlineTravelDetails][:departureDate] = options[:airline_travel_details][:departure_date] if options[:airline_travel_details][:departure_date]
217
+ post[:airlineTravelDetails][:origin] = options[:airline_travel_details][:origin] if options[:airline_travel_details][:origin]
218
+ post[:airlineTravelDetails][:computerizedReservationSystem] = options[:airline_travel_details][:computerized_reservation_system] if options[:airline_travel_details][:computerized_reservation_system]
219
+ post[:airlineTravelDetails][:customerReferenceNumber] = options[:airline_travel_details][:customer_reference_number] if options[:airline_travel_details][:customer_reference_number]
220
+
221
+ add_ticket_details(post, options)
222
+ add_travel_agency_details(post, options)
223
+ add_trip_legs(post, options)
224
+ end
225
+
226
+ def add_ticket_details(post, options)
227
+ return unless ticket = options[:airline_travel_details][:ticket]
228
+
229
+ post[:airlineTravelDetails][:ticket] = {}
230
+ post[:airlineTravelDetails][:ticket][:ticketNumber] = ticket[:ticket_number] if ticket[:ticket_number]
231
+ post[:airlineTravelDetails][:ticket][:isRestrictedTicket] = ticket[:is_restricted_ticket] if ticket[:is_restricted_ticket]
232
+ end
233
+
234
+ def add_travel_agency_details(post, options)
235
+ return unless agency = options[:airline_travel_details][:travel_agency]
236
+
237
+ post[:airlineTravelDetails][:travelAgency] = {}
238
+ post[:airlineTravelDetails][:travelAgency][:name] = agency[:name] if agency[:name]
239
+ post[:airlineTravelDetails][:travelAgency][:code] = agency[:code] if agency[:code]
240
+ end
241
+
242
+ def add_trip_legs(post, options)
243
+ return unless trip_legs = options[:airline_travel_details][:trip_legs]
244
+
245
+ trip_legs_hash = {}
246
+ trip_legs.each.with_index(1) do |leg, i|
247
+ my_leg = "leg#{i}".to_sym
248
+ details = add_leg_details(my_leg, leg[1])
249
+
250
+ trip_legs_hash[my_leg] = details
251
+ end
252
+ post[:airlineTravelDetails][:tripLegs] = trip_legs_hash
253
+ end
254
+
255
+ def add_leg_details(obj, leg)
256
+ details = {}
257
+ add_flight_details(details, obj, leg)
258
+ details[:serviceClass] = leg[:service_class] if leg[:service_class]
259
+ details[:isStopOverAllowed] = leg[:is_stop_over_allowed] if leg[:is_stop_over_allowed]
260
+ details[:destination] = leg[:destination] if leg[:destination]
261
+ details[:fareBasis] = leg[:fare_basis] if leg[:fare_basis]
262
+ details[:departureDate] = leg[:departure_date] if leg[:departure_date]
263
+
264
+ details
265
+ end
266
+
267
+ def add_flight_details(details, obj, leg)
268
+ details[:flight] = {}
269
+ details[:flight][:carrierCode] = leg[:flight][:carrier_code] if leg[:flight][:carrier_code]
270
+ details[:flight][:flightNumber] = leg[:flight][:flight_number] if leg[:flight][:flight_number]
271
+ end
272
+
273
+ def add_split_pay_details(post, options)
274
+ return unless options[:split_pay]
275
+
276
+ split_pay = []
277
+ options[:split_pay].each do |pmnt|
278
+ split = {}
279
+
280
+ split[:linkedAccount] = pmnt[:linked_account]
281
+ split[:amount] = pmnt[:amount].to_i if pmnt[:amount]
282
+ split[:percent] = pmnt[:percent].to_i if pmnt[:percent]
283
+
284
+ split_pay << split
285
+ end
286
+ post[:splitpay] = split_pay
287
+ end
288
+
289
+ def add_stored_credential(post, options)
290
+ return unless options[:stored_credential]
291
+
292
+ post[:storedCredential] = {}
293
+
294
+ case options[:stored_credential][:initial_transaction]
295
+ when true
296
+ post[:storedCredential][:occurrence] = 'INITIAL'
297
+ when false
298
+ post[:storedCredential][:occurrence] = 'SUBSEQUENT'
299
+ end
300
+
301
+ case options[:stored_credential][:reason_type]
302
+ when 'recurring', 'installment'
303
+ post[:storedCredential][:type] = 'RECURRING'
304
+ when 'unscheduled'
305
+ if options[:stored_credential][:initiator] == 'merchant'
306
+ post[:storedCredential][:type] = 'TOPUP'
307
+ elsif options[:stored_credential][:initiator] == 'cardholder'
308
+ post[:storedCredential][:type] = 'ADHOC'
309
+ else
310
+ return
311
+ end
312
+ end
313
+
314
+ post[:storedCredential][:initialTransactionId] = options[:stored_credential][:network_transaction_id] if options[:stored_credential][:network_transaction_id]
315
+ end
316
+
317
+ def mastercard?(payment)
318
+ return false unless payment.respond_to?(:brand)
319
+
320
+ payment.brand == 'master'
204
321
  end
205
322
 
206
323
  def parse(body)
324
+ return {} if body.empty?
325
+
207
326
  JSON.parse(body)
208
327
  end
209
328
 
@@ -217,7 +336,7 @@ module ActiveMerchant #:nodoc:
217
336
  success,
218
337
  message_from(success, response),
219
338
  response,
220
- authorization: authorization_from(response),
339
+ authorization: authorization_from(action, response),
221
340
  avs_result: AVSResult.new(code: response['avsResponse']),
222
341
  cvv_result: CVVResult.new(response['cvvVerification']),
223
342
  test: test?,
@@ -225,21 +344,10 @@ module ActiveMerchant #:nodoc:
225
344
  )
226
345
  end
227
346
 
228
- def commit_for_redact(method, action, parameters, options)
229
- url = url(action)
230
- response = raw_ssl_request(method, url, post_data(parameters, options), headers)
231
- success = true if response.code == '200'
232
-
233
- Response.new(
234
- success,
235
- message: response.message
236
- )
237
- end
238
-
239
347
  def headers
240
348
  {
241
349
  'Content-Type' => 'application/json',
242
- 'Authorization' => "Basic #{Base64.strict_encode64(@options[:api_key].to_s)}"
350
+ 'Authorization' => 'Basic ' + Base64.strict_encode64("#{@options[:username]}:#{@options[:password]}")
243
351
  }
244
352
  end
245
353
 
@@ -265,8 +373,21 @@ module ActiveMerchant #:nodoc:
265
373
  "Error(s)- code:#{response['error']['code']}, message:#{response['error']['message']}"
266
374
  end
267
375
 
268
- def authorization_from(response)
269
- response['id']
376
+ def authorization_from(action, response)
377
+ if action == 'profiles'
378
+ pm = response['cards'].first['paymentToken']
379
+ "#{pm}|#{response['id']}"
380
+ else
381
+ response['id']
382
+ end
383
+ end
384
+
385
+ def get_pm_from_store_auth(authorization)
386
+ authorization.split('|')[0]
387
+ end
388
+
389
+ def get_id_from_store_auth(authorization)
390
+ authorization.split('|')[1]
270
391
  end
271
392
 
272
393
  def post_data(parameters = {}, options = {})
@@ -17,6 +17,7 @@ module ActiveMerchant #:nodoc:
17
17
  BRAND_MAP = {
18
18
  'visa' => 'VISA',
19
19
  'master' => 'MASTERCARD',
20
+ 'maestro' => 'MASTERCARD',
20
21
  'american_express' => 'AMEX',
21
22
  'diners_club' => 'DINERS',
22
23
  'naranja' => 'NARANJA',
@@ -278,6 +279,10 @@ module ActiveMerchant #:nodoc:
278
279
  Digest::MD5.hexdigest(signature_string)
279
280
  end
280
281
 
282
+ def codensa_bin?(number)
283
+ number.start_with?('590712')
284
+ end
285
+
281
286
  def add_payment_method(post, payment_method, options)
282
287
  if payment_method.is_a?(String)
283
288
  brand, token = split_authorization(payment_method)
@@ -295,7 +300,7 @@ module ActiveMerchant #:nodoc:
295
300
  credit_card[:name] = payment_method.name.strip
296
301
  credit_card[:processWithoutCvv2] = true if add_process_without_cvv2(payment_method, options)
297
302
  post[:transaction][:creditCard] = credit_card
298
- post[:transaction][:paymentMethod] = BRAND_MAP[payment_method.brand.to_s]
303
+ post[:transaction][:paymentMethod] = codensa_bin?(payment_method.number) ? 'CODENSA' : BRAND_MAP[payment_method.brand.to_s]
299
304
  end
300
305
  end
301
306
 
@@ -387,26 +392,36 @@ module ActiveMerchant #:nodoc:
387
392
  def message_from(action, success, response)
388
393
  case action
389
394
  when 'store'
390
- return response['code'] if success
391
-
392
- error_description = response['creditCardToken']['errorDescription'] if response['creditCardToken']
393
- response['error'] || error_description || 'FAILED'
395
+ message_from_store(success, response)
394
396
  when 'verify_credentials'
395
- return 'VERIFIED' if success
396
-
397
- 'FAILED'
397
+ message_from_verify_credentials(success)
398
398
  else
399
- if response['transactionResponse']
400
- response_message = response['transactionResponse']['responseMessage']
399
+ message_from_transaction_response(success, response)
400
+ end
401
+ end
401
402
 
402
- response_code = response['transactionResponse']['responseCode'] || response['transactionResponse']['pendingReason']
403
+ def message_from_store(success, response)
404
+ return response['code'] if success
403
405
 
404
- response_message = response_code + ' | ' + response['transactionResponse']['paymentNetworkResponseErrorMessage'] unless response['transactionResponse']['paymentNetworkResponseErrorMessage'].nil?
405
- end
406
- return response_code if success
406
+ error_description = response['creditCardToken']['errorDescription'] if response['creditCardToken']
407
+ response['error'] || error_description || 'FAILED'
408
+ end
407
409
 
408
- response_message || response['error'] || response_code || 'FAILED'
409
- end
410
+ def message_from_verify_credentials(success)
411
+ return 'VERIFIED' if success
412
+
413
+ 'FAILED'
414
+ end
415
+
416
+ def message_from_transaction_response(success, response)
417
+ response_code = response.dig('transactionResponse', 'responseCode') || response.dig('transactionResponse', 'pendingReason')
418
+ return response_code if success
419
+ return response_code + ' | ' + response.dig('transactionResponse', 'paymentNetworkResponseErrorMessage') if response.dig('transactionResponse', 'paymentNetworkResponseErrorMessage')
420
+ return response.dig('transactionResponse', 'responseMessage') if response.dig('transactionResponse', 'responseMessage')
421
+ return response['error'] if response['error']
422
+ return response_code if response_code
423
+
424
+ 'FAILED'
410
425
  end
411
426
 
412
427
  def authorization_from(action, response)