Files
marketplaces/docs/backend/PHASE-5-SELLER-PORTAL-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

4.3 KiB
Raw Blame History

Phase 5 Backend Contract — Seller Portal

Companion to PRODUCT-PLAN-v3.1-DELIVERY-PLAN.md Phase 5 (Sprints 5.15.3). Depends on Phase 3 (Offer) and Phase 2 (unified Order + Fulfillment).

Status: ready to build behind the launch gate. Frontend note: Seller Management is currently a static placeholder, feature-flagged off by default, with zero backend bytes and zero HttpClient reference — this contract is a from-scratch build, not a gateway swap.


1. Multi-seller model reminder

Per the Phase 2 unified-orders decision: a seller never owns a separate Order. They see the Fulfillment group(s) that belong to them within shared orders, and the OrderLines scoped to their sellerId. All endpoints below are pre-filtered server-side to the authenticated seller — never trust a frontend-supplied sellerId filter.

2. Entities

interface SellerOrganization {
  id: string;
  marketplaceId: string;
  legalName: string;
  status: 'pending' | 'approved' | 'suspended' | 'rejected';
  bankDetailsRef: string;  // pointer into secret storage, never raw account numbers over the wire
  createdAt: string;
}

interface SellerUser {
  id: string;
  sellerOrganizationId: string;
  role: 'SELLER_OWNER' | 'SELLER_CATALOG_MANAGER' | 'SELLER_ORDER_MANAGER' | 'SELLER_FINANCE_VIEWER' | 'SELLER_VIEWER';
  email: string;
  status: 'active' | 'invited' | 'suspended';
}

interface SellerMarketplaceMembership {
  sellerOrganizationId: string;
  marketplaceId: string;
  status: 'pending' | 'approved' | 'suspended';
}

interface SellerIntegration {
  sellerOrganizationId: string;
  apiCredentialRef: string;
  webhookUrl?: string;
  lastSyncAt?: string;
  lastSyncError?: string;
}

3. Endpoints (all scoped server-side to the authenticated seller's org)

POST /api/seller/v1/onboarding                 { legalName, contacts, marketplaceId }
GET  /api/seller/v1/profile
GET  /api/seller/v1/offers?status=&page=
POST /api/seller/v1/offers
PATCH /api/seller/v1/offers/{id}
POST /api/seller/v1/offers/bulk-price-update    -- mass price/stock edit, see Phase 3 §6 for the shared bulk-import pattern
GET  /api/seller/v1/orders?fulfillmentStatus=
PATCH /api/seller/v1/orders/{orderId}/fulfillment/{fulfillmentId}   { status, evidence }
GET  /api/seller/v1/finance/accruals
GET  /api/seller/v1/finance/settlements
POST /api/seller/v1/finance/bank-details        -- step-up auth + audit event required, see §5
GET  /api/seller/v1/team
POST /api/seller/v1/team/invite                 { email, role }
GET  /api/seller/v1/integrations

4. Roles (fixed set, enforced backend-side)

SELLER_OWNER            - full access within the org
SELLER_CATALOG_MANAGER  - offers/catalog only
SELLER_ORDER_MANAGER    - orders/fulfillment only
SELLER_FINANCE_VIEWER   - read-only finance
SELLER_VIEWER           - read-only everything

No UI-only gating. Every endpoint above checks SellerUser.role server-side regardless of what the frontend renders — this is the same principle as Track S, scoped to the seller domain specifically.

5. Sensitive-action rules

  • Bank/payment detail changes (POST .../finance/bank-details) require step-up authentication, produce an audit event, and — if maker/checker mode is enabled for the tenant — require a second approver before taking effect.
  • A seller can never query, by any endpoint or parameter manipulation, another seller's products, orders, customers, finance data, or API keys. This must be enforced at the query layer (implicit WHERE sellerOrganizationId = :authenticatedSeller), not left to the frontend to "not ask for it."

6. What the frontend will start doing once this ships

  • Replace the static Seller Management placeholder with real screens: Onboarding, Catalog, Prices & Stock, Orders, Finance, Team, Integrations (per plan §2.2).
  • Resolve the two competing seller type shapes flagged in GAPS-AND-IMPROVEMENTS.md (SellerConfig in bootstrap models vs. Seller/SellerBranding in the domain layer) against this contract's SellerOrganization/SellerUser shapes.
  • First-ever exercise of the sellerManagement.enabled flag at true — write a fixture test, since it has never been tested at its real-world-eventual value.