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>
This commit is contained in:
sdarbinyan
2026-08-17 23:04:11 +04:00
parent c91b75c036
commit 707db6d43c
3 changed files with 270 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
# Phase 5 Backend Contract — Seller Portal
Companion to [PRODUCT-PLAN-v3.1-DELIVERY-PLAN.md](../PRODUCT-PLAN-v3.1-DELIVERY-PLAN.md) Phase 5 (Sprints 5.15.3). Depends on [Phase 3](PHASE-3-CATALOG-OFFER-FULFILLMENT-CONTRACT.md) (Offer) and [Phase 2](PHASE-2-ORDERS-NOTIFICATIONS-CONTRACT.md) (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 `OrderLine`s 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
```ts
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](TRACK-S-SECURITY-RBAC-CONTRACT.md), 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.