solidus_stripe 2.0.0 → 3.2.1

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -1
  3. data/README.md +131 -7
  4. data/app/assets/javascripts/spree/frontend/solidus_stripe.js +6 -0
  5. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-cart-page-checkout.js +88 -0
  6. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-elements.js +148 -0
  7. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-init.js +20 -0
  8. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-payment-intents.js +84 -0
  9. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-payment-request-button-shared.js +123 -0
  10. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-payment.js +16 -0
  11. data/app/controllers/solidus_stripe/intents_controller.rb +29 -20
  12. data/app/decorators/models/spree/order_update_attributes_decorator.rb +39 -0
  13. data/app/decorators/models/spree/payment_decorator.rb +11 -0
  14. data/app/models/solidus_stripe/create_intents_order_service.rb +70 -0
  15. data/app/models/spree/payment_method/stripe_credit_card.rb +4 -9
  16. data/lib/generators/solidus_stripe/install/install_generator.rb +2 -5
  17. data/lib/solidus_stripe/engine.rb +0 -1
  18. data/lib/solidus_stripe/version.rb +1 -1
  19. data/lib/views/frontend/spree/checkout/payment/_stripe.html.erb +1 -1
  20. data/lib/views/frontend/spree/checkout/payment/v3/_elements.html.erb +1 -0
  21. data/lib/views/frontend/spree/checkout/payment/v3/_form_elements.html.erb +1 -3
  22. data/lib/views/frontend/spree/checkout/payment/v3/_intents.html.erb +1 -5
  23. data/lib/views/frontend/spree/checkout/payment/v3/_stripe.html.erb +2 -5
  24. data/lib/views/frontend/spree/orders/_stripe_payment_request_button.html.erb +1 -79
  25. data/solidus_stripe.gemspec +1 -1
  26. data/spec/features/stripe_checkout_spec.rb +143 -68
  27. data/spec/spec_helper.rb +1 -0
  28. data/spec/support/solidus_address_helper.rb +15 -0
  29. metadata +17 -8
  30. data/app/assets/javascripts/solidus_stripe/stripe-init.js +0 -1
  31. data/app/assets/javascripts/solidus_stripe/stripe-init/base.js +0 -180
  32. data/lib/views/frontend/spree/checkout/payment/v3/_elements_js.html.erb +0 -28
  33. data/lib/views/frontend/spree/checkout/payment/v3/_intents_js.html.erb +0 -48
@@ -5,11 +5,8 @@ module SolidusStripe
5
5
  class InstallGenerator < Rails::Generators::Base
6
6
  class_option :auto_run_migrations, type: :boolean, default: false
7
7
 
8
- def add_stylesheets
9
- filename = 'vendor/assets/stylesheets/spree/frontend/all.css'
10
- if File.file? filename
11
- inject_into_file filename, " *= require spree/frontend/solidus_stripe\n", before: '*/', verbose: true
12
- end
8
+ def add_javascripts
9
+ append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/solidus_stripe\n"
13
10
  end
14
11
 
15
12
  def add_migrations
@@ -21,7 +21,6 @@ module SolidusStripe
21
21
 
22
22
  if SolidusSupport.frontend_available?
23
23
  paths["app/views"] << "lib/views/frontend"
24
- config.assets.precompile += ['solidus_stripe/stripe-init.js']
25
24
  end
26
25
 
27
26
  if SolidusSupport.api_available?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidusStripe
4
- VERSION = "2.0.0"
4
+ VERSION = "3.2.1"
5
5
  end
@@ -1,5 +1,5 @@
1
1
  <% if payment_method.v3_elements? %>
2
- <%= render 'spree/checkout/payment/v3/stripe', payment_method: payment_method %>
2
+ <%= render 'spree/checkout/payment/v3/elements', payment_method: payment_method %>
3
3
  <% elsif payment_method.v3_intents? %>
4
4
  <%= render 'spree/checkout/payment/v3/intents', payment_method: payment_method %>
5
5
  <% else %>
@@ -0,0 +1 @@
1
+ <%= render 'spree/checkout/payment/v3/form_elements', payment_method: payment_method, stripe_v3_api: 'elements' %>
@@ -1,4 +1,4 @@
1
- <div id="payment-request-button" data-stripe-config="<%= payment_method.stripe_config(current_order).to_json %>"></div>
1
+ <div id="payment-request-button" data-stripe-config="<%= payment_method.stripe_config(current_order).to_json %>" data-v3-api="<%= stripe_v3_api %>"></div>
2
2
 
