activemerchant 1.123.0 → 1.126.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +206 -0
  3. data/lib/active_merchant/billing/check.rb +5 -8
  4. data/lib/active_merchant/billing/credit_card.rb +10 -0
  5. data/lib/active_merchant/billing/credit_card_methods.rb +18 -3
  6. data/lib/active_merchant/billing/gateway.rb +3 -2
  7. data/lib/active_merchant/billing/gateways/adyen.rb +66 -11
  8. data/lib/active_merchant/billing/gateways/airwallex.rb +341 -0
  9. data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +2 -1
  10. data/lib/active_merchant/billing/gateways/blue_pay.rb +1 -1
  11. data/lib/active_merchant/billing/gateways/blue_snap.rb +31 -21
  12. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +6 -1
  13. data/lib/active_merchant/billing/gateways/braintree/token_nonce.rb +113 -0
  14. data/lib/active_merchant/billing/gateways/braintree_blue.rb +87 -15
  15. data/lib/active_merchant/billing/gateways/card_connect.rb +1 -1
  16. data/lib/active_merchant/billing/gateways/card_stream.rb +17 -13
  17. data/lib/active_merchant/billing/gateways/cashnet.rb +15 -5
  18. data/lib/active_merchant/billing/gateways/checkout_v2.rb +34 -5
  19. data/lib/active_merchant/billing/gateways/credorax.rb +10 -0
  20. data/lib/active_merchant/billing/gateways/cyber_source.rb +24 -36
  21. data/lib/active_merchant/billing/gateways/d_local.rb +61 -6
  22. data/lib/active_merchant/billing/gateways/decidir.rb +17 -1
  23. data/lib/active_merchant/billing/gateways/decidir_plus.rb +344 -0
  24. data/lib/active_merchant/billing/gateways/ebanx.rb +19 -3
  25. data/lib/active_merchant/billing/gateways/elavon.rb +6 -3
  26. data/lib/active_merchant/billing/gateways/element.rb +20 -2
  27. data/lib/active_merchant/billing/gateways/global_collect.rb +137 -32
  28. data/lib/active_merchant/billing/gateways/ipg.rb +415 -0
  29. data/lib/active_merchant/billing/gateways/kushki.rb +7 -0
  30. data/lib/active_merchant/billing/gateways/litle.rb +93 -1
  31. data/lib/active_merchant/billing/gateways/mercado_pago.rb +3 -1
  32. data/lib/active_merchant/billing/gateways/mit.rb +260 -0
  33. data/lib/active_merchant/billing/gateways/moka.rb +24 -11
  34. data/lib/active_merchant/billing/gateways/moneris.rb +35 -8
  35. data/lib/active_merchant/billing/gateways/mundipagg.rb +8 -6
  36. data/lib/active_merchant/billing/gateways/nmi.rb +27 -8
  37. data/lib/active_merchant/billing/gateways/orbital.rb +357 -319
  38. data/lib/active_merchant/billing/gateways/pay_arc.rb +9 -7
  39. data/lib/active_merchant/billing/gateways/pay_conex.rb +3 -1
  40. data/lib/active_merchant/billing/gateways/pay_trace.rb +1 -1
  41. data/lib/active_merchant/billing/gateways/payflow.rb +76 -6
  42. data/lib/active_merchant/billing/gateways/paymentez.rb +35 -9
  43. data/lib/active_merchant/billing/gateways/paysafe.rb +155 -34
  44. data/lib/active_merchant/billing/gateways/payu_latam.rb +31 -16
  45. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +3 -3
  46. data/lib/active_merchant/billing/gateways/pin.rb +31 -4
  47. data/lib/active_merchant/billing/gateways/priority.rb +369 -0
  48. data/lib/active_merchant/billing/gateways/rapyd.rb +258 -0
  49. data/lib/active_merchant/billing/gateways/realex.rb +18 -0
  50. data/lib/active_merchant/billing/gateways/safe_charge.rb +7 -6
  51. data/lib/active_merchant/billing/gateways/simetrik.rb +362 -0
  52. data/lib/active_merchant/billing/gateways/stripe.rb +30 -8
  53. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +175 -72
  54. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +2 -1
  55. data/lib/active_merchant/billing/gateways/trust_commerce.rb +2 -1
  56. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +20 -6
  57. data/lib/active_merchant/billing/gateways/visanet_peru.rb +6 -2
  58. data/lib/active_merchant/billing/gateways/wompi.rb +193 -0
  59. data/lib/active_merchant/billing/gateways/worldpay.rb +196 -64
  60. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +1 -1
  61. data/lib/active_merchant/billing/response.rb +4 -0
  62. data/lib/active_merchant/version.rb +1 -1
  63. metadata +11 -2
