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

3.8 KiB
Raw Blame History

Phase 6 Backend Contract — Server Cart + Checkout Session

Companion to PRODUCT-PLAN-v3.1-DELIVERY-PLAN.md Phase 6 (Sprints 6.16.2). Extends Phase 1 §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

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 StockReservations (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:

POST /api/v2/storefront/checkout    { cartId, currency, deliveryOptionId }

reads the server-owned Cart/CartLines 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).

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.