3
3
  <%= image_tag 'credit_cards/credit_card.gif', id: 'credit-card-image' %>
4
4
  <% param_prefix = "payment_source[#{payment_method.id}]" %>
@@ -38,5 +38,3 @@
38
38
  <% end %>
39
39
 
40
40
  <%= hidden_field_tag "#{param_prefix}[cc_type]", '', id: "cc_type", class: 'ccType' %>
41
-
42
- <script src="https://js.stripe.com/v3/"></script>
@@ -1,5 +1 @@
1
- <%= render 'spree/checkout/payment/v3/form_elements', payment_method: payment_method %>
2
-
3
- <%= javascript_include_tag "solidus_stripe/stripe-init.js" %>
4
-
5
- <%= render "spree/checkout/payment/v3/intents_js" %>
1
+ <%= render 'spree/checkout/payment/v3/form_elements', payment_method: payment_method, stripe_v3_api: 'payment-intents' %>
@@ -1,5 +1,2 @@
1
- <%= render 'spree/checkout/payment/v3/form_elements', payment_method: payment_method %>
2
-
3
- <%= javascript_include_tag "solidus_stripe/stripe-init.js" %>
4
-
5
- <%= render "spree/checkout/payment/v3/elements_js" %>
1
+ <% ActiveSupport::Deprecation.warn 'Partial `spree/checkout/payment/v3/_stripe.html.erb` is deprecated and will be removed soon. Please use partial `spree/checkout/payment/v3/elements`', caller(1) %>
2
+ <%= render 'spree/checkout/payment/v3/elements', payment_method: payment_method, stripe_v3_api: 'elements' %>
@@ -1,5 +1,5 @@
1
1
  <% if current_order.present? && cart_checkout_payment_method.present? %>
2
- <div id="stripe-payment-request" class="stripe-payment-request" style="display:none">
2
+ <div id="stripe-payment-request" class="stripe-payment-request" data-v3-api="payment-request-button" style="display:none">
3
3
  <div id="payment-request-button"
4
4
  data-stripe-config="<%= cart_checkout_payment_method.stripe_config(current_order).to_json %>"
5
5
  data-order-token="<%= current_order.guest_token %>"
@@ -11,82 +11,4 @@
11
11
  <div id="card-errors" class='errorExplanation' role="alert" style="display: none">
12
12
  </div>
13
13
  </div>
14
-
15
- <script src="https://js.stripe.com/v3/"></script>
16
- <%= javascript_include_tag "solidus_stripe/stripe-init" %>
17
-
18
- <script>
19
- var paymentRequestConfig = SolidusStripe.paymentMethod.config.payment_request;
20
- var errorElement = $('#card-errors');
21
-
22
- if (typeof paymentRequestConfig !== 'undefined') {
23
- paymentRequestConfig.requestShipping = true;
24
-
25
- var onPrButtonMounted = function(buttonId, success) {
26
- var container = document.getElementById('stripe-payment-request');
27
-
28
- if (success) {
29
- container.style.display = '';
30
- } else {
31
- container.style.display = 'none';
32
- }
33
- };
34
-
35
- var paymentRequest = setUpPaymentRequest(paymentRequestConfig, onPrButtonMounted);
36
-
37
- function handlePayment(result) {
38
- fetch('/stripe/update_order', {
39
- method: 'POST',
40
- headers: { 'Content-Type': 'application/json' },
41
- body: JSON.stringify({
42
- shipping_address: result.shippingAddress,
43
- shipping_option: result.shippingOption,
44
- email: result.payerEmail,
45
- name: result.payerName,
46
- authenticity_token: authToken
47
- })
48
- }).then(function(response) {
49
- response.json().then(function(json) {
50
- handleServerResponse(json, result);
51
- })
52
- });
53
- };
54
-
55
- function stripeTokenHandler(token) {
56
- return {
57
- order: {
58
- payments_attributes: [
59
- {
60
- payment_method_id: SolidusStripe.paymentMethod.config.id,
61
- source_attributes: {
62
- gateway_payment_profile_id: token.id,
63
- last_digits: token.card.last4,
64
- month: token.card.exp_month,
65
- year: token.card.exp_year
66
- }
67
- }
68
- ]
69
- }
70
- }
71
- };
72
-
73
- function submitPayment(payment) {
74
- $.ajax({
75
- url : $('[data-submit-url]').data('submit-url'),
76
- headers: {
77
- 'X-Spree-Order-Token': $('[data-order-token]').data('order-token')
78
- },
79
- type : 'PATCH',
80
- contentType: 'application/json',
81
- data : JSON.stringify(stripeTokenHandler(payment.paymentMethod)),
82
- success: function() {
83
- window.location = $('[data-complete-url]').data('complete-url');
84
- },
85
- error: function(xhr,status,error) {
86
- showError(xhr.responseJSON.error);
87
- }
88
- });
89
- };
90
- }
91
- </script>
92
14
  <% end %>
@@ -29,7 +29,7 @@ Gem::Specification.new do |s|
29
29
  s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
30
 
31
31
  s.add_dependency 'solidus_core', ['>= 2.3', '< 3']
32
- s.add_dependency 'solidus_support', '~> 0.4.0'
32
+ s.add_dependency 'solidus_support', '~> 0.4'
33
33
  # ActiveMerchant v1.58 through v1.59 introduced a breaking change
34
34
  # to the stripe gateway.
35
35
  #
@@ -6,6 +6,8 @@ RSpec.describe "Stripe checkout", type: :feature do
6
6
  let(:zone) { FactoryBot.create(:zone) }
7
7
  let(:country) { FactoryBot.create(:country) }
8
8
 
9
+ let(:card_3d_secure) { "4000 0025 0000 3155" }
10
+
9
11
  before do
10
12
  FactoryBot.create(:store)
11
13
  zone.members << Spree::ZoneMember.create!(zoneable: country)
@@ -41,8 +43,7 @@ RSpec.describe "Stripe checkout", type: :feature do
41
43
  expect(page).to have_current_path("/checkout/address")
42
44
 
43
45
  within("#billing") do
44
- fill_in "First Name", with: "Han"
45
- fill_in "Last Name", with: "Solo"
46
+ fill_in_name
46
47
  fill_in "Street Address", with: "YT-1300"
47
48
  fill_in "City", with: "Mos Eisley"
48
49
  select "United States of America", from: "Country"
@@ -100,8 +101,7 @@ RSpec.describe "Stripe checkout", type: :feature do
100
101
  expect(page).to have_current_path("/checkout/address")
101
102
 
102
103
  within("#billing") do
103
- fill_in "First Name", with: "Han"
104
- fill_in "Last Name", with: "Solo"
104
+ fill_in_name
105
105
  fill_in "Street Address", with: "YT-1300"
106
106
  fill_in "City", with: "Mos Eisley"
107
107
  select "United States of America", from: "Country"
@@ -264,8 +264,7 @@ RSpec.describe "Stripe checkout", type: :feature do
264
264
  expect(page).to have_current_path("/checkout/address")
265
265
 
266
266
  within("#billing") do
267
- fill_in "First Name", with: "Han"
268
- fill_in "Last Name", with: "Solo"
267
+ fill_in_name
269
268
  fill_in "Street Address", with: "YT-1300"
270
269
  fill_in "City", with: "Mos Eisley"
271
270
  select "United States of America", from: "Country"
@@ -304,24 +303,8 @@ RSpec.describe "Stripe checkout", type: :feature do
304
303
  end
305
304
 
306
305
  context "when using a valid 3D Secure card" do
307
- let(:card_number) { "4000 0027 6000 3184" }
308
-
309
306
  it "successfully completes the checkout" do
310
- within_frame find('#card_number iframe') do
311
- card_number.split('').each { |n| find_field('cardnumber').native.send_keys(n) }
312
- end
313
- within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
314
- within_frame(find '#card_expiry iframe') do
315
- '0132'.split('').each { |n| find_field('exp-date').native.send_keys(n) }
316
- end
317
-
318
- click_button "Save and Continue"
319
-
320
- within_3d_secure_modal do
321
- expect(page).to have_content '$19.99 using 3D Secure'
322
-
323
- click_button 'Complete authentication'
324
- end
307
+ authenticate_3d_secure_card(card_3d_secure)
325
308
 
326
309
  expect(page).to have_current_path("/checkout/confirm")
327
310
 
@@ -371,60 +354,133 @@ RSpec.describe "Stripe checkout", type: :feature do
371
354
  end
372
355
  end
373
356
 
