Files
openfoodnetwork/spec/system/consumer/checkout/payment_spec.rb
2025-05-29 14:18:32 +10:00

390 lines
13 KiB
Ruby

# frozen_string_literal: true
require "system_helper"
RSpec.describe "As a consumer, I want to checkout my order" do
include ShopWorkflow
include CheckoutHelper
include FileHelper
include StripeHelper
include StripeStubs
include PaypalHelper
include AuthenticationHelper
let!(:zone) { create(:zone_with_member) }
let(:supplier) { create(:supplier_enterprise) }
let(:distributor) { create(:distributor_enterprise, charges_sales_tax: true) }
let(:product) {
create(:taxed_product, supplier_id: supplier.id, price: 10, zone:, tax_rate_amount: 0.1)
}
let(:variant) { product.variants.first }
let!(:order_cycle) {
create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor],
coordinator: create(:distributor_enterprise), variants: [variant])
}
let(:order) {
create(:order, order_cycle:, distributor:, bill_address_id: nil,
ship_address_id: nil, state: "cart",
line_items: [create(:line_item, variant:)])
}
let(:fee_tax_rate) { create(:tax_rate, amount: 0.10, zone:, included_in_price: true) }
let(:fee_tax_category) { create(:tax_category, tax_rates: [fee_tax_rate]) }
let(:enterprise_fee) { create(:enterprise_fee, amount: 1.23, tax_category: fee_tax_category) }
let(:free_shipping_with_required_address) {
create(:shipping_method, require_ship_address: true,
name: "A Free Shipping with required address")
}
let!(:payment_with_fee) {
create(:payment_method, distributors: [distributor],
name: "Payment with Fee", description: "Payment with fee",
calculator: Calculator::FlatRate.new(preferred_amount: 1.23))
}
before do
add_enterprise_fee enterprise_fee
pick_order order
distributor.shipping_methods.push(free_shipping_with_required_address)
end
context "as a logged in user" do
let(:user) { create(:user) }
before do
login_as(user)
visit checkout_path
end
context "payment step" do
let(:order) { create(:order_ready_for_payment, distributor:) }
context "with one payment method, with a fee" do
it "preselect the payment method if only one is available" do
visit checkout_step_path(:payment)
expect(page).to have_title "Checkout Payment - Open Food Network"
expect(page).to have_checked_field "Payment with Fee"
expect(page).to have_content "Payment with Fee $1.23"
end
end
context "with a transaction fee" do
before do
click_button "Next - Order summary"
end
shared_examples "displays the transaction fee" do |checkout_page|
it "on the #{checkout_page} page" do
expect(page).to have_content("Transaction fee #{with_currency(1.23)}")
if checkout_page.eql?("order confirmation")
expect(page).to have_content "Your order has been processed successfully"
end
end
end
it_behaves_like "displays the transaction fee", "order summary"
context "after completing the order" do
before do
click_on "Complete order"
end
it_behaves_like "displays the transaction fee", "order confirmation"
end
end
context "with more than one payment method" do
let!(:payment_method) { create(:payment_method, distributors: [distributor]) }
before do
visit checkout_step_path(:payment)
end
it "don't preselect the payment method if more than one is available" do
expect(page).to have_field "payment_method_#{payment_with_fee.id}", checked: false
expect(page).to have_field "payment_method_#{payment_method.id}", checked: false
end
it "requires choosing a payment method" do
click_on "Next - Order summary"
expect(page).to have_content "Select a payment method"
end
end
describe "vouchers" do
context "with no voucher available" do
before do
visit checkout_step_path(:payment)
end
it "doesn't show voucher input" do
expect(page).not_to have_content "Apply voucher"
end
end
context "with voucher available" do
let!(:voucher) do
create(:voucher_flat_rate, code: 'some_code', enterprise: distributor, amount: 15)
end
describe "adding voucher to the order" do
before do
visit checkout_step_path(:payment)
end
it "adds a voucher to the order" do
apply_voucher "some_code"
expect(page).to have_content "$15.00 Voucher"
expect(order.reload.voucher_adjustments.length).to eq(1)
end
context "when voucher covers more then the order total" do
before do
order.total = 6
order.save!
end
it "shows a warning message and doesn't require payment" do
apply_voucher "some_code"
expect(page).to have_content "$15.00 Voucher"
expect(page).to have_content(
"Note: if your order total is less than your voucher " \
"you may not be able to spend the remaining value."
)
expect(page).to have_content "No payment required"
click_button "Next - Order summary"
# Expect to be on the Order Summary page
expect(page).to have_content "Delivery details"
end
end
context "voucher doesn't exist" do
it "show an error" do
fill_in "Enter voucher code", with: "non_code"
click_button("Apply")
expect(page).to have_content("Voucher code invalid")
end
end
context "with a VINE voucher", :vcr, feature: :connected_apps do
let!(:vine_connected_app) {
ConnectedApps::Vine.create(
enterprise: distributor, data: { api_key: "1234568", secret: "my_secret" }
)
}
before do
allow(ENV).to receive(:fetch).and_call_original
allow(ENV).to receive(:fetch).with("VINE_API_URL").and_return("https://vine-staging.openfoodnetwork.org.au/api/v1")
end
it "adds a voucher to the order" do
apply_voucher "CI3922"
expect(page).to have_content "$5.00 Voucher"
expect(order.reload.voucher_adjustments.length).to eq(1)
expect(Vouchers::Vine.find_by(code: "CI3922",
enterprise: distributor)).not_to be_nil
end
context "with an invalid voucher" do
it "show an error" do
fill_in "Enter voucher code", with: "KM1891"
click_button("Apply")
expect(page).to have_content("There was an error while adding the voucher")
expect(Vouchers::Vine.find_by(code: "KM1891", enterprise: distributor)).to be_nil
end
end
end
end
describe "removing voucher from order" do
before do
add_voucher_to_order(voucher, order)
visit checkout_step_path(:payment)
accept_confirm "Are you sure you want to remove the voucher?" do
click_on "Remove code"
end
within '#voucher-section' do
expect(page).to have_button("Apply", disabled: true)
expect(page).to have_field "Enter voucher code" # Currently no confirmation msg
end
end
it "removes voucher" do
expect(page).not_to have_content "No payment required"
expect(order.voucher_adjustments.length).to eq(0)
end
it "can re-enter a voucher" do
apply_voucher "some_code"
expect(page).to have_content("$15.00 Voucher")
expect(order.reload.voucher_adjustments.length).to eq(1)
expect(page).to have_content "No payment required"
click_button "Next - Order summary"
# Expect to be on the Order Summary page
expect(page).to have_content "Delivery details"
end
it "can proceed with payment" do
click_button "Next - Order summary"
# Expect to be on the Order Summary page
expect(page).to have_content "Delivery details"
end
end
end
end
describe "choosing" do
shared_examples "different payment methods" do |pay_method|
context "checking out with #{pay_method}", if: pay_method.eql?("Stripe SCA") == false do
before do
visit checkout_step_path(:payment)
end
it "proceeds to the summary step and completes the order" do
choose pay_method.to_s
proceed_to_summary
place_order
expect(page).to have_content "Paying via: #{pay_method}"
expect(order.reload.state).to eq "complete"
end
end
context "for Stripe SCA", if: pay_method.eql?("Stripe SCA") do
around do |example|
with_stripe_setup { example.run }
end
before do
stripe_enable
visit checkout_step_path(:payment)
end
it "selects Stripe SCA and proceeds to the summary step" do
choose pay_method.to_s
fill_out_card_details
click_on "Next - Order summary"
proceed_to_summary
end
context "when saving card" do
it "selects Stripe SCA and proceeds to the summary step" do
stub_customers_post_request(email: order.user.email)
stub_payment_method_attach_request
choose pay_method.to_s
fill_out_card_details
check "Save card for future use"
click_on "Next - Order summary"
proceed_to_summary
# Verify card has been saved with correct stripe IDs
user_credit_card = order.reload.user.credit_cards.first
expect(user_credit_card.gateway_payment_profile_id).to eq "pm_123"
expect(user_credit_card.gateway_customer_profile_id).to eq "cus_A123"
end
end
end
end
describe "shared examples" do
let!(:cash) { create(:payment_method, distributors: [distributor], name: "Cash") }
context "Cash" do
it_behaves_like "different payment methods", "Cash"
end
context "Paypal" do
let!(:paypal) do
Spree::Gateway::PayPalExpress.create!(
name: "Paypal",
environment: "test",
distributor_ids: [distributor.id]
)
end
before do
stub_paypal_response(
success: true,
redirect: payment_gateways_confirm_paypal_path(
payment_method_id: paypal.id, token: "t123", PayerID: 'p123'
)
)
stub_paypal_confirm
end
it_behaves_like "different payment methods", "Paypal"
end
context "Stripe SCA" do
let!(:stripe_account) { create(:stripe_account, enterprise: distributor) }
let!(:stripe_sca_payment_method) {
create(:stripe_sca_payment_method, distributors: [distributor], name: "Stripe SCA")
}
it_behaves_like "different payment methods", "Stripe SCA"
end
end
end
describe "hiding a payment method with a default rule" do
let!(:tagged_customer) { create(:customer, user:, enterprise: distributor) }
let!(:hidden_method) {
create(:payment_method, distributors: [distributor], name: "Hidden", tag_list: "hide_pm")
}
before do
create(:filter_payment_methods_tag_rule,
enterprise: distributor,
is_default: true,
preferred_payment_method_tags: "hide_pm",
preferred_matched_payment_methods_visibility: 'hidden')
visit checkout_step_path(:payment)
end
context "with no exceptions set to a customer" do
it "hides the payment method" do
expect(page).not_to have_content hidden_method.name
end
end
context "with an exception set to a customer" do
before do
create(:filter_payment_methods_tag_rule,
enterprise: distributor,
preferred_customer_tags: "show_pm",
preferred_payment_method_tags: "hide_pm",
preferred_matched_payment_methods_visibility: 'visible')
tagged_customer.update_attribute(:tag_list, "show_pm")
visit checkout_step_path(:payment)
end
it "displays the payment method" do
expect(page).to have_content hidden_method.name
end
end
end
end
end
def add_voucher_to_order(voucher, order)
voucher.create_adjustment(voucher.code, order)
OrderManagement::Order::Updater.new(order).update_voucher
end
end