activemerchant 1.123.0 → 1.125.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 (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 = {})