374
- it "can re-use saved cards" do
375
- within_frame find('#card_number iframe') do
376
- "4000 0027 6000 3184".split('').each { |n| find_field('cardnumber').native.send_keys(n) }
377
- end
378
- within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
379
- within_frame(find '#card_expiry iframe') do
380
- '0132'.split('').each { |n| find_field('exp-date').native.send_keys(n) }
381
- end
382
- click_button "Save and Continue"
357
+ context "when reusing a card" do
358
+ stub_authorization!
383
359
 
384
- within_3d_secure_modal do
385
- click_button 'Complete authentication'
360
+ it "succesfully creates a second payment that can be captured in the backend" do
361
+ authenticate_3d_secure_card(card_3d_secure)
362
+
363
+ expect(page).to have_current_path("/checkout/confirm")
364
+ click_button "Place Order"
365
+ expect(page).to have_content("Your order has been processed successfully")
366
+
367
+ visit spree.root_path
368
+ click_link "DL-44"
369
+ click_button "Add To Cart"
370
+
371
+ expect(page).to have_current_path("/cart")
372
+ click_button "Checkout"
373
+
374
+ # Address
375
+ expect(page).to have_current_path("/checkout/address")
376
+
377
+ within("#billing") do
378
+ fill_in_name
379
+ fill_in "Street Address", with: "YT-1300"
380
+ fill_in "City", with: "Mos Eisley"
381
+ select "United States of America", from: "Country"
382
+ select country.states.first.name, from: "order_bill_address_attributes_state_id"
383
+ fill_in "Zip", with: "12010"
384
+ fill_in "Phone", with: "(555) 555-5555"
385
+ end
386
+ click_on "Save and Continue"
387
+
388
+ # Delivery
389
+ expect(page).to have_current_path("/checkout/delivery")
390
+ expect(page).to have_content("UPS Ground")
391
+ click_on "Save and Continue"
392
+
393
+ # Payment
394
+ expect(page).to have_current_path("/checkout/payment")
395
+ choose "Use an existing card on file"
396
+ click_button "Save and Continue"
397
+
398
+ # Confirm
399
+ expect(page).to have_current_path("/checkout/confirm")
400
+ click_button "Place Order"
401
+ expect(page).to have_content("Your order has been processed successfully")
402
+
403
+ # Capture in backend
404
+ Spree::Order.complete.each do |order|
405
+ visit spree.admin_path
406
+
407
+ expect(page).to have_selector("#listing_orders tbody tr", count: 2)
408
+
409
+ click_link order.number
410
+
411
+ click_link "Payments"
412
+ find(".fa-capture").click
413
+
414
+ expect(page).to have_content "Payment Updated"
415
+ expect(find("table#payments")).to have_content "Completed"
416
+ end
386
417
  end
418
+ end
387
419
 
388
- expect(page).to have_current_path("/checkout/confirm")
389
- click_button "Place Order"
390
- expect(page).to have_content("Your order has been processed successfully")
420
+ context "when paying with multiple payment methods" do
421
+ stub_authorization!
391
422
 
392
- visit spree.root_path
393
- click_link "DL-44"
394
- click_button "Add To Cart"
423
+ context "when paying first with regular card, then with 3D-Secure card" do
424
+ let(:regular_card) { "4242 4242 4242 4242"}
395
425
 
396
- expect(page).to have_current_path("/cart")
397
- click_button "Checkout"
426
+ it "voids the first stripe payment and successfully pays with 3DS card" do
427
+ within_frame find('#card_number iframe') do
428
+ regular_card.split('').each { |n| find_field('cardnumber').native.send_keys(n) }
429
+ end
430
+ within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
431
+ within_frame(find '#card_expiry iframe') do
432
+ '0132'.split('').each { |n| find_field('exp-date').native.send_keys(n) }
433
+ end
434
+ click_button "Save and Continue"
398
435
 
399
- # Address
400
- expect(page).to have_current_path("/checkout/address")
436
+ expect(page).to have_content "Ending in 4242"
401
437
 
