activemerchant 1.123.0 → 1.126.0

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