Files
marketplaces/docs/backend/PHASE-6-CART-CHECKOUT-CONTRACT.md
sdarbinyan 707db6d43c docs: backend contracts for Phases 5-7 (seller portal, server cart, reconciliation)
- Phase 5: Seller Portal from scratch (zero backend bytes exist today) -
  SellerOrganization/SellerUser/SellerMarketplaceMembership, all endpoints
  scoped server-side to the unified-orders Fulfillment model from Phase 2.
- Phase 6: server-owned Cart/CartLine/CheckoutSession, extending Phase 1's
  server-authoritative-amount contract into the cart itself. Replaces
  localStorage/Telegram-CloudStorage cart persistence.
- Phase 7: Refund and ReconciliationRecord entities, settlement contract.
  Flags additional payment providers (wallets/BNPL) as still an open
  business decision - not blocking, schema is provider-agnostic already.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 23:04:11 +04:00

91 lines
3.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 6 Backend Contract — Server Cart + Checkout Session
Companion to [PRODUCT-PLAN-v3.1-DELIVERY-PLAN.md](../PRODUCT-PLAN-v3.1-DELIVERY-PLAN.md) Phase 6 (Sprints 6.16.2). Extends [Phase 1](PHASE-1-MONEY-FX-PAYMENTS-CONTRACT.md) §5 (server-authoritative checkout amount) into a full server-owned cart.
**Status: ready to build** — payment chain unfrozen per Sprint 0.1.
---
## 1. Why this exists
Cart today is `localStorage` + Telegram CloudStorage — no backend cart exists at all. `features/website/checkout/` is an empty directory; checkout lives entirely inside a 751-line cart popup component. Phase 1 §5 already specifies the server-authoritative *amount* at checkout time; this phase makes the *cart itself* server-owned, from add-to-cart onward.
## 2. Entities
```ts
interface Cart {
id: string;
marketplaceId: string;
customerId?: string; // set for authenticated customers
sessionToken?: string; // set for guest carts
createdAt: string;
expiresAt: string; // TTL for inactive carts
}
interface CartLine {
id: string;
cartId: string;
offerId: string; // never a client-supplied price - see Phase 1 §5
qty: number;
addedAt: string;
}
interface CheckoutSession {
id: string;
cartId: string;
customerContact: { email?: string; phone?: string; verified: boolean };
deliveryOptionId: string;
status: 'open' | 'confirmed' | 'expired';
createdAt: string;
expiresAt: string;
}
interface DeliveryOption {
id: string;
marketplaceId: string;
label: string;
price: Money;
type: 'pickup' | 'courier' | 'digital';
}
```
## 3. Cart endpoints
```
POST /api/v2/storefront/cart/lines { offerId, qty }
PATCH /api/v2/storefront/cart/lines/{lineId} { qty }
DELETE /api/v2/storefront/cart/lines/{lineId}
GET /api/v2/storefront/cart
```
Invariants:
- Idempotent add/update/remove.
- Quantity validated against `Offer`/`InventoryRecord` (Phase 3) on every mutation, not just at checkout.
- Guest cart identified by `sessionToken` (cookie or header); authenticated cart bound to `customerId`. Adding to a guest cart, then logging in, must merge into the customer's cart — not silently drop items.
- Inactive carts and their `StockReservation`s (Phase 3 §3) clear on `expiresAt`.
## 4. Price-refresh rule
If an offer's price changed since it was added to the cart, `GET /api/v2/storefront/cart` returns both the line's captured price and the current price, with a `priceChanged: boolean` flag. The frontend must show this and require explicit confirmation before checkout proceeds if the total moved — this is a UX requirement on the frontend, but the backend must expose the comparison, not silently use whichever price it prefers.
## 5. Checkout session
Builds directly on [Phase 1 §5.2](PHASE-1-MONEY-FX-PAYMENTS-CONTRACT.md#5-server-authoritative-checkout-amount):
```
POST /api/v2/storefront/checkout { cartId, currency, deliveryOptionId }
```
reads the server-owned `Cart`/`CartLine`s directly (no client-supplied offer list needed anymore, unlike the Phase 1 doc's example which pre-dates the server cart). Response shape unchanged from Phase 1 §5.2.
Additional checkout-time validation beyond Phase 1:
- Contact requirement enforced per tenant policy: email and/or phone must be present and (if the tenant requires it) verified before `CheckoutSession.status` can move to `confirmed`.
- Guest checkout allowed/disallowed per tenant policy (`MarketplaceFeatureSet`, see [Phase 9](PHASE-9-TENANT-REGISTRY-DOMAINS-CONTRACT.md)).
## 6. What the frontend will start doing once this ships
- Build the `features/website/checkout/` module for real — currently an empty directory.
- Retire `localStorage`/Telegram-CloudStorage cart persistence.
- Show the price-refresh confirmation UI described in §4.
- Delete the client-side offer/qty tracking currently duplicated inside the cart popup component.