Files
vmall/openspec/changes/replace-mock-api-wave-3/proposal.md
T
james e1a0a5dbdb feat(mall): run the transaction chain against the live API
Wave 3 of replacing the fixed-data mock adapter: cart, orders, shipments and
invoices flip together, so one purchase runs end to end against the backend.

- cart: CartItemView carries the line's shop and the SKU's stock, so the cart
  keeps grouping per shop and the quantity stepper caps at real stock instead
  of a hard-coded 999
- contract: Shipment.items is optional and Invoice.invoice_no nullable, both
  matching what the API actually returns. Invoice was declared twice in
  types.ts and TypeScript merges duplicate interfaces, so the duplicate had to
  go for the change to take effect at all
- an anonymous add-to-cart redirects to /login?redirect=..., and sign-in
  honours only same-origin paths
- the fixed-data adapter learns the new cart fields, and its persisted state
  key moves to v2 because a cart saved by an older build is no longer valid
- order surfaces drop their storeById lookups and keep the generic store label
  until the public store read arrives

Verified end to end: two-shop cart grouping with live shop names, stock caps
read from the API, checkout, payment, shipment, delivery confirmation and an
issued invoice. Rollback re-verified with every domain on fixed data and the
backend stopped.

Also checks off Wave 3 in docs/TBD-migrate-wave.md and re-points that file at
the mock content that remains.

OpenSpec change: openspec/changes/replace-mock-api-wave-3
2026-09-17 16:33:22 +00:00

1.8 KiB

Proposal

Why

Cart, orders, shipments and invoices are the last mocked domains, and they cannot move one at a time: the fixed-data adapter keeps cart -> orders -> shipments in one shared state, so a live cart with a mock checkout dies on EMPTY_CART and mock invoices 404 on live order ids.

What Changes

  • Flip cart, orders, shipments and invoices to live together, so one purchase runs end to end.
  • BREAKING (data): cart and order state moves from localStorage to Redis/Postgres; existing mock carts and orders do not carry over.
  • Extend the cart line with its shop and stock so the cart keeps grouping per shop and the stepper caps at real stock; CartItemView already joins both (apps/api/src/cart.rs:68-74).
  • Ask an anonymous shopper to sign in rather than fail: a live addCartItem answers 401 on a public page, which redirects to /login?redirect=….
  • Stop the shared contract lying: Shipment.items is required in TS but never returned, and Invoice.invoice_no is nullable live but non-null in TS.
  • Keep MOCK_ADDRESSES behind checkout: live checkout takes the address in the request body.

Capabilities

New Capabilities

(none)

Modified Capabilities

  • cart: the cart view carries each line's shop and current stock.
  • frontend-mall: the shopping and transaction flows run against the live API, and an anonymous add-to-cart prompts sign-in.

Impact

apps/api/src/cart.rs; packages/shared/src/{api,types}.ts; the mall's plugins/api.ts, nuxt.config.ts, cart/checkout/order/invoice pages and middleware/auth.ts. The contract change means all three frontends rebuild.

Non-goals

No addresses, favourites, coupons or storefront content. No stock check on add-to-cart; checkout stays the authority. Store names on order surfaces keep their generic fallback until Wave 4.