activemerchant 1.121.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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +217 -0
  3. data/README.md +1 -1
  4. data/lib/active_merchant/billing/check.rb +13 -19
  5. data/lib/active_merchant/billing/credit_card.rb +13 -0
  6. data/lib/active_merchant/billing/credit_card_formatting.rb +1 -0
  7. data/lib/active_merchant/billing/credit_card_methods.rb +24 -12
  8. data/lib/active_merchant/billing/gateway.rb +1 -1
  9. data/lib/active_merchant/billing/gateways/adyen.rb +75 -27
  10. data/lib/active_merchant/billing/gateways/authorize_net.rb +10 -8
  11. data/lib/active_merchant/billing/gateways/blue_pay.rb +29 -0
  12. data/lib/active_merchant/billing/gateways/blue_snap.rb +2 -2
  13. data/lib/active_merchant/billing/gateways/braintree_blue.rb +6 -3
  14. data/lib/active_merchant/billing/gateways/card_stream.rb +17 -13
  15. data/lib/active_merchant/billing/gateways/cashnet.rb +15 -5
  16. data/lib/active_merchant/billing/gateways/checkout_v2.rb +33 -4
  17. data/lib/active_merchant/billing/gateways/credorax.rb +2 -1
  18. data/lib/active_merchant/billing/gateways/cyber_source.rb +41 -6
  19. data/lib/active_merchant/billing/gateways/d_local.rb +12 -6
  20. data/lib/active_merchant/billing/gateways/decidir.rb +7 -1
  21. data/lib/active_merchant/billing/gateways/decidir_plus.rb +173 -0
  22. data/lib/active_merchant/billing/gateways/ebanx.rb +16 -1
  23. data/lib/active_merchant/billing/gateways/elavon.rb +65 -30
  24. data/lib/active_merchant/billing/gateways/element.rb +22 -2
  25. data/lib/active_merchant/billing/gateways/global_collect.rb +130 -26
  26. data/lib/active_merchant/billing/gateways/ipg.rb +416 -0
  27. data/lib/active_merchant/billing/gateways/kushki.rb +30 -0
  28. data/lib/active_merchant/billing/gateways/mercado_pago.rb +6 -3
  29. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +2 -0
  30. data/lib/active_merchant/billing/gateways/mit.rb +260 -0
  31. data/lib/active_merchant/billing/gateways/moka.rb +290 -0
  32. data/lib/active_merchant/billing/gateways/monei.rb +228 -144
  33. data/lib/active_merchant/billing/gateways/mundipagg.rb +22 -11
  34. data/lib/active_merchant/billing/gateways/nmi.rb +29 -10
  35. data/lib/active_merchant/billing/gateways/orbital.rb +46 -8
  36. data/lib/active_merchant/billing/gateways/pay_arc.rb +392 -0
  37. data/lib/active_merchant/billing/gateways/pay_conex.rb +3 -1
  38. data/lib/active_merchant/billing/gateways/pay_trace.rb +404 -0
  39. data/lib/active_merchant/billing/gateways/payeezy.rb +4 -0
  40. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -0
  41. data/lib/active_merchant/billing/gateways/payflow.rb +21 -4
  42. data/lib/active_merchant/billing/gateways/payment_express.rb +2 -2
  43. data/lib/active_merchant/billing/gateways/paymentez.rb +14 -2
  44. data/lib/active_merchant/billing/gateways/paysafe.rb +412 -0
  45. data/lib/active_merchant/billing/gateways/payu_latam.rb +9 -4
  46. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +3 -3
  47. data/lib/active_merchant/billing/gateways/pin.rb +31 -4
  48. data/lib/active_merchant/billing/gateways/priority.rb +347 -0
  49. data/lib/active_merchant/billing/gateways/realex.rb +18 -0
  50. data/lib/active_merchant/billing/gateways/redsys.rb +35 -32
  51. data/lib/active_merchant/billing/gateways/safe_charge.rb +8 -2
  52. data/lib/active_merchant/billing/gateways/spreedly_core.rb +13 -4
  53. data/lib/active_merchant/billing/gateways/stripe.rb +27 -7
  54. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +115 -39
  55. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +2 -1
  56. data/lib/active_merchant/billing/gateways/trust_commerce.rb +2 -1
  57. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +21 -7
  58. data/lib/active_merchant/billing/gateways/vpos.rb +49 -6
  59. data/lib/active_merchant/billing/gateways/wompi.rb +193 -0
  60. data/lib/active_merchant/billing/gateways/worldpay.rb +226 -62
  61. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +1 -1
  62. data/lib/active_merchant/billing/response.rb +4 -0
  63. data/lib/active_merchant/billing/three_d_secure_eci_mapper.rb +27 -0
  64. data/lib/active_merchant/billing.rb +1 -0
  65. data/lib/active_merchant/version.rb +1 -1
  66. metadata +13 -3
@@ -23,9 +23,9 @@ module ActiveMerchant #:nodoc:
23
23
  'unchecked' => 'P'
24
24
  }
25
25
 
26
- DEFAULT_API_VERSION = '2015-04-07'
26
+ DEFAULT_API_VERSION = '2020-08-27'
27
27
 
28
- self.supported_countries = %w(AT AU BE BG BR CA CH CY CZ DE DK EE ES FI FR GB GR HK IE IT JP LT LU LV MT MX NL NO NZ PL PT RO SE SG SI SK US)
28
+ self.supported_countries = %w(AE AT AU BE BG BR CA CH CY CZ DE DK EE ES FI FR GB GR HK HU IE IN IT JP LT LU LV MT MX MY NL NO NZ PL PT RO SE SG SI SK US)
29
29
  self.default_currency = 'USD'
30
30
  self.money_format = :cents
31
31
  self.supported_cardtypes = %i[visa master american_express discover jcb diners_club maestro unionpay]
@@ -223,9 +223,10 @@ module ActiveMerchant #:nodoc:
223
223
 
224
224
  post[:default_card] = r.params['id'] if options[:set_default] && r.success? && !r.params['id'].blank?
225
225
 
226
- r.process { update_customer(options[:customer], post) } if post.count > 0
226
+ r.process { update_customer(options[:customer], post.merge(expand: [:sources])) } if post.count > 0
227
227
  end
228
228
  else
229
+ post[:expand] = [:sources]
229
230
  commit(:post, 'customers', post.merge(params), options)
230
231
  end
231
232
  end
@@ -286,13 +287,25 @@ module ActiveMerchant #:nodoc:
286
287
  gsub(%r(((\[card\]|card)\[encrypted_pin\]=)[^&]+(&?)), '\1[FILTERED]\3').
287
288
  gsub(%r(((\[card\]|card)\[encrypted_pin_key_id\]=)[\w=]+(&?)), '\1[FILTERED]\3').
288
289
  gsub(%r(((\[card\]|card)\[number\]=)\d+), '\1[FILTERED]').
289
- gsub(%r(((\[card\]|card)\[swipe_data\]=)[^&]+(&?)), '\1[FILTERED]\3')
290
+ gsub(%r(((\[card\]|card)\[swipe_data\]=)[^&]+(&?)), '\1[FILTERED]\3').
291
+ gsub(%r(((\[bank_account\]|bank_account)\[account_number\]=)\d+), '\1[FILTERED]')
290
292
  end
291
293
 
292
294
  def supports_network_tokenization?
293
295
  true
294
296
  end
295
297
 
298
+ # Helper method to prevent hitting the external_account limit from remote test runs
299
+ def delete_latest_test_external_account(account)
300
+ return unless test?
301
+
302
+ auth_header = { 'Authorization': "Bearer #{options[:login]}" }
303
+ url = "#{live_url}accounts/#{CGI.escape(account)}/external_accounts"
304
+ accounts_response = JSON.parse(ssl_get("#{url}?limit=100", auth_header))
305
+ to_delete = accounts_response['data'].reject { |ac| ac['default_for_currency'] }
306
+ ssl_request(:delete, "#{url}/#{to_delete.first['id']}", nil, auth_header)
307
+ end
308
+
296
309
  private
297
310
 
298
311
  class StripePaymentToken < PaymentToken
@@ -379,6 +392,7 @@ module ActiveMerchant #:nodoc:
379
392
  add_destination(post, options)
380
393
  add_level_three(post, options)
381
394
  add_connected_account(post, options)
395
+ add_radar_data(post, options)
382
396
  post
383
397
  end
384
398
 
@@ -532,7 +546,6 @@ module ActiveMerchant #:nodoc:
532
546
  post[:metadata].merge!(options[:metadata]) if options[:metadata]
533
547
  post[:metadata][:email] = options[:email] if options[:email]
534
548
  post[:metadata][:order_id] = options[:order_id] if options[:order_id]
535
- post.delete(:metadata) if post[:metadata].empty?
536
549
  end
537
550
 
538
551
  def add_emv_metadata(post, creditcard)
@@ -570,6 +583,14 @@ module ActiveMerchant #:nodoc:
570
583
  post[:application_fee_amount] = options[:application_fee_amount] if options[:application_fee_amount]
571
584
  end
572
585
 
586
+ def add_radar_data(post, options = {})
587
+ radar_options = {}
588
+ radar_options[:session] = options[:radar_session_id] if options[:radar_session_id]
589
+ radar_options[:skip_rules] = ['all'] if options[:skip_radar_rules]
590
+
591
+ post[:radar_options] = radar_options unless radar_options.empty?
592
+ end
593
+
573
594
  def parse(body)
574
595
  JSON.parse(body)
575
596
  end
@@ -658,7 +679,6 @@ module ActiveMerchant #:nodoc:
658
679
  card = card_from_response(response)
659
680
  avs_code = AVS_CODE_TRANSLATOR["line1: #{card['address_line1_check']}, zip: #{card['address_zip_check']}"]
660
681
  cvc_code = CVC_CODE_TRANSLATOR[card['cvc_check']]
661
-
662
682
  Response.new(success,
663
683
  message_from(success, response),
664
684
  response,
@@ -754,7 +774,7 @@ module ActiveMerchant #:nodoc:
754
774
  country: 'US',
755
775
  currency: 'usd',
756
776
  routing_number: bank_account.routing_number,
757
- name: bank_account.name,
777
+ account_holder_name: bank_account.name,
758
778
  account_holder_type: account_holder_type
759
779
  }
760
780
  }
@@ -5,33 +5,42 @@ module ActiveMerchant #:nodoc:
5
5
  # This gateway uses the current Stripe {Payment Intents API}[https://stripe.com/docs/api/payment_intents].
6
6
  # For the legacy API, see the Stripe gateway
7
7
  class StripePaymentIntentsGateway < StripeGateway
8
- self.supported_countries = %w(AT AU BE BG BR CA CH CY CZ DE DK EE ES FI FR GB GR HK IE IT JP LT LU LV MT MX NL NO NZ PL PT RO SE SG SI SK US)
9
-
10
8
  ALLOWED_METHOD_STATES = %w[automatic manual].freeze
11
9
  ALLOWED_CANCELLATION_REASONS = %w[duplicate fraudulent requested_by_customer abandoned].freeze
12
10
  CREATE_INTENT_ATTRIBUTES = %i[description statement_descriptor_suffix statement_descriptor receipt_email save_payment_method]
13
11
  CONFIRM_INTENT_ATTRIBUTES = %i[receipt_email return_url save_payment_method setup_future_usage off_session]
14
12
  UPDATE_INTENT_ATTRIBUTES = %i[description statement_descriptor_suffix statement_descriptor receipt_email setup_future_usage]
15
- DEFAULT_API_VERSION = '2019-05-16'
13
+ DEFAULT_API_VERSION = '2020-08-27'
14
+ NO_WALLET_SUPPORT = %w(apple_pay google_pay android_pay)
16
15
 
17
16
  def create_intent(money, payment_method, options = {})
17
+ card_source_pay = payment_method.source.to_s if defined?(payment_method.source)
18
+ card_brand_pay = card_brand(payment_method) unless payment_method.is_a?(String) || payment_method.nil?
19
+ if NO_WALLET_SUPPORT.include?(card_source_pay) || NO_WALLET_SUPPORT.include?(card_brand_pay)
20
+ store_apple_or_google_pay_token = 'Direct Apple Pay and Google Pay transactions are not supported. Those payment methods must be stored before use.'
21
+ return Response.new(false, store_apple_or_google_pay_token)
22
+ end
18
23
  post = {}
19
24
  add_amount(post, money, options, true)
20
25
  add_capture_method(post, options)
21
26
  add_confirmation_method(post, options)
22
27
  add_customer(post, options)
23
- payment_method = add_payment_method_token(post, payment_method, options)
24
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
28
+ result = add_payment_method_token(post, payment_method, options)
29
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
25
30
 
26
31
  add_external_three_d_secure_auth_data(post, options)
27
32
  add_metadata(post, options)
28
33
  add_return_url(post, options)
29
34
  add_connected_account(post, options)
35
+ add_radar_data(post, options)
30
36
  add_shipping_address(post, options)
31
37
  setup_future_usage(post, options)
32
38
  add_exemption(post, options)
33
39
  add_stored_credentials(post, options)
40
+ add_ntid(post, options)
41
+ add_claim_without_transaction_id(post, options)
34
42
  add_error_on_requires_action(post, options)
43
+ add_fulfillment_date(post, options)
35
44
  request_three_d_secure(post, options)
36
45
 
37
46
  CREATE_INTENT_ATTRIBUTES.each do |attribute|
@@ -47,23 +56,24 @@ module ActiveMerchant #:nodoc:
47
56
 
48
57
  def confirm_intent(intent_id, payment_method, options = {})
49
58
  post = {}
50
- payment_method = add_payment_method_token(post, payment_method, options)
51
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
59
+ result = add_payment_method_token(post, payment_method, options)
60
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
52
61
 
53
62
  CONFIRM_INTENT_ATTRIBUTES.each do |attribute|
54
63
  add_whitelisted_attribute(post, options, attribute)
55
64
  end
65
+
56
66
  commit(:post, "payment_intents/#{intent_id}/confirm", post, options)
57
67
  end
58
68
 
59
69
  def create_payment_method(payment_method, options = {})
60
- post_data = create_payment_method_data(payment_method, options)
70
+ post_data = add_payment_method_data(payment_method, options)
61
71
 
62
72
  options = format_idempotency_key(options, 'pm')
63
73
  commit(:post, 'payment_methods', post_data, options)
64
74
  end
65
75
 
66
- def create_payment_method_data(payment_method, options = {})
76
+ def add_payment_method_data(payment_method, options = {})
67
77
  post_data = {}
68
78
  post_data[:type] = 'card'
69
79
  post_data[:card] = {}
@@ -72,6 +82,7 @@ module ActiveMerchant #:nodoc:
72
82
  post_data[:card][:exp_year] = payment_method.year
73
83
  post_data[:card][:cvc] = payment_method.verification_value if payment_method.verification_value
74
84
  add_billing_address(post_data, options)
85
+ add_name_only(post_data, payment_method) if post_data[:billing_details].nil?
75
86
  post_data
76
87
  end
77
88
 
@@ -79,14 +90,15 @@ module ActiveMerchant #:nodoc:
79
90
  post = {}
80
91
  add_amount(post, money, options)
81
92
 
82
- payment_method = add_payment_method_token(post, payment_method, options)
83
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
93
+ result = add_payment_method_token(post, payment_method, options)
94
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
84
95
 
85
96
  add_payment_method_types(post, options)
86
97
  add_customer(post, options)
87
98
  add_metadata(post, options)
88
99
  add_shipping_address(post, options)
89
100
  add_connected_account(post, options)
101
+ add_fulfillment_date(post, options)
90
102
 
91
103
  UPDATE_INTENT_ATTRIBUTES.each do |attribute|
92
104
  add_whitelisted_attribute(post, options, attribute)
@@ -97,11 +109,12 @@ module ActiveMerchant #:nodoc:
97
109
  def create_setup_intent(payment_method, options = {})
98
110
  post = {}
99
111
  add_customer(post, options)
100
- payment_method = add_payment_method_token(post, payment_method, options)
101
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
112
+ result = add_payment_method_token(post, payment_method, options)
113
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
102
114
 
103
115
  add_metadata(post, options)
104
116
  add_return_url(post, options)
117
+ add_fulfillment_date(post, options)
105
118
  post[:on_behalf_of] = options[:on_behalf_of] if options[:on_behalf_of]
106
119
  post[:usage] = options[:usage] if %w(on_session off_session).include?(options[:usage])
107
120
  post[:description] = options[:description] if options[:description]
@@ -177,8 +190,8 @@ module ActiveMerchant #:nodoc:
177
190
  # If customer option is provided, create a payment method and attach to customer id
178
191
  # Otherwise, create a customer, then attach
179
192
  if payment_method.is_a?(StripePaymentToken) || payment_method.is_a?(ActiveMerchant::Billing::CreditCard)
180
- payment_method = add_payment_method_token(params, payment_method, options)
181
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
193
+ result = add_payment_method_token(params, payment_method, options)
194
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
182
195
 
183
196
  if options[:customer]
184
197
  customer_id = options[:customer]
@@ -186,6 +199,7 @@ module ActiveMerchant #:nodoc:
186
199
  post[:description] = options[:description] if options[:description]
187
200
  post[:email] = options[:email] if options[:email]
188
201
  options = format_idempotency_key(options, 'customer')
202
+ post[:expand] = [:sources]
189
203
  customer = commit(:post, 'customers', post, options)
190
204
  customer_id = customer.params['id']
191
205
  end
@@ -211,6 +225,16 @@ module ActiveMerchant #:nodoc:
211
225
  create_setup_intent(payment_method, options.merge!(confirm: true))
212
226
  end
213
227
 
228
+ def setup_purchase(money, options = {})
229
+ requires!(options, :payment_method_types)
230
+ post = {}
231
+ add_currency(post, options, money)
232
+ add_amount(post, money, options)
233
+ add_payment_method_types(post, options)
234
+ add_metadata(post, options)
235
+ commit(:post, 'payment_intents', post, options)
236
+ end
237
+
214
238
  private
215
239
 
216
240
  def off_session_request?(options = {})
@@ -241,6 +265,16 @@ module ActiveMerchant #:nodoc:
241
265
  post[:customer] = customer if customer.start_with?('cus_')
242
266
  end
243
267
 
268
+ def add_fulfillment_date(post, options)
269
+ post[:fulfillment_date] = options[:fulfillment_date].to_i if options[:fulfillment_date]
270
+ end
271
+
272
+ def add_metadata(post, options = {})
273
+ super
274
+
275
+ post[:metadata][:event_type] = options[:event_type] if options[:event_type]
276
+ end
277
+
244
278
  def add_return_url(post, options)
245
279
  return unless options[:confirm]
246
280
 
@@ -249,35 +283,36 @@ module ActiveMerchant #:nodoc:
249
283
  end
250
284
 
251
285
  def add_payment_method_token(post, payment_method, options)
252
- return if payment_method.nil?
253
-
254
- if payment_method.is_a?(ActiveMerchant::Billing::CreditCard)
255
- if off_session_request?(options)
256
- post[:payment_method_data] = create_payment_method_data(payment_method, options)
257
- return
258
- else
259
- p = create_payment_method(payment_method, options)
260
- return p unless p.success?
261
-
262
- payment_method = p.params['id']
263
- end
264
- end
265
-
266
286
  case payment_method
267
287
  when StripePaymentToken
268
288
  post[:payment_method] = payment_method.payment_data['id']
269
289
  when String
270
- if payment_method.include?('|')
271
- customer_id, payment_method_id = payment_method.split('|')
272
- token = payment_method_id
273
- post[:customer] = customer_id
274
- else
275
- token = payment_method
276
- end
277
- post[:payment_method] = token
290
+ extract_token_from_string_and_maybe_add_customer_id(post, payment_method)
291
+ when ActiveMerchant::Billing::CreditCard
292
+ get_payment_method_data_from_card(post, payment_method, options)
278
293
  end
294
+ end
279
295
 
280
- post
296
+ def extract_token_from_string_and_maybe_add_customer_id(post, payment_method)
297
+ if payment_method.include?('|')
298
+ customer_id, payment_method = payment_method.split('|')
299
+ post[:customer] = customer_id
300
+ end
301
+
302
+ post[:payment_method] = payment_method
303
+ end
304
+
305
+ def get_payment_method_data_from_card(post, payment_method, options)
306
+ return create_payment_method_and_extract_token(post, payment_method, options) unless off_session_request?(options)
307
+
308
+ post[:payment_method_data] = add_payment_method_data(payment_method, options)
309
+ end
310
+
311
+ def create_payment_method_and_extract_token(post, payment_method, options)
312
+ payment_method_response = create_payment_method(payment_method, options)
313
+ return payment_method_response if payment_method_response.failure?
314
+
315
+ add_payment_method_token(post, payment_method_response.params['id'], options)
281
316
  end
282
317
 
283
318
  def add_payment_method_types(post, options)
@@ -295,6 +330,11 @@ module ActiveMerchant #:nodoc:
295
330
  post[:payment_method_options][:card][:moto] = true if options[:moto]
296
331
  end
297
332
 
333
+ # Stripe Payment Intents does not pass any parameters for cardholder/merchant initiated
334
+ # it also does not support installments for any country other than Mexico (reason for this is unknown)
335
+ # The only thing that Stripe PI requires for stored credentials to work currently is the network_transaction_id
336
+ # network_transaction_id is created when the card is authenticated using the field `setup_for_future_usage` with the value `off_session` see def setup_future_usage below
337
+
298
338
  def add_stored_credentials(post, options = {})
299
339
  return unless options[:stored_credential] && !options[:stored_credential].values.all?(&:nil?)
300
340
 
@@ -304,8 +344,33 @@ module ActiveMerchant #:nodoc:
304
344
  post[:payment_method_options][:card][:mit_exemption] = {}
305
345
 
306
346
  # Stripe PI accepts network_transaction_id and ds_transaction_id via mit field under card.
307
- post[:payment_method_options][:card][:mit_exemption][:network_transaction_id] = stored_credential[:network_transaction_id] if stored_credential[:network_transaction_id]
347
+ # The network_transaction_id can be sent in nested under stored credentials OR as its own field (add_ntid handles when it is sent in on its own)
348
+ # If it is sent is as its own field AND under stored credentials, the value sent under its own field is what will send.
308
349
  post[:payment_method_options][:card][:mit_exemption][:ds_transaction_id] = stored_credential[:ds_transaction_id] if stored_credential[:ds_transaction_id]
350
+ post[:payment_method_options][:card][:mit_exemption][:network_transaction_id] = stored_credential[:network_transaction_id] if stored_credential[:network_transaction_id]
351
+ end
352
+
353
+ def add_ntid(post, options = {})
354
+ return unless options[:network_transaction_id]
355
+
356
+ post[:payment_method_options] ||= {}
357
+ post[:payment_method_options][:card] ||= {}
358
+ post[:payment_method_options][:card][:mit_exemption] = {}
359
+
360
+ post[:payment_method_options][:card][:mit_exemption][:network_transaction_id] = options[:network_transaction_id] if options[:network_transaction_id]
361
+ end
362
+
363
+ def add_claim_without_transaction_id(post, options = {})
364
+ return if options[:stored_credential] || options[:network_transaction_id] || options[:ds_transaction_id]
365
+ return unless options[:claim_without_transaction_id]
366
+
367
+ post[:payment_method_options] ||= {}
368
+ post[:payment_method_options][:card] ||= {}
369
+ post[:payment_method_options][:card][:mit_exemption] = {}
370
+
371
+ # Stripe PI accepts claim_without_transaction_id for transactions without transaction ids.
372
+ # Gateway validation for this field occurs through a different service, before the transaction request is sent to the gateway.
373
+ post[:payment_method_options][:card][:mit_exemption][:claim_without_transaction_id] = options[:claim_without_transaction_id]
309
374
  end
310
375
 
311
376
  def add_error_on_requires_action(post, options = {})
@@ -357,6 +422,13 @@ module ActiveMerchant #:nodoc:
357
422
  post[:billing_details][:phone] = billing[:phone] if billing[:phone]
358
423
  end
359
424
 
425
+ def add_name_only(post, payment_method)
426
+ post[:billing_details] = {} unless post[:billing_details]
427
+
428
+ name = [payment_method.first_name, payment_method.last_name].compact.join(' ')
429
+ post[:billing_details][:name] = name
430
+ end
431
+
360
432
  def add_shipping_address(post, options = {})
361
433
  return unless shipping = options[:shipping]
362
434
 
@@ -390,6 +462,10 @@ module ActiveMerchant #:nodoc:
390
462
 
391
463
  super(response, options)
392
464
  end
465
+
466
+ def add_currency(post, options, money)
467
+ post[:currency] = options[:currency] || currency(money)
468
+ end
393
469
  end
394
470
  end
395
471
  end
@@ -317,7 +317,8 @@ module ActiveMerchant #:nodoc:
317
317
  gsub(%r((<[^>]+pan>)[^<]+(<))i, '\1[FILTERED]\2').
318
318
  gsub(%r((<[^>]+sec>)[^<]+(<))i, '\1[FILTERED]\2').
319
319
  gsub(%r((<[^>]+id>)[^<]+(<))i, '\1[FILTERED]\2').
320
- gsub(%r((<[^>]+regKey>)[^<]+(<))i, '\1[FILTERED]\2')
320
+ gsub(%r((<[^>]+regKey>)[^<]+(<))i, '\1[FILTERED]\2').
321
+ gsub(%r((<[^>]+acctNr>)[^<]+(<))i, '\1[FILTERED]\2')
321
322
  end