402
- within("#billing") do
403
- fill_in "First Name", with: "Han"
404
- fill_in "Last Name", with: "Solo"
405
- fill_in "Street Address", with: "YT-1300"
406
- fill_in "City", with: "Mos Eisley"
407
- select "United States of America", from: "Country"
408
- select country.states.first.name, from: "order_bill_address_attributes_state_id"
409
- fill_in "Zip", with: "12010"
410
- fill_in "Phone", with: "(555) 555-5555"
438
+ click_link "Payment"
439
+
440
+ authenticate_3d_secure_card(card_3d_secure)
441
+ click_button "Place Order"
442
+ expect(page).to have_content "Your order has been processed successfully"
443
+
444
+ visit spree.admin_path
445
+ click_link Spree::Order.complete.first.number
446
+ click_link "Payments"
447
+
448
+ payments = all('table#payments tbody tr')
449
+
450
+ expect(payments.first).to have_content "Stripe"
451
+ expect(payments.first).to have_content "Void"
452
+
453
+ expect(payments.last).to have_content "Stripe"
454
+ expect(payments.last).to have_content "Pending"
455
+ end
411
456
  end
412
- click_on "Save and Continue"
413
457
 
414
- # Delivery
415
- expect(page).to have_current_path("/checkout/delivery")
416
- expect(page).to have_content("UPS Ground")
417
- click_on "Save and Continue"
458
+ context "when paying first with 3D-Secure card, then with check" do
459
+ before { create :check_payment_method }
418
460
 
419
- # Payment
420
- expect(page).to have_current_path("/checkout/payment")
421
- choose "Use an existing card on file"
422
- click_button "Save and Continue"
461
+ it "voids the stripe payment and successfully pays with check" do
462
+ authenticate_3d_secure_card(card_3d_secure)
463
+ expect(page).to have_current_path("/checkout/confirm")
423
464
 
424
- # Confirm
425
- expect(page).to have_current_path("/checkout/confirm")
426
- click_button "Place Order"
427
- expect(page).to have_content("Your order has been processed successfully")
465
+ click_link "Payment"
466
+ choose "Check"
467
+ click_button "Save and Continue"
468
+ expect(find(".payment-info")).to have_content "Check"
469
+ expect(page).to have_content "Your order has been processed successfully"
470
+
471
+ visit spree.admin_path
472
+ click_link Spree::Order.complete.first.number
473
+ click_link "Payments"
474
+ payments = all('table#payments tbody tr')
475
+
476
+ stripe_payment = payments.first
477
+ expect(stripe_payment).to have_content "Stripe"
478
+ expect(stripe_payment).to have_content "Void"
479
+
480
+ check_payment = payments.last
481
+ expect(check_payment).to have_content "Check"
482
+ end
483
+ end
428
484
  end
429
485
 
430
486
  it_behaves_like "Stripe Elements invalid payments"
@@ -432,9 +488,28 @@ RSpec.describe "Stripe checkout", type: :feature do
432
488
 
433
489
  def within_3d_secure_modal
434
490
  within_frame "__privateStripeFrame10" do
435
- within_frame "challengeFrame" do
436
- yield
491
+ within_frame "__stripeJSChallengeFrame" do
492
+ within_frame "acsFrame" do
493
+ yield
494
+ end
437
495
  end
438
496
  end
439
497
  end
498
+
499
+ def authenticate_3d_secure_card(card_number)
500
+ within_frame find('#card_number iframe') do
501
+ card_number.split('').each { |n| find_field('cardnumber').native.send_keys(n) }
502
+ end
503
+ within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
504
+ within_frame(find '#card_expiry iframe') do
505
+ '0132'.split('').each { |n| find_field('exp-date').native.send_keys(n) }
506
+ end
507
+ click_button "Save and Continue"
508
+
509
+ within_3d_secure_modal do
510
+ expect(page).to have_content '$19.99 using 3D Secure'
511
+
512
+ click_button 'Complete authentication'
513
+ end
514
+ end
440
515
  end
@@ -19,4 +19,5 @@ RSpec.configure do |config|
19
19
  config.infer_spec_type_from_file_location!
20
20
  FactoryBot.find_definitions
21
21
  config.use_transactional_fixtures = false
22
+ config.include SolidusAddressNameHelper, type: :feature
22
23
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Since https://github.com/solidusio/solidus/pull/3524 was merged,
4
+ # we need to verify if we're using the single "Name" field or the
5
+ # previous first/last name combination.
6
+ module SolidusAddressNameHelper
7
+ def fill_in_name
8
+ if Spree::Config.preferences[:use_combined_first_and_last_name_in_address]
9
+ fill_in "Name", with: "Han Solo"
10
+ else
11
+ fill_in "First Name", with: "Han"
12
+ fill_in "Last Name", with: "Solo"
13
+ end
14
+ end
15
+ end