@spree/docs 0.1.251 → 0.1.253

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.
@@ -250,7 +250,7 @@ For providers like Stripe, create a payment session and confirm it with their ow
250
250
  const order = await client.carts.complete(cartId)
251
251
  ```
252
252
 
253
- You get back an [Order](orders.md).
253
+ You get back an [Order](orders.md) — or, in a marketplace where the cart held several sellers' goods, an [order group](sellers.md#one-checkout-several-sellers) holding one order per seller. Tell them apart with `isOrderGroup` from `@spree/sdk`.
254
254
 
255
255
 
256
256
  Discount codes, gift cards and store credit can be applied any time before completion:
@@ -280,6 +280,10 @@ Completing a cart moves real money, so Spree is careful about it:
280
280
 
281
281
  **Totals are worked out at the last moment.** The amount charged is calculated when the cart is completed, not taken from an earlier request — so a price or promotion that changed while the customer was reviewing can't lead to the wrong charge.
282
282
 
283
+ **The order keeps what the cart collected.** The customer, addresses, items, notes, purchase order number, tax identifier and the cart's `metadata` are all copied onto the order. When a checkout is split into one order per seller, every one of those orders receives the same copy. After that the cart and the order are independent — changing one never changes the other.
284
+
285
+ > **WARNING:** A customer can write a cart's `metadata` through the Store API, so once it is copied onto the order, treat those keys as customer input. Do not keep anything there that your own code later trusts, such as a fraud or approval flag.
286
+
283
287
  If a cart can't be completed you get told why — a payment failure, or unmet [requirements](#checkout-requirements) — and the cart is left alone so the customer can fix it and try again.
284
288
 
285
289
  ## Abandoned carts
@@ -5,7 +5,7 @@ description: How Spree models orders — a permanent record created by completin
5
5
 
6
6
  ## Overview
7
7
 
8
- An order is a placed purchase. It's created by completing a [Cart](carts.md) and holds its own copy of everything the customer agreed to — items, prices, taxes, discounts and delivery.
8
+ An order is a placed purchase. It's created by completing a [Cart](carts.md) and holds its own copy of everything the customer agreed to — items, prices, taxes, discounts and delivery. Anything your storefront stored in the cart's `metadata` is copied onto the order too, including onto every order of a checkout split between sellers.
9
9
 
10
10
  ```mermaid
11
11
  erDiagram
@@ -246,6 +246,15 @@ Two details worth knowing:
246
246
  > A back office must handle it too: an order raised from the admin divides the same way, so completing one may answer with a group instead of an order.
247
247
 
248
248
 
249
+ ```typescript Store SDK
250
+ import { isOrderGroup } from '@spree/sdk'
251
+
252
+ const result = await client.carts.complete(cartId, { spreeToken: cart.token })
253
+
254
+ // One order per seller when the cart held several sellers' goods.
255
+ const orders = isOrderGroup(result) ? result.orders : [result]
256
+ ```
257
+
249
258
  ```typescript Admin SDK
250
259
  import { isOrderGroup } from '@spree/admin-sdk'
251
260
 
@@ -41,6 +41,11 @@ Spree::Checkout::Registry.add_requirement(
41
41
  While `satisfied:` returns false the cart reports the requirement and refuses to
42
42
  complete. Its `code` is derived as `<field>_required` — `vat_number_required` here.
43
43
 
44
+ The storefront satisfies it by writing the value into the cart's `metadata`. When the
45
+ cart is completed, its `metadata` is copied onto the order — onto every order if the
46
+ checkout is split between sellers — so the VAT number is still there after placement as
47
+ `order.metadata['vat_number']`.
48
+
44
49
  Pass `applicable:` to scope it. It is checked before `satisfied:`, so a requirement
45
50
  that doesn't apply never appears at all:
46
51
 
@@ -122,9 +122,26 @@ await client.carts.update(cartId, {
122
122
  ### Complete Checkout
123
123
 
124
124
  ```typescript
125
- await client.carts.complete(cartId, { spreeToken: cart.token });
125
+ import { isOrderGroup } from '@spree/sdk';
126
+
127
+ const result = await client.carts.complete(cartId, { spreeToken: cart.token });
128
+ ```
129
+
130
+ Completing a cart usually returns one order. In a [marketplace](../../core-concepts/sellers.md#one-checkout-several-sellers), a cart holding several sellers' goods divides into one order per seller, and the response is the **order group** that ties them together: one purchase, one payment, combined totals, and the per-seller orders in `orders`. Use `isOrderGroup` to tell the two apart:
131
+
132
+ ```typescript
133
+ if (isOrderGroup(result)) {
134
+ // One purchase, shown to the customer under the group's number and total.
135
+ console.log(result.number, result.display_total);
136
+ // How it will arrive: one order per seller, each with its own items and delivery.
137
+ result.orders.forEach((order) => console.log(order.number, order.items));
138
+ } else {
139
+ console.log(result.number, result.display_total);
140
+ }
126
141
  ```
127
142
 
143
+ A storefront that never sells through more than one seller always gets an order back, but checking costs nothing and keeps the confirmation page working if sellers are added later.
144
+
128
145
  ### Fulfillments
129
146
 
130
147
  Fulfillments are included in the cart response — there is no separate list endpoint.
@@ -313,13 +313,15 @@ Replace it by swapping the handler:
313
313
 
314
314
  ```ruby
315
315
  # config/initializers/spree.rb
316
- Rails.application.config.after_initialize do
317
- Spree.hooks.unregister('returns.create.validate', 'Spree::Returns::EligibilityValidator')
318
- Spree.hooks.register('returns.create.validate', 'MyStore::ReturnPolicy')
319
- end
316
+ Spree.hooks.unregister('returns.create.validate', 'Spree::Returns::EligibilityValidator')
317
+ Spree.hooks.register('returns.create.validate', 'MyStore::ReturnPolicy')
318
+
319
+ # The same validator guards exchanges — swap it there too if your policy should cover them:
320
+ Spree.hooks.unregister('exchanges.create.validate', 'Spree::Returns::EligibilityValidator')
321
+ Spree.hooks.register('exchanges.create.validate', 'MyStore::ReturnPolicy')
320
322
  ```
321
323
 
322
- Wrap the swap in `after_initialize`: core registers the default validator after your initializers load, so an `unregister` at the top level of an initializer would run too early and do nothing.
324
+ Core registers the default validator before your initializers load, so the swap works at the top level of an initializer.
323
325
 
324
326
  A handler receives the workflow (so it can read `order`, `items`, `created_by`, `order.market`) and calls `workflow.reject!(message)` to veto. Every one of the fifteen transitions has a leading `validate` hook, so the same seam gates approving, receiving and refunding.
325
327
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spree/docs",
3
- "version": "0.1.251",
3
+ "version": "0.1.253",
4
4
  "description": "Spree Commerce developer documentation for AI agents and local reference",
5
5
  "type": "module",
6
6
  "license": "CC-BY-4.0",