@@ -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]
@@ -48,7 +48,8 @@ module ActiveMerchant #:nodoc:
48
48
  'processing_error' => STANDARD_ERROR_CODE[:processing_error],
49
49
  'incorrect_pin' => STANDARD_ERROR_CODE[:incorrect_pin],
50
50
  'test_mode_live_card' => STANDARD_ERROR_CODE[:test_mode_live_card],
51
- 'pickup_card' => STANDARD_ERROR_CODE[:pickup_card]
51
+ 'pickup_card' => STANDARD_ERROR_CODE[:pickup_card],
52
+ 'amount_too_small' => STANDARD_ERROR_CODE[:invalid_amount]
52
53
  }
53
54
 
54
55
  BANK_ACCOUNT_HOLDER_TYPE_MAPPING = {
@@ -223,9 +224,10 @@ module ActiveMerchant #:nodoc:
223
224
 
224
225
  post[:default_card] = r.params['id'] if options[:set_default] && r.success? && !r.params['id'].blank?
225
226
 
226
- r.process { update_customer(options[:customer], post) } if post.count > 0
227
+ r.process { update_customer(options[:customer], post.merge(expand: [:sources])) } if post.count > 0
227
228
  end
228
229
  else
230
+ post[:expand] = [:sources]
229
231
  commit(:post, 'customers', post.merge(params), options)
230
232
  end
231
233
  end
@@ -286,13 +288,26 @@ module ActiveMerchant #:nodoc:
286
288
  gsub(%r(((\[card\]|card)\[encrypted_pin\]=)[^&]+(&?)), '\1[FILTERED]\3').
287
289
  gsub(%r(((\[card\]|card)\[encrypted_pin_key_id\]=)[\w=]+(&?)), '\1[FILTERED]\3').
288
290
  gsub(%r(((\[card\]|card)\[number\]=)\d+), '\1[FILTERED]').
289
- gsub(%r(((\[card\]|card)\[swipe_data\]=)[^&]+(&?)), '\1[FILTERED]\3')
291
+ gsub(%r(((\[card\]|card)\[swipe_data\]=)[^&]+(&?)), '\1[FILTERED]\3').
292
+ gsub(%r(((\[bank_account\]|bank_account)\[account_number\]=)\d+), '\1[FILTERED]').
293
+ gsub(%r(((\[payment_method_data\]|payment_method_data)\[card\]\[token\]=)[^&]+(&?)), '\1[FILTERED]\3')
290
294
  end
291
295
 
292
296
  def supports_network_tokenization?
293
297
  true
294
298
  end
295
299
 
300
+ # Helper method to prevent hitting the external_account limit from remote test runs
301
+ def delete_latest_test_external_account(account)
302
+ return unless test?
303
+
304
+ auth_header = { 'Authorization': "Bearer #{options[:login]}" }
305
+ url = "#{live_url}accounts/#{CGI.escape(account)}/external_accounts"
306
+ accounts_response = JSON.parse(ssl_get("#{url}?limit=100", auth_header))
307
+ to_delete = accounts_response['data'].reject { |ac| ac['default_for_currency'] }
308
+ ssl_request(:delete, "#{url}/#{to_delete.first['id']}", nil, auth_header)
309
+ end
310
+
296
311
  private
297
312
 
298
313
  class StripePaymentToken < PaymentToken
@@ -379,6 +394,7 @@ module ActiveMerchant #:nodoc:
379
394
  add_destination(post, options)
380
395
  add_level_three(post, options)
381
396
  add_connected_account(post, options)
397
+ add_radar_data(post, options)
382
398
  post
383
399
  end
384
400
 
@@ -532,7 +548,6 @@ module ActiveMerchant #:nodoc:
532
548
  post[:metadata].merge!(options[:metadata]) if options[:metadata]
533
549
  post[:metadata][:email] = options[:email] if options[:email]
534
550
  post[:metadata][:order_id] = options[:order_id] if options[:order_id]
535
- post.delete(:metadata) if post[:metadata].empty?
536
551
  end
537
552
 
538
553
  def add_emv_metadata(post, creditcard)
@@ -570,6 +585,14 @@ module ActiveMerchant #:nodoc:
570
585
  post[:application_fee_amount] = options[:application_fee_amount] if options[:application_fee_amount]
571
586
  end
572
587
 
588
+ def add_radar_data(post, options = {})
589
+ radar_options = {}
590
+ radar_options[:session] = options[:radar_session_id] if options[:radar_session_id]
591
+ radar_options[:skip_rules] = ['all'] if options[:skip_radar_rules]
592
+
593
+ post[:radar_options] = radar_options unless radar_options.empty?
594
+ end
595
+
573
596
  def parse(body)
574
597
  JSON.parse(body)
575
598
  end
@@ -658,7 +681,6 @@ module ActiveMerchant #:nodoc:
658
681
  card = card_from_response(response)
659
682
  avs_code = AVS_CODE_TRANSLATOR["line1: #{card['address_line1_check']}, zip: #{card['address_zip_check']}"]
660
683
  cvc_code = CVC_CODE_TRANSLATOR[card['cvc_check']]
661
-
662
684
  Response.new(success,
663
685
  message_from(success, response),
664
686
  response,
@@ -754,7 +776,7 @@ module ActiveMerchant #:nodoc:
754
776
  country: 'US',
755
777
  currency: 'usd',
756
778
  routing_number: bank_account.routing_number,
757
- name: bank_account.name,
779
+ account_holder_name: bank_account.name,
758
780
  account_holder_type: account_holder_type
759
781
  }
760
782
  }
