@spree/docs 0.1.181 → 0.1.183

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 (31) hide show
  1. package/dist/api-reference/seller-api/errors.md +1 -1
  2. package/dist/api-reference/store.yaml +78 -91
  3. package/dist/developer/core-concepts/addresses.md +106 -198
  4. package/dist/developer/core-concepts/architecture.md +97 -126
  5. package/dist/developer/core-concepts/calculators.md +75 -252
  6. package/dist/developer/core-concepts/carts.md +1 -1
  7. package/dist/developer/core-concepts/channels.md +0 -4
  8. package/dist/developer/core-concepts/companies-and-catalogs.md +1 -1
  9. package/dist/developer/core-concepts/customers.md +0 -3
  10. package/dist/developer/core-concepts/discounts.md +133 -0
  11. package/dist/developer/core-concepts/events.md +83 -576
  12. package/dist/developer/core-concepts/fees.md +144 -0
  13. package/dist/developer/core-concepts/imports-exports.md +105 -679
  14. package/dist/developer/core-concepts/inventory.md +114 -248
  15. package/dist/developer/core-concepts/markets.md +9 -12
  16. package/dist/developer/core-concepts/media.md +9 -11
  17. package/dist/developer/core-concepts/metafields.md +123 -200
  18. package/dist/developer/core-concepts/order-totals.md +110 -0
  19. package/dist/developer/core-concepts/orders.md +1 -1
  20. package/dist/developer/core-concepts/payments.md +11 -14
  21. package/dist/developer/core-concepts/pricing.md +11 -13
  22. package/dist/developer/core-concepts/products.md +173 -19
  23. package/dist/developer/core-concepts/promotions.md +12 -11
  24. package/dist/developer/core-concepts/search-filtering.md +2 -4
  25. package/dist/developer/core-concepts/store-credits-gift-cards.md +0 -3
  26. package/dist/developer/core-concepts/taxes.md +125 -113
  27. package/dist/developer/core-concepts/translations.md +61 -68
  28. package/dist/developer/core-concepts/webhooks.md +25 -59
  29. package/dist/developer/how-to/custom-promotion.md +3 -3
  30. package/package.json +1 -1
  31. package/dist/developer/core-concepts/taxes-discounts-fees.md +0 -199
@@ -3,8 +3,6 @@ title: Pricing
3
3
  description: Prices, Price Lists, Price Rules, and the Pricing Context — Spree's flexible pricing engine for regional, wholesale, volume, and market-based pricing.
4
4
  ---
5
5
 
6
- import { Since } from '/snippets/since.mdx';
7
-
8
6
  ## Overview
9
7
 
10
8
  Spree's pricing system supports both simple single-currency pricing and advanced multi-currency, rule-based pricing through Price Lists. Every [Variant](products.md#variants) can have multiple prices — a base price per currency, plus additional prices from Price Lists that apply conditionally based on rules like geography, customer segment, or quantity.
@@ -94,7 +92,7 @@ spree api post /prices/bulk_upsert -d '{
94
92
  ```
95
93
 
96
94
 
97
- ## Price Lists
95
+ ## Price Lists
98
96
 
99
97
  Price Lists allow you to create different pricing strategies based on various conditions. This enables advanced pricing scenarios like:
100
98
 
@@ -165,27 +163,27 @@ spree api patch /price_lists/pl_xxx/activate
165
163
  ```
166
164
 
167
165
 
168
- ## Price Rules
166
+ ## Price Rules
169
167
 
170
168
  Price Rules define conditions that must be met for a Price List to apply. Spree includes five built-in rule types:
171
169
 
172
170
  | Rule | Description | Use Case |
173
171
  |------|-------------|----------|
174
172
  | **Market Rule** | Matches based on the current [Market](markets.md) | Regional pricing across markets |
175
- | **Zone Rule** | Matches based on the customer's geographic zone | Country or state-level pricing |
173
+ | **Channel Rule** | Matches based on the [Channel](channels.md) the customer is buying through | App-only or in-store pricing |
176
174
  | **User Rule** | Matches specific customer accounts | VIP customers, wholesale accounts |
177
175
  | **Customer Group Rule** | Matches members of customer groups | Loyalty tiers, membership pricing |
178
176
  | **Volume Rule** | Matches based on quantity purchased | Bulk discounts, tiered pricing |
179
177
 
180
- ### Market Rule
178
+ ### Market Rule
181
179
 
182
- The recommended approach for regional pricing when using Markets. Applies the Price List when the customer is in one of the specified markets.
180
+ Regional pricing. Applies the Price List when the customer is in one of the specified markets.
183
181
 
184
182
  **Example:** Price a product at $29.99 in North America and €24.99 in Europe, rather than relying on exchange rate conversion.
185
183
 
186
- ### Zone Rule
184
+ ### Channel Rule
187
185
 
188
- Applies based on the customer's geographic zone. Useful for regional or country-specific pricing when not using Markets.
186
+ Applies when the customer is buying through a particular sales channel — a discount that exists only in the mobile app, or a price that only applies at a retail till.
189
187
 
190
188
  ### User Rule
191
189
 
@@ -207,7 +205,7 @@ Applies based on quantity purchased. Supports `min_quantity` and `max_quantity`
207
205
 
208
206
  > **INFO:** Custom Price Rules can be created for specialized pricing logic. See the [Customization Quickstart](../customization/quickstart.md) for details.
209
207
 
210
- ## Pricing Context
208
+ ## Pricing Context
211
209
 
212
210
  When resolving prices, Spree considers the full context of the request:
213
211
 
@@ -215,8 +213,8 @@ When resolving prices, Spree considers the full context of the request:
215
213
  |---------|--------|-------------|
216
214
  | Currency | Market or request header | The currency to price in |
217
215
  | Market | Customer's country | The [Market](markets.md) for market-based rules |
218
- | Zone | Customer's address | The geographic zone for zone-based rules |
219
- | Customer | JWT authentication | The logged-in customer for user-based rules |
216
+ | Channel | The API credential in use | The [Channel](channels.md) for channel-based rules |
217
+ | Customer | Authentication | The signed-in customer for user-based rules |
220
218
  | Quantity | Cart line item | The quantity for volume-based rules |
221
219
  | Date | Current time | For time-based Price List scheduling |
222
220
 
@@ -236,7 +234,7 @@ Price Lists are managed in the Admin Panel under **Products → Price Lists**, o
236
234
 
237
235
  Each Price List contains prices for specific variants and currencies. Products can be added to a Price List, and individual variant prices set within it.
238
236
 
239
- ## Price History (EU Omnibus Directive)
237
+ ## Price History (EU Omnibus Directive)
240
238
 
241
239
  Spree automatically records price changes for EU Omnibus Directive compliance. When a product goes on sale, EU regulations require displaying the lowest price in the preceding 30 days alongside the discounted price.
242
240
 
@@ -3,8 +3,6 @@ title: Products
3
3
  description: How Spree models products, variants, option types, images, prices, and categories — the building blocks of every catalog and storefront.
4
4
  ---
5
5
 
6
- import { Since } from '/snippets/since.mdx';
7
-
8
6
  ## Overview
9
7
 
10
8
  A product represents something you sell. Each product has one or more **variants** — the actual purchasable items with their own SKU, price, and inventory. For example, a "T-Shirt" product might have variants for each size and color combination.
