mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-04 22:16:08 +00:00
remove checkout controller
This commit is contained in:
committed by
Filipe
parent
fc0cca2210
commit
c2aaf88e98
@@ -1,187 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'open_food_network/address_finder'
|
||||
|
||||
class CheckoutController < ::BaseController
|
||||
include OrderStockCheck
|
||||
include OrderCompletion
|
||||
include WhiteLabel
|
||||
|
||||
layout 'darkswarm'
|
||||
|
||||
helper 'terms_and_conditions'
|
||||
helper 'checkout'
|
||||
|
||||
# We need pessimistic locking to avoid race conditions.
|
||||
# Otherwise we fail on duplicate indexes or end up with negative stock.
|
||||
prepend_around_action CurrentOrderLocker, only: [:edit, :update]
|
||||
|
||||
prepend_before_action :check_hub_ready_for_checkout
|
||||
prepend_before_action :check_order_cycle_expiry
|
||||
prepend_before_action :require_order_cycle
|
||||
prepend_before_action :require_distributor_chosen
|
||||
|
||||
before_action :load_order
|
||||
|
||||
before_action :handle_insufficient_stock
|
||||
|
||||
before_action :associate_user
|
||||
before_action :check_authorization
|
||||
|
||||
before_action :hide_ofn_navigation, only: :edit
|
||||
|
||||
helper 'spree/orders'
|
||||
|
||||
def edit; end
|
||||
|
||||
def update
|
||||
params_adapter = Checkout::FormDataAdapter.new(permitted_params, @order, spree_current_user)
|
||||
return action_failed unless @order.update(params_adapter.params[:order] || {})
|
||||
|
||||
checkout_workflow(params_adapter.shipping_method_id)
|
||||
rescue Spree::Core::GatewayError => e
|
||||
gateway_error(e)
|
||||
action_failed(e)
|
||||
rescue StandardError => e
|
||||
flash[:error] = I18n.t("checkout.failed")
|
||||
action_failed(e)
|
||||
ensure
|
||||
@order.update_order!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_authorization
|
||||
authorize!(:edit, current_order, session[:access_token])
|
||||
end
|
||||
|
||||
def load_order
|
||||
load_checkout_order
|
||||
|
||||
return handle_invalid_stock unless valid_order_line_items?
|
||||
|
||||
before_address
|
||||
setup_for_current_state
|
||||
end
|
||||
|
||||
def handle_invalid_stock
|
||||
reset_order_to_cart
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
redirect_to main_app.cart_path
|
||||
end
|
||||
|
||||
format.json do
|
||||
render json: { path: main_app.cart_path }, status: :bad_request
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def setup_for_current_state
|
||||
method_name = :"before_#{@order.state}"
|
||||
__send__(method_name) if respond_to?(method_name, true)
|
||||
end
|
||||
|
||||
def before_address
|
||||
associate_user
|
||||
|
||||
finder = OpenFoodNetwork::AddressFinder.new(@order.email, @order.customer, spree_current_user)
|
||||
|
||||
@order.bill_address = finder.bill_address
|
||||
@order.ship_address = finder.ship_address
|
||||
end
|
||||
|
||||
def before_payment
|
||||
current_order.payments.destroy_all if request.put?
|
||||
end
|
||||
|
||||
def checkout_workflow(shipping_method_id)
|
||||
while @order.state != "complete"
|
||||
if @order.state == "payment"
|
||||
update_payment_total
|
||||
return if redirect_to_payment_gateway
|
||||
|
||||
return action_failed if @order.errors.any?
|
||||
return action_failed unless @order.process_payments!
|
||||
end
|
||||
|
||||
next if OrderWorkflow.new(@order).next({ "shipping_method_id" => shipping_method_id })
|
||||
|
||||
return action_failed
|
||||
end
|
||||
|
||||
update_response
|
||||
end
|
||||
|
||||
def update_payment_total
|
||||
@order.update_totals
|
||||
@order.updater.update_pending_payment
|
||||
end
|
||||
|
||||
def redirect_to_payment_gateway
|
||||
return unless selected_payment_method.external_gateway?
|
||||
return unless (redirect_url = selected_payment_method.external_payment_url(order: @order))
|
||||
|
||||
render json: { path: redirect_url }, status: :ok
|
||||
true
|
||||
end
|
||||
|
||||
def selected_payment_method
|
||||
@selected_payment_method ||= Spree::PaymentMethod.find(
|
||||
params.dig(:order, :payments_attributes, 0, :payment_method_id)
|
||||
)
|
||||
end
|
||||
|
||||
def update_response
|
||||
if order_complete?
|
||||
processing_succeeded
|
||||
update_succeeded_response
|
||||
else
|
||||
action_failed(RuntimeError.new("Order not complete after the checkout workflow"))
|
||||
end
|
||||
end
|
||||
|
||||
def order_complete?
|
||||
@order.state == "complete" || @order.completed?
|
||||
end
|
||||
|
||||
def update_succeeded_response
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
respond_with(@order, location: order_completion_route)
|
||||
end
|
||||
format.json do
|
||||
render json: { path: order_completion_route }, status: :ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def action_failed(error = RuntimeError.new(order_processing_error))
|
||||
processing_failed(error)
|
||||
action_failed_response
|
||||
end
|
||||
|
||||
def action_failed_response
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
render :edit
|
||||
end
|
||||
format.json do
|
||||
discard_flash_errors
|
||||
render json: { errors: @order.errors, flash: flash.to_hash }.to_json, status: :bad_request
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def permitted_params
|
||||
PermittedAttributes::Checkout.new(params).call
|
||||
end
|
||||
|
||||
def discard_flash_errors
|
||||
# Marks flash errors for deletion after the current action has completed.
|
||||
# This ensures flash errors generated during XHR requests are not persisted in the
|
||||
# session for longer than expected.
|
||||
flash.discard(:error)
|
||||
end
|
||||
end
|
||||
@@ -13,8 +13,6 @@ module OrderStockCheck
|
||||
def handle_insufficient_stock
|
||||
return if sufficient_stock?
|
||||
|
||||
reset_order_to_cart
|
||||
|
||||
flash[:error] = Spree.t(:inventory_error_flash_for_insufficient_quantity)
|
||||
redirect_to main_app.cart_path
|
||||
end
|
||||
@@ -43,10 +41,4 @@ module OrderStockCheck
|
||||
def sufficient_stock?
|
||||
@sufficient_stock ||= @order.insufficient_stock_lines.blank?
|
||||
end
|
||||
|
||||
def reset_order_to_cart
|
||||
return if OpenFoodNetwork::FeatureToggle.enabled? :split_checkout, spree_current_user
|
||||
|
||||
OrderCheckoutRestart.new(@order).call
|
||||
end
|
||||
end
|
||||
|
||||
@@ -25,9 +25,7 @@ module Spree
|
||||
order.update_totals
|
||||
order.payment_required?
|
||||
}
|
||||
go_to_state :confirmation, if: ->(order) {
|
||||
OpenFoodNetwork::FeatureToggle.enabled? :split_checkout, order.created_by
|
||||
}
|
||||
go_to_state :confirmation
|
||||
go_to_state :complete
|
||||
end
|
||||
|
||||
@@ -319,8 +317,7 @@ module Spree
|
||||
# Creates new tax charges if there are any applicable rates. If prices already
|
||||
# include taxes then price adjustments are created instead.
|
||||
def create_tax_charge!
|
||||
return if state.in?(["cart", "address", "delivery"]) &&
|
||||
OpenFoodNetwork::FeatureToggle.enabled?(:split_checkout)
|
||||
return if state.in?(["cart", "address", "delivery"])
|
||||
|
||||
clear_legacy_taxes!
|
||||
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Adapts checkout form data (params) so that the order can be directly saved to the database
|
||||
module Checkout
|
||||
class FormDataAdapter
|
||||
attr_reader :params, :shipping_method_id
|
||||
|
||||
def initialize(params, order, current_user)
|
||||
@params = params.deep_dup.to_h.with_indifferent_access
|
||||
@order = order
|
||||
@current_user = current_user
|
||||
|
||||
move_payment_source_to_payment_attributes!
|
||||
|
||||
fill_in_card_type
|
||||
|
||||
set_amount_in_payments_attributes
|
||||
|
||||
construct_saved_card_attributes if @params.dig(:order, :existing_card_id)
|
||||
|
||||
@shipping_method_id = @params[:order]&.delete(:shipping_method_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# For payment step, filter order parameters to produce the expected
|
||||
# nested attributes for a single payment and its source,
|
||||
# discarding attributes for payment methods other than the one selected
|
||||
def move_payment_source_to_payment_attributes!
|
||||
return unless @params[:payment_source].present? &&
|
||||
payment_source_params = delete_payment_source_params!
|
||||
|
||||
@params.dig(:order, :payments_attributes).first[:source_attributes] = payment_source_params
|
||||
end
|
||||
|
||||
# Ensures cc_type is always passed to the model by inferring the type when
|
||||
# the frontend didn't provide it.
|
||||
def fill_in_card_type
|
||||
return unless payment_source_attributes
|
||||
|
||||
return if payment_source_attributes.dig(:number).blank?
|
||||
|
||||
payment_source_attributes[:cc_type] ||= card_brand(payment_source_attributes[:number])
|
||||
end
|
||||
|
||||
def payment_source_attributes
|
||||
@payment_source_attributes ||=
|
||||
@params.dig(:order, :payments_attributes)&.first&.dig(:source_attributes)
|
||||
end
|
||||
|
||||
def card_brand(number)
|
||||
ActiveMerchant::Billing::CreditCard.brand?(number)
|
||||
end
|
||||
|
||||
def delete_payment_source_params!
|
||||
@params.delete(:payment_source)[
|
||||
@params.dig(:order, :payments_attributes).first[:payment_method_id].underscore
|
||||
]
|
||||
end
|
||||
|
||||
def set_amount_in_payments_attributes
|
||||
return unless @params.dig(:order, :payments_attributes)
|
||||
|
||||
@params.dig(:order, :payments_attributes).first[:amount] = @order.total
|
||||
end
|
||||
|
||||
def construct_saved_card_attributes
|
||||
existing_card_id = @params[:order].delete(:existing_card_id)
|
||||
return if existing_card_id.blank?
|
||||
|
||||
add_to_payment_attributes(existing_card_id)
|
||||
|
||||
@params.dig(:order, :payments_attributes).first.delete :source_attributes
|
||||
end
|
||||
|
||||
def add_to_payment_attributes(existing_card_id)
|
||||
credit_card = Spree::CreditCard.find(existing_card_id)
|
||||
if credit_card.try(:user_id).blank? || credit_card.user_id != @current_user.try(:id)
|
||||
raise Spree::Core::GatewayError, I18n.t(:invalid_credit_card)
|
||||
end
|
||||
|
||||
@params.dig(:order, :payments_attributes).first[:source] = credit_card
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,25 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module PermittedAttributes
|
||||
class Checkout
|
||||
def initialize(params)
|
||||
@params = params
|
||||
end
|
||||
|
||||
def call
|
||||
@params.permit(
|
||||
order: [
|
||||
:email, :special_instructions,
|
||||
:existing_card_id, :shipping_method_id,
|
||||
{ payments_attributes: [
|
||||
:payment_method_id,
|
||||
{ source_attributes: PermittedAttributes::PaymentSource.attributes }
|
||||
],
|
||||
ship_address_attributes: PermittedAttributes::Address.attributes,
|
||||
bill_address_attributes: PermittedAttributes::Address.attributes }
|
||||
],
|
||||
payment_source: PermittedAttributes::PaymentSource.attributes
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -84,24 +84,15 @@ Openfoodnetwork::Application.routes.draw do
|
||||
get "/stripe/authorize/:order_number", to: "stripe#authorize", as: :authorize_stripe
|
||||
end
|
||||
|
||||
constraints FeatureToggleConstraint.new(:split_checkout) do
|
||||
get '/checkout', to: 'split_checkout#edit'
|
||||
get '/checkout', to: 'split_checkout#edit'
|
||||
|
||||
constraints step: /(details|payment|summary)/ do
|
||||
get '/checkout/:step', to: 'split_checkout#edit', as: :checkout_step
|
||||
put '/checkout/:step', to: 'split_checkout#update', as: :checkout_update
|
||||
end
|
||||
|
||||
# Redirects to the new checkout for any other 'step' (ie. /checkout/cart from the legacy checkout)
|
||||
get '/checkout/:other', to: redirect('/checkout')
|
||||
constraints step: /(details|payment|summary)/ do
|
||||
get '/checkout/:step', to: 'split_checkout#edit', as: :checkout_step
|
||||
put '/checkout/:step', to: 'split_checkout#update', as: :checkout_update
|
||||
end
|
||||
|
||||
# When the split_checkout feature is disabled for the current user, use the legacy checkout
|
||||
constraints FeatureToggleConstraint.new(:split_checkout, negate: true) do
|
||||
get '/checkout', to: 'checkout#edit'
|
||||
put '/checkout', to: 'checkout#update', as: :update_checkout
|
||||
get '/checkout/:state', to: 'checkout#edit', as: :checkout_state
|
||||
end
|
||||
# Redirects to the new checkout for any other 'step' (ie. /checkout/cart from the legacy checkout)
|
||||
get '/checkout/:other', to: redirect('/checkout')
|
||||
|
||||
get 'embedded_shopfront/shopfront_session', to: 'application#shopfront_session'
|
||||
post 'embedded_shopfront/enable', to: 'application#enable_embedded_styles'
|
||||
|
||||
@@ -1,346 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe CheckoutController, type: :controller do
|
||||
include StripeStubs
|
||||
|
||||
let(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true) }
|
||||
let(:order_cycle) { create(:simple_order_cycle) }
|
||||
let(:order) { create(:order) }
|
||||
|
||||
before do
|
||||
allow(order).to receive(:checkout_allowed?).and_return true
|
||||
allow(controller).to receive(:check_authorization).and_return true
|
||||
end
|
||||
|
||||
it "redirects home when no distributor is selected" do
|
||||
get :edit
|
||||
expect(response).to redirect_to root_path
|
||||
end
|
||||
|
||||
it "redirects to the shop when no order cycle is selected" do
|
||||
allow(controller).to receive(:current_distributor).and_return(distributor)
|
||||
get :edit
|
||||
expect(response).to redirect_to shop_path
|
||||
end
|
||||
|
||||
it "redirects to shopfront with message if order cycle is expired" do
|
||||
allow(controller).to receive(:current_distributor).and_return(distributor)
|
||||
expect(controller).to receive(:current_order_cycle).and_return(order_cycle).at_least(:once)
|
||||
expect(controller).to receive(:current_order).and_return(order).at_least(:once)
|
||||
expect(order_cycle).to receive(:closed?).and_return(true)
|
||||
expect(order).to receive(:empty!)
|
||||
expect(order).to receive(:set_order_cycle!).with(nil)
|
||||
|
||||
get :edit
|
||||
|
||||
expect(response).to redirect_to shop_url
|
||||
expect(flash[:info]).to eq 'The order cycle you\'ve selected has just closed. Please try again!'
|
||||
end
|
||||
|
||||
it "redirects home with message if hub is not ready for checkout" do
|
||||
allow(distributor).to receive(:ready_for_checkout?) { false }
|
||||
allow(order).to receive_messages(distributor: distributor, order_cycle: order_cycle)
|
||||
allow(controller).to receive(:current_order).and_return(order)
|
||||
|
||||
expect(order).to receive(:empty!)
|
||||
expect(order).to receive(:set_distribution!).with(nil, nil)
|
||||
|
||||
get :edit
|
||||
|
||||
expect(response).to redirect_to root_url
|
||||
expect(flash[:info]).to eq('The hub you have selected is temporarily closed for orders. Please try again later.')
|
||||
end
|
||||
|
||||
describe "#update" do
|
||||
let(:user) { order.user }
|
||||
let(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true) }
|
||||
let(:order_cycle) { create(:order_cycle, distributors: [distributor]) }
|
||||
let(:order) { create(:order, distributor: distributor, order_cycle: order_cycle) }
|
||||
let(:payment_method) { distributor.payment_methods.first }
|
||||
let(:shipping_method) { distributor.shipping_methods.first }
|
||||
|
||||
before do
|
||||
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)
|
||||
allow(controller).to receive(:current_order).and_return(order)
|
||||
allow(controller).to receive(:spree_current_user).and_return(user)
|
||||
|
||||
user.bill_address = create(:address)
|
||||
user.ship_address = create(:address)
|
||||
user.save!
|
||||
end
|
||||
|
||||
it "completes the order and redirects to the order confirmation page" do
|
||||
params = {
|
||||
"order" => {
|
||||
"bill_address_attributes" => order.bill_address.attributes,
|
||||
"default_bill_address" => false,
|
||||
"default_ship_address" => false,
|
||||
"email" => user.email,
|
||||
"payments_attributes" => [{ "payment_method_id" => payment_method.id }],
|
||||
"ship_address_attributes" => order.bill_address.attributes,
|
||||
"shipping_method_id" => shipping_method.id
|
||||
}
|
||||
}
|
||||
expect { post :update, params: params }.
|
||||
to change { Customer.count }.by(1)
|
||||
expect(order.completed?).to be true
|
||||
expect(response).to redirect_to order_path(order, order_token: order.token)
|
||||
end
|
||||
end
|
||||
|
||||
describe "running out of stock" do
|
||||
let(:order_cycle_distributed_variants) { double(:order_cycle_distributed_variants) }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:current_order).and_return(order)
|
||||
allow(order).to receive(:distributor).and_return(distributor)
|
||||
order.update(order_cycle: order_cycle)
|
||||
|
||||
allow(OrderCycleDistributedVariants).to receive(:new).and_return(order_cycle_distributed_variants)
|
||||
end
|
||||
|
||||
context "handling stock issues" 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
|
||||
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "building the order" do
|
||||
before do
|
||||
allow(controller).to receive(:current_distributor).and_return(distributor)
|
||||
allow(controller).to receive(:current_order_cycle).and_return(order_cycle)
|
||||
allow(controller).to receive(:current_order).and_return(order)
|
||||
end
|
||||
|
||||
it "set shipping_address_from_distributor when re-rendering edit" do
|
||||
expect(order.updater).to receive(:shipping_address_from_distributor)
|
||||
allow(order).to receive(:update).and_return false
|
||||
spree_post :update, format: :json, order: {}
|
||||
end
|
||||
|
||||
it "set shipping_address_from_distributor when the order state cannot be advanced" do
|
||||
expect(order.updater).to receive(:shipping_address_from_distributor)
|
||||
allow(order).to receive(:update).and_return true
|
||||
allow(order).to receive(:next).and_return false
|
||||
spree_post :update, format: :json, order: {}
|
||||
end
|
||||
|
||||
context "#update with shipping_method_id" do
|
||||
let(:test_shipping_method_id) { "111" }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:order_completion_reset)
|
||||
allow(order).to receive(:update).and_return true
|
||||
allow(controller).to receive(:current_order).and_return order
|
||||
|
||||
# make order workflow pass through delivery
|
||||
allow(order).to receive(:next).twice do
|
||||
if order.state == 'cart'
|
||||
order.update_column :state, 'delivery'
|
||||
else
|
||||
order.update_column :state, 'complete'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "does not fail to update" do
|
||||
expect(controller).to_not receive(:clear_ship_address)
|
||||
spree_post :update, order: { shipping_method_id: test_shipping_method_id }
|
||||
end
|
||||
|
||||
it "does not send shipping_method_id to the order model as an attribute" do
|
||||
expect(order).to receive(:update).with({})
|
||||
spree_post :update, order: { shipping_method_id: test_shipping_method_id }
|
||||
end
|
||||
|
||||
it "selects the shipping_method in the order" do
|
||||
expect(order).to receive(:select_shipping_method).with(test_shipping_method_id)
|
||||
spree_post :update, order: { shipping_method_id: test_shipping_method_id }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when completing the order' do
|
||||
before do
|
||||
order.state = 'complete'
|
||||
order.save!
|
||||
allow(order).to receive(:update).and_return(true)
|
||||
allow(order).to receive(:next).and_return(true)
|
||||
allow(order).to receive(:set_distributor!).and_return(true)
|
||||
end
|
||||
|
||||
it "sets the new order's token to the same as the old order" do
|
||||
order = controller.current_order(true)
|
||||
spree_post :update, order: {}
|
||||
expect(controller.current_order.token).to eq order.token
|
||||
end
|
||||
|
||||
it 'expires the current order' do
|
||||
allow(controller).to receive(:expire_current_order)
|
||||
put :update, params: { order: {} }
|
||||
expect(controller).to have_received(:expire_current_order)
|
||||
end
|
||||
|
||||
it 'sets the access_token of the session' do
|
||||
put :update, params: { order: {} }
|
||||
expect(session[:access_token]).to eq(controller.current_order.token)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#expire_current_order' do
|
||||
it 'empties the order_id of the session' do
|
||||
expect(session).to receive(:[]=).with(:order_id, nil)
|
||||
controller.send(:expire_current_order)
|
||||
end
|
||||
|
||||
it 'resets the @current_order ivar' do
|
||||
controller.send(:expire_current_order)
|
||||
expect(controller.instance_variable_get(:@current_order)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "via xhr" do
|
||||
before do
|
||||
allow(controller).to receive(:current_distributor).and_return(distributor)
|
||||
|
||||
allow(controller).to receive(:current_order_cycle).and_return(order_cycle)
|
||||
allow(controller).to receive(:current_order).and_return(order)
|
||||
end
|
||||
|
||||
it "returns errors and flash if order.update fails" do
|
||||
spree_post :update, format: :json, order: {}
|
||||
expect(response.status).to eq(400)
|
||||
expect(response.body).to eq({ errors: assigns[:order].errors,
|
||||
flash: { error: order.errors.full_messages.to_sentence } }.to_json)
|
||||
end
|
||||
|
||||
it "returns errors and flash if order.next fails" do
|
||||
allow(order).to receive(:update).and_return true
|
||||
allow(order).to receive(:next).and_return false
|
||||
spree_post :update, format: :json, order: {}
|
||||
expect(response.body).to eq({ errors: assigns[:order].errors,
|
||||
flash: { error: "Payment could not be processed, please check the details you entered" } }.to_json)
|
||||
end
|
||||
|
||||
it "returns order confirmation url on success" do
|
||||
expect(controller).to receive(:expire_current_order)
|
||||
expect(controller).to receive(:build_new_order).with(order.distributor, order.token)
|
||||
|
||||
allow(order).to receive(:update).and_return true
|
||||
allow(order).to receive(:state).and_return "complete"
|
||||
|
||||
spree_post :update, format: :json, order: {}
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.body).to eq({ path: order_path(order, order_token: order.token) }.to_json)
|
||||
end
|
||||
|
||||
it "returns an error on unexpected failure" do
|
||||
allow(order).to receive(:update).and_raise
|
||||
|
||||
spree_post :update, format: :json, order: {}
|
||||
expect(response.status).to eq(400)
|
||||
expect(response.body).to eq({ errors: {},
|
||||
flash: { error: 'The checkout failed. Please let us know so that we can process your order.' } }.to_json)
|
||||
end
|
||||
|
||||
it "returns a specific error on Spree::Core::GatewayError" do
|
||||
allow(order).to receive(:update).and_raise(Spree::Core::GatewayError.new("Gateway blow up"))
|
||||
spree_post :update, format: :json, order: {}
|
||||
|
||||
expect(response.status).to eq(400)
|
||||
flash_message = "There was a problem with your payment information: %s" % 'Gateway blow up'
|
||||
expect(json_response["flash"]["error"]).to eq flash_message
|
||||
end
|
||||
|
||||
describe "stale object handling" do
|
||||
it "retries when a stale object error is encountered" do
|
||||
expect(controller).to receive(:expire_current_order)
|
||||
expect(controller).to receive(:build_new_order).with(order.distributor, order.token)
|
||||
|
||||
allow(order).to receive(:update).and_return true
|
||||
allow(controller).to receive(:state_callback)
|
||||
|
||||
# The first time, raise a StaleObjectError. The second time, succeed.
|
||||
allow(order).to receive(:next).once.
|
||||
and_raise(ActiveRecord::StaleObjectError.new(Spree::Variant.new, 'update'))
|
||||
allow(order).to receive(:next).once do
|
||||
order.update_column :state, 'complete'
|
||||
true
|
||||
end
|
||||
|
||||
spree_post :update, format: :json, order: {}
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
it "tries a maximum of 3 times before giving up and returning an error" do
|
||||
allow(order).to receive(:update).and_return true
|
||||
allow(order).to receive(:next) {
|
||||
raise ActiveRecord::StaleObjectError.new(Spree::Variant.new, 'update')
|
||||
}
|
||||
|
||||
spree_post :update, format: :json, order: {}
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Payment redirects" do
|
||||
before do
|
||||
allow(controller).to receive(:current_distributor) { distributor }
|
||||
allow(controller).to receive(:current_order_cycle) { order_cycle }
|
||||
allow(controller).to receive(:current_order) { order }
|
||||
allow(order).to receive(:update) { true }
|
||||
allow(order).to receive(:state) { "payment" }
|
||||
end
|
||||
|
||||
describe "redirecting to an external payment gateway" do
|
||||
let(:payment_method) { create(:payment_method) }
|
||||
|
||||
it "should call Stripe redirect and redirect if a path is provided" do
|
||||
expect(Spree::PaymentMethod).to receive(:find).and_return(payment_method)
|
||||
expect(payment_method).to receive(:external_gateway?).and_return(true)
|
||||
expect(payment_method).to receive(:external_payment_url).and_return("test_path")
|
||||
|
||||
spree_post :update,
|
||||
order: { payments_attributes: [{ payment_method_id: payment_method.id }] }
|
||||
|
||||
expect(response.body).to eq({ path: "test_path" }.to_json)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#action_failed" do
|
||||
let(:restart_checkout) { instance_double(OrderCheckoutRestart, call: true) }
|
||||
|
||||
before do
|
||||
controller.instance_variable_set(:@order, order)
|
||||
allow(OrderCheckoutRestart).to receive(:new) { restart_checkout }
|
||||
allow(controller).to receive(:current_order) { order }
|
||||
end
|
||||
|
||||
it "set shipping_address_from_distributor and restarts the checkout" do
|
||||
expect(order.updater).to receive(:shipping_address_from_distributor)
|
||||
expect(restart_checkout).to receive(:call)
|
||||
expect(controller).to receive(:respond_to)
|
||||
|
||||
controller.send(:action_failed)
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user