@@ -5,41 +5,50 @@ 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'
16
14
 
17
15
  def create_intent(money, payment_method, options = {})
18
- post = {}
19
- add_amount(post, money, options, true)
20
- add_capture_method(post, options)
21
- add_confirmation_method(post, options)
22
- 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)
25
-
26
- add_external_three_d_secure_auth_data(post, options)
27
- add_metadata(post, options)
28
- add_return_url(post, options)
29
- add_connected_account(post, options)
30
- add_shipping_address(post, options)
31
- setup_future_usage(post, options)
32
- add_exemption(post, options)
33
- add_stored_credentials(post, options)
34
- add_ntid(post, options)
35
- add_error_on_requires_action(post, options)
36
- request_three_d_secure(post, options)
37
-
38
- CREATE_INTENT_ATTRIBUTES.each do |attribute|
39
- add_whitelisted_attribute(post, options, attribute)
16
+ MultiResponse.run do |r|
17
+ if payment_method.is_a?(NetworkTokenizationCreditCard)
18
+ r.process { tokenize_apple_google(payment_method, options) }
19
+ payment_method = (r.params['token']['id']) if r.success?
20
+ end
21
+ r.process do
22
+ post = {}
23
+ add_amount(post, money, options, true)
24
+ add_capture_method(post, options)
25
+ add_confirmation_method(post, options)
26
+ add_customer(post, options)
27
+
28
+ result = add_payment_method_token(post, payment_method, options)
29
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
30
+
31
+ add_external_three_d_secure_auth_data(post, options)
32
+ add_metadata(post, options)
33
+ add_return_url(post, options)
34
+ add_connected_account(post, options)
35
+ add_radar_data(post, options)
36
+ add_shipping_address(post, options)
37
+ setup_future_usage(post, options)
38
+ add_exemption(post, options)
39
+ add_stored_credentials(post, options)
40
+ add_ntid(post, options)
41
+ add_claim_without_transaction_id(post, options)
42
+ add_error_on_requires_action(post, options)
43
+ add_fulfillment_date(post, options)
44
+ request_three_d_secure(post, options)
45
+
46
+ CREATE_INTENT_ATTRIBUTES.each do |attribute|
47
+ add_whitelisted_attribute(post, options, attribute)
48
+ end
49
+ commit(:post, 'payment_intents', post, options)
50
+ end
40
51
  end
