Files
openfoodnetwork/spec/controllers/payment_gateways/stripe_controller_spec.rb
Ahmed Ejaz 243190491b Fix specs
2025-10-31 04:40:44 +05:00

348 lines
12 KiB
Ruby

# frozen_string_literal: true
require 'spec_helper'
RSpec.describe PaymentGateways::StripeController 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:, order_cycle:) }
let(:exchange) { order_cycle.exchanges.to_enterprises(distributor).outgoing.first }
let(:order_cycle_distributed_variants) {
instance_double(OrderCycles::DistributedVariantsService)
}
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:,
response_code: "pi_123"
)
}
before do
Stripe.api_key = "sk_test_12345"
stub_payment_intent_get_request
stub_successful_capture_request(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).not_to receive(:empty!)
expect(order).not_to receive(:assign_order_cycle!).with(nil)
get :confirm, params: { payment_intent: "pi_123" }
message = "The order cycle you've selected has just closed. " \
"Please contact us to complete your order ##{order.number}!"
expect(response).to redirect_to shops_url
expect(flash[:info]).to eq(message)
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 "when items run out of stock during order completion" do
before do
mock_order_check_stock_service(order)
end
it "redirects to cart when some items are out of stock" do
allow(controller).to receive(:valid_payment_intent?).and_return true
get :confirm, params: { payment_intent: "pi_123" }
expect(response).to redirect_to checkout_step_path(step: :details)
end
context "handling pending payments" do
let!(:payment) { create(:payment, state: "pending", amount: order.total, order:) }
let!(:transaction_fee) {
create(:adjustment, state: "open", amount: 10, order:, adjustable: payment)
}
before do
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 checkout_step_path(step: :details)
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:, distributor: customer.enterprise,
state: "payment")
}
let(:payment_method) { create(:stripe_sca_payment_method) }
let!(:payment) {
create(
:payment,
payment_method:,
redirect_auth_url: "https://stripe.com/redirect",
response_code: "pi_123",
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: }
expect(response).to redirect_to order_path(order)
payment.reload
expect(payment.state).to eq("completed")
expect(payment.redirect_auth_url).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: }
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: }
expect(response).to redirect_to order_path(order)
payment.reload
expect(payment.state).to eq("completed")
expect(payment.redirect_auth_url).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: }
expect(response).to redirect_to order_path(order)
payment.reload
expect(payment.state).to eq("completed")
expect(payment.redirect_auth_url).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: }
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.redirect_auth_url).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(Orders::FindPaymentService, last_payment: payment) }
before do
allow(payment).to receive(:response_code).and_return("invalid")
allow(Orders::FindPaymentService).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: }
expect(response).to redirect_to order_path(order)
expect(flash[:error]).to eq("The payment could not be processed. ")
payment.reload
expect(payment.redirect_auth_url).to eq("https://stripe.com/redirect")
expect(payment.state).to eq("requires_authorization")
end
end
end
end
def mock_order_check_stock_service(order)
check_stock_service_mock = instance_double(Orders::CheckStockService)
expect(Orders::CheckStockService).to receive(:new).and_return(check_stock_service_mock)
expect(check_stock_service_mock).to receive(:sufficient_stock?).and_return(false).twice
expect(check_stock_service_mock).to receive(:update_line_items).and_return(order.variants)
end
end