activemerchant 1.123.0 → 1.125.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +131 -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 +6 -3
  6. data/lib/active_merchant/billing/gateway.rb +1 -1
  7. data/lib/active_merchant/billing/gateways/adyen.rb +61 -9
  8. data/lib/active_merchant/billing/gateways/card_stream.rb +17 -13
  9. data/lib/active_merchant/billing/gateways/cashnet.rb +15 -5
  10. data/lib/active_merchant/billing/gateways/checkout_v2.rb +33 -4
  11. data/lib/active_merchant/billing/gateways/cyber_source.rb +11 -3
  12. data/lib/active_merchant/billing/gateways/d_local.rb +12 -6
  13. data/lib/active_merchant/billing/gateways/decidir_plus.rb +173 -0
  14. data/lib/active_merchant/billing/gateways/ebanx.rb +16 -1
  15. data/lib/active_merchant/billing/gateways/elavon.rb +6 -3
  16. data/lib/active_merchant/billing/gateways/element.rb +20 -2
  17. data/lib/active_merchant/billing/gateways/global_collect.rb +111 -16
  18. data/lib/active_merchant/billing/gateways/ipg.rb +416 -0
  19. data/lib/active_merchant/billing/gateways/kushki.rb +7 -0
  20. data/lib/active_merchant/billing/gateways/mercado_pago.rb +3 -1
  21. data/lib/active_merchant/billing/gateways/mit.rb +260 -0
  22. data/lib/active_merchant/billing/gateways/moka.rb +24 -11
  23. data/lib/active_merchant/billing/gateways/mundipagg.rb +8 -6
  24. data/lib/active_merchant/billing/gateways/nmi.rb +15 -1
  25. data/lib/active_merchant/billing/gateways/orbital.rb +19 -3
  26. data/lib/active_merchant/billing/gateways/pay_arc.rb +9 -7
  27. data/lib/active_merchant/billing/gateways/pay_conex.rb +3 -1
  28. data/lib/active_merchant/billing/gateways/pay_trace.rb +1 -1
  29. data/lib/active_merchant/billing/gateways/payflow.rb +14 -6
  30. data/lib/active_merchant/billing/gateways/paymentez.rb +9 -2
  31. data/lib/active_merchant/billing/gateways/paysafe.rb +144 -23
  32. data/lib/active_merchant/billing/gateways/payu_latam.rb +6 -1
  33. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +3 -3
  34. data/lib/active_merchant/billing/gateways/pin.rb +31 -4
  35. data/lib/active_merchant/billing/gateways/priority.rb +347 -0
  36. data/lib/active_merchant/billing/gateways/realex.rb +18 -0
  37. data/lib/active_merchant/billing/gateways/safe_charge.rb +6 -2
  38. data/lib/active_merchant/billing/gateways/stripe.rb +27 -7
  39. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +96 -38
  40. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +2 -1
  41. data/lib/active_merchant/billing/gateways/trust_commerce.rb +2 -1
  42. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +20 -6
  43. data/lib/active_merchant/billing/gateways/wompi.rb +193 -0
  44. data/lib/active_merchant/billing/gateways/worldpay.rb +196 -64
  45. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +1 -1
  46. data/lib/active_merchant/billing/response.rb +4 -0
  47. data/lib/active_merchant/version.rb +1 -1
  48. metadata +7 -2