41
-
42
- commit(:post, 'payment_intents', post, options)
43
52
  end
44
53
 
45
54
  def show_intent(intent_id, options)
@@ -48,23 +57,25 @@ module ActiveMerchant #:nodoc:
48
57
 
49
58
  def confirm_intent(intent_id, payment_method, options = {})
50
59
  post = {}
51
- payment_method = add_payment_method_token(post, payment_method, options)
52
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
60
+ result = add_payment_method_token(post, payment_method, options)
61
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
53
62
 
63
+ add_payment_method_types(post, options)
54
64
  CONFIRM_INTENT_ATTRIBUTES.each do |attribute|
55
65
  add_whitelisted_attribute(post, options, attribute)
56
66
  end
67
+
57
68
  commit(:post, "payment_intents/#{intent_id}/confirm", post, options)
58
69
  end
59
70
 
60
71
  def create_payment_method(payment_method, options = {})
61
- post_data = create_payment_method_data(payment_method, options)
72
+ post_data = add_payment_method_data(payment_method, options)
62
73
 
63
74
  options = format_idempotency_key(options, 'pm')
64
75
  commit(:post, 'payment_methods', post_data, options)
65
76
  end
66
77
 
67
- def create_payment_method_data(payment_method, options = {})
78
+ def add_payment_method_data(payment_method, options = {})
68
79
  post_data = {}
69
80
  post_data[:type] = 'card'
70
81
  post_data[:card] = {}
@@ -73,21 +84,30 @@ module ActiveMerchant #:nodoc:
73
84
  post_data[:card][:exp_year] = payment_method.year
74
85
  post_data[:card][:cvc] = payment_method.verification_value if payment_method.verification_value
75
86
  add_billing_address(post_data, options)
87
+ add_name_only(post_data, payment_method) if post_data[:billing_details].nil?
76
88
  post_data
77
89
  end
78
90
 
91
+ def add_payment_method_card_data_token(post_data, payment_method)
92
+ post_data.merge!({
93
+ payment_method_types: ['card'],
94
+ payment_method_data: { type: 'card', card: { token: payment_method } }
95
+ })
96
+ end
97
+
79
98
  def update_intent(money, intent_id, payment_method, options = {})
80
99
  post = {}
81
100
  add_amount(post, money, options)
82
101
 
83
- payment_method = add_payment_method_token(post, payment_method, options)
84
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
102
+ result = add_payment_method_token(post, payment_method, options)
103
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
85
104
 
86
105
  add_payment_method_types(post, options)
87
106
  add_customer(post, options)
88
107
  add_metadata(post, options)
89
108
  add_shipping_address(post, options)
90
109
  add_connected_account(post, options)
110
+ add_fulfillment_date(post, options)
91
111
 
92
112
  UPDATE_INTENT_ATTRIBUTES.each do |attribute|
93
113
  add_whitelisted_attribute(post, options, attribute)
@@ -98,11 +118,13 @@ module ActiveMerchant #:nodoc:
98
118
  def create_setup_intent(payment_method, options = {})
99
119
  post = {}
100
120
  add_customer(post, options)
101
- payment_method = add_payment_method_token(post, payment_method, options)
102
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
121
+ result = add_payment_method_token(post, payment_method, options)
122
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
103
123
 
104
124
  add_metadata(post, options)
105
125
  add_return_url(post, options)
