@spree/docs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -0
- package/dist/api-reference/platform/authentication.md +38 -0
- package/dist/api-reference/store-api/authentication.md +188 -0
- package/dist/api-reference/store-api/errors.md +277 -0
- package/dist/api-reference/store-api/idempotency.md +129 -0
- package/dist/api-reference/store-api/introduction.md +34 -0
- package/dist/api-reference/store-api/localization.md +279 -0
- package/dist/api-reference/store-api/metadata.md +160 -0
- package/dist/api-reference/store-api/monetary-amounts.md +65 -0
- package/dist/api-reference/store-api/querying.md +399 -0
- package/dist/api-reference/store-api/rate-limitting.md +103 -0
- package/dist/api-reference/store-api/relations.md +185 -0
- package/dist/api-reference/storefront/authentication.md +88 -0
- package/dist/api-reference/tutorials/adyen-integration-guide-for-android.md +165 -0
- package/dist/api-reference/tutorials/adyen-integration-guide-for-ios.md +194 -0
- package/dist/api-reference/tutorials/quick-checkout-with-stripe.md +248 -0
- package/dist/api-reference/v2/fetching-multiple-resources.md +26 -0
- package/dist/api-reference/v2/filtering-and-sorting.md +53 -0
- package/dist/api-reference/v2/introduction.md +22 -0
- package/dist/api-reference/v2/pagination.md +37 -0
- package/dist/api-reference/webhooks-events.md +883 -0
- package/dist/developer/admin/admin.md +205 -0
- package/dist/developer/admin/authentication.md +59 -0
- package/dist/developer/admin/components.md +711 -0
- package/dist/developer/admin/custom-css.md +243 -0
- package/dist/developer/admin/custom-javascript.md +116 -0
- package/dist/developer/admin/extending-ui.md +1964 -0
- package/dist/developer/admin/form-builder.md +444 -0
- package/dist/developer/admin/helper-methods.md +531 -0
- package/dist/developer/admin/navigation.md +805 -0
- package/dist/developer/admin/tables.md +491 -0
- package/dist/developer/advanced/adding_spree_to_rails_app.md +106 -0
- package/dist/developer/cli/quickstart.md +137 -0
- package/dist/developer/contributing/creating-an-extension.md +258 -0
- package/dist/developer/contributing/developing-spree.md +339 -0
- package/dist/developer/contributing/quickstart.md +32 -0
- package/dist/developer/contributing/updating-extensions.md +67 -0
- package/dist/developer/core-concepts/addresses.md +265 -0
- package/dist/developer/core-concepts/adjustments.md +107 -0
- package/dist/developer/core-concepts/architecture.md +177 -0
- package/dist/developer/core-concepts/calculators.md +323 -0
- package/dist/developer/core-concepts/customers.md +230 -0
- package/dist/developer/core-concepts/events.md +624 -0
- package/dist/developer/core-concepts/imports-exports.md +698 -0
- package/dist/developer/core-concepts/inventory.md +191 -0
- package/dist/developer/core-concepts/markets.md +250 -0
- package/dist/developer/core-concepts/media.md +167 -0
- package/dist/developer/core-concepts/metafields.md +187 -0
- package/dist/developer/core-concepts/orders.md +328 -0
- package/dist/developer/core-concepts/payments.md +710 -0
- package/dist/developer/core-concepts/pricing.md +163 -0
- package/dist/developer/core-concepts/products.md +360 -0
- package/dist/developer/core-concepts/promotions.md +322 -0
- package/dist/developer/core-concepts/reports.md +206 -0
- package/dist/developer/core-concepts/search-filtering.md +237 -0
- package/dist/developer/core-concepts/shipments.md +212 -0
- package/dist/developer/core-concepts/slugs.md +111 -0
- package/dist/developer/core-concepts/staff-roles.md +123 -0
- package/dist/developer/core-concepts/store-credits-gift-cards.md +317 -0
- package/dist/developer/core-concepts/stores.md +117 -0
- package/dist/developer/core-concepts/taxes.md +135 -0
- package/dist/developer/core-concepts/translations.md +120 -0
- package/dist/developer/core-concepts/users.md +299 -0
- package/dist/developer/core-concepts/webhooks.md +378 -0
- package/dist/developer/create-spree-app/quickstart.md +158 -0
- package/dist/developer/customization/api.md +93 -0
- package/dist/developer/customization/authentication.md +88 -0
- package/dist/developer/customization/checkout.md +204 -0
- package/dist/developer/customization/configuration.md +55 -0
- package/dist/developer/customization/decorators.md +523 -0
- package/dist/developer/customization/dependencies.md +232 -0
- package/dist/developer/customization/emails.md +21 -0
- package/dist/developer/customization/extensions.md +92 -0
- package/dist/developer/customization/metadata.md +236 -0
- package/dist/developer/customization/model-preferences.md +130 -0
- package/dist/developer/customization/permissions.md +265 -0
- package/dist/developer/customization/quickstart.md +229 -0
- package/dist/developer/customization/routes.md +24 -0
- package/dist/developer/customization/v4/admin-panel.md +78 -0
- package/dist/developer/customization/v4/authentication.md +210 -0
- package/dist/developer/customization/v4/checkout.md +212 -0
- package/dist/developer/customization/v4/deface.md +251 -0
- package/dist/developer/customization/v4/images.md +86 -0
- package/dist/developer/customization/v4/storefront.md +450 -0
- package/dist/developer/deployment/assets.md +87 -0
- package/dist/developer/deployment/aws.md +335 -0
- package/dist/developer/deployment/caching.md +27 -0
- package/dist/developer/deployment/cdn.md +39 -0
- package/dist/developer/deployment/database.md +155 -0
- package/dist/developer/deployment/docker.md +128 -0
- package/dist/developer/deployment/emails.md +77 -0
- package/dist/developer/deployment/environment_variables.md +111 -0
- package/dist/developer/deployment/heroku.md +51 -0
- package/dist/developer/deployment/render.md +95 -0
- package/dist/developer/getting-started/quickstart.md +82 -0
- package/dist/developer/how-to/custom-payment-method.md +374 -0
- package/dist/developer/how-to/custom-promotion.md +373 -0
- package/dist/developer/how-to/custom-report.md +387 -0
- package/dist/developer/how-to/custom-search-provider.md +230 -0
- package/dist/developer/multi-store/quickstart.md +71 -0
- package/dist/developer/multi-store/setup.md +38 -0
- package/dist/developer/multi-tenant/configuration.md +41 -0
- package/dist/developer/multi-tenant/core-concepts.md +75 -0
- package/dist/developer/multi-tenant/installation.md +96 -0
- package/dist/developer/multi-tenant/quickstart.md +20 -0
- package/dist/developer/multi-vendor/installation.md +45 -0
- package/dist/developer/multi-vendor/quickstart.md +17 -0
- package/dist/developer/sdk/admin/quickstart.md +22 -0
- package/dist/developer/sdk/authentication.md +89 -0
- package/dist/developer/sdk/configuration.md +225 -0
- package/dist/developer/sdk/quickstart.md +82 -0
- package/dist/developer/sdk/store/account.md +67 -0
- package/dist/developer/sdk/store/cart-checkout.md +140 -0
- package/dist/developer/sdk/store/markets.md +151 -0
- package/dist/developer/sdk/store/payments.md +96 -0
- package/dist/developer/sdk/store/products.md +149 -0
- package/dist/developer/sdk/store/wishlists.md +52 -0
- package/dist/developer/security/pci_compliance.md +15 -0
- package/dist/developer/security/security_policy.md +68 -0
- package/dist/developer/storefront/blocks.md +285 -0
- package/dist/developer/storefront/custom-css.md +260 -0
- package/dist/developer/storefront/custom-javascript.md +166 -0
- package/dist/developer/storefront/helper-methods.md +1288 -0
- package/dist/developer/storefront/links.md +298 -0
- package/dist/developer/storefront/nextjs/architecture.md +150 -0
- package/dist/developer/storefront/nextjs/customization.md +141 -0
- package/dist/developer/storefront/nextjs/deployment.md +180 -0
- package/dist/developer/storefront/nextjs/quickstart.md +92 -0
- package/dist/developer/storefront/nextjs/spree-next-package.md +314 -0
- package/dist/developer/storefront/pages.md +163 -0
- package/dist/developer/storefront/sections.md +569 -0
- package/dist/developer/storefront/storefront.md +56 -0
- package/dist/developer/storefront/themes.md +161 -0
- package/dist/developer/tutorial/admin.md +134 -0
- package/dist/developer/tutorial/extending-models.md +380 -0
- package/dist/developer/tutorial/file-uploads.md +121 -0
- package/dist/developer/tutorial/introduction.md +33 -0
- package/dist/developer/tutorial/model.md +41 -0
- package/dist/developer/tutorial/page-builder.md +487 -0
- package/dist/developer/tutorial/rich-text.md +73 -0
- package/dist/developer/tutorial/seo.md +332 -0
- package/dist/developer/tutorial/storefront.md +352 -0
- package/dist/developer/tutorial/testing.md +558 -0
- package/dist/developer/upgrades/2.0-to-2.1.md +46 -0
- package/dist/developer/upgrades/2.1-to-2.2.md +59 -0
- package/dist/developer/upgrades/2.2-to-2.3.md +44 -0
- package/dist/developer/upgrades/2.3-to-2.4.md +42 -0
- package/dist/developer/upgrades/3.0-to-3.1.md +47 -0
- package/dist/developer/upgrades/3.1-to-3.2.md +34 -0
- package/dist/developer/upgrades/3.2-to-3.3.md +70 -0
- package/dist/developer/upgrades/3.3-to-3.4.md +36 -0
- package/dist/developer/upgrades/3.4-to-3.5.md +44 -0
- package/dist/developer/upgrades/3.5-to-3.6.md +40 -0
- package/dist/developer/upgrades/3.6-to-3.7.md +62 -0
- package/dist/developer/upgrades/3.7-to-4.0.md +152 -0
- package/dist/developer/upgrades/4.0-to-4.1.md +92 -0
- package/dist/developer/upgrades/4.1-to-4.2.md +109 -0
- package/dist/developer/upgrades/4.10-to-5.0.md +129 -0
- package/dist/developer/upgrades/4.2-to-4.3.md +100 -0
- package/dist/developer/upgrades/4.3-to-4.4.md +125 -0
- package/dist/developer/upgrades/4.4-to-4.5.md +94 -0
- package/dist/developer/upgrades/4.5-to-4.6.md +119 -0
- package/dist/developer/upgrades/4.6-to-4.7.md +39 -0
- package/dist/developer/upgrades/4.8-to-4.9.md +24 -0
- package/dist/developer/upgrades/4.9-to-4.10.md +24 -0
- package/dist/developer/upgrades/4.x-to-4.8.md +52 -0
- package/dist/developer/upgrades/5.0-to-5.1.md +28 -0
- package/dist/developer/upgrades/5.1-to-5.2.md +127 -0
- package/dist/developer/upgrades/5.2-to-5.3.md +338 -0
- package/dist/developer/upgrades/5.3-to-5.4.md +248 -0
- package/dist/developer/upgrades/quickstart.md +36 -0
- package/dist/integrations/analytics/google-analytics.md +64 -0
- package/dist/integrations/analytics/google-tag-manager.md +78 -0
- package/dist/integrations/integrations.md +39 -0
- package/dist/integrations/marketing/klaviyo.md +99 -0
- package/dist/integrations/payments/adyen.md +90 -0
- package/dist/integrations/payments/paypal.md +41 -0
- package/dist/integrations/payments/razorpay.md +45 -0
- package/dist/integrations/payments/stripe.md +109 -0
- package/dist/integrations/search/meilisearch.md +236 -0
- package/dist/integrations/sso-mfa-social-login/admin-dashboard.md +57 -0
- package/dist/integrations/sso-mfa-social-login/storefront.md +56 -0
- package/package.json +27 -0
|
@@ -0,0 +1,710 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Payments
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
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.
|
|
8
|
+
|
|
9
|
+
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.
|
|
10
|
+
|
|
11
|
+
The `Payment` model in Spree tracks payments against [Orders](orders). Payments relate to a `source` which indicates how the payment was made, and a `PaymentMethod`, indicating the processor used for this payment.
|
|
12
|
+
|
|
13
|
+
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.
|
|
14
|
+
|
|
15
|
+
### Payment Architecture Diagram
|
|
16
|
+
|
|
17
|
+
```mermaid
|
|
18
|
+
erDiagram
|
|
19
|
+
Payment {
|
|
20
|
+
string number
|
|
21
|
+
decimal amount
|
|
22
|
+
string state
|
|
23
|
+
string response_code
|
|
24
|
+
string avs_response
|
|
25
|
+
string source_type
|
|
26
|
+
string source_id
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
PaymentMethod {
|
|
30
|
+
string name
|
|
31
|
+
string type
|
|
32
|
+
string description
|
|
33
|
+
boolean active
|
|
34
|
+
string display_on
|
|
35
|
+
integer position
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
PaymentSession {
|
|
39
|
+
string status
|
|
40
|
+
decimal amount
|
|
41
|
+
string currency
|
|
42
|
+
string external_id
|
|
43
|
+
json external_data
|
|
44
|
+
datetime expires_at
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
PaymentSetupSession {
|
|
48
|
+
string status
|
|
49
|
+
string external_id
|
|
50
|
+
string external_client_secret
|
|
51
|
+
json external_data
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
PaymentSource {
|
|
55
|
+
string type
|
|
56
|
+
string gateway_payment_profile_id
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
CreditCard {
|
|
60
|
+
string last_digits
|
|
61
|
+
string cc_type
|
|
62
|
+
integer month
|
|
63
|
+
integer year
|
|
64
|
+
string name
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
GatewayCustomer {
|
|
68
|
+
string profile_id
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
StoreCredit {
|
|
72
|
+
decimal amount
|
|
73
|
+
decimal amount_used
|
|
74
|
+
decimal amount_authorized
|
|
75
|
+
string currency
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
Refund {
|
|
79
|
+
decimal amount
|
|
80
|
+
string reason
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
LogEntry {
|
|
84
|
+
text details
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
Order ||--o{ Payment : "has many"
|
|
88
|
+
Order ||--o{ PaymentSession : "has many"
|
|
89
|
+
Payment }o--|| PaymentMethod : "belongs to"
|
|
90
|
+
Payment }o--o| CreditCard : "source"
|
|
91
|
+
Payment }o--o| PaymentSource : "source"
|
|
92
|
+
Payment }o--o| StoreCredit : "source"
|
|
93
|
+
Payment ||--o| PaymentSession : "linked via response_code"
|
|
94
|
+
Payment ||--o{ LogEntry : "has many"
|
|
95
|
+
Payment ||--o{ Refund : "has many"
|
|
96
|
+
PaymentMethod ||--o{ PaymentSession : "has many"
|
|
97
|
+
PaymentMethod ||--o{ PaymentSetupSession : "has many"
|
|
98
|
+
PaymentMethod ||--o{ GatewayCustomer : "has many"
|
|
99
|
+
PaymentMethod }o--|| Store : "belongs to"
|
|
100
|
+
PaymentSetupSession }o--o| PaymentSource : "creates"
|
|
101
|
+
PaymentSetupSession }o--|| Customer : "belongs to"
|
|
102
|
+
GatewayCustomer }o--|| Customer : "belongs to"
|
|
103
|
+
CreditCard }o--|| Customer : "belongs to"
|
|
104
|
+
StoreCredit }o--|| Customer : "belongs to"
|
|
105
|
+
Refund }o--|| Payment : "belongs to"
|
|
106
|
+
Refund }o--|| Reimbursement : "belongs to"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Key relationships:**
|
|
110
|
+
- **Payment** tracks each payment attempt against an [Order](/developer/core-concepts/orders)
|
|
111
|
+
- **Payment Method** defines how payments are processed (Stripe, Adyen, PayPal, Check, etc.)
|
|
112
|
+
- **Payment Session** manages the gateway-side payment lifecycle (e.g., Stripe PaymentIntent, Adyen Session)
|
|
113
|
+
- **Payment Setup Session** manages saving payment methods for future use without an immediate charge (e.g., Stripe SetupIntent)
|
|
114
|
+
- **Source** is polymorphic - can be a Credit Card, Payment Source (for alternative methods like Klarna, iDEAL), or Store Credit
|
|
115
|
+
- **Gateway Customer** stores the provider-specific customer profile (e.g., Stripe Customer ID)
|
|
116
|
+
- **Log Entries** record gateway responses for debugging
|
|
117
|
+
- **Refunds** track money returned to customers
|
|
118
|
+
|
|
119
|
+
## Payment Methods
|
|
120
|
+
|
|
121
|
+
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.).
|
|
122
|
+
|
|
123
|
+
A `PaymentMethod` can have the following attributes:
|
|
124
|
+
|
|
125
|
+
| Attribute | Description | Example |
|
|
126
|
+
|--------------|-----------------------------------------------------------------------------------------------|------------------------|
|
|
127
|
+
| `type` | The payment method type | `Check` |
|
|
128
|
+
| `name` | The visible name for this payment method | `Check` |
|
|
129
|
+
| `description`| The description for this payment method | `Pay by check.` |
|
|
130
|
+
| `active` | Whether or not this payment method is active. Set it `false` to hide it in the Store API. | `true` |
|
|
131
|
+
| `display_on` | Determines where the payment method can be visible. Values can be `front` for Store API, `back` for admin panel only or `both` for both. | `both` |
|
|
132
|
+
| `position` | The position of the payment method in lists. Lower numbers appear first. | `1` |
|
|
133
|
+
|
|
134
|
+
> **INFO:** 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.
|
|
135
|
+
|
|
136
|
+
### Session-based vs Legacy Payment Methods
|
|
137
|
+
|
|
138
|
+
Payment methods indicate whether they use the modern session-based flow via the `session_required?` method:
|
|
139
|
+
|
|
140
|
+
| Method | Description | Default |
|
|
141
|
+
|--------|-------------|---------|
|
|
142
|
+
| `session_required?` | Returns `true` if this payment method requires a Payment Session for processing. | `false` |
|
|
143
|
+
| `setup_session_supported?` | Returns `true` if this payment method supports saving payment methods for future use (Payment Setup Sessions). | `false` |
|
|
144
|
+
| `payment_session_class` | Returns the STI subclass of `Spree::PaymentSession` for this gateway (e.g., `Spree::PaymentSessions::Stripe`). | `nil` |
|
|
145
|
+
| `payment_setup_session_class` | Returns the STI subclass of `Spree::PaymentSetupSession` for this gateway. | `nil` |
|
|
146
|
+
|
|
147
|
+
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.
|
|
148
|
+
|
|
149
|
+
### Non-Session Payment Methods (Manual/Offline)
|
|
150
|
+
|
|
151
|
+
Payment methods where `session_required?` returns `false` don't need a payment session. These are typically offline or manual payment methods such as:
|
|
152
|
+
|
|
153
|
+
- **Check** — built-in (`Spree::PaymentMethod::Check`)
|
|
154
|
+
- **Cash on Delivery** — customer pays upon delivery
|
|
155
|
+
- **Bank Transfer / Wire** — customer transfers money to a bank account
|
|
156
|
+
- **Purchase Order** — common in B2B, customer provides a PO number
|
|
157
|
+
|
|
158
|
+
For these methods, the Store API allows creating a payment directly without going through the payment session flow:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
const options = { spreeToken: cart.token }
|
|
162
|
+
|
|
163
|
+
// Create a payment for a non-session payment method
|
|
164
|
+
const payment = await client.carts.payments.create(cart.id, {
|
|
165
|
+
payment_method_id: 'pm_xyz789',
|
|
166
|
+
amount: '99.99', // Optional, defaults to order total minus store credits
|
|
167
|
+
metadata: { // Optional, write-only metadata (e.g. PO number)
|
|
168
|
+
purchase_order_number: 'PO-12345',
|
|
169
|
+
},
|
|
170
|
+
}, options)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The payment is created in `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.
|
|
174
|
+
|
|
175
|
+
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).
|
|
176
|
+
|
|
177
|
+
## Payment Flow
|
|
178
|
+
|
|
179
|
+
Spree supports two payment flows depending on the payment method type:
|
|
180
|
+
|
|
181
|
+
### Session-Based Flow (Stripe, Adyen, PayPal, etc.)
|
|
182
|
+
|
|
183
|
+
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).
|
|
184
|
+
|
|
185
|
+
```mermaid
|
|
186
|
+
sequenceDiagram
|
|
187
|
+
participant Frontend
|
|
188
|
+
participant Spree API
|
|
189
|
+
participant Payment Provider
|
|
190
|
+
|
|
191
|
+
rect rgb(240, 245, 255)
|
|
192
|
+
note right of Frontend: Phase 1: Payment Session
|
|
193
|
+
Frontend->>Spree API: Create Payment Session
|
|
194
|
+
Spree API->>Payment Provider: Create session (PaymentIntent/Session)
|
|
195
|
+
Payment Provider-->>Spree API: Session ID + client_secret
|
|
196
|
+
Spree API-->>Frontend: PaymentSession (pending)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
rect rgb(240, 255, 240)
|
|
200
|
+
note right of Frontend: Phase 2: Customer Pays
|
|
201
|
+
Frontend->>Payment Provider: Collect payment (using client_secret)
|
|
202
|
+
Note over Frontend,Payment Provider: 3DS / offsite redirect handled here
|
|
203
|
+
Payment Provider-->>Frontend: Payment result
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
rect rgb(255, 248, 240)
|
|
207
|
+
note right of Frontend: Phase 3: Complete Payment Session
|
|
208
|
+
Frontend->>Spree API: Complete Payment Session
|
|
209
|
+
Spree API->>Payment Provider: Verify payment status
|
|
210
|
+
Spree API->>Spree API: Create Payment record
|
|
211
|
+
Spree API-->>Frontend: PaymentSession (completed)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
rect rgb(255, 240, 245)
|
|
215
|
+
note right of Frontend: Phase 4: Complete Order
|
|
216
|
+
Frontend->>Spree API: POST /carts/:id/complete
|
|
217
|
+
Spree API->>Spree API: Validate & finalize order
|
|
218
|
+
Spree API-->>Frontend: Completed order
|
|
219
|
+
end
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**Step 1: Create Payment Session**
|
|
223
|
+
|
|
224
|
+
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.
|
|
225
|
+
|
|
226
|
+
> **INFO:** 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.
|
|
227
|
+
|
|
228
|
+
**Step 2: Customer pays on the frontend**
|
|
229
|
+
|
|
230
|
+
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.
|
|
231
|
+
|
|
232
|
+
**Step 3: Complete Payment Session**
|
|
233
|
+
|
|
234
|
+
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.
|
|
235
|
+
|
|
236
|
+
**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.
|
|
237
|
+
|
|
238
|
+
**Step 4: Complete Order**
|
|
239
|
+
|
|
240
|
+
The frontend calls `POST /carts/:id/complete` to finalize the order. Spree validates the order is ready (addresses, shipments, payment), advances through any remaining checkout states, and marks the order as complete.
|
|
241
|
+
|
|
242
|
+
This separation ensures the same flow works for all payment types — inline cards, offsite redirects, and wallet payments.
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
#### Offsite Payment Flow (CashApp, 3D Secure, Klarna, etc.)
|
|
246
|
+
|
|
247
|
+
For payment methods that redirect the customer away from your site, use an intermediate **confirm-payment** page:
|
|
248
|
+
|
|
249
|
+
```mermaid
|
|
250
|
+
sequenceDiagram
|
|
251
|
+
participant Frontend
|
|
252
|
+
participant Gateway
|
|
253
|
+
participant Spree API
|
|
254
|
+
|
|
255
|
+
Frontend->>Gateway: confirmPayment (redirects to gateway)
|
|
256
|
+
Gateway-->>Frontend: Redirect back to /confirm-payment/:id?session=...
|
|
257
|
+
Frontend->>Spree API: Complete Payment Session
|
|
258
|
+
Spree API-->>Frontend: Session completed
|
|
259
|
+
Frontend->>Spree API: POST /carts/:id/complete
|
|
260
|
+
Spree API-->>Frontend: Order completed
|
|
261
|
+
Frontend->>Frontend: Redirect to thank-you page
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
#### Webhook-Driven Completion (Browser Closed)
|
|
265
|
+
|
|
266
|
+
If the customer closes the browser after paying but before the frontend calls `complete`, Spree handles this via payment webhooks:
|
|
267
|
+
|
|
268
|
+
```mermaid
|
|
269
|
+
sequenceDiagram
|
|
270
|
+
participant Payment Provider
|
|
271
|
+
participant Spree API
|
|
272
|
+
|
|
273
|
+
Payment Provider->>Spree API: POST /api/v3/webhooks/payments/:pm_id
|
|
274
|
+
Spree API->>Spree API: Verify signature
|
|
275
|
+
Spree API->>Spree API: Enqueue HandleWebhookJob
|
|
276
|
+
Spree API-->>Payment Provider: 200 OK
|
|
277
|
+
|
|
278
|
+
note over Spree API: Async processing
|
|
279
|
+
Spree API->>Spree API: Create/update Payment
|
|
280
|
+
Spree API->>Spree API: Complete order via Carts::Complete
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
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.
|
|
284
|
+
|
|
285
|
+
### Direct Payment Flow (Check, Cash on Delivery, Bank Transfer, etc.)
|
|
286
|
+
|
|
287
|
+
Non-session payment methods use a simpler flow where a payment is created directly without involving an external payment provider:
|
|
288
|
+
|
|
289
|
+
```mermaid
|
|
290
|
+
sequenceDiagram
|
|
291
|
+
participant Frontend
|
|
292
|
+
participant Spree API
|
|
293
|
+
|
|
294
|
+
Frontend->>Spree API: GET /payment_methods
|
|
295
|
+
Spree API-->>Frontend: Payment methods (with session_required flag)
|
|
296
|
+
|
|
297
|
+
Frontend->>Spree API: POST /payments (payment_method_id)
|
|
298
|
+
Spree API->>Spree API: Create Payment (checkout state)
|
|
299
|
+
Spree API-->>Frontend: Payment created
|
|
300
|
+
|
|
301
|
+
Frontend->>Spree API: PATCH /orders/:id/complete
|
|
302
|
+
Spree API->>Spree API: process_payments! (authorize succeeds immediately)
|
|
303
|
+
Spree API->>Spree API: Payment → pending/completed
|
|
304
|
+
Spree API-->>Frontend: Order completed
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Step 1: List payment methods**
|
|
308
|
+
|
|
309
|
+
The frontend calls `GET /payment_methods` and checks the `session_required` flag on each method. Methods with `session_required: false` use this direct flow.
|
|
310
|
+
|
|
311
|
+
**Step 2: Create payment**
|
|
312
|
+
|
|
313
|
+
The frontend calls `POST /payments` with the `payment_method_id`. Spree creates a `Payment` record in `checkout` state. No external provider interaction is needed.
|
|
314
|
+
|
|
315
|
+
**Step 3: Complete order**
|
|
316
|
+
|
|
317
|
+
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.
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
### Payment Session
|
|
321
|
+
|
|
322
|
+
A `PaymentSession` (`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.
|
|
323
|
+
|
|
324
|
+
#### Attributes
|
|
325
|
+
|
|
326
|
+
| Attribute | Description | Example Value |
|
|
327
|
+
|-----------|-------------|---------------|
|
|
328
|
+
| `status` | Current session state: `pending`, `processing`, `completed`, `failed`, `canceled`, `expired` | `completed` |
|
|
329
|
+
| `amount` | The payment amount | `99.99` |
|
|
330
|
+
| `currency` | ISO currency code | `USD` |
|
|
331
|
+
| `external_id` | The provider-side session ID (e.g., Stripe PaymentIntent ID) | `pi_3ABC123` |
|
|
332
|
+
| `external_data` | Provider-specific data including `client_secret` for frontend SDK | `{"client_secret": "pi_3ABC_secret_xyz"}` |
|
|
333
|
+
| `customer_external_id` | The provider's customer ID | `cus_ABC123` |
|
|
334
|
+
| `expires_at` | When the session expires | `2025-01-01T12:00:00Z` |
|
|
335
|
+
|
|
336
|
+
#### States
|
|
337
|
+
|
|
338
|
+
```mermaid
|
|
339
|
+
stateDiagram-v2
|
|
340
|
+
[*] --> pending
|
|
341
|
+
pending --> processing
|
|
342
|
+
pending --> completed
|
|
343
|
+
pending --> failed
|
|
344
|
+
pending --> canceled
|
|
345
|
+
pending --> expired
|
|
346
|
+
processing --> completed
|
|
347
|
+
processing --> failed
|
|
348
|
+
processing --> canceled
|
|
349
|
+
processing --> expired
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
#### API
|
|
353
|
+
|
|
354
|
+
**Create a Payment Session:**
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
const options = { spreeToken: cart.token }
|
|
358
|
+
|
|
359
|
+
// Create a payment session for the selected payment method
|
|
360
|
+
const session = await client.carts.paymentSessions.create(cart.id, {
|
|
361
|
+
payment_method_id: 'pm_xyz789',
|
|
362
|
+
amount: '99.99', // Optional, defaults to order total
|
|
363
|
+
external_data: {}, // Optional, provider-specific data
|
|
364
|
+
}, options)
|
|
365
|
+
|
|
366
|
+
// The session contains provider-specific data for the frontend SDK
|
|
367
|
+
console.log(session.external_id) // e.g. 'pi_3ABC123' (Stripe PaymentIntent ID)
|
|
368
|
+
console.log(session.external_data.client_secret) // Use with Stripe.js or Adyen Drop-in
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
**Response shape (`StorePaymentSession`):**
|
|
372
|
+
|
|
373
|
+
```json
|
|
374
|
+
{
|
|
375
|
+
"id": "ps_abc123",
|
|
376
|
+
"status": "pending",
|
|
377
|
+
"amount": "99.99",
|
|
378
|
+
"currency": "USD",
|
|
379
|
+
"external_id": "pi_3ABC123",
|
|
380
|
+
"external_data": {
|
|
381
|
+
"client_secret": "pi_3ABC123_secret_xyz"
|
|
382
|
+
},
|
|
383
|
+
"customer_external_id": "cus_ABC123",
|
|
384
|
+
"expires_at": "2025-01-01T12:00:00Z",
|
|
385
|
+
"payment_method_id": "pm_xyz789",
|
|
386
|
+
"order_id": "or_ABC123",
|
|
387
|
+
"payment": null
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**Update a Payment Session** (e.g., after order total changes):
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
const updated = await client.carts.paymentSessions.update(
|
|
395
|
+
cart.id, session.id,
|
|
396
|
+
{ amount: '149.99' },
|
|
397
|
+
options
|
|
398
|
+
)
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
**Complete a Payment Session** (after customer confirms payment on the frontend):
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
const completed = await client.carts.paymentSessions.complete(
|
|
405
|
+
cart.id, session.id,
|
|
406
|
+
{ session_result: '...', external_data: {} },
|
|
407
|
+
options
|
|
408
|
+
)
|
|
409
|
+
console.log(completed.status) // 'completed'
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
> **WARNING:** Completing a payment session does **not** complete the order. You must call `POST /carts/:id/complete` separately after the session is completed. This separation prevents race conditions between the frontend and payment webhooks.
|
|
413
|
+
|
|
414
|
+
**Complete the Order** (after the payment session is completed):
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
const order = await client.carts.complete(cart.id, options)
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Payment Webhooks
|
|
421
|
+
|
|
422
|
+
Spree provides a generic webhook endpoint at `POST /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:
|
|
423
|
+
|
|
424
|
+
1. Verifies the webhook signature synchronously (returns `401` if invalid)
|
|
425
|
+
2. Enqueues a background job to process the event
|
|
426
|
+
3. Returns `200 OK` immediately
|
|
427
|
+
|
|
428
|
+
The background job creates/updates the Payment record, marks the session as completed, and completes the order if needed.
|
|
429
|
+
|
|
430
|
+
#### Gateway Interface
|
|
431
|
+
|
|
432
|
+
Gateway extensions implement `parse_webhook_event` to normalize provider-specific payloads:
|
|
433
|
+
|
|
434
|
+
```ruby
|
|
435
|
+
class MyGateway < Spree::Gateway
|
|
436
|
+
def parse_webhook_event(raw_body, headers)
|
|
437
|
+
# Verify signature — raise WebhookSignatureError if invalid
|
|
438
|
+
event = verify_signature(raw_body, headers)
|
|
439
|
+
|
|
440
|
+
case event.type
|
|
441
|
+
when 'payment.captured'
|
|
442
|
+
session = Spree::PaymentSession.find_by(external_id: event.payment_id)
|
|
443
|
+
{ action: :captured, payment_session: session }
|
|
444
|
+
when 'payment.failed'
|
|
445
|
+
session = Spree::PaymentSession.find_by(external_id: event.payment_id)
|
|
446
|
+
{ action: :failed, payment_session: session }
|
|
447
|
+
else
|
|
448
|
+
nil # unsupported event
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
end
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
Supported actions: `:captured`, `:authorized`, `:failed`, `:canceled`.
|
|
455
|
+
|
|
456
|
+
### Payment
|
|
457
|
+
|
|
458
|
+
Once a Payment Session is completed, Spree creates a `Payment` record (`Spree::Payment`) to track the result. The Payment is linked to the session via `response_code` matching the session's `external_id`.
|
|
459
|
+
|
|
460
|
+
#### Attributes
|
|
461
|
+
|
|
462
|
+
| Attribute | Description | Example Value |
|
|
463
|
+
|-------------------|-----------------------------------------------------------------------------|---------------------|
|
|
464
|
+
| `number` | A unique identifier for the payment. | `P123456789` |
|
|
465
|
+
| `source_type` | The type of source used for the payment. | `Spree::CreditCard` |
|
|
466
|
+
| `source_id` | The ID of the source used for the payment. | `1` |
|
|
467
|
+
| `amount` | The amount of the payment. | `99.99` |
|
|
468
|
+
| `payment_method_id`| The ID of the payment method used. | `2` |
|
|
469
|
+
| `state` | The current state of the payment (e.g., processing, completed, failed). | `completed` |
|
|
470
|
+
| `response_code` | The gateway transaction ID. Links to the Payment Session's `external_id`. | `pi_3ABC123` |
|
|
471
|
+
| `avs_response` | The address verification system response code. | `D` |
|
|
472
|
+
|
|
473
|
+
#### Payment States
|
|
474
|
+
|
|
475
|
+
After a Payment Session completes, the resulting Payment transitions through these states:
|
|
476
|
+
|
|
477
|
+
```mermaid
|
|
478
|
+
stateDiagram-v2
|
|
479
|
+
[*] --> checkout
|
|
480
|
+
checkout --> processing : process!
|
|
481
|
+
processing --> completed : purchase! succeeds
|
|
482
|
+
processing --> pending : authorize! succeeds
|
|
483
|
+
processing --> failed : payment declined
|
|
484
|
+
pending --> completed : capture!
|
|
485
|
+
pending --> void : void!
|
|
486
|
+
completed --> void : void!
|
|
487
|
+
checkout --> void : void!
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
| State | Description |
|
|
491
|
+
|-------|-------------|
|
|
492
|
+
| `checkout` | Initial state. The payment has been created but not yet processed. |
|
|
493
|
+
| `processing` | The payment is being processed (temporary - prevents double submission). |
|
|
494
|
+
| `pending` | The payment has been authorized but not yet captured. Awaiting manual or scheduled capture. |
|
|
495
|
+
| `failed` | The payment was rejected (e.g., card declined, insufficient funds). |
|
|
496
|
+
| `void` | The payment has been voided and should not be counted against the order. |
|
|
497
|
+
| `completed` | The payment has been captured. Only payments in this state count against the order total. |
|
|
498
|
+
|
|
499
|
+
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.
|
|
500
|
+
|
|
501
|
+
#### Order Payment States
|
|
502
|
+
|
|
503
|
+
Each payment update also recalculates the order's `payment_state`:
|
|
504
|
+
|
|
505
|
+
| Payment State | Description |
|
|
506
|
+
|---------------|-------------|
|
|
507
|
+
| `balance_due` | Payment is required for this order |
|
|
508
|
+
| `failed` | The last payment for the order failed |
|
|
509
|
+
| `credit_owed` | This order has been paid for in excess of its total |
|
|
510
|
+
| `paid` | This order has been paid for in full |
|
|
511
|
+
|
|
512
|
+
> **WARNING:** You may want to keep tabs on the number of orders with a `payment_state` of `failed`. A sudden increase could indicate a problem with your payment gateway and most likely a serious problem affecting customer satisfaction. Check the latest `log_entries` for the most recent payments if this is happening.
|
|
513
|
+
|
|
514
|
+
### Log Entries
|
|
515
|
+
|
|
516
|
+
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.
|
|
517
|
+
|
|
518
|
+
## Payment Sources
|
|
519
|
+
|
|
520
|
+
Payment sources represent the actual instrument used for a payment. They are created automatically when a Payment Session completes.
|
|
521
|
+
|
|
522
|
+
### Credit Cards (`Spree::CreditCard`)
|
|
523
|
+
|
|
524
|
+
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.
|
|
525
|
+
|
|
526
|
+
| Attribute | Description | Example Value |
|
|
527
|
+
|---------------------|-----------------------------------------------------------------------------------------------|------------------------|
|
|
528
|
+
| `month` | The month the credit card expires. | `6` |
|
|
529
|
+
| `year` | The year the credit card expires. | `2026` |
|
|
530
|
+
| `cc_type` | The type of credit card (e.g., visa, mastercard). | `visa` |
|
|
531
|
+
| `last_digits` | The last four digits of the credit card number. | `1234` |
|
|
532
|
+
| `name` | The name of the credit card holder. | `John Doe` |
|
|
533
|
+
| `gateway_payment_profile_id` | The payment token from the gateway (e.g., Stripe `pm_xxx`, Adyen `storedPaymentMethodId`). | `pm_1ABC123` |
|
|
534
|
+
|
|
535
|
+
> **NOTE:** 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.
|
|
536
|
+
|
|
537
|
+
### Payment Sources
|
|
538
|
+
|
|
539
|
+
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).
|
|
540
|
+
|
|
541
|
+
### Gateway Customers (`Spree::GatewayCustomer`)
|
|
542
|
+
|
|
543
|
+
Maps a Spree customer to their provider-specific customer profile. This enables features like saved payment methods, recurring billing, and customer-level fraud detection.
|
|
544
|
+
|
|
545
|
+
| Attribute | Description | Example Value |
|
|
546
|
+
|-----------|-------------|---------------|
|
|
547
|
+
| `profile_id` | The provider's customer ID (encrypted at rest) | `cus_ABC123` |
|
|
548
|
+
| `payment_method_id` | The gateway this customer belongs to | `1` |
|
|
549
|
+
| `user_id` | The Spree user | `42` |
|
|
550
|
+
|
|
551
|
+
Each customer has at most one `GatewayCustomer` record per payment method. The `profile_id` is encrypted using Active Record Encryption when available.
|
|
552
|
+
|
|
553
|
+
## Payment Setup Sessions
|
|
554
|
+
|
|
555
|
+
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.
|
|
556
|
+
|
|
557
|
+
### Use Cases
|
|
558
|
+
|
|
559
|
+
- Saving a credit card to the customer's account for faster future checkouts
|
|
560
|
+
- Authorizing a payment method for subscription billing
|
|
561
|
+
- Adding a payment method during account onboarding (before any purchase)
|
|
562
|
+
|
|
563
|
+
### How Payment Setup Sessions Work
|
|
564
|
+
|
|
565
|
+
```mermaid
|
|
566
|
+
sequenceDiagram
|
|
567
|
+
participant Frontend
|
|
568
|
+
participant Spree API
|
|
569
|
+
participant Payment Provider
|
|
570
|
+
|
|
571
|
+
Frontend->>Spree API: Create Payment Setup Session
|
|
572
|
+
Spree API->>Payment Provider: Create setup session (SetupIntent)
|
|
573
|
+
Payment Provider-->>Spree API: Session ID + client_secret
|
|
574
|
+
Spree API-->>Frontend: PaymentSetupSession (pending)
|
|
575
|
+
|
|
576
|
+
Frontend->>Payment Provider: Collect card details (using client_secret)
|
|
577
|
+
Note over Frontend,Payment Provider: 3DS verification if needed
|
|
578
|
+
|
|
579
|
+
Frontend->>Spree API: Complete Payment Setup Session
|
|
580
|
+
Spree API->>Payment Provider: Verify setup result
|
|
581
|
+
Spree API->>Spree API: Create PaymentSource (saved card)
|
|
582
|
+
Spree API-->>Frontend: Completed PaymentSetupSession
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
### Payment Setup Session Attributes
|
|
586
|
+
|
|
587
|
+
| Attribute | Description | Example Value |
|
|
588
|
+
|-----------|-------------|---------------|
|
|
589
|
+
| `status` | Current session state: `pending`, `processing`, `completed`, `failed`, `canceled`, `expired` | `completed` |
|
|
590
|
+
| `external_id` | The provider-side session ID (e.g., Stripe SetupIntent ID) | `seti_ABC123` |
|
|
591
|
+
| `external_client_secret` | Client secret for the frontend SDK | `seti_ABC123_secret_xyz` |
|
|
592
|
+
| `external_data` | Provider-specific data | `{}` |
|
|
593
|
+
| `payment_source_id` | The saved payment source created after completion | `ps_xyz789` |
|
|
594
|
+
| `payment_source_type` | The type of saved payment source | `Spree::CreditCard` |
|
|
595
|
+
|
|
596
|
+
### Payment Setup Session API
|
|
597
|
+
|
|
598
|
+
> **NOTE:** Payment Setup Sessions require customer authentication. The customer must be logged in.
|
|
599
|
+
|
|
600
|
+
**Create a Payment Setup Session:**
|
|
601
|
+
|
|
602
|
+
```typescript
|
|
603
|
+
const options = { token: jwtToken }
|
|
604
|
+
|
|
605
|
+
// Create a setup session for saving a payment method
|
|
606
|
+
const setupSession = await client.customer.paymentSetupSessions.create({
|
|
607
|
+
payment_method_id: 'pm_xyz789',
|
|
608
|
+
external_data: {},
|
|
609
|
+
}, options)
|
|
610
|
+
|
|
611
|
+
// Use the client secret with the gateway's frontend SDK
|
|
612
|
+
console.log(setupSession.external_client_secret) // e.g. 'seti_ABC123_secret_xyz'
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
**Response shape (`StorePaymentSetupSession`):**
|
|
616
|
+
|
|
617
|
+
```json
|
|
618
|
+
{
|
|
619
|
+
"id": "pss_abc123",
|
|
620
|
+
"status": "pending",
|
|
621
|
+
"external_id": "seti_ABC123",
|
|
622
|
+
"external_client_secret": "seti_ABC123_secret_xyz",
|
|
623
|
+
"external_data": {},
|
|
624
|
+
"payment_method_id": "pm_xyz789",
|
|
625
|
+
"payment_source_id": null,
|
|
626
|
+
"payment_source_type": null,
|
|
627
|
+
"customer_id": "usr_def456"
|
|
628
|
+
}
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
**Get a Payment Setup Session:**
|
|
632
|
+
|
|
633
|
+
```typescript
|
|
634
|
+
const session = await client.customer.paymentSetupSessions.get('pss_abc123', options)
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
**Complete a Payment Setup Session** (after the customer completes setup on the frontend using the gateway SDK and `external_client_secret`):
|
|
638
|
+
|
|
639
|
+
```typescript
|
|
640
|
+
const completed = await client.customer.paymentSetupSessions.complete(
|
|
641
|
+
'pss_abc123',
|
|
642
|
+
{ external_data: {} },
|
|
643
|
+
options
|
|
644
|
+
)
|
|
645
|
+
console.log(completed.status) // 'completed'
|
|
646
|
+
console.log(completed.payment_source_id) // e.g. 'ps_xyz789' - the saved payment method
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
Spree will verify the result with the provider and create a saved payment source (e.g., `Spree::CreditCard`) that can be used for future payments.
|
|
650
|
+
|
|
651
|
+
## Supported Gateways
|
|
652
|
+
|
|
653
|
+
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.
|
|
654
|
+
|
|
655
|
+
- [Stripe](/integrations/payments/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.
|
|
656
|
+
|
|
657
|
+
- [Adyen](/integrations/payments/adyen) — Adyen integration, supports all Adyen payment methods, including credit cards, bank transfers, Apple Pay, Google Pay, Klarna, and more.
|
|
658
|
+
|
|
659
|
+
- [PayPal](/integrations/payments/paypal) — Native PayPal integration, supports PayPal, PayPal Credit, and PayPal Pay Later.
|
|
660
|
+
|
|
661
|
+
## Payment Events
|
|
662
|
+
|
|
663
|
+
Spree publishes events throughout the payment lifecycle that you can subscribe to:
|
|
664
|
+
|
|
665
|
+
### Payment Events
|
|
666
|
+
| Event | Description |
|
|
667
|
+
|-------|-------------|
|
|
668
|
+
| `payment.paid` | Payment was completed |
|
|
669
|
+
| `order.paid` | Order is fully paid |
|
|
670
|
+
|
|
671
|
+
### Payment Session Events
|
|
672
|
+
| Event | Description |
|
|
673
|
+
|-------|-------------|
|
|
674
|
+
| `payment_session.processing` | Session is being processed |
|
|
675
|
+
| `payment_session.completed` | Session completed successfully |
|
|
676
|
+
| `payment_session.failed` | Session processing failed |
|
|
677
|
+
| `payment_session.canceled` | Session was canceled |
|
|
678
|
+
| `payment_session.expired` | Session expired |
|
|
679
|
+
|
|
680
|
+
### Payment Setup Session Events
|
|
681
|
+
| Event | Description |
|
|
682
|
+
|-------|-------------|
|
|
683
|
+
| `payment_setup_session.completed` | Setup completed, payment source saved |
|
|
684
|
+
| `payment_setup_session.failed` | Setup failed |
|
|
685
|
+
| `payment_setup_session.canceled` | Setup was canceled |
|
|
686
|
+
|
|
687
|
+
See [Events](/developer/core-concepts/events) for more details on subscribing to events.
|
|
688
|
+
|
|
689
|
+
## Related Documentation
|
|
690
|
+
|
|
691
|
+
- [Build a Custom Payment Method](/developer/how-to/custom-payment-method) - Step-by-step guide to creating your own payment gateway integration
|
|
692
|
+
- [Orders](/developer/core-concepts/orders) - Order management and state machine
|
|
693
|
+
- [Checkout Customization](/developer/customization/checkout) - Customizing the checkout flow
|
|
694
|
+
- [Events](/developer/core-concepts/events) - Subscribe to payment events
|
|
695
|
+
|
|
696
|
+
## Key Services
|
|
697
|
+
|
|
698
|
+
| Service | Description |
|
|
699
|
+
|---------|-------------|
|
|
700
|
+
| `Spree::Carts::Complete` | Completes the order — validates, processes payments (if not already done), advances state machine. Used by both the `POST /carts/:id/complete` endpoint and the webhook handler. |
|
|
701
|
+
| `Spree::Payments::HandleWebhook` | Processes a normalized webhook event — creates Payment, marks session completed, calls `Carts::Complete`. |
|
|
702
|
+
| `Spree::Payments::HandleWebhookJob` | Background job that wraps `HandleWebhook` — enqueued by the webhook controller for async processing. |
|
|
703
|
+
|
|
704
|
+
Both `Carts::Complete` and `HandleWebhook` are registered in `Spree::Dependencies` and can be replaced with custom implementations:
|
|
705
|
+
|
|
706
|
+
```ruby
|
|
707
|
+
# config/initializers/spree.rb
|
|
708
|
+
Spree::Dependencies.carts_complete_service = 'MyApp::CustomCartComplete'
|
|
709
|
+
Spree::Dependencies.payments_handle_webhook_service = 'MyApp::CustomWebhookHandler'
|
|
710
|
+
```
|