Overview
This guide walks you through building a custom payment method integration for Spree. By the end, you’ll have a fully functional payment gateway that:- Appears as a payment option during checkout
- Uses Payment Sessions for PCI-compliant payment collection
- Supports 3D Secure and alternative payment methods
- Handles webhooks for reliable payment confirmation
- Optionally supports saving payment methods for future use (Payment Setup Sessions)
Step 1: Create the Payment Method Model
Create a new model inheriting fromSpree::PaymentMethod. This is the central class that defines how your gateway processes payments.
app/models/my_gateway.rb
Step 2: Register the Payment Method
Add your gateway to the list of available payment methods so it appears in the admin panel:config/initializers/spree.rb
Step 3: Add Payment Session Support
Payment Sessions are the modern, PCI-compliant way to handle payments. Your gateway creates a session with the provider, the frontend collects payment details using the provider’s SDK, and Spree records the result.Define the STI Subclass
Create a Payment Session subclass for your gateway. This uses Single Table Inheritance (STI) on thespree_payment_sessions table:
app/models/spree/payment_sessions/my_gateway.rb
Implement Session Methods on the Gateway
Override the key methods on yourPaymentMethod subclass:
app/models/my_gateway.rb
How the Frontend Uses It
The frontend creates a session, then uses the provider’s SDK to collect payment:Important: Always create the payment session after shipping is selected. If the order total changes (shipping rate change, coupon applied), create a new payment session with the updated amount. The
complete call in step 5 only handles payment — step 6 finalizes the order.Step 4: Handle Webhooks (Recommended)
Webhooks ensure payments are captured even if the customer closes their browser after paying. Spree provides a generic webhook endpoint atPOST /api/v3/webhooks/payments/:payment_method_id — you don’t need to create your own controller or routes.
Your gateway just needs to implement parse_webhook_event to normalize the provider-specific payload:
app/models/my_gateway.rb
- Verifies the signature synchronously (returns
401ifWebhookSignatureErroris raised) - Enqueues a background job (
Spree::Payments::HandleWebhookJob) for async processing - Returns
200 OKimmediately to prevent provider retries - The job creates/updates the
Paymentrecord, marks the session completed, and completes the order
payment_method.webhook_url — register this with your provider during setup.
Supported Webhook Actions
Step 5: Control Payment Method Visibility (Optional)
Override visibility methods to control where and when your payment method appears:app/models/my_gateway.rb
Step 6: Add Payment Setup Session Support (Optional)
If your provider supports saving payment methods for future use (like Stripe’s SetupIntent), you can add Payment Setup Session support:app/models/my_gateway.rb
app/models/spree/payment_setup_sessions/my_gateway.rb
Gateway Options Reference
For every gateway action (authorize, purchase, capture, void, credit), Spree passes a hash of gateway options:Related Documentation
- Payments - Payment architecture and core concepts
- Checkout Customization - Customizing the checkout flow
- Events - Subscribe to payment events
- Stripe Integration - Reference implementation using Stripe
- Adyen Integration - Reference implementation using Adyen