@@ -16,15 +14,16 @@ Products are organized into **categories** — a flexible hierarchy for grouping
16
14
  ```mermaid
17
15
  erDiagram
18
16
  Product ||--o{ Variant : "has many"
19
- Product }o--o{ OptionType : "has many"
20
- Product ||--o{ Classification : "has many"
21
- Variant ||--o{ Price : "has many"
22
- Variant ||--o{ StockItem : "has many"
23
- Variant ||--o{ Image : "has many"
24
- Variant }o--o{ OptionValue : "has many"
17
+ Product }o--o{ OptionType : "varies by"
18
+ Product }o--o{ Category : "filed under"
19
+ Product }o--o{ Collection : "grouped into"
20
+ Product ||--o{ Media : "images and video"
21
+ Product }o--|| DeliveryProfile : "ships by"
22
+ Variant ||--o{ Price : "one per currency"
23
+ Variant ||--o{ StockLevel : "stocked per location"
24
+ Variant }o--o{ OptionValue : "identified by"
25
25
  OptionType ||--o{ OptionValue : "has many"
26
- Taxon ||--o{ Classification : "has many"
27
- Taxonomy ||--o{ Taxon : "has many"
26
+ Category ||--o{ Category : "nests under"
28
27
 
29
28
  Product {
30
29
  string name
@@ -36,7 +35,7 @@ erDiagram
36
35
 
37
36
  Variant {
38
37
  string sku
39
- boolean is_master
38
+ string barcode
40
39
  decimal weight
41
40
  }
42
41
 
@@ -64,7 +63,7 @@ erDiagram
64
63
  | `name` | Product name | Yes |
65
64
  | `description` | Full product description | Yes |
66
65
  | `slug` | URL-friendly identifier (e.g., `spree-tote`) | Yes |
67
- | `status` | `draft`, `active`, or `archived` | No |
66
+ | `status` | `draft`, `active`, or `archived`. A marketplace adds `proposed` and `rejected` — see Seller submissions below | No |
68
67
  | `available_on` | Date the product becomes available for sale | No |
69
68
  | `discontinue_on` | Date the product is no longer available | No |
70
69
  | `meta_title` | Custom SEO title | Yes |
@@ -221,6 +220,144 @@ spree api delete /products/prod_xxx
221
220
 
222
221
  > **TIP:** Operating on many products at once? The Admin API has bulk actions — `bulkStatusUpdate`, `bulkAddToCategories`, `bulkAddTags`, `bulkDestroy`, and more. See the [Admin API endpoint index](../../api-reference/admin-api/endpoints.md).
223
222
 
223
+ ## Seller submissions
224
+
225
+ On a marketplace, a seller lists a product but does not publish one. They submit it, and the marketplace decides. This adds two statuses to the three above — both hidden from the storefront, since only `active` is visible.
226
+
227
+ > **INFO:** This applies to products that belong to a seller. A marketplace's own catalog is unaffected: an operator publishing their own product sets `status` directly and answers to nobody.
228
+
229
+ ### The lifecycle
230
+
231
+ ```mermaid
232
+ stateDiagram-v2
233
+ [*] --> draft
234
+ draft --> proposed : seller submits
235
+ proposed --> active : operator approves
236
+ proposed --> rejected : operator sends back
237
+ rejected --> proposed : seller revises and resubmits
238
+ proposed --> draft : seller withdraws
239
+ proposed --> archived : seller withdraws
240
+ active --> draft : seller takes it down
241
+ active --> archived : seller withdraws it
242
+ ```
243
+
244
+ Withdrawing a submission before anyone has ruled on it closes the open row as `withdrawn`, so a `pending` row always means the marketplace still owes an answer. A product that was already rejected keeps that decision at the head of its trail instead.
245
+
246
+ | Status | Meaning | Storefront |
247
+ |---|---|:---:|
248
+ | `draft` | The seller is still working on it | Hidden |
249
+ | `proposed` | Submitted, waiting on the marketplace | Hidden |
250
+ | `rejected` | Sent back with a reason, awaiting changes | Hidden |
251
+ | `active` | Approved and on sale | **Visible** |
252
+ | `archived` | Withdrawn | Hidden |
253
+
254
+ A seller can always take their own listing down — that is not a review decision. Putting one up is.
255
+
256
+ ### The submission record
257
+
258
+ Each submission and each decision on it is a `Spree::ProductSubmission` row. The product's `status` stays the operational truth; these rows are how it got there.
259
+
260
+ ```mermaid
261
+ erDiagram
262
+ Product ||--o{ ProductSubmission : "has many"
263
+ AdminUser ||--o{ ProductSubmission : "submitted"
264
+ AdminUser ||--o{ ProductSubmission : "reviewed"
265
+
266
+ ProductSubmission {
267
+ string status
268
+ bigint product_id
269
+ bigint submitted_by_id
270
+ bigint reviewed_by_id
271
+ datetime reviewed_at
272
+ text review_note
273
+ json metadata
274
+ }
275
+ ```
276
+
277
+ | Column | Description |
278
+ |---|---|
279
+ | `status` | `pending`, `approved`, `rejected`, or `withdrawn` |
280
+ | `submitted_by_id` | The seller's staff member who asked |
281
+ | `reviewed_by_id` | The marketplace's staff member who decided |
282
+ | `reviewed_at` | When the decision was made |
283
+ | `review_note` | Why it was sent back — this is what the seller reads |
284
+
285
+ Rows accumulate rather than overwrite, so a seller sent back three times leaves three rows. The latest row for a product is the live one; the ones before it are the trail.
286
+
287
+ > **WARNING:** Never store a rejection reason on the product itself. A seller can write their own product's `metadata`, so a note kept there is erased the next time they save.
288
+
289
+ An approval with no `reviewed_by_id` and `metadata.auto_approved` set means the store approves listings automatically — never a decision whose author was lost. Turn that on with the `auto_approve_seller_products` store preference.
290
+
291
+ ### Submitting, as a seller
292
+
293
+ Status is not writable on the seller branch. A seller moves a product with an explicit action:
294
+
295
+
296
+ ```typescript Seller SDK
297
+ import { createSellerClient } from '@spree/seller-sdk'
298
+
299
+ const client = createSellerClient({
300
+ baseUrl: 'https://marketplace.example.com',
301
+ sellerId: 'sel_xxx',
302
+ })
303
+
304
+ await client.products.submit('prod_xxx') // draft or rejected → proposed
305
+ await client.products.draft('prod_xxx') // take it back down
306
+ await client.products.archive('prod_xxx') // withdraw it
307
+
308
+ // Why it was sent back
309
+ const product = await client.products.get('prod_xxx', 'submission')
310
+ product.submission?.review_note
311
+ ```
312
+
313
+ ```bash CLI
314
+ spree api patch /seller/products/prod_xxx/submit
315
+ spree api patch /seller/products/prod_xxx/draft
316
+ spree api patch /seller/products/prod_xxx/archive
317
+ ```
318
+
319
+
320
+ ### Deciding, as the marketplace
321
+
322
+
323
+ ```typescript Admin SDK
324
+ await client.products.approve('prod_xxx')
325
+ await client.products.reject('prod_xxx', { reason: 'Please add a photo showing scale.' })
326
+
327
+ // The review queue
328
+ const pending = await client.products.list({ filter: { status_eq: 'proposed' } })
329
+
330
+ // Who decided, and when
331
+ const product = await client.products.get('prod_xxx', { expand: ['submission'] })
332
+ product.submission?.reviewed_by_name
333
+ ```
334
+
335
+ ```bash CLI
336
+ spree api patch /products/prod_xxx/approve
337
+ spree api patch /products/prod_xxx/reject -d '{"reason": "Please add a photo showing scale."}'
338
+ ```
339
+
340
+
341
+ > **NOTE:** The seller sees the note and when the decision was made, but never who made it.
342
+
343
+ ### Leaving review is a decision
344
+
345
+ A product in `proposed` or `rejected` cannot have its status changed by an ordinary update — that would put it on sale with nobody's name against it. The refusal lives in the product update workflow, so every caller inherits it, and bulk status updates skip those products and report how many they left behind.
346
+
347
+ ### Events
348
+
349
+ Each transition publishes an event you can subscribe to:
350
+
351
+ | Event | Published when |
352
+ |---|---|
353
+ | `product.proposed` | A seller submits for review |
354
+ | `product.approved` | The marketplace accepts it |
355
+ | `product.rejected` | The marketplace sends it back |
356
+ | `product.drafted` | A seller takes a listing down |
357
+ | `product.archived` | A seller withdraws it |
358
+
359
+ The submission row itself also publishes `product_submission.created` and `product_submission.updated`, carrying the status, the note and the product. See [Events](events.md).
360
+
224
361
  ## Product Filters
225
362
 
226
363
  Get available filter options for building a faceted search UI. Returns price ranges, option values, and categories with counts:
@@ -468,14 +605,19 @@ See the [Pricing](pricing.md) guide for details on Price Lists, Price Rules, and
468
605
 
469
606
  ## Categories
470
607
 
471
- Categories provide a flexible way to organize products into hierarchical trees. Internally, Spree uses Taxonomies (category trees) and Taxons (nodes within those trees), but the Store API exposes them simply as **Categories**.
608
+ There are two ways to group products, and they answer different questions.
609
+
610
+ **Categories** are a hierarchy — the navigation tree a shopper browses. Clothing contains T-Shirts, which contains Long Sleeve. A product can sit in several categories, and each one has a permalink built from its path.
611
+
612
+ **Collections** are flat groupings — "Summer 2025", "Best Sellers", "Under $50". A collection can be curated by hand, or defined by rules so products join and leave it on their own as their price, tags or stock change.
472
613
 
473
- For example:
474
- - **Categories** → Clothing → T-Shirts, Dresses
475
- - **Brands** Nike, Adidas, Puma
476
- - **Collections** Summer 2025, Best Sellers
614
+ | | Categories | Collections |
615
+ |---|---|---|
616
+ | Shape | Nested tree | Flat list |
617
+ | Membership | You assign it | Assigned, or matched by rules |
618
+ | Typical use | Site navigation | Merchandising and campaigns |
477
619
 
478
- Products can belong to multiple categories.
620
+ A brand is usually best modelled as one or the other rather than as a separate concept — a category if you want it in the navigation tree, a collection if it's a landing page.
479
621
 
480
622
 
481
623
  ```typescript Store SDK
@@ -512,7 +654,19 @@ curl 'https://api.mystore.com/api/v3/store/categories/clothing/shirts/products?l
512
654
 
513
655
  > **INFO:** Category `name` and `description` fields are translatable.
514
656
 
515
- ## Publications and Sales Channels
657
+ Collections work the same way from a storefront's point of view:
658
+
659
+ ```typescript Store SDK
660
+ const { data: collections } = await client.collections.list()
661
+
662
+ const { data: products } = await client.collections.products.list('summer-2025', {
663
+ limit: 12,
664
+ })
665
+ ```
666
+
667
+ A rule-based collection is defined once and maintains itself — set it to match everything tagged `sale` and under $50, and products appear and disappear as those facts change. Ordering can be manual or by a rule such as newest first.
668
+
669
+ ## Publications and Sales Channels
516
670
 
517
671
  A product is visible on a [Channel](channels.md) only when a `ProductPublication` record joins the two. Publications carry an optional time window so a product can be scheduled to go live and come down without code or manual toggles.
518
672
 
@@ -5,7 +5,9 @@ description: Build percentage and fixed-amount discounts, free shipping, BOGO of
5
5
 
6
6
  ## Overview
7
7
 
8
- Promotions give money off an order — a percentage off, free shipping, or free items added to the cart. A promotion is a campaign definition: **rules** decide when it applies, **actions** decide what it does. When a promotion applies, its actions write [Discount](taxes-discounts-fees.md#discounts) rows on the order.
8
+ A promotion is a campaign: a percentage off, free shipping, or free items in the cart. **Rules** decide when it applies, **actions** decide what it does.
9
+
10
+ A promotion is a *rule that exists on its own*, set up in advance and lasting across many orders. What lands on any one order is a [Discount](discounts.md) — the record of money actually coming off. The promotion is the campaign; the discount is what a particular customer got.
9
11
 
10
12
  ```mermaid
11
13
  erDiagram
@@ -52,14 +54,13 @@ Promotions can be activated in two ways:
52
54
  - **Automatic promotions** — applied on their own when the rules are met (e.g. free shipping on orders over $50)
53
55
  - **Coupon code promotions** — applied when a customer enters a valid code during checkout
54
56
 
55
- ### How discounts are recorded
57
+ ### When promotions compete
56
58
 
57
- Each applied promotion becomes one or more Discount rows on the order — on a specific line item or on a fulfillment, never a loose adjustment. Each row keeps a copy of the coupon code and the configured value ("10% off"), so the order still tells the whole story even if the promotion is deleted later.
59
+ Several promotions can qualify for the same item at once. Spree applies **only the one that saves the customer the most**, and records just that one.
58
60
 
59
- Two things follow from this design:
61
+ Losing candidates aren't stored or tracked — they're simply reconsidered on every cart change. So a promotion that loses today can win tomorrow, when the basket changes, without any bookkeeping to keep straight.
60
62
 
61
- - **Only winners are stored.** When several promotions compete for the same item, Spree picks the one that saves the customer the most and writes only that one. Losing candidates are recomputed on every cart change, so a promotion that loses today can win tomorrow without any bookkeeping.
62
- - **A discount on the whole order is shared out across the items.** If a customer later returns one item, Spree knows exactly how much of the discount belonged to it.
63
+ What an applied promotion leaves behind on the order is a [Discount](discounts.md) a permanent record that survives the promotion being edited or deleted.
63
64
 
64
65
  ## Promotion Attributes
65
66
 
@@ -112,7 +113,7 @@ You can also [build your own rules](../how-to/custom-promotion.md) for business-
112
113
 
113
114
  Actions define what happens when a promotion applies.
114
115
 
115
- ### CreateAdjustment
116
+ ### Order discount (`CreateAdjustment`)
116
117
 
117
118
  A discount on the whole order. The amount is distributed proportionally across the line items — there is no single order-level row, so per-item reporting and partial returns always know their share.
118
119
 
@@ -120,7 +121,7 @@ A discount on the whole order. The amount is distributed proportionally across t
120
121
 
121
122
  **Use case:** "10% off your order", "$20 off orders over $100".
122
123
 
123
- ### CreateItemAdjustments
124
+ ### Item discount (`CreateItemAdjustments`)
124
125
 
125
126
  A discount on individual line items. Only items that match the promotion's rules receive it — if a Category rule says "Electronics", only electronics are discounted.
126
127
 
@@ -128,13 +129,13 @@ A discount on individual line items. Only items that match the promotion's rules
128
129
 
129
130
  **Use case:** "15% off shoes", "Buy 2+ shirts get 10% off each".
130
131
 
131
- ### FreeShipping
132
+ ### Free shipping (`FreeShipping`)
132
133
 
133
134
  Writes a discount on each fulfillment covering its delivery cost. The row is kept even when the cost is zero — its presence is what marks the order as having free shipping.
134
135
 
135
136
  **Use case:** "Free shipping on orders over $75", "Free shipping with code FREESHIP".
136
137
 
137
- ### CreateLineItems
138
+ ### Free items (`CreateLineItems`)
138
139
 
139
140
  Adds specified products to the cart when the promotion is eligible, checking stock first. Items are not removed automatically if eligibility is lost — customers remove them manually.
140
141
 
@@ -245,7 +246,7 @@ Automatic promotions need no customer action at all — they are evaluated on ev
245
246
 
246
247
  ## Related Documentation
247
248
 
248
- - [Taxes, Discounts & Fees](taxes-discounts-fees.md) — the Discount rows promotions write, and manual discounts
249
+ - [Discounts](discounts.md) — the rows promotions write, and manual discounts
249
250
  - [Build Custom Promotion Rules & Actions](../how-to/custom-promotion.md) — step-by-step guide to custom rules, actions and adjusters
250
251
  - [Calculators](calculators.md) — promotion calculators
251
252
  - [Carts](carts.md) — the cart lifecycle promotions act on
@@ -3,13 +3,11 @@ title: Search & Filtering
3
3
  description: "Use Ransack-style query parameters to search, filter, sort, and paginate products, taxons, and other resources through the Spree Store API."
4
4
  ---
5
5
 
6
- import { Since } from '/snippets/since.mdx';
7
-
8
6
  ## Overview
9
7
 
10
8
  Spree provides powerful search, filtering, and sorting capabilities for products and other resources. The Store API supports:
11
9
 
12
- - Full-text search across product names, SKUs, and searchable product metafields
10
+ - Full-text search across product names, SKUs, and searchable custom fields
13
11
  - Attribute-based filtering (price range, availability, stock status)
14
12
  - Category and taxon filtering
15
13
  - Faceted search with filter counts
@@ -276,7 +274,7 @@ curl 'https://api.mystore.com/api/v3/store/products?page=1&limit=24' \
276
274
 
277
275
  See [Querying](../../api-reference/store-api/querying.md) for the full list of filtering, sorting, and pagination options.
278
276
 
279
- ## Search Providers
277
+ ## Search Providers
280
278
 
281
279
  Spree uses a pluggable search provider architecture. The default provider uses SQL (ILIKE + Ransack). For production catalogs with 1,000+ products, we recommend switching to [Meilisearch](../../integrations/search/meilisearch.md) for typo tolerance, relevance ranking, and faster faceted search.
282
280
 
@@ -3,9 +3,6 @@ title: Store Credits & Gift Cards
3
3
  description: How Spree models store credits and gift cards — stored value balances, redeemable codes, and checkout usage for refunds, loyalty, and gifting.
4
4
  ---
5
5
 
6
- import { Since } from '/snippets/since.mdx';
7
-
8
-
9
6
  ## Overview
10
7
 
11
8
  Spree provides two stored value mechanisms that customers can use at checkout: