activemerchant 1.121.0 → 1.125.0

Sign up to get free protection for your applications and to get access to all the features.
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