126
+ add_fulfillment_date(post, options)
127
+ request_three_d_secure(post, options)
106
128
  post[:on_behalf_of] = options[:on_behalf_of] if options[:on_behalf_of]
107
129
  post[:usage] = options[:usage] if %w(on_session off_session).include?(options[:usage])
108
130
  post[:description] = options[:description] if options[:description]
@@ -174,12 +196,11 @@ module ActiveMerchant #:nodoc:
174
196
  def store(payment_method, options = {})
175
197
  params = {}
176
198
  post = {}
177
-
178
199
  # If customer option is provided, create a payment method and attach to customer id
179
200
  # Otherwise, create a customer, then attach
180
201
  if payment_method.is_a?(StripePaymentToken) || payment_method.is_a?(ActiveMerchant::Billing::CreditCard)
181
- payment_method = add_payment_method_token(params, payment_method, options)
182
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
202
+ result = add_payment_method_token(params, payment_method, options)
203
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
183
204
 
184
205
  if options[:customer]
185
206
  customer_id = options[:customer]
@@ -187,6 +208,7 @@ module ActiveMerchant #:nodoc:
187
208
  post[:description] = options[:description] if options[:description]
188
209
  post[:email] = options[:email] if options[:email]
189
210
  options = format_idempotency_key(options, 'customer')
211
+ post[:expand] = [:sources]
190
212
  customer = commit(:post, 'customers', post, options)
191
213
  customer_id = customer.params['id']
192
214
  end
@@ -212,6 +234,16 @@ module ActiveMerchant #:nodoc:
212
234
  create_setup_intent(payment_method, options.merge!(confirm: true))
213
235
  end
214
236
 
237
+ def setup_purchase(money, options = {})
238
+ requires!(options, :payment_method_types)
239
+ post = {}
240
+ add_currency(post, options, money)
241
+ add_amount(post, money, options)
242
+ add_payment_method_types(post, options)
243
+ add_metadata(post, options)
244
+ commit(:post, 'payment_intents', post, options)
245
+ end
246
+
215
247
  private
216
248
 
217
249
  def off_session_request?(options = {})
@@ -242,6 +274,16 @@ module ActiveMerchant #:nodoc:
242
274
  post[:customer] = customer if customer.start_with?('cus_')
243
275
  end
244
276
 
277
+ def add_fulfillment_date(post, options)
278
+ post[:fulfillment_date] = options[:fulfillment_date].to_i if options[:fulfillment_date]
279
+ end
280
+
281
+ def add_metadata(post, options = {})
282
+ super
283
+
284
+ post[:metadata][:event_type] = options[:event_type] if options[:event_type]
285
+ end
286
+
245
287
  def add_return_url(post, options)
246
288
  return unless options[:confirm]
247
289
 
@@ -250,35 +292,69 @@ module ActiveMerchant #:nodoc:
250
292
  end
251
293
 
252
294
  def add_payment_method_token(post, payment_method, options)
253
- return if payment_method.nil?
295
+ case payment_method
296
+ when StripePaymentToken
297
+ post[:payment_method_data] = {
298
+ type: 'card',
299
+ card: {
300
+ token: payment_method.payment_data['id'] || payment_method.payment_data
301
+ }
302
+ }
303
+ post[:payment_method] = payment_method.payment_data['id'] || payment_method.payment_data
304
+ when String
305
+ extract_token_from_string_and_maybe_add_customer_id(post, payment_method)
306
+ when ActiveMerchant::Billing::CreditCard
307
+ get_payment_method_data_from_card(post, payment_method, options)
308
+ end
309
+ end
254
310
 
255
- if payment_method.is_a?(ActiveMerchant::Billing::CreditCard)
256
- if off_session_request?(options)
257
- post[:payment_method_data] = create_payment_method_data(payment_method, options)
258
- return
259
- else
260
- p = create_payment_method(payment_method, options)
261
- return p unless p.success?
311
+ def extract_token_from_string_and_maybe_add_customer_id(post, payment_method)
312
+ if payment_method.include?('|')
313
+ customer_id, payment_method = payment_method.split('|')
314
+ post[:customer] = customer_id
315
+ end
262
316
 