322
323
 
323
324
  private
@@ -331,7 +331,8 @@ module ActiveMerchant #:nodoc:
331
331
  gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
332
332
  gsub(%r((&?cc=)\d*(&?)), '\1[FILTERED]\2').
333
333
  gsub(%r((&?password=)[^&]+(&?)), '\1[FILTERED]\2').
334
- gsub(%r((&?cvv=)\d*(&?)), '\1[FILTERED]\2')
334
+ gsub(%r((&?cvv=)\d*(&?)), '\1[FILTERED]\2').
335
+ gsub(%r((&?account=)\d*(&?)), '\1[FILTERED]\2')
335
336
  end
336
337
 
337
338
  private
@@ -16,7 +16,8 @@ module ActiveMerchant #:nodoc:
16
16
  refund: 'cc:refund',
17
17
  void: 'cc:void',
18
18
  void_release: 'cc:void:release',
19
- check_purchase: 'check:sale'
19
+ check_purchase: 'check:sale',
20
+ store: 'cc:save'
20
21
  }
21
22
 
22
23
  STANDARD_ERROR_CODE_MAPPING = {
@@ -43,14 +44,14 @@ module ActiveMerchant #:nodoc:
43
44
  super
44
45
  end
45
46
 
46
- def authorize(money, credit_card, options = {})
47
+ def authorize(money, payment, options = {})
47
48
  post = {}
48
49
 
49
50
  add_amount(post, money)
50
51
  add_invoice(post, options)
51
- add_payment(post, credit_card)
52
- unless credit_card.track_data.present?
53
- add_address(post, credit_card, options)
52
+ add_payment(post, payment)
53
+ unless payment.is_a?(CreditCard) && payment.track_data.present?
54
+ add_address(post, payment, options)
54
55
  add_customer_data(post, options)
55
56
  end
56
57
  add_split_payments(post, options)
@@ -97,6 +98,12 @@ module ActiveMerchant #:nodoc:
97
98
  commit(:refund, post)
98
99
  end
99
100
 
101
+ def store(payment, options = {})
102
+ post = {}
103
+ add_payment(post, payment, options)
104
+ commit(:store, post)
105
+ end
106
+
100
107
  def verify(creditcard, options = {})
101
108
  MultiResponse.run(:use_first_response) do |r|
102
109
  r.process { authorize(1, creditcard, options) }
@@ -213,6 +220,8 @@ module ActiveMerchant #:nodoc:
213
220
  elsif payment.respond_to?(:track_data) && payment.track_data.present?
214
221
  post[:magstripe] = payment.track_data
215
222
  post[:cardpresent] = true
223
+ elsif payment.is_a?(String)
224
+ post[:card] = payment
216
225
  else
217
226
  post[:card] = payment.number
218
227
  post[:cvv2] = payment.verification_value if payment.verification_value?
@@ -299,6 +308,7 @@ module ActiveMerchant #:nodoc:
299
308
  status: fields['UMstatus'],
300
309
  auth_code: fields['UMauthCode'],
301
310
  ref_num: fields['UMrefNum'],
311
+ card_ref: fields['UMcardRef'],
302
312
  batch: fields['UMbatch'],
303
313
  avs_result: fields['UMavsResult'],
304
314
  avs_result_code: fields['UMavsResultCode'],
@@ -321,7 +331,7 @@ module ActiveMerchant #:nodoc:
321
331
  error_code = (STANDARD_ERROR_CODE_MAPPING[response[:error_code]] || STANDARD_ERROR_CODE[:processing_error]) unless approved
322
332
  Response.new(approved, message_from(response), response,
323
333
  test: test?,
324
- authorization: response[:ref_num],
334
+ authorization: authorization_from(action, response),
325
335
  cvv_result: response[:cvv2_result_code],
326
336
  avs_result: { code: response[:avs_result_code] },
327
337
  error_code: error_code)
@@ -337,13 +347,17 @@ module ActiveMerchant #:nodoc:
337
347
  end
338
348
  end
339
349
 
350
+ def authorization_from(action, response)
351
+ return (action == :store ? response[:card_ref] : response[:ref_num])
352
+ end
353
+
340
354
  def post_data(action, parameters = {})
341
355
  parameters[:command] = TRANSACTIONS[action]
342
356
  parameters[:key] = @options[:login]
343
357
  parameters[:software] = 'Active Merchant'
344
358
  parameters[:testmode] = (@options[:test] ? 1 : 0) unless parameters.has_key?(:testmode)
345
359
  seed = SecureRandom.hex(32).upcase
346
- hash = Digest::SHA1.hexdigest("#{parameters[:command]}:#{@options[:password]}:#{parameters[:amount]}:#{parameters[:invoice]}:#{seed}")
360
+ hash = Digest::SHA1.hexdigest("#{parameters[:command]}:#{@options[:pin] || @options[:password]}:#{parameters[:amount]}:#{parameters[:invoice]}:#{seed}")
347
361
  parameters[:hash] = "s/#{seed}/#{hash}/n"
348
362
 
349
363
  parameters.collect { |key, value| "UM#{key}=#{CGI.escape(value.to_s)}" }.join('&')
@@ -63,6 +63,42 @@ module ActiveMerchant #:nodoc:
63
63
  commit(:pci_buy_rollback, post)
64
64
  end
65
65
 
66
+ def credit(money, payment, options = {})
67
+ # Not permitted for foreign cards.
68
+ commerce = options[:commerce] || @options[:commerce]
69
+ commerce_branch = options[:commerce_branch] || @options[:commerce_branch]
70
+
71
+ token = generate_token(@shop_process_id, 'refund', commerce, commerce_branch, amount(money), currency(money))
72
+ post = {}
73
+ post[:token] = token
74
+ post[:commerce] = commerce.to_i
75
+ post[:commerce_branch] = commerce_branch.to_i
76
+ post[:shop_process_id] = @shop_process_id
77
+ add_invoice(post, money, options)
78
+ add_card_data(post, payment)
79
+ add_customer_data(post, options)
80
+ post[:origin_shop_process_id] = options[:original_shop_process_id] if options[:original_shop_process_id]
81
+ commit(:refund, post)
82
+ end
83
+
84
+ def refund(money, authorization, options = {})
85
+ commerce = options[:commerce] || @options[:commerce]
86
+ commerce_branch = options[:commerce_branch] || @options[:commerce_branch]
87
+ shop_process_id = options[:shop_process_id] || @shop_process_id
88
+ _, original_shop_process_id = authorization.to_s.split('#')
89
+
90
+ token = generate_token(shop_process_id, 'refund', commerce, commerce_branch, amount(money), currency(money))
91
+ post = {}
92
+ post[:token] = token
93
+ post[:commerce] = commerce.to_i
94
+ post[:commerce_branch] = commerce_branch.to_i
95
+ post[:shop_process_id] = shop_process_id
96
+ add_invoice(post, money, options)
97
+ add_customer_data(post, options)
98
+ post[:origin_shop_process_id] = original_shop_process_id || options[:original_shop_process_id]
99
+ commit(:refund, post)
100
+ end
101
+
66
102
  def supports_scrubbing?
67
103
  true
68
104
  end
@@ -146,15 +182,22 @@ module ActiveMerchant #:nodoc:
146
182
  end
147
183
 
148
184
  def message_from(response)
149
- response.dig('confirmation', 'extended_response_description') ||
150
- response.dig('confirmation', 'response_description') ||
151
- response.dig('confirmation', 'response_details') ||
152
- response.dig('messages', 0, 'key')
185
+ %w(confirmation refund).each do |m|
186
+ message =
187
+ response.dig(m, 'extended_response_description') ||
188
+ response.dig(m, 'response_description') ||
189
+ response.dig(m, 'response_details')
190
+ return message if message
191
+ end
192
+ [response.dig('messages', 0, 'key'), response.dig('messages', 0, 'dsc')].join(':')
153
193
  end
154
194
 
155
195
  def authorization_from(response)
156
- authorization_number = response.dig('confirmation', 'authorization_number')
157
- shop_process_id = response.dig('confirmation', 'shop_process_id')
196
+ response_body = response.dig('confirmation') || response.dig('refund')
197
+ return unless response_body
198
+
199
+ authorization_number = response_body.dig('authorization_number') || response_body.dig('authorization_code')
200
+ shop_process_id = response_body.dig('shop_process_id')
158
201
 
159
202
  "#{authorization_number}##{shop_process_id}"
160
203
  end