@@ -0,0 +1,260 @@
1
+ require 'json'
2
+ require 'openssl'
3
+ require 'digest'
4
+ require 'base64'
5
+
6
+ module ActiveMerchant #:nodoc:
7
+ module Billing #:nodoc:
8
+ class MitGateway < Gateway
9
+ self.live_url = 'https://wpy.mitec.com.mx/ModuloUtilWS/activeCDP.htm'
10
+
11
+ self.supported_countries = ['MX']
12
+ self.default_currency = 'MXN'
13
+ self.supported_cardtypes = %i[visa master]
14
+
15
+ self.homepage_url = 'http://www.centrodepagos.com.mx/'
16
+ self.display_name = 'MIT Centro de pagos'
17
+
18
+ self.money_format = :dollars
19
+
20
+ def initialize(options = {})
21
+ requires!(options, :commerce_id, :user, :api_key, :key_session)
22
+ super
23
+ end
24
+
25
+ def purchase(money, payment, options = {})
26
+ MultiResponse.run do |r|
27
+ r.process { authorize(money, payment, options) }
28
+ r.process { capture(money, r.authorization, options) }
29
+ end
30
+ end
31
+
32
+ def cipher_key
33
+ @options[:key_session]
34
+ end
35
+
36
+ def decrypt(val, keyinhex)
37
+ # Splits the first 16 bytes (the IV bytes) in array format
38
+ unpacked = val.unpack('m')
39
+ iv_base64 = unpacked[0].bytes.slice(0, 16)
40
+ # Splits the second bytes (the encrypted text bytes) these would be the
41
+ # original message
42
+ full_data = unpacked[0].bytes.slice(16, unpacked[0].bytes.length)
43
+ # Creates the engine
44
+ engine = OpenSSL::Cipher::AES128.new(:CBC)
45
+ # Set engine as decrypt mode
46
+ engine.decrypt
47
+ # Converts the key from hex to bytes
48
+ engine.key = [keyinhex].pack('H*')
49
+ # Converts the ivBase64 array into bytes
50
+ engine.iv = iv_base64.pack('c*')
51
+ # Decrypts the texts and returns the original string
52
+ engine.update(full_data.pack('c*')) + engine.final
53
+ end
54
+
55
+ def encrypt(val, keyinhex)
56
+ # Creates the engine motor
57
+ engine = OpenSSL::Cipher::AES128.new(:CBC)
58
+ # Set engine as encrypt mode
59
+ engine.encrypt
60
+ # Converts the key from hex to bytes
61
+ engine.key = [keyinhex].pack('H*')
62
+ # Generates a random iv with this settings
63
+ iv_rand = engine.random_iv
64
+ # Packs IV as a Base64 string
65
+ iv_base64 = [iv_rand].pack('m')
66
+ # Converts the packed key into bytes
67
+ unpacked = iv_base64.unpack('m')
68
+ iv = unpacked[0]
69
+ # Sets the IV into the engine
70
+ engine.iv = iv
71
+ # Encrypts the texts and stores the bytes
72
+ encrypted_bytes = engine.update(val) + engine.final
73
+ # Concatenates the (a) IV bytes and (b) the encrypted bytes then returns a
74
+ # base64 representation
75
+ [iv << encrypted_bytes].pack('m')
76
+ end
77
+
78
+ def authorize(money, payment, options = {})
79
+ post = {
80
+ operation: 'Authorize',
81
+ commerce_id: @options[:commerce_id],
82
+ user: @options[:user],
83
+ apikey: @options[:api_key],
84
+ testMode: (test? ? 'YES' : 'NO')
85
+ }
86
+ add_invoice(post, money, options)
87
+ # Payments contains the card information
88
+ add_payment(post, payment)
89
+ add_customer_data(post, options)
90
+ post[:key_session] = @options[:key_session]
91
+
92
+ post_to_json = post.to_json
93
+ post_to_json_encrypt = encrypt(post_to_json, @options[:key_session])
94
+
95
+ final_post = '<authorization>' + post_to_json_encrypt + '</authorization><dataID>' + @options[:user] + '</dataID>'
96
+ json_post = {}
97
+ json_post[:payload] = final_post
98
+ commit('sale', json_post)
99
+ end
100
+
101
+ def capture(money, authorization, options = {})
102
+ post = {
103
+ operation: 'Capture',
104
+ commerce_id: @options[:commerce_id],
105
+ user: @options[:user],
106
+ apikey: @options[:api_key],
107
+ testMode: (test? ? 'YES' : 'NO'),
108
+ transaction_id: authorization,
109
+ amount: amount(money)
110
+ }
111
+ post[:key_session] = @options[:key_session]
112
+
113
+ post_to_json = post.to_json
114
+ post_to_json_encrypt = encrypt(post_to_json, @options[:key_session])
115
+
116
+ final_post = '<capture>' + post_to_json_encrypt + '</capture><dataID>' + @options[:user] + '</dataID>'
117
+ json_post = {}
118
+ json_post[:payload] = final_post
119
+ commit('capture', json_post)
120
+ end
121
+
122
+ def refund(money, authorization, options = {})
123
+ post = {
124
+ operation: 'Refund',
125
+ commerce_id: @options[:commerce_id],
126
+ user: @options[:user],
127
+ apikey: @options[:api_key],
128
+ testMode: (test? ? 'YES' : 'NO'),
129
+ transaction_id: authorization,
130
+ auth: authorization,
131
+ amount: amount(money)
132
+ }
133
+ post[:key_session] = @options[:key_session]
134
+
135
+ post_to_json = post.to_json
136
+ post_to_json_encrypt = encrypt(post_to_json, @options[:key_session])
137
+
138
+ final_post = '<refund>' + post_to_json_encrypt + '</refund><dataID>' + @options[:user] + '</dataID>'
139
+ json_post = {}
140
+ json_post[:payload] = final_post
141
+ commit('refund', json_post)
142
+ end
143
+
144
+ def supports_scrubbing?
145
+ true
146
+ end
147
+
148
+ def scrub(transcript)
149
+ ret_transcript = transcript
150
+ auth_origin = ret_transcript[/<authorization>(.*?)<\/authorization>/, 1]
151
+ unless auth_origin.nil?
152
+ auth_decrypted = decrypt(auth_origin, @options[:key_session])
153
+ auth_json = JSON.parse(auth_decrypted)
154
+ auth_json['card'] = '[FILTERED]'
155
+ auth_json['cvv'] = '[FILTERED]'
156
+ auth_json['apikey'] = '[FILTERED]'
157
+ auth_json['key_session'] = '[FILTERED]'
158
+ auth_to_json = auth_json.to_json
159
+ auth_tagged = '<authorization>' + auth_to_json + '</authorization>'
160
+ ret_transcript = ret_transcript.gsub(/<authorization>(.*?)<\/authorization>/, auth_tagged)
161
+ end
162
+
163
+ cap_origin = ret_transcript[/<capture>(.*?)<\/capture>/, 1]
164
+ unless cap_origin.nil?
165
+ cap_decrypted = decrypt(cap_origin, @options[:key_session])
166
+ cap_json = JSON.parse(cap_decrypted)
167
+ cap_json['apikey'] = '[FILTERED]'
168
+ cap_json['key_session'] = '[FILTERED]'
169
+ cap_to_json = cap_json.to_json
170
+ cap_tagged = '<capture>' + cap_to_json + '</capture>'
171
+ ret_transcript = ret_transcript.gsub(/<capture>(.*?)<\/capture>/, cap_tagged)
172
+ end
173
+
174
+ ref_origin = ret_transcript[/<refund>(.*?)<\/refund>/, 1]
175
+ unless ref_origin.nil?
176
+ ref_decrypted = decrypt(ref_origin, @options[:key_session])
177
+ ref_json = JSON.parse(ref_decrypted)
178
+ ref_json['apikey'] = '[FILTERED]'
179
+ ref_json['key_session'] = '[FILTERED]'
180
+ ref_to_json = ref_json.to_json
181
+ ref_tagged = '<refund>' + ref_to_json + '</refund>'
182
+ ret_transcript = ret_transcript.gsub(/<refund>(.*?)<\/refund>/, ref_tagged)
183
+ end
184
+
185
+ res_origin = ret_transcript[/#{Regexp.escape('reading ')}(.*?)#{Regexp.escape('read')}/m, 1]
186
+ loop do
187
+ break if res_origin.nil?
188
+
189
+ resp_origin = res_origin[/#{Regexp.escape('"')}(.*?)#{Regexp.escape('"')}/m, 1]
190
+ resp_decrypted = decrypt(resp_origin, @options[:key_session])
191
+ ret_transcript[/#{Regexp.escape('reading ')}(.*?)#{Regexp.escape('read')}/m, 1] = resp_decrypted
192
+ ret_transcript = ret_transcript.sub('reading ', 'response: ')
193
+ res_origin = ret_transcript[/#{Regexp.escape('reading ')}(.*?)#{Regexp.escape('read')}/m, 1]
194
+ end
195
+
196
+ ret_transcript
197
+ end
198
+
199
+ private
200
+
201
+ def add_customer_data(post, options)
202
+ post[:email] = options[:email] || 'nadie@mit.test'
203
+ end
204
+
205
+ def add_invoice(post, money, options)
206
+ post[:amount] = amount(money)
207
+ post[:currency] = (options[:currency] || currency(money))
208
+ post[:reference] = options[:order_id]
209
+ post[:transaction_id] = options[:order_id]
210
+ end
211
+
212
+ def add_payment(post, payment)
213
+ post[:installments] = 1
214
+ post[:card] = payment.number
215
+ post[:expmonth] = payment.month
216
+ post[:expyear] = payment.year
217
+ post[:cvv] = payment.verification_value
218
+ post[:name_client] = [payment.first_name, payment.last_name].join(' ')
219
+ end
220
+
221
+ def commit(action, parameters)
222
+ json_str = JSON.generate(parameters)
223
+ cleaned_str = json_str.gsub('\n', '')
224
+ raw_response = ssl_post(live_url, cleaned_str, { 'Content-type' => 'application/json' })
225
+ response = JSON.parse(decrypt(raw_response, @options[:key_session]))
226
+
227
+ Response.new(
228
+ success_from(response),
229
+ message_from(response),
230
+ response,
231
+ authorization: authorization_from(response),
232
+ avs_result: AVSResult.new(code: response['some_avs_response_key']),
233
+ cvv_result: CVVResult.new(response['some_cvv_response_key']),
234
+ test: test?,
235
+ error_code: error_code_from(response)
236
+ )
237
+ end
238
+
239
+ def success_from(response)
240
+ response['response'] == 'approved'
241
+ end
242
+
243
+ def message_from(response)
244
+ response['response']
245
+ end
246
+
247
+ def authorization_from(response)
248
+ response['reference']
249
+ end
250
+
251
+ def post_data(action, parameters = {})
252
+ parameters.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&')
253
+ end
254
+
255
+ def error_code_from(response)
256
+ response['message'].split(' -- ', 2)[0] unless success_from(response)
257
+ end
258
+ end
259
+ end
260
+ end
@@ -1,7 +1,7 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
3
  class MokaGateway < Gateway
4
- self.test_url = 'https://service.testmoka.com'
4
+ self.test_url = 'https://service.refmoka.com'
5
5
  self.live_url = 'https://service.moka.com'
6
6
 
7
7
  self.supported_countries = %w[GB TR US]
@@ -55,8 +55,10 @@ module ActiveMerchant #:nodoc:
55
55
  post[:PaymentDealerRequest] = {}
56
56
  options[:pre_auth] = 0
57
57
  add_auth_purchase(post, money, payment, options)
58
+ add_3ds_data(post, options) if options[:execute_threed]
58
59
 
59
- commit('purchase', post)
60
+ action = options[:execute_threed] ? 'three_ds_purchase' : 'purchase'
61
+ commit(action, post)
60
62
  end
61
63
 
62
64
  def authorize(money, payment, options = {})
@@ -64,8 +66,10 @@ module ActiveMerchant #:nodoc:
64
66
  post[:PaymentDealerRequest] = {}
65
67
  options[:pre_auth] = 1
66
68
  add_auth_purchase(post, money, payment, options)
69
+ add_3ds_data(post, options) if options[:execute_threed]
67
70
 
68
- commit('authorize', post)
71
+ action = options[:execute_threed] ? 'three_ds_authorize' : 'authorize'
72
+ commit(action, post)
69
73
  end
70
74
 
71
75
  def capture(money, authorization, options = {})
@@ -73,7 +77,7 @@ module ActiveMerchant #:nodoc:
73
77
  post[:PaymentDealerRequest] = {}
74
78
  add_payment_dealer_authentication(post)
75
79
  add_transaction_reference(post, authorization)
76
- add_additional_transaction_data(post, options)
80
+ add_invoice(post, money, options)
77
81
 
78
82
  commit('capture', post)
79
83
  end
@@ -83,7 +87,6 @@ module ActiveMerchant #:nodoc:
83
87
  post[:PaymentDealerRequest] = {}
84
88
  add_payment_dealer_authentication(post)
85
89
  add_transaction_reference(post, authorization)
86
- add_additional_transaction_data(post, options)
87
90
  add_void_refund_reason(post)
88
91
  add_amount(post, money)
89
92
 
@@ -95,7 +98,6 @@ module ActiveMerchant #:nodoc:
95
98
  post[:PaymentDealerRequest] = {}
96
99
  add_payment_dealer_authentication(post)
97
100
  add_transaction_reference(post, authorization)
98
- add_additional_transaction_data(post, options)
99
101
  add_void_refund_reason(post)
100
102
 
101
103
  commit('void', post)
@@ -134,6 +136,12 @@ module ActiveMerchant #:nodoc:
134
136
  add_basket_product(post, options[:basket_product]) if options[:basket_product]
135
137
  end
136
138
 
139
+ def add_3ds_data(post, options)
140
+ post[:PaymentDealerRequest][:ReturnHash] = 1
141
+ post[:PaymentDealerRequest][:RedirectUrl] = options[:redirect_url] || ''
142
+ post[:PaymentDealerRequest][:RedirectType] = options[:redirect_type] || 0
143
+ end
144
+
137
145
  def add_payment_dealer_authentication(post)
138
146
  post[:PaymentDealerAuthentication] = {
139
147
  DealerCode: @options[:dealer_code],
@@ -156,18 +164,19 @@ module ActiveMerchant #:nodoc:
156
164
  def add_payment(post, card)
157
165
  post[:PaymentDealerRequest][:CardHolderFullName] = card.name
158
166
  post[:PaymentDealerRequest][:CardNumber] = card.number
159
- post[:PaymentDealerRequest][:ExpMonth] = card.month
167
+ post[:PaymentDealerRequest][:ExpMonth] = card.month.to_s.rjust(2, '0')
160
168
  post[:PaymentDealerRequest][:ExpYear] = card.year
161
- post[:PaymentDealerRequest][:CvcNumber] = card.verification_value
169
+ post[:PaymentDealerRequest][:CvcNumber] = card.verification_value || ''
162
170
  end
163
171
 
164
172
  def add_amount(post, money)
165
- post[:PaymentDealerRequest][:Amount] = money || 0
173
+ post[:PaymentDealerRequest][:Amount] = amount(money) || 0
166
174
  end
167
175
 
168
176
  def add_additional_auth_purchase_data(post, options)
169
177
  post[:PaymentDealerRequest][:IsPreAuth] = options[:pre_auth]
170
- post[:PaymentDealerRequest][:Description] = options[:order_id] if options[:order_id]
178
+ post[:PaymentDealerRequest][:Description] = options[:description] if options[:description]
179
+ post[:PaymentDealerRequest][:InstallmentNumber] = options[:installment_number].to_i if options[:installment_number]
171
180
  post[:SubMerchantName] = options[:sub_merchant_name] if options[:sub_merchant_name]
172
181
  post[:IsPoolPayment] = options[:is_pool_payment] || 0
173
182
  end
@@ -232,6 +241,8 @@ module ActiveMerchant #:nodoc:
232
241
 
233
242
  def endpoint(action)
234
243
  case action
244
+ when 'three_ds_authorize', 'three_ds_purchase'
245
+ 'DoDirectPaymentThreeD'
235
246
  when 'purchase', 'authorize'
236
247
  'DoDirectPayment'
237
248
  when 'capture'
@@ -256,7 +267,9 @@ module ActiveMerchant #:nodoc:
256
267
  end
257
268
 
258
269
  def success_from(response)
259
- response.dig('Data', 'IsSuccessful')
270
+ return response.dig('Data', 'IsSuccessful') if response.dig('Data', 'IsSuccessful').to_s.present?
271
+
272
+ response['ResultCode']&.casecmp('success') == 0
260
273
  end
261
274
 
262
275
  def message_from(response)
@@ -276,14 +276,14 @@ module ActiveMerchant #:nodoc:
276
276
  end
277
277
 
278
278
  Response.new(
279
- success_from(response),
279
+ success_from(response, action),
280
280
  message_from(response),
281
281
  response,
282
282
  authorization: authorization_from(response, action),
283
283
  avs_result: AVSResult.new(code: response['some_avs_response_key']),
284
284
  cvv_result: CVVResult.new(response['some_cvv_response_key']),
285
285
  test: test?,
286
- error_code: error_code_from(response)
286
+ error_code: error_code_from(response, action)
287
287
  )
288
288
  rescue ResponseError => e
289
289
  message = get_error_messages(e)
@@ -297,8 +297,10 @@ module ActiveMerchant #:nodoc:
297
297
  )
298
298
  end
299
299
 
300
- def success_from(response)
301
- %w[pending paid processing canceled active].include? response['status']
300
+ def success_from(response, action)
301
+ success = response.try(:[], 'last_transaction').try(:[], 'success') unless action == 'store'
302
+ success = !response.try(:[], 'id').nil? if action == 'store'
303
+ success
302
304
  end
303
305
 
304
306
  def message_from(response)
@@ -350,8 +352,8 @@ module ActiveMerchant #:nodoc:
350
352
  parameters.to_json
351
353
  end
352
354
 
353
- def error_code_from(response)
354
- return if success_from(response)
355
+ def error_code_from(response, action)
356
+ return if success_from(response, action)
355
357
  return response['last_transaction']['acquirer_return_code'] if response['last_transaction']
356
358
 
357
359
  STANDARD_ERROR_CODE[:processing_error]
@@ -206,7 +206,8 @@ module ActiveMerchant #:nodoc:
206
206
  post[:stored_credential_indicator] = 'stored'
207
207
  else
208
208
  post[:stored_credential_indicator] = 'used'
209
- post[:initial_transaction_id] = stored_credential[:network_transaction_id]
209
+ # should only send :initial_transaction_id if it is a MIT
210
+ post[:initial_transaction_id] = stored_credential[:network_transaction_id] if post[:initiated_by] == 'merchant'
210
211
  end
211
212
  end
212
213
 
@@ -236,6 +237,19 @@ module ActiveMerchant #:nodoc:
236
237
  post[:shipping_zip] = shipping_address[:zip]
237
238
  post[:shipping_phone] = shipping_address[:phone]
238
239
  end
240
+
241
+ if (descriptor = options[:descriptors])
242
+ post[:descriptor] = descriptor[:descriptor]
243
+ post[:descriptor_phone] = descriptor[:descriptor_phone]
244
+ post[:descriptor_address] = descriptor[:descriptor_address]
245
+ post[:descriptor_city] = descriptor[:descriptor_city]
246
+ post[:descriptor_state] = descriptor[:descriptor_state]
247
+ post[:descriptor_postal] = descriptor[:descriptor_postal]
248
+ post[:descriptor_country] = descriptor[:descriptor_country]
249
+ post[:descriptor_mcc] = descriptor[:descriptor_mcc]
250
+ post[:descriptor_merchant_id] = descriptor[:descriptor_merchant_id]
251
+ post[:descriptor_url] = descriptor[:descriptor_url]
252
+ end
239
253
  end
240
254
 
241
255
  def add_vendor_data(post, options)
@@ -220,8 +220,9 @@ module ActiveMerchant #:nodoc:
220
220
  end
221
221
 
222
222
  def verify(creditcard, options = {})
223
+ amount = allow_0_auth?(creditcard) ? 0 : 100
223
224
  MultiResponse.run(:use_first_response) do |r|
224
- r.process { authorize(100, creditcard, options) }
225
+ r.process { authorize(amount, creditcard, options) }
225
226
  r.process(:ignore_result) { void(r.authorization) }
226
227
  end
227
228
  end
@@ -276,6 +277,11 @@ module ActiveMerchant #:nodoc:
276
277
  commit(order, :void, options[:retry_logic], options[:trace_number])
277
278
  end
278
279
 
280
+ def allow_0_auth?(credit_card)
281
+ # Discover does not support a $0.00 authorization instead use $1.00
282
+ %w(visa master american_express diners_club jcb).include?(credit_card.brand)
283
+ end
284
+
279
285
  # ==== Customer Profiles
280
286
  # :customer_ref_num should be set unless you're happy with Orbital providing one
281
287
  #
@@ -337,7 +343,8 @@ module ActiveMerchant #:nodoc:
337
343
  gsub(%r((<CustomerMerchantID>).+(</CustomerMerchantID>)), '\1[FILTERED]\2').
338
344
  gsub(%r((<CustomerProfileMessage>).+(</CustomerProfileMessage>)), '\1[FILTERED]\2').
339
345
  gsub(%r((<CheckDDA>).+(</CheckDDA>)), '\1[FILTERED]\2').
340
- gsub(%r((<BCRtNum>).+(</BCRtNum>)), '\1[FILTERED]\2')
346
+ gsub(%r((<BCRtNum>).+(</BCRtNum>)), '\1[FILTERED]\2').
347
+ gsub(%r((<DigitalTokenCryptogram>).+(</DigitalTokenCryptogram>)), '\1[FILTERED]\2')
341
348
  end
342
349
 
343
350
  private
@@ -569,7 +576,7 @@ module ActiveMerchant #:nodoc:
569
576
  # - http://download.chasepaymentech.com/docs/orbital/orbital_gateway_xml_specification.pdf
570
577
  unless creditcard.nil?
571
578
  if creditcard.verification_value?
572
- xml.tag! :CardSecValInd, '1' if %w(visa discover).include?(creditcard.brand)
579
+ xml.tag! :CardSecValInd, '1' if %w(visa master discover).include?(creditcard.brand)
573
580
  xml.tag! :CardSecVal, creditcard.verification_value
574
581
  end
575
582
  end
@@ -631,6 +638,14 @@ module ActiveMerchant #:nodoc:
631
638
  xml.tag!(:SCARecurringPayment, parameters[:sca_recurring]) if valid_eci
632
639
  end
633
640
 
641
+ def add_mc_sca_merchant_initiated(xml, creditcard, parameters, three_d_secure)
642
+ return unless parameters && parameters[:sca_merchant_initiated] && creditcard.brand == 'master'
643
+
644
+ valid_eci = three_d_secure && three_d_secure[:eci] && three_d_secure[:eci] == '7'
645
+
646
+ xml.tag!(:SCAMerchantInitiatedTransaction, parameters[:sca_merchant_initiated]) if valid_eci
647
+ end
648
+
634
649
  def add_dpanind(xml, creditcard)
635
650
  return unless creditcard.is_a?(NetworkTokenizationCreditCard)
636
651
 
@@ -899,6 +914,7 @@ module ActiveMerchant #:nodoc:
899
914
  add_card_indicators(xml, parameters)
900
915
  add_stored_credentials(xml, parameters)
901
916
  add_pymt_brand_program_code(xml, payment_source, three_d_secure)
917
+ add_mc_sca_merchant_initiated(xml, payment_source, parameters, three_d_secure)
902
918
  add_mc_scarecurring(xml, payment_source, parameters, three_d_secure)
903
919
  add_mc_program_protocol(xml, payment_source, three_d_secure)
904
920
  add_mc_directory_trans_id(xml, payment_source, three_d_secure)
@@ -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)
@@ -289,20 +289,28 @@ module ActiveMerchant #:nodoc:
289
289
  xml.tag! 'ECI', three_d_secure[:eci] unless three_d_secure[:eci].blank?
290
290
  xml.tag! 'CAVV', three_d_secure[:cavv] unless three_d_secure[:cavv].blank?
291
291
  xml.tag! 'XID', three_d_secure[:xid] unless three_d_secure[:xid].blank?
292
- xml.tag! 'THREEDSVERSION', three_d_secure[:version] unless three_d_secure[:version].blank?
293
- xml.tag! 'DSTRANSACTIONID', three_d_secure[:ds_transaction_id] unless three_d_secure[:ds_transaction_id].blank?
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?
294
294
  end
295
295
  end
296
296
  end
297
297
 
298
298
  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]
299
+ status = if three_d_secure[:authentication_response_status].present?
300
+ three_d_secure[:authentication_response_status]
301
+ elsif three_d_secure[:directory_response_status].present?
302
+ three_d_secure[:directory_response_status]
303
+ end
304
+ if status.present?
305
+ xml.tag! 'Status', status
306
+ xml.tag! 'AuthenticationStatus', status if version_2_or_newer?(three_d_secure)
303
307
  end
304
308
  end
305
309
 
310
+ def version_2_or_newer?(three_d_secure)
311
+ three_d_secure[:version]&.start_with?('2')
312
+ end
313
+
306
314
  def credit_card_type(credit_card)
307
315
  return '' if card_brand(credit_card).blank?
308
316
 
@@ -9,7 +9,7 @@ module ActiveMerchant #:nodoc:
9
9
 
10
10
  self.supported_countries = %w[MX EC CO BR CL PE]
11
11
  self.default_currency = 'USD'
12
- self.supported_cardtypes = %i[visa master american_express diners_club elo alia olimpica]
12
+ self.supported_cardtypes = %i[visa master american_express diners_club elo alia olimpica discover maestro sodexo carnet unionpay jcb]
13
13
 
14
14
  self.homepage_url = 'https://secure.paymentez.com/'
15
15
  self.display_name = 'Paymentez'
@@ -39,7 +39,14 @@ module ActiveMerchant #:nodoc:
39
39
  'master' => 'mc',
40
40
  'american_express' => 'ax',
41
41
  'diners_club' => 'di',
42
- 'elo' => 'el'
42
+ 'elo' => 'el',
43
+ 'discover' => 'dc',
44
+ 'maestro' => 'ms',
45
+ 'sodexo' => 'sx',
46
+ 'olimpica' => 'ol',
47
+ 'carnet' => 'ct',
48
+ 'unionpay' => 'up',
49
+ 'jcb' => 'jc'
43
50
  }.freeze
44
51
 
45
52
  def initialize(options = {})