- 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>
89 lines
4.3 KiB
Markdown
89 lines
4.3 KiB
Markdown
# 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.1–5.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.
|