@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.
Files changed (183) hide show
  1. package/README.md +54 -0
  2. package/dist/api-reference/platform/authentication.md +38 -0
  3. package/dist/api-reference/store-api/authentication.md +188 -0
  4. package/dist/api-reference/store-api/errors.md +277 -0
  5. package/dist/api-reference/store-api/idempotency.md +129 -0
  6. package/dist/api-reference/store-api/introduction.md +34 -0
  7. package/dist/api-reference/store-api/localization.md +279 -0
  8. package/dist/api-reference/store-api/metadata.md +160 -0
  9. package/dist/api-reference/store-api/monetary-amounts.md +65 -0
  10. package/dist/api-reference/store-api/querying.md +399 -0
  11. package/dist/api-reference/store-api/rate-limitting.md +103 -0
  12. package/dist/api-reference/store-api/relations.md +185 -0
  13. package/dist/api-reference/storefront/authentication.md +88 -0
  14. package/dist/api-reference/tutorials/adyen-integration-guide-for-android.md +165 -0
  15. package/dist/api-reference/tutorials/adyen-integration-guide-for-ios.md +194 -0
  16. package/dist/api-reference/tutorials/quick-checkout-with-stripe.md +248 -0
  17. package/dist/api-reference/v2/fetching-multiple-resources.md +26 -0
  18. package/dist/api-reference/v2/filtering-and-sorting.md +53 -0
  19. package/dist/api-reference/v2/introduction.md +22 -0
  20. package/dist/api-reference/v2/pagination.md +37 -0
  21. package/dist/api-reference/webhooks-events.md +883 -0
  22. package/dist/developer/admin/admin.md +205 -0
  23. package/dist/developer/admin/authentication.md +59 -0
  24. package/dist/developer/admin/components.md +711 -0
  25. package/dist/developer/admin/custom-css.md +243 -0
  26. package/dist/developer/admin/custom-javascript.md +116 -0
  27. package/dist/developer/admin/extending-ui.md +1964 -0
  28. package/dist/developer/admin/form-builder.md +444 -0
  29. package/dist/developer/admin/helper-methods.md +531 -0
  30. package/dist/developer/admin/navigation.md +805 -0
  31. package/dist/developer/admin/tables.md +491 -0
  32. package/dist/developer/advanced/adding_spree_to_rails_app.md +106 -0
  33. package/dist/developer/cli/quickstart.md +137 -0
  34. package/dist/developer/contributing/creating-an-extension.md +258 -0
  35. package/dist/developer/contributing/developing-spree.md +339 -0
  36. package/dist/developer/contributing/quickstart.md +32 -0
  37. package/dist/developer/contributing/updating-extensions.md +67 -0
  38. package/dist/developer/core-concepts/addresses.md +265 -0
  39. package/dist/developer/core-concepts/adjustments.md +107 -0
  40. package/dist/developer/core-concepts/architecture.md +177 -0
  41. package/dist/developer/core-concepts/calculators.md +323 -0
  42. package/dist/developer/core-concepts/customers.md +230 -0
  43. package/dist/developer/core-concepts/events.md +624 -0
  44. package/dist/developer/core-concepts/imports-exports.md +698 -0
  45. package/dist/developer/core-concepts/inventory.md +191 -0
  46. package/dist/developer/core-concepts/markets.md +250 -0
  47. package/dist/developer/core-concepts/media.md +167 -0
  48. package/dist/developer/core-concepts/metafields.md +187 -0
  49. package/dist/developer/core-concepts/orders.md +328 -0
  50. package/dist/developer/core-concepts/payments.md +710 -0
  51. package/dist/developer/core-concepts/pricing.md +163 -0
  52. package/dist/developer/core-concepts/products.md +360 -0
  53. package/dist/developer/core-concepts/promotions.md +322 -0
  54. package/dist/developer/core-concepts/reports.md +206 -0
  55. package/dist/developer/core-concepts/search-filtering.md +237 -0
  56. package/dist/developer/core-concepts/shipments.md +212 -0
  57. package/dist/developer/core-concepts/slugs.md +111 -0
  58. package/dist/developer/core-concepts/staff-roles.md +123 -0
  59. package/dist/developer/core-concepts/store-credits-gift-cards.md +317 -0
  60. package/dist/developer/core-concepts/stores.md +117 -0
  61. package/dist/developer/core-concepts/taxes.md +135 -0
  62. package/dist/developer/core-concepts/translations.md +120 -0
  63. package/dist/developer/core-concepts/users.md +299 -0
  64. package/dist/developer/core-concepts/webhooks.md +378 -0
  65. package/dist/developer/create-spree-app/quickstart.md +158 -0
  66. package/dist/developer/customization/api.md +93 -0
  67. package/dist/developer/customization/authentication.md +88 -0
  68. package/dist/developer/customization/checkout.md +204 -0
  69. package/dist/developer/customization/configuration.md +55 -0
  70. package/dist/developer/customization/decorators.md +523 -0
  71. package/dist/developer/customization/dependencies.md +232 -0
  72. package/dist/developer/customization/emails.md +21 -0
  73. package/dist/developer/customization/extensions.md +92 -0
  74. package/dist/developer/customization/metadata.md +236 -0
  75. package/dist/developer/customization/model-preferences.md +130 -0
  76. package/dist/developer/customization/permissions.md +265 -0
  77. package/dist/developer/customization/quickstart.md +229 -0
  78. package/dist/developer/customization/routes.md +24 -0
  79. package/dist/developer/customization/v4/admin-panel.md +78 -0
  80. package/dist/developer/customization/v4/authentication.md +210 -0
  81. package/dist/developer/customization/v4/checkout.md +212 -0
  82. package/dist/developer/customization/v4/deface.md +251 -0
  83. package/dist/developer/customization/v4/images.md +86 -0
  84. package/dist/developer/customization/v4/storefront.md +450 -0
  85. package/dist/developer/deployment/assets.md +87 -0
  86. package/dist/developer/deployment/aws.md +335 -0
  87. package/dist/developer/deployment/caching.md +27 -0
  88. package/dist/developer/deployment/cdn.md +39 -0
  89. package/dist/developer/deployment/database.md +155 -0
  90. package/dist/developer/deployment/docker.md +128 -0
  91. package/dist/developer/deployment/emails.md +77 -0
  92. package/dist/developer/deployment/environment_variables.md +111 -0
  93. package/dist/developer/deployment/heroku.md +51 -0
  94. package/dist/developer/deployment/render.md +95 -0
  95. package/dist/developer/getting-started/quickstart.md +82 -0
  96. package/dist/developer/how-to/custom-payment-method.md +374 -0
  97. package/dist/developer/how-to/custom-promotion.md +373 -0
  98. package/dist/developer/how-to/custom-report.md +387 -0
  99. package/dist/developer/how-to/custom-search-provider.md +230 -0
  100. package/dist/developer/multi-store/quickstart.md +71 -0
  101. package/dist/developer/multi-store/setup.md +38 -0
  102. package/dist/developer/multi-tenant/configuration.md +41 -0
  103. package/dist/developer/multi-tenant/core-concepts.md +75 -0
  104. package/dist/developer/multi-tenant/installation.md +96 -0
  105. package/dist/developer/multi-tenant/quickstart.md +20 -0
  106. package/dist/developer/multi-vendor/installation.md +45 -0
  107. package/dist/developer/multi-vendor/quickstart.md +17 -0
  108. package/dist/developer/sdk/admin/quickstart.md +22 -0
  109. package/dist/developer/sdk/authentication.md +89 -0
  110. package/dist/developer/sdk/configuration.md +225 -0
  111. package/dist/developer/sdk/quickstart.md +82 -0
  112. package/dist/developer/sdk/store/account.md +67 -0
  113. package/dist/developer/sdk/store/cart-checkout.md +140 -0
  114. package/dist/developer/sdk/store/markets.md +151 -0
  115. package/dist/developer/sdk/store/payments.md +96 -0
  116. package/dist/developer/sdk/store/products.md +149 -0
  117. package/dist/developer/sdk/store/wishlists.md +52 -0
  118. package/dist/developer/security/pci_compliance.md +15 -0
  119. package/dist/developer/security/security_policy.md +68 -0
  120. package/dist/developer/storefront/blocks.md +285 -0
  121. package/dist/developer/storefront/custom-css.md +260 -0
  122. package/dist/developer/storefront/custom-javascript.md +166 -0
  123. package/dist/developer/storefront/helper-methods.md +1288 -0
  124. package/dist/developer/storefront/links.md +298 -0
  125. package/dist/developer/storefront/nextjs/architecture.md +150 -0
  126. package/dist/developer/storefront/nextjs/customization.md +141 -0
  127. package/dist/developer/storefront/nextjs/deployment.md +180 -0
  128. package/dist/developer/storefront/nextjs/quickstart.md +92 -0
  129. package/dist/developer/storefront/nextjs/spree-next-package.md +314 -0
  130. package/dist/developer/storefront/pages.md +163 -0
  131. package/dist/developer/storefront/sections.md +569 -0
  132. package/dist/developer/storefront/storefront.md +56 -0
  133. package/dist/developer/storefront/themes.md +161 -0
  134. package/dist/developer/tutorial/admin.md +134 -0
  135. package/dist/developer/tutorial/extending-models.md +380 -0
  136. package/dist/developer/tutorial/file-uploads.md +121 -0
  137. package/dist/developer/tutorial/introduction.md +33 -0
  138. package/dist/developer/tutorial/model.md +41 -0
  139. package/dist/developer/tutorial/page-builder.md +487 -0
  140. package/dist/developer/tutorial/rich-text.md +73 -0
  141. package/dist/developer/tutorial/seo.md +332 -0
  142. package/dist/developer/tutorial/storefront.md +352 -0
  143. package/dist/developer/tutorial/testing.md +558 -0
  144. package/dist/developer/upgrades/2.0-to-2.1.md +46 -0
  145. package/dist/developer/upgrades/2.1-to-2.2.md +59 -0
  146. package/dist/developer/upgrades/2.2-to-2.3.md +44 -0
  147. package/dist/developer/upgrades/2.3-to-2.4.md +42 -0
  148. package/dist/developer/upgrades/3.0-to-3.1.md +47 -0
  149. package/dist/developer/upgrades/3.1-to-3.2.md +34 -0
  150. package/dist/developer/upgrades/3.2-to-3.3.md +70 -0
  151. package/dist/developer/upgrades/3.3-to-3.4.md +36 -0
  152. package/dist/developer/upgrades/3.4-to-3.5.md +44 -0
  153. package/dist/developer/upgrades/3.5-to-3.6.md +40 -0
  154. package/dist/developer/upgrades/3.6-to-3.7.md +62 -0
  155. package/dist/developer/upgrades/3.7-to-4.0.md +152 -0
  156. package/dist/developer/upgrades/4.0-to-4.1.md +92 -0
  157. package/dist/developer/upgrades/4.1-to-4.2.md +109 -0
  158. package/dist/developer/upgrades/4.10-to-5.0.md +129 -0
  159. package/dist/developer/upgrades/4.2-to-4.3.md +100 -0
  160. package/dist/developer/upgrades/4.3-to-4.4.md +125 -0
  161. package/dist/developer/upgrades/4.4-to-4.5.md +94 -0
  162. package/dist/developer/upgrades/4.5-to-4.6.md +119 -0
  163. package/dist/developer/upgrades/4.6-to-4.7.md +39 -0
  164. package/dist/developer/upgrades/4.8-to-4.9.md +24 -0
  165. package/dist/developer/upgrades/4.9-to-4.10.md +24 -0
  166. package/dist/developer/upgrades/4.x-to-4.8.md +52 -0
  167. package/dist/developer/upgrades/5.0-to-5.1.md +28 -0
  168. package/dist/developer/upgrades/5.1-to-5.2.md +127 -0
  169. package/dist/developer/upgrades/5.2-to-5.3.md +338 -0
  170. package/dist/developer/upgrades/5.3-to-5.4.md +248 -0
  171. package/dist/developer/upgrades/quickstart.md +36 -0
  172. package/dist/integrations/analytics/google-analytics.md +64 -0
  173. package/dist/integrations/analytics/google-tag-manager.md +78 -0
  174. package/dist/integrations/integrations.md +39 -0
  175. package/dist/integrations/marketing/klaviyo.md +99 -0
  176. package/dist/integrations/payments/adyen.md +90 -0
  177. package/dist/integrations/payments/paypal.md +41 -0
  178. package/dist/integrations/payments/razorpay.md +45 -0
  179. package/dist/integrations/payments/stripe.md +109 -0
  180. package/dist/integrations/search/meilisearch.md +236 -0
  181. package/dist/integrations/sso-mfa-social-login/admin-dashboard.md +57 -0
  182. package/dist/integrations/sso-mfa-social-login/storefront.md +56 -0
  183. 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
+ ```