solidus_stripe 2.0.0 → 3.2.1

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