Overview
Spree has a highly flexible payments model which allows multiple payment methods to be available during the checkout. The logic for processing payments is decoupled from orders, making it easy to define custom payment methods with their own processing logic. Payment methods typically represent a payment gateway. Gateways will process card payments, online bank transfers, buy-now-pay-later, wallet payments, and other types of payments. Spree also comes with a Check option for offline processing. ThePayment model in Spree tracks payments against Orders. Payments relate to a source which indicates how the payment was made, and a PaymentMethod, indicating the processor used for this payment.
When a payment is created, it is given a unique, 8-character identifier. This is used when sending the payment details to the payment processor. Without this identifier, some payment gateways mistakenly reported duplicate payments.
Payment Architecture Diagram
Key relationships:- Payment tracks each payment attempt against an Order
- Payment Method defines how payments are processed (Stripe, Adyen, PayPal, Check, etc.)
- Payment Session manages the gateway-side payment lifecycle (e.g., Stripe PaymentIntent, Adyen Session)
- Payment Setup Session manages saving payment methods for future use without an immediate charge (e.g., Stripe SetupIntent)
- Source is polymorphic - can be a Credit Card, Payment Source (for alternative methods like Klarna, iDEAL), or Store Credit
- Gateway Customer stores the provider-specific customer profile (e.g., Stripe Customer ID)
- Log Entries record gateway responses for debugging
- Refunds track money returned to customers
Payment Methods
Payment methods represent the different options a customer has for making a payment. Most sites will accept credit card payments through a payment gateway, but there are other options. Spree also comes with built-in support for a Check payment, which can be used to represent any offline payment. Gateway providers such as Stripe, Adyen, and PayPal provide a wide range of payment methods, including credit cards, bank transfers, buy-now-pay-later, and digital wallets (Apple Pay, Google Pay, etc.). APaymentMethod can have the following attributes:
Each payment method is associated to a Store, so you can decide which Payment Method will appear on which Store. This allows you to create different experiences for your customers in different countries.
Session-based vs Legacy Payment Methods
Payment methods indicate whether they use the modern session-based flow via thesession_required? method:
Modern gateways like Stripe and Adyen set
session_required? to true. The Store API serializer includes this as the session_required field so your frontend knows which flow to use.
Non-Session Payment Methods (Manual/Offline)
Payment methods wheresession_required? returns false don’t need a payment session. These are typically offline or manual payment methods such as:
- Check — built-in (
Spree::PaymentMethod::Check) - Cash on Delivery — customer pays upon delivery
- Bank Transfer / Wire — customer transfers money to a bank account
- Purchase Order — common in B2B, customer provides a PO number
checkout state. When the order transitions to complete, Spree calls process_payments! which runs the payment method’s authorize (or purchase if auto-capture is enabled). For manual payment methods like Check, this is a no-op that succeeds immediately — the payment moves to pending (or completed with auto-capture), allowing the order to complete.
The merchant can later capture or void the payment from the Admin Panel once the actual payment is received (e.g., check arrives, bank transfer clears, cash is collected on delivery).
Payment Flow
Spree supports two payment flows depending on the payment method type:Session-Based Flow (Stripe, Adyen, PayPal, etc.)
Modern payment gateways use a three-phase approach: first a Payment Session is created with the gateway, then the customer completes payment on the frontend, and finally the order is completed via an explicit API call. Payment processing and order completion are intentionally separated — this prevents race conditions and ensures reliable checkout regardless of payment method type (cards, wallets, offsite redirects).1
Create Payment Session
The frontend calls the API to create a Payment Session for a specific payment method and order. Spree calls the gateway to create a provider-side session (e.g., Stripe PaymentIntent, Adyen Session) and returns the session data including a
client_secret for the frontend SDK.The payment session should be created (or recreated) after the shipping method is selected, so the amount includes shipping costs. If the order total changes (e.g., customer selects a different shipping rate or applies a coupon), create a new payment session with the updated amount.
2
Customer pays on the frontend
The frontend uses the gateway’s JavaScript SDK (e.g., Stripe.js, Adyen Drop-in) with the
client_secret to securely collect payment details. Card data never touches your server — it goes directly to the payment provider, ensuring PCI compliance. If the payment requires 3D Secure authentication or redirects to an offsite gateway (CashApp, Klarna, etc.), the gateway SDK handles it automatically.3
Complete Payment Session
After the customer completes payment, the frontend calls the Complete Payment Session endpoint. Spree verifies the payment status with the gateway, creates a
Payment record, creates the appropriate payment source (Credit Card, wallet, etc.), and marks the session as completed.This step does NOT complete the order — it only handles payment processing. For wallet payments (Apple Pay, Google Pay), the gateway also patches the order’s billing address with data from the wallet at this stage.4
Complete Order
The frontend calls
POST /carts/:id/complete to finalize the order. Spree validates the order is ready (addresses, fulfillments, payment), advances through any remaining checkout states, and marks the order as complete.This separation ensures the same flow works for all payment types — inline cards, offsite redirects, and wallet payments.Offsite Payment Flow (CashApp, 3D Secure, Klarna, etc.)
For payment methods that redirect the customer away from your site, use an intermediate confirm-payment page:Webhook-Driven Completion (Browser Closed)
If the customer closes the browser after paying but before the frontend callscomplete, Spree handles this via payment webhooks:
Gateway extensions implement parse_webhook_event to normalize provider-specific payloads into a standard format. Spree core handles the rest — creating the payment record, completing the session, and finalizing the order.
Direct Payment Flow (Check, Cash on Delivery, Bank Transfer, etc.)
Non-session payment methods use a simpler flow where a payment is created directly without involving an external payment provider:1
List payment methods
The frontend reads the cart’s embedded
payment_methods (returned by GET /carts/:id) and checks the session_required flag on each method. Methods with session_required: false use this direct flow.2
Create payment
The frontend calls
POST /payments with the payment_method_id. Spree creates a Payment record in checkout state. No external provider interaction is needed.3
Complete order
The frontend completes the order. Spree’s
process_payments! runs the payment method’s authorize (or purchase with auto-capture). For manual methods like Check, these return an immediate success — no external service is called. The payment transitions to pending (without auto-capture) or completed (with auto-capture) and the order completes. The merchant can later capture pending payments from the Admin Panel once the physical payment is received.Payment Session
APaymentSession (Spree::PaymentSession) represents a server-side session with the payment gateway. It is the entry point for every payment attempt and holds the provider-specific data needed by the frontend SDK.
Attributes
States
API
Create a Payment Session:StorePaymentSession):
Payment Webhooks
Spree provides a generic webhook endpoint atPOST /api/v3/webhooks/payments/:payment_method_id that payment gateway extensions can use. When a payment provider sends a webhook (e.g., Stripe payment_intent.succeeded), Spree:
- Verifies the webhook signature synchronously (returns
401if invalid) - Enqueues a background job to process the event
- Returns
200 OKimmediately
Gateway Interface
Gateway extensions implementparse_webhook_event to normalize provider-specific payloads:
:captured, :authorized, :failed, :canceled.
Payment
Once a Payment Session is completed, Spree creates aPayment record (Spree::Payment) to track the result. The Payment is linked to the session via response_code matching the session’s external_id.
Attributes
Payment States
After a Payment Session completes, the resulting Payment transitions through these states:
With auto-capture enabled (default for most gateways), the Payment goes directly from
checkout → processing → completed. With manual capture, it stops at pending until an admin captures it.
Order Payment States
Each payment update also recalculates the order’spayment_state:
Log Entries
Responses from payment gateways are stored as log entries for debugging purposes. These can be viewed in the Admin Panel on the payment detail page.Payment Sources
Payment sources represent the actual instrument used for a payment. They are created automatically when a Payment Session completes.Credit Cards (Spree::CreditCard)
Stores non-sensitive credit card information. With modern gateways, the actual card data is tokenized by the provider - Spree only stores reference IDs and display information.
Spree never stores full credit card numbers. With modern gateways, card data is collected entirely by the gateway’s frontend SDK (e.g., Stripe.js, Adyen Drop-in) and never touches your server. Spree only stores the tokenized reference (
gateway_payment_profile_id) returned by the provider.Payment Sources
A generic payment source model for non-card payment methods such as digital wallets, bank transfers, and buy-now-pay-later services. Gateway integrations create subtypes for each payment method type (e.g., Klarna, Afterpay, iDEAL, Apple Pay, Google Pay, PayPal).Gateway Customers (Spree::GatewayCustomer)
Maps a Spree customer to their provider-specific customer profile. This enables features like saved payment methods, recurring billing, and customer-level fraud detection.
Each customer has at most one
GatewayCustomer record per payment method. The profile_id is encrypted using Active Record Encryption when available.
Payment Setup Sessions
Payment Setup Sessions (Spree::PaymentSetupSession) allow customers to save payment methods for future use without making an immediate payment. This maps to concepts like Stripe’s SetupIntent - a secure way to collect and tokenize payment details for later charges.
Use Cases
- Saving a credit card to the customer’s account for faster future checkouts
- Authorizing a payment method for subscription billing
- Adding a payment method during account onboarding (before any purchase)
How Payment Setup Sessions Work
Payment Setup Session Attributes
Payment Setup Session API
Payment Setup Sessions require customer authentication. The customer must be logged in.
StorePaymentSetupSession):
external_client_secret):
Spree::CreditCard) that can be used for future payments.
Supported Gateways
Spree team maintains several payment gateway integrations. All of these gateways are fully PCI compliant, using native gateway SDKs, meaning no sensitive payment data is stored or processed through Spree.Stripe
Stripe integration, supports all Stripe payment methods, including credit cards, bank transfers, Apple Pay, Google Pay, Klarna, Afterpay, and more. Also supports quick checkout.
Adyen
Adyen integration, supports all Adyen payment methods, including credit cards, bank transfers, Apple Pay, Google Pay, Klarna, and more.
PayPal
Native PayPal integration, supports PayPal, PayPal Credit, and PayPal Pay Later.
Payment Events
Spree publishes events throughout the payment lifecycle that you can subscribe to. For the delivered payload schemas of these events (e.g.payment.paid, payment_session.completed), see the Webhooks & Events reference:
Payment Events
Payment Session Events
Payment Setup Session Events
See Events for more details on subscribing to events.
Related Documentation
- Payments (Store SDK) - SDK how-to for payment sessions, payments, and setup sessions
- Build a Custom Payment Method - Step-by-step guide to creating your own payment gateway integration
- Orders - Order management and state machine
- Checkout Customization - Customizing the checkout flow
- Events - Subscribe to payment events
Key Services
Both
Carts::Complete and HandleWebhook are registered in Spree::Dependencies and can be replaced with custom implementations:

