Merge pull request #7779 from Matt-Yorkley/stripe-stock-check

Stripe stock check
This commit is contained in:
Matt-Yorkley
2021-07-13 14:46:34 +02:00
committed by GitHub
4 changed files with 70 additions and 17 deletions

View File

@@ -37,7 +37,7 @@ class CheckoutController < ::BaseController
# This is only required because of spree_paypal_express. If we implement
# a version of paypal that uses this controller, and more specifically
# the #action_failed method, then we can remove this call
OrderCheckoutRestart.new(@order).call
reset_order_to_cart
rescue Spree::Core::GatewayError => e
rescue_from_spree_gateway_error(e)
end
@@ -82,7 +82,7 @@ class CheckoutController < ::BaseController
@order = current_order
redirect_to(main_app.shop_path) && return if redirect_to_shop?
redirect_to_cart_path && return unless valid_order_line_items?
handle_invalid_stock && return unless valid_order_line_items?
before_address
setup_for_current_state
@@ -100,7 +100,10 @@ class CheckoutController < ::BaseController
distributes_order_variants?(@order)
end
def redirect_to_cart_path
def handle_invalid_stock
cancel_incomplete_payments if valid_payment_intent_provided?
reset_order_to_cart
respond_to do |format|
format.html do
redirect_to main_app.cart_path
@@ -112,6 +115,20 @@ class CheckoutController < ::BaseController
end
end
def cancel_incomplete_payments
# The checkout could not complete due to stock running out. We void any pending (incomplete)
# Stripe payments here as the order will need to be changed and resubmitted (or abandoned).
@order.payments.incomplete.each do |payment|
payment.void!
payment.adjustment&.update_columns(eligible: false, state: "finalized")
end
flash[:notice] = I18n.t("checkout.payment_cancelled_due_to_stock")
end
def reset_order_to_cart
OrderCheckoutRestart.new(@order).call
end
def setup_for_current_state
method_name = :"before_#{@order.state}"
__send__(method_name) if respond_to?(method_name, true)

View File

@@ -46,9 +46,10 @@ module Spree
scope :from_credit_card, -> { where(source_type: 'Spree::CreditCard') }
scope :with_state, ->(s) { where(state: s.to_s) }
scope :completed, -> { with_state('completed') }
scope :incomplete, -> { where(state: %w(checkout pending requires_authorization)) }
scope :pending, -> { with_state('pending') }
scope :failed, -> { with_state('failed') }
scope :valid, -> { where('state NOT IN (?)', %w(failed invalid)) }
scope :valid, -> { where.not(state: %w(failed invalid)) }
scope :authorization_action_required, -> { where.not(cvv_response_message: nil) }
scope :requires_authorization, -> { with_state("requires_authorization") }
scope :with_payment_intent, ->(code) { where(response_code: code) }
@@ -72,7 +73,7 @@ module Spree
transition from: [:processing, :pending, :checkout, :requires_authorization], to: :completed
end
event :void do
transition from: [:pending, :completed, :checkout], to: :void
transition from: [:pending, :completed, :requires_authorization, :checkout], to: :void
end
# when the card brand isnt supported
event :invalidate do

View File

@@ -1315,6 +1315,7 @@ en:
message_html: "I agree to the seller's %{terms_and_conditions_link} and the platform %{tos_link}."
terms_and_conditions: "Terms and Conditions"
failed: "The checkout failed. Please let us know so that we can process your order."
payment_cancelled_due_to_stock: "Payment cancelled: the checkout could not be completed due to stock issues."
shops:
hubs:
show_closed_shops: "Show closed shops"

View File

@@ -49,8 +49,7 @@ describe CheckoutController, type: :controller do
let(:shipping_method) { distributor.shipping_methods.first }
before do
order.line_items << create(:line_item,
variant: order_cycle.variants_distributed_by(distributor).first)
order.contents.add(order_cycle.variants_distributed_by(distributor).first)
allow(controller).to receive(:current_distributor).and_return(distributor)
allow(controller).to receive(:current_order_cycle).and_return(order_cycle)
@@ -92,19 +91,54 @@ describe CheckoutController, type: :controller do
allow(OrderCycleDistributedVariants).to receive(:new).and_return(order_cycle_distributed_variants)
end
it "redirects when some items are out of stock" do
allow(order).to receive_message_chain(:insufficient_stock_lines, :empty?).and_return false
context "running out of stock" do
it "redirects when some items are out of stock" do
allow(order).to receive_message_chain(:insufficient_stock_lines, :empty?).and_return false
get :edit
expect(response).to redirect_to cart_path
end
get :edit
expect(response).to redirect_to cart_path
end
it "redirects when some items are not available" do
allow(order).to receive_message_chain(:insufficient_stock_lines, :empty?).and_return true
expect(order_cycle_distributed_variants).to receive(:distributes_order_variants?).with(order).and_return(false)
it "redirects when some items are not available" do
allow(order).to receive_message_chain(:insufficient_stock_lines, :empty?).and_return true
expect(order_cycle_distributed_variants).to receive(:distributes_order_variants?).with(order).and_return(false)
get :edit
expect(response).to redirect_to cart_path
get :edit
expect(response).to redirect_to cart_path
end
context "after redirecting back from Stripe" do
let(:order) { create(:order_with_totals_and_distribution) }
let!(:payment) { create(:payment, state: "pending", amount: order.total, order: order) }
let!(:transaction_fee) {
create(:adjustment, state: "open", amount: 10, order: order, adjustable: payment)
}
before do
allow(order).to receive_message_chain(:insufficient_stock_lines, :empty?).and_return(false)
allow(order_cycle_distributed_variants).to receive(:distributes_order_variants?).
with(order).and_return(true)
allow(controller).to receive(:valid_payment_intent_provided?) { true }
order.save
allow(order).to receive_message_chain(:payments, :completed) { [] }
allow(order).to receive_message_chain(:payments, :incomplete) { [payment] }
allow(payment).to receive(:adjustment) { transaction_fee }
end
it "cancels the payment and resets the order to cart" do
expect(payment).to receive(:void!).and_call_original
spree_post :edit
expect(response).to redirect_to cart_path
expect(flash[:notice]).to eq I18n.t('checkout.payment_cancelled_due_to_stock')
expect(order.state).to eq "cart"
expect(payment.state).to eq "void"
expect(transaction_fee.reload.eligible).to eq false
expect(transaction_fee.state).to eq "finalized"
end
end
end
describe "when items are available and in stock" do