Files
openfoodnetwork/spec/controllers/payment_gateways/stripe_controller_spec.rb

332 lines
12 KiB
Ruby

# frozen_string_literal: true
require 'spec_helper'
module PaymentGateways
describe StripeController, type: :controller do
include StripeStubs
let!(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true) }
let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor]) }
let!(:order) { create(:order_with_totals, distributor: distributor, order_cycle: order_cycle) }
let(:exchange) { order_cycle.exchanges.to_enterprises(distributor).outgoing.first }
let(:order_cycle_distributed_variants) { instance_double(OrderCycleDistributedVariants) }
before do
exchange.variants << order.line_items.first.variant
allow(controller).to receive(:current_order).and_return(order)
end
describe "#confirm" do
context "when the order is in payment state and a stripe payment intent is provided" do
let(:user) { order.user }
let(:payment_method) { create(:stripe_sca_payment_method) }
let(:payment) {
create(
:payment,
amount: order.total,
state: "requires_authorization",
payment_method: payment_method,
response_code: "pi_123"
)
}
before do
Stripe.api_key = "sk_test_12345"
stub_payment_intent_get_request
stub_successful_capture_request(order: order)
allow(controller).to receive(:spree_current_user).and_return(user)
user.bill_address = create(:address)
user.ship_address = create(:address)
user.save!
order.update_attribute :state, "payment"
order.payments << payment
order.update_attribute :state, "confirmation"
end
it "creates a customer record" do
order.update_columns(customer_id: nil)
Customer.delete_all
expect {
get :confirm, params: { payment_intent: "pi_123" }
}.to change { Customer.count }.by(1)
end
context "when the order cycle has closed" do
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)
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 :confirm, params: { payment_intent: "pi_123" }
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
end
it "completes the order and redirects to the order confirmation page" do
expect(controller).to receive(:processing_succeeded).and_call_original
expect(controller).to receive(:order_completion_reset).and_call_original
get :confirm, params: { payment_intent: "pi_123" }
expect(order.completed?).to be true
expect(response).to redirect_to order_path(order, order_token: order.token)
expect(flash[:notice]).to eq 'Your order has been processed successfully'
end
context 'when order completion fails' do
it "redirects to checkout state path" do
expect(controller).to receive(:process_payment_completion!).and_call_original
allow(order).to receive(:process_payments!).and_return(false)
expect(
get(:confirm, params: { payment_intent: "pi_123" })
).to redirect_to checkout_step_path(step: :payment)
expect(flash[:error]).to eq(
'Payment could not be processed, please check the details you entered'
)
end
end
end
context "when the order is not in payment state" do
before { order.update_columns(state: "cart", completed_at: nil) }
it "fails" do
expect(controller).to receive(:processing_failed).and_call_original
get :confirm, params: { payment_intent: "pi_123" }
expect(order.completed?).to be false
expect(response).to redirect_to checkout_step_path(step: :details)
expect(flash[:error]).to eq "Payment could not be processed, \
please check the details you entered"
end
end
context "when a valid payment intent is not provided" do
it "fails" do
expect(controller).to receive(:processing_failed).and_call_original
get :confirm, params: { payment_intent: "pi_666" }
expect(order.completed?).to be false
expect(response).to redirect_to checkout_step_path(step: :details)
expect(flash[:error]).to eq "Payment could not be processed, \
please check the details you entered"
end
end
context "when items in the cart are invalid" do
before do
allow(order_cycle_distributed_variants).
to receive(:distributes_order_variants?).and_return(false)
end
it "fails" do
expect(controller).to receive(:processing_failed).and_call_original
get :confirm, params: { payment_intent: "pi_123" }
expect(order.completed?).to be false
expect(response).to redirect_to checkout_step_path(step: :details)
expect(flash[:error]).to eq "Payment could not be processed, \
please check the details you entered"
end
end
context "items running out of stock during order completion" do
it "redirects to cart when some items are out of stock" do
allow(controller).to receive(:valid_payment_intent?).and_return true
allow(order).to receive_message_chain(:insufficient_stock_lines, :empty?).and_return false
get :confirm, params: { payment_intent: "pi_123" }
expect(response).to redirect_to cart_path
end
context "handling pending payments" do
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?) { false }
order.save
allow(order).to receive_message_chain(:payments, :completed) { [] }
allow(order).to receive_message_chain(:payments, :incomplete) { [payment] }
allow(controller).to receive(:valid_payment_intent?) { true }
allow(controller).to receive(:last_payment) { 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_transaction!).and_call_original
get :confirm, params: { payment_intent: "pi_123" }
expect(response).to redirect_to cart_path
expect(flash[:notice]).to eq "Payment cancelled: the checkout could not be \
completed due to stock issues."
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
end
describe "#authorize" do
let(:customer) { create(:customer) }
let(:order) {
create(:order_with_totals, customer: customer, distributor: customer.enterprise,
state: "payment")
}
let(:payment_method) { create(:stripe_sca_payment_method) }
let!(:payment) {
create(
:payment,
payment_method: payment_method,
cvv_response_message: "https://stripe.com/redirect",
response_code: "pi_123",
order: order,
state: "requires_authorization"
)
}
before do
allow(controller).to receive(:spree_current_user) { current_user }
end
context "after returning from Stripe to authorize a payment" do
let(:current_user) { order.user }
context "with a valid payment intent" do
let(:payment_intent) { "pi_123" }
let(:payment_intent_response) { double(id: "pi_123", status: "requires_capture") }
before do
allow(Stripe::PaymentIntentValidator)
.to receive_message_chain(:new, :call).and_return(payment_intent_response)
allow(Spree::Order).to receive(:find_by) { order }
end
context "when the order is in payment state" do
it "completes the payment" do
expect(order).to receive(:process_payments!) do
payment.complete!
end
get :authorize, params: { order_number: order.number, payment_intent: payment_intent }
expect(response).to redirect_to order_path(order)
payment.reload
expect(payment.state).to eq("completed")
expect(payment.cvv_response_message).to be nil
end
it "moves the order state to completed" do
expect(order).to receive(:process_payments!) do
payment.complete!
end
get :authorize, params: { order_number: order.number, payment_intent: payment_intent }
expect(order.reload.state).to eq "complete"
end
end
context "when the order is already completed" do
before do
order.update_columns(state: "complete")
end
it "should still process the payment" do
expect(order).to receive(:process_payments!) do
payment.complete!
end
get :authorize, params: { order_number: order.number, payment_intent: payment_intent }
expect(response).to redirect_to order_path(order)
payment.reload
expect(payment.state).to eq("completed")
expect(payment.cvv_response_message).to be nil
end
end
context "when the order cycle has closed" do
it "should still authorize the payment successfully" do
expect(order).to receive(:process_payments!) do
payment.complete!
end
get :authorize, params: { order_number: order.number, payment_intent: payment_intent }
expect(response).to redirect_to order_path(order)
payment.reload
expect(payment.state).to eq("completed")
expect(payment.cvv_response_message).to be nil
end
end
end
context "when the payment intent response has errors" do
let(:payment_intent) { "pi_123" }
before do
allow(Stripe::PaymentIntentValidator)
.to receive_message_chain(:new, :call).and_raise(Stripe::StripeError, "error message")
end
it "does not complete the payment" do
get :authorize, params: { order_number: order.number, payment_intent: payment_intent }
expect(response).to redirect_to order_path(order)
expect(flash[:error]).to eq("The payment could not be processed. error message")
payment.reload
expect(payment.cvv_response_message).to be nil
expect(payment.state).to eq("failed")
end
end
context "with an invalid last payment" do
let(:payment_intent) { "valid" }
let(:finder) { instance_double(OrderPaymentFinder, last_payment: payment) }
before do
allow(payment).to receive(:response_code).and_return("invalid")
allow(OrderPaymentFinder).to receive(:new).with(order).and_return(finder)
allow(Stripe::PaymentIntentValidator)
.to receive_message_chain(:new, :call).and_return(payment_intent)
stub_payment_intent_get_request(payment_intent_id: "valid")
end
it "does not complete the payment" do
get :authorize, params: { order_number: order.number, payment_intent: payment_intent }
expect(response).to redirect_to order_path(order)
expect(flash[:error]).to eq("The payment could not be processed. ")
payment.reload
expect(payment.cvv_response_message).to eq("https://stripe.com/redirect")
expect(payment.state).to eq("requires_authorization")
end
end
end
end
end
end