263
- payment_method = p.params['id']
264
- end
317
+ if payment_method.include?('tok_')
318
+ add_payment_method_card_data_token(post, payment_method)
319
+ else
320
+ post[:payment_method] = payment_method
265
321
  end
322
+ end
266
323
 
267
- case payment_method
268
- when StripePaymentToken
269
- post[:payment_method] = payment_method.payment_data['id']
270
- when String
271
- if payment_method.include?('|')
272
- customer_id, payment_method_id = payment_method.split('|')
273
- token = payment_method_id
274
- post[:customer] = customer_id
275
- else
276
- token = payment_method
277
- end
278
- post[:payment_method] = token
324
+ def tokenize_apple_google(payment, options = {})
325
+ tokenization_method = payment.source == :google_pay ? :android_pay : payment.source
326
+ post = {
327
+ card: {
328
+ number: payment.number,
329
+ exp_month: payment.month,
330
+ exp_year: payment.year,
331
+ tokenization_method: tokenization_method,
332
+ eci: payment.eci,
333
+ cryptogram: payment.payment_cryptogram
334
+ }
335
+ }
336
+ token_response = api_request(:post, 'tokens', post, options)
337
+ success = token_response['error'].nil?
338
+ if success && token_response['id']
339
+ Response.new(success, nil, token: token_response)
340
+ elsif token_response['error']['message']
341
+ Response.new(false, "The tokenization process fails. #{token_response['error']['message']}")
342
+ else
343
+ Response.new(false, "The tokenization process fails. #{token_response}")
279
344
  end
345
+ end
280
346
 
281
- post
347
+ def get_payment_method_data_from_card(post, payment_method, options)
348
+ return create_payment_method_and_extract_token(post, payment_method, options) unless off_session_request?(options)
349
+
350
+ post[:payment_method_data] = add_payment_method_data(payment_method, options)
351
+ end
352
+
353
+ def create_payment_method_and_extract_token(post, payment_method, options)
354
+ payment_method_response = create_payment_method(payment_method, options)
355
+ return payment_method_response if payment_method_response.failure?
356
+
357
+ add_payment_method_token(post, payment_method_response.params['id'], options)
282
358
  end
283
359
 
284
360
  def add_payment_method_types(post, options)
@@ -326,6 +402,19 @@ module ActiveMerchant #:nodoc:
326
402
  post[:payment_method_options][:card][:mit_exemption][:network_transaction_id] = options[:network_transaction_id] if options[:network_transaction_id]
327
403
  end
328
404
 
405
+ def add_claim_without_transaction_id(post, options = {})
406
+ return if options[:stored_credential] || options[:network_transaction_id] || options[:ds_transaction_id]
407
+ return unless options[:claim_without_transaction_id]
408
+
409
+ post[:payment_method_options] ||= {}
410
+ post[:payment_method_options][:card] ||= {}
411
+ post[:payment_method_options][:card][:mit_exemption] = {}
412
+
413
+ # Stripe PI accepts claim_without_transaction_id for transactions without transaction ids.
414
+ # Gateway validation for this field occurs through a different service, before the transaction request is sent to the gateway.
415
+ post[:payment_method_options][:card][:mit_exemption][:claim_without_transaction_id] = options[:claim_without_transaction_id]
416
+ end
417
+
329
418
  def add_error_on_requires_action(post, options = {})
330
419
  return unless options[:confirm]
331
420
 
@@ -375,22 +464,32 @@ module ActiveMerchant #:nodoc:
375
464
  post[:billing_details][:phone] = billing[:phone] if billing[:phone]
376
465
  end
377
466
 
467
+ def add_name_only(post, payment_method)
468
+ post[:billing_details] = {} unless post[:billing_details]
469
+
470
+ name = [payment_method.first_name, payment_method.last_name].compact.join(' ')
471
+ post[:billing_details][:name] = name
472
+ end
473
+
378
474
  def add_shipping_address(post, options = {})
379
- return unless shipping = options[:shipping]
475
+ return unless shipping = options[:shipping_address]
380
476
 
381
477
  post[:shipping] = {}
382
- post[:shipping][:address] = {}
383
- post[:shipping][:address][:line1] = shipping[:address][:line1]
384
- post[:shipping][:address][:city] = shipping[:address][:city] if shipping[:address][:city]
385
- post[:shipping][:address][:country] = shipping[:address][:country] if shipping[:address][:country]
386
- post[:shipping][:address][:line2] = shipping[:address][:line2] if shipping[:address][:line2]
387
- post[:shipping][:address][:postal_code] = shipping[:address][:postal_code] if shipping[:address][:postal_code]
388
- post[:shipping][:address][:state] = shipping[:address][:state] if shipping[:address][:state]
389
478
 
479
+ # fields required by Stripe PI
480
+ post[:shipping][:address] = {}
481
+ post[:shipping][:address][:line1] = shipping[:address1]
390
482
  post[:shipping][:name] = shipping[:name]
391
- post[:shipping][:carrier] = shipping[:carrier] if shipping[:carrier]
392
- post[:shipping][:phone] = shipping[:phone] if shipping[:phone]
393
- post[:shipping][:tracking_number] = shipping[:tracking_number] if shipping[:tracking_number]
483
+
484
+ # fields considered optional by Stripe PI
485
+ post[:shipping][:address][:city] = shipping[:city] if shipping[:city]
486
+ post[:shipping][:address][:country] = shipping[:country] if shipping[:country]
487
+ post[:shipping][:address][:line2] = shipping[:address2] if shipping[:address2]
488
+ post[:shipping][:address][:postal_code] = shipping[:zip] if shipping[:zip]
489
+ post[:shipping][:address][:state] = shipping[:state] if shipping[:state]
490
+ post[:shipping][:phone] = shipping[:phone_number] if shipping[:phone_number]
491
+ post[:shipping][:carrier] = (shipping[:carrier] || options[:shipping_carrier]) if shipping[:carrier] || options[:shipping_carrier]
492
+ post[:shipping][:tracking_number] = (shipping[:tracking_number] || options[:shipping_tracking_number]) if shipping[:tracking_number] || options[:shipping_tracking_number]
394
493
  end
395
494
 
396
495
  def format_idempotency_key(options, suffix)
@@ -408,6 +507,10 @@ module ActiveMerchant #:nodoc:
408
507
 
409
508
  super(response, options)
410
509
  end
510
+
511
+ def add_currency(post, options, money)
512
+ post[:currency] = options[:currency] || currency(money)
513
+ end
411
514
  end
412
515
  end
413
516
  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,6 +347,10 @@ 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]
@@ -90,8 +90,8 @@ module ActiveMerchant #:nodoc:
90
90
  CURRENCY_CODES['PEN'] = 604
91
91
 
92
92
  def add_invoice(params, money, options)
93
- # Visanet Peru expects a 9-digit numeric purchaseNumber
94
- params[:purchaseNumber] = (SecureRandom.random_number(900_000_000) + 100_000_000).to_s
93
+ # Visanet Peru expects a 12-digit alphanumeric purchaseNumber
94
+ params[:purchaseNumber] = generate_purchase_number_stamp
95
95
  params[:externalTransactionId] = options[:order_id]
96
96
  params[:amount] = amount(money)
97
97
  params[:currencyId] = CURRENCY_CODES[options[:currency] || currency(money)]
@@ -142,6 +142,10 @@ module ActiveMerchant #:nodoc:
142
142
  authorization.split('|')
143
143
  end
144
144
 
145
+ def generate_purchase_number_stamp
146
+ (Time.now.to_f.round(2) * 100).to_i.to_s
147
+ end
148
+
145
149
  def commit(action, params, options = {})
146
150
  raw_response = ssl_request(method(action), url(action, params, options), params.to_json, headers)
147
151
  response = parse(raw_response)