Files
openfoodnetwork/spec/lib/stripe/payment_intent_validator_spec.rb
David Rodríguez 27975252f5 Rename pm_card to payment_method_id
And also remove a couple of now unused `let`'s that were already using
this terminology.

Co-authored-by: David Cook <david@openfoodnetwork.org.au>
2025-10-27 10:52:36 +01:00

237 lines
9.1 KiB
Ruby

# frozen_string_literal: true
require 'spec_helper'
require 'stripe/payment_intent_validator'
RSpec.describe Stripe::PaymentIntentValidator do
# These are test payment method IDs, recognised by Stripe for testing purposes.
# See Cards By Brand > PaymentMethods here: https://docs.stripe.com/testing?testing-method=payment-methods#cards
# We do not send raw card numbers to the API.
self::VALID_NON_3DS_TEST_PAYMENT_METHODS = {
"pm_card_visa" => "Visa",
"pm_card_visa_debit" => "Visa (debit)",
"pm_card_mastercard" => "Mastercard",
"pm_card_mastercard_debit" => "Mastercard (debit)",
"pm_card_mastercard_prepaid" => "Mastercard (prepaid)",
"pm_card_amex" => "American Express",
"pm_card_discover" => "Discover",
"pm_card_diners" => "Diners Club",
"pm_card_jcb" => "JCB",
"pm_card_unionpay" => "UnionPay"
}.freeze
self::VALID_3DS_TEST_PAYMENT_METHODS = {
"pm_card_authenticationRequiredOnSetup" => "Authenticate unless set up",
"pm_card_authenticationRequired" => "Always authenticate",
"pm_card_authenticationRequiredSetupForOffSession" => "Already set up",
"pm_card_authenticationRequiredChargeDeclinedInsufficientFunds" => "Insufficient funds"
}.freeze
self::INVALID_TEST_PAYMENT_METHODS = {
"pm_card_visa_chargeDeclined" => {
type: "Generic decline",
message: "Your card was declined."
},
"pm_card_visa_chargeDeclinedInsufficientFunds" => {
type: "Insufficient funds decline",
message: "Your card has insufficient funds."
},
"pm_card_visa_chargeDeclinedLostCard" => {
type: "Lost card decline",
message: "Your card was declined."
},
"pm_card_visa_chargeDeclinedStolenCard" => {
type: "Stolen card decline",
message: "Your card was declined."
},
"pm_card_chargeDeclinedExpiredCard" => {
type: "Expired card decline",
message: "Your card has expired."
},
"pm_card_chargeDeclinedIncorrectCvc" => {
type: "Incorrect CVC decline",
message: "Your card's security code is incorrect."
},
"pm_card_chargeDeclinedProcessingError" => {
type: "Processing error decline",
message: "An error occurred while processing your card. Try again in a little bit."
},
"pm_card_visa_chargeDeclinedVelocityLimitExceeded" => {
type: "Exceeding velocity limit decline",
message: %(Your card was declined for making repeated attempts too frequently
or exceeding its amount limit.).squish
}
}.freeze
let(:payment_method) {
create(:stripe_sca_payment_method, distributor_ids: [create(:distributor_enterprise).id],
preferred_enterprise_id: create(:enterprise).id)
}
let(:year_valid) { Time.zone.now.year.next }
describe "#call", :vcr, :stripe_version do
let!(:user) { create(:user, email: "apple.customer@example.com") }
let(:credit_card) { create(:credit_card, user:) }
let(:payment) {
create(:payment, amount: payment_intent.amount, payment_method:,
response_code: payment_intent.id, source: credit_card)
}
let(:validator) { Stripe::PaymentIntentValidator.new(payment) }
describe "as a guest" do
context "when payment intent is valid" do
self::VALID_NON_3DS_TEST_PAYMENT_METHODS.each do |payment_method_id, card_type|
context "from #{card_type}" do
let!(:payment_intent) do
Stripe::PaymentIntent.create({
amount: 100,
currency: 'eur',
payment_method: payment_method_id,
payment_method_types: ['card'],
capture_method: 'manual',
})
end
before do
Stripe::PaymentIntent.confirm(payment_intent.id)
end
it "returns payment intent id" do
result = validator.call
expect(result.id).to eq(payment_intent.id)
end
it "captures the payment" do
expect(Stripe::PaymentIntent.retrieve(
payment_intent.id
).status).to eq("requires_capture")
Stripe::PaymentIntent.capture(payment_intent.id)
expect(Stripe::PaymentIntent.retrieve(
payment_intent.id
).status).to eq("succeeded")
end
end
end
self::VALID_3DS_TEST_PAYMENT_METHODS.each_key do |payment_method_id|
xcontext "from 3D card #{payment_method_id}" do
pending("updating spec to handle 3D2S cards")
it "is correctly handled"
end
end
end
context "when payment intent is invalid" do
self::INVALID_TEST_PAYMENT_METHODS.each do |payment_method_id, error|
context "from #{error[:type]}" do
let(:payment_intent) do
Stripe::PaymentIntent.create({
amount: 100,
currency: 'eur',
payment_method: payment_method_id,
payment_method_types: ['card'],
capture_method: 'manual',
})
end
it "raises Stripe error with payment intent last_payment_error as message" do
expect {
Stripe::PaymentIntent.confirm(payment_intent.id)
}.to raise_error Stripe::StripeError, error[:message]
end
end
end
end
end
describe "as a Stripe customer" do
context "when payment intent is valid" do
let(:customer_id) { customer.id }
let(:customer) do
Stripe::Customer.create({
name: 'Apple Customer',
email: 'applecustomer@example.com',
})
end
self::VALID_NON_3DS_TEST_PAYMENT_METHODS.each do |payment_method_id, card_type|
context "from #{card_type}" do
let!(:payment_intent) do
Stripe::PaymentIntent.create({
amount: 100,
currency: 'eur',
payment_method: payment_method_id,
payment_method_types: ['card'],
capture_method: 'manual',
customer: customer.id,
setup_future_usage: "off_session"
})
end
before do
Stripe::PaymentIntent.confirm(payment_intent.id)
end
it "returns payment intent id" do
result = validator.call
expect(result.id).to eq(payment_intent.id)
end
it "captures the payment" do
expect(Stripe::PaymentIntent.retrieve(
payment_intent.id
).status).to eq("requires_capture")
Stripe::PaymentIntent.capture(payment_intent.id)
expect(Stripe::PaymentIntent.retrieve(
payment_intent.id
).status).to eq("succeeded")
end
end
end
self::VALID_3DS_TEST_PAYMENT_METHODS.each_key do |payment_method_id|
xcontext "from 3D card #{payment_method_id}" do
pending("updating spec to handle 3D2S cards")
it "is correctly handled"
end
end
end
context "when payment intent is invalid" do
let(:customer_id) { customer.id }
let(:customer) do
Stripe::Customer.create({
name: 'Apple Customer',
email: 'applecustomer@example.com',
})
end
self::INVALID_TEST_PAYMENT_METHODS.each do |payment_method_id, error|
context "from #{error[:type]}" do
let(:payment_intent) do
Stripe::PaymentIntent.create({
amount: 100,
currency: 'eur',
payment_method: payment_method_id,
payment_method_types: ['card'],
capture_method: 'manual',
customer: customer.id,
setup_future_usage: "off_session"
})
end
it "raises Stripe error with payment intent last_payment_error as message" do
expect {
Stripe::PaymentIntent.confirm(payment_intent.id)
}.to raise_error Stripe::StripeError, error[:message]
end
end
end
end
end
end
end