Files
openfoodnetwork/spec/controllers/payment_gateways/stripe_controller_spec.rb
Maikel Linke 56667f6142 Simplify test setup with enabled features
If a feature is activated or not depends on the database which is reset
after each test scenario. So enabling a feature doesn't leak into other
scenarios.

Just enabling the feature is less code and more realistic than mocking a
method call.
2022-10-27 15:32:05 +11:00

316 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
end
shared_examples "successful order completion" do
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 I18n.t(:order_processed_successfully)
end
end
include_examples "successful order completion"
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 I18n.t('order_cycle_closed')
end
end
context "using split checkout" do
before do
Flipper.enable(:split_checkout)
order.update_attribute :state, "confirmation"
end
include_examples "successful order completion"
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_path
expect(flash[:error]).to eq I18n.t(:payment_processing_failed)
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_path
expect(flash[:error]).to eq I18n.t(:payment_processing_failed)
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_path
expect(flash[:error]).to eq I18n.t(:payment_processing_failed)
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 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
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
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("#{I18n.t('payment_could_not_process')}. 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("#{I18n.t('payment_could_not_process')}. ")
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