# Product Plan v3.1 — What They Want vs. What We Have

**Source:** `Marketplaces-Platform-Product-Plan-v3.1.pdf` (27 pages, RU). Version block inside still reads `3.0 / 17 августа 2026` — the filename says v3.1. Section 14 is the v3.1 addition (appended after the document's own conclusion).

**Our side, as verified in this repo:** Angular frontend only (426 `.ts` files). Sources for "what we have": [BACKEND-API-REFERENCE.md](../BACKEND-API-REFERENCE.md), [GAPS-AND-IMPROVEMENTS.md](../GAPS-AND-IMPROVEMENTS.md), and direct source inspection.

---

## 1. What they are actually asking for

One sentence: **stop building storefronts, build a platform** — a single multi-tenant commerce core where launching a new marketplace is a configuration act, not an engineering project.

Their own acceptance bar (§"ГЛАВНЫЙ КРИТЕРИЙ" and §13):

> A real product walks the whole path: seller → catalog → storefront → cart → checkout → payment → order → notification → fulfillment → reconciliation.

Three things the document is really about, under the product language:

1. **They do not trust our numbers.** Traffic counters, payment timings, order totals and currency amounts are all called out as unexplainable. §10.2 says it outright: don't fix appearance, fix the data.
2. **They suspect demo behaviour in production.** "No fixed 5-second payment", "no synthetic traffic in production analytics", "no special branch for banks/inspectors" (§3.2, §3.1, §3.6, §10.2, and again in the launch checklist). This is an audit/compliance posture, not a feature request — a bank or NSPK is checking this platform.
3. **Commerce Core is no longer optional.** In v3.0 language, Catalog/Seller Portal/Cart/Checkout/Payments/Orders stopped being "a possible extension" and became mandatory platform modules. Gorbushka is demoted to "one tenant scenario" (§11) — it does not define the architecture.

**Launch blockers they define (§3, "LAUNCH BLOCKERS"):** all P0s — money/FX, payment timeline, notifications, external order ingestion, price traceability, guaranteed fulfillability of published offers.

---

## 2. What is new in v3.1 vs v3.0

Everything in **§14 "Customer Identity и коммуникация после покупки"** (pages 26–27). Nothing else in the document is marked as changed.

| New in v3.1 | Detail | Our state |
|---|---|---|
| **VK ID as primary social login** | Backend completes OAuth 2.1/PKCE, links external identity to `Customer` | Zero. No `vk` reference anywhere in source; one `oauth` reference total. |
| **MAX messenger bot** | Bot-assisted account linking via one-time code; official MAX Bot API | Zero. |
| **Telegram demoted** | Kept, but as *one* identity provider among several | Today Telegram is the **only** login for both customers and admins. |
| **Notification Orchestrator** | Routes `order.paid` to the customer's chosen channel; backoffice notification always fires even if the messenger is down | Zero. |
| **Delivery Conversation State Machine** | `not_started → awaiting_customer → details_received → manager_assigned/auto_confirmed → shipment_planned → completed`, bot collects delivery details, manager handoff | Zero. |
| **Channel choice in checkout** | "Where should we send confirmation?" — VK / MAX / Telegram / email-SMS fallback, recorded in `OrderContactSnapshot` | Zero. |
| **`ExternalIdentity` / `ContactChannel` / `BotConversationBinding` / `MessagingConsent`** | Four new entities | Zero. |

**Manager note:** §14 partially collides with our approved [email/phone OTP login spec](superpowers/specs/2026-08-15-email-phone-login-design.md). v3.1 keeps email/phone but reduces them to *recovery/fallback* when a messenger is unavailable. Our in-flight work is still valid, but its priority drops below VK ID. Needs a call before that spec is implemented.

---

## 3. The differences — detailed

Legend: ✅ have · 🟡 partial / mock only · ❌ missing · ⚠️ conflicts with something we already decided.

### 3.1 Platform components (§1.1) — 8 named components, we have 2

| Plan component | Our state |
|---|---|
| Storefront Runtime | ✅ Bootstrap-driven, tenant-configured, no per-project fork. This is our strongest match to the plan. |
| Platform Backoffice | 🟡 14 admin modules exist, but only **Categories** has a real HTTP backend. 9 of 11 admin domains inject their mock gateway directly — no DI seam to swap at all. |
| Platform API | 🟡 Storefront catalog/search/cart-payment are live; everything admin-side is mock. |
| Seller Portal | ❌ A static placeholder page, feature flag `false` by default, zero backend bytes, zero `HttpClient` reference. |
| Workers / Event Processing | ❌ Nothing. No event bus, no retry, no dead-letter. |
| Integration Hub | ❌ Nothing. Zero `reconcil*`, zero `idempot*` in the whole codebase. |
| Domain Automation | ❌ Nothing. Zero `hostinger` references — the plan's §8.2 lists seven Hostinger DNS endpoints we have never touched. |
| Marketplace Registry / Launch Center | ❌ Nothing shipped. Closest thing is our unshipped [super-admin Phase 1 design](superpowers/specs/superuser.md), which covers cross-tenant *viewing* but not registry/feature-set/launch. |

### 3.2 Catalog model (§2.1) — the biggest structural gap

The plan's core catalog idea is a **two-layer split**: `Product` (content card) vs. `Offer/Listing` (the seller's commercial proposition, which owns price, stock, currency, status). Order lines then snapshot the offer.

| Plan entity | Our state |
|---|---|
| `Product` / `Variant` / `SKU` | 🟡 Exists as admin mock + a separate live storefront `Item` domain. Two unrelated `Category` types, both fed by the same response, both in use. |
| `Offer / Listing` | ❌ Does not exist. Price and stock hang off the product. Multi-seller pricing on one product card is not expressible. |
| `PriceSnapshot` | ❌ Does not exist. |
| `InventoryRecord` (available/reserved/sold) | ❌ Does not exist. No reservations, no TTL, no oversell queue. |
| `PriceHistory` | ❌ Does not exist. |
| Draft → moderation → published → paused/archived | 🟡 An admin Moderation module exists, on mock data. |
| Bulk import CSV/API with pre-apply error preview | ❌ Only bulk *edit* actions inside Admin Categories. No import pipeline. |
| "Storefront search/filters run on published data, not local mock arrays" | ⚠️ Directly aimed at us. `PRODUCT_DATA_PROVIDER` and `CATEGORY_REPOSITORY` silently always resolve to the real API — but Search, wishlist/compare, cart contents and CMS are entirely `localStorage`. |

### 3.3 Money, FX and price traceability (§2.3, §3.3, §3.8, §7)

This is where the plan is most explicit, and where we most clearly do the forbidden thing.

| Plan requirement | Our state |
|---|---|
| `Money = amountMinor + currency`, **no float for money math** | ⚠️ We use plain `number` prices and float division/multiplication in `CurrencyRatesService.convert()`. |
| Rates come from a configurable **external source** with `source`, `rate`, `timestamp`, `TTL` | ⚠️ Rates are **hand-typed by an admin** into Admin Settings and stored in **browser `localStorage`** (`currencyRates.v1`), with hardcoded fallbacks (`USD: 0.011`, `AMD: 4.3`). They never update and drift from market. |
| `FxQuote { base, quote, rate, source, observedAt, expiresAt, quoteId }` | ❌ Does not exist. |
| Stale-quote control blocks checkout | ❌ Does not exist. |
| Checkout writes an immutable price snapshot; old orders never recalculated | ❌ Does not exist. |
| `PriceBook` (base currency + allowed display/checkout currencies) | ❌ Does not exist. |
| Backoffice shows the total formula: lines × qty − discounts + delivery + fees, plus the FX quote used | ❌ Does not exist. |
| Reconciliation of internal orders vs. provider transactions | ❌ Does not exist (`reconcil*` = 0 hits repo-wide). |

**Nuance worth telling them:** their §3.3 complaint is *"switching RUB/USD/AMD keeps the same number"*. Our storefront **does** convert the displayed number. Their real, unstated problem is the one our own [§12.7](../BACKEND-API-REFERENCE.md) already flagged: the **charged** amount is computed client-side in RUB and posted to `/cart` as `amount`, so bank settlement totals don't reconcile against order counts. We agree with the plan here — we raised it first.

### 3.4 Cart and Checkout (§2.5, §2.6) — ⚠️ head-on conflict with a frozen system

| Plan requirement | Our state |
|---|---|
| Cart is **server-side**, keyed on `offerId` | ⚠️ Cart is `localStorage` + Telegram CloudStorage. There is no backend cart at all. |
| "Client never sends a trusted price to the server" | ⚠️ `CartPaymentRequest` sends `amount`, `currency`, and a per-item `price` array from the browser. This is exactly the pattern the plan forbids. |
| Checkout is a **server session** producing a price snapshot + contact snapshot | ❌ Checkout is an inline popup in `pages/cart/cart.component.ts` (751 lines). `features/website/checkout/` is an empty directory. |
| Idempotent order creation keyed on the payment | ❌ `/orders` is called fire-and-forget after payment success. Zero `idempot*` in the codebase. |
| Backend re-validates offers/stock at checkout | ❌ No stock concept exists to validate. |
| Multi-seller cart grouped by seller and fulfillment rules | ❌ Undefined behaviour — already flagged in our own gaps doc. |
| No duplicate payment intents on double-click | 🟡 Popup state guards the UI; nothing server-side. |

**Blocker:** [BACKEND-API-REFERENCE.md §7](../BACKEND-API-REFERENCE.md) states *"Payments are frozen — this call chain is explicitly out of scope for changes."* The plan's P0-A and P0-C cannot be delivered without unfreezing it. **This needs an explicit decision from whoever froze it.**

### 3.5 Payments (§2.7, §3.2)

| Plan requirement | Our state |
|---|---|
| Explicit state machines: `PaymentIntent` / `Payment` / `Order` | ❌ None. Payment status is a client-side signal with values `creating/waiting/success/timeout/error`. |
| Webhook signature verification + idempotency | ❌ None. `webhook` appears only as a display field in the admin **monitoring mock**. |
| Store `provider event id`, `provider timestamp`, `receivedAt`, `processedAt` | ❌ None. |
| "No artificial fixed delays" | ✅ **We already comply.** We poll real provider status (`/qr/dynamic/{partnerId}/{qrId}`, `/card/{partnerId}/{orderId}`) on an interval bounded by the QR TTL. There is no 5-second timer in this codebase. |
| Refunds as a first-class operation with reason/actor/order-line link | ❌ `requestRefund(id)` exists only as a mock gateway method. |
| Reconciliation queue | ❌ None. |

**Ask them:** §3.2 describes a fixed 5-second payment. We cannot reproduce it here. Either they observed a different build/environment, or they inferred it from the *admin* mock data. Worth pinning down before we spend P0 budget on a problem that may not be ours.

### 3.6 Orders and Fulfillment (§2.8, §3.6)

| Plan requirement | Our state |
|---|---|
| Canonical `Order` regardless of source (storefront / external marketplace / backoffice / API partner) | ❌ Admin Orders is a **static 24-row in-memory seed with no create path**, and no DI token to swap it. |
| `OrderLine` with SKU/title/price snapshots | ❌ |
| `Source mapping` (`externalMarketplace`, `externalOrderId`, `connectorId`) | ❌ |
| `Fulfillment` (manual / warehouse / pickup / digital) with evidence | ❌ One `fulfil*` hit in the entire codebase. |
| `Timeline` of all order events | ❌ Already logged as our own frontend-blocked TODO ("Real order audit trail"). |
| Admin actions: assign, resend notification, replay sync, cancel/refund by permission | ❌ |
| **No special branch for inspectors — any published, available product must be genuinely buyable and fulfillable** | ❌ We have no publish-time executability validation and no fulfillment flow, so we cannot currently *prove* compliance either way. |

### 3.7 Customer identity (§2.9, §3.4, §14)

| Plan requirement | Our state |
|---|---|
| `Customer` + multiple `ExternalIdentity` + verified `ContactMethod` | ❌ Telegram user is effectively the customer identity. |
| `emailVerifiedAt` / `phoneVerifiedAt` / `telegramLinkedAt` | ❌ |
| Order contact snapshot, immutable after order creation | ❌ |
| Email/phone OTP | 🟡 **Designed, not built** — spec approved 2026-08-15. |
| VK ID / MAX | ❌ New in v3.1, nothing exists. |
| Guest checkout toggled by tenant policy | ❌ |

### 3.8 Notifications (§2.10, §3.5)

| Plan requirement | Our state |
|---|---|
| Platform event bus emitting `order.created` / `order.paid` / `payment.failed` / `webhook.error` / `stock.low` / `oversell` / `refund.*` / `external_order.imported` | ❌ |
| Notification with `unread/read`, `severity`, `marketplaceId`, entity type/id, **deep link** | 🟡 `AdminOrderWatcherService` polls for new orders and toasts/badges the admin — the right shape, wrong data source. |
| Unread counter + filter by marketplace / event type in backoffice | 🟡 Partial (counter yes, marketplace filter no). |
| External channel delivery status logged; a Telegram/email failure must not lose the internal notification | ❌ |

**Status:** the notification feature is built and **functionally inert** — it polls the mock Orders gateway, which has no create path, so no new order can ever appear. It starts working the day Orders gets a real backend, with no further frontend change.

### 3.9 Analytics (§3.1, §6.3)

| Plan requirement | Our state |
|---|---|
| Server-side event logging: `session_started`, `page_view`, `product_view`, `add_to_cart`, `checkout_started`, `payment_started/success/failed`, `order_created` | ❌ **No tracking pipeline exists at all.** Not a missing endpoint — missing infrastructure. Our own docs rate it the single largest remaining backend effort. |
| Operational metrics: notification latency, fulfillment time, connector lag, webhook lag | ❌ |
| Quality metrics: frontend/backend errors, checkout validation failures, FX stale blocks | ❌ |
| Real funnel in backoffice | ❌ Admin Analytics composes five mock gateways and has no data source. |
| Synthetic traffic technically separated from production analytics | ⚠️ Cannot comply — there is no production analytics to separate it from. |
| Product view counts | 🟡 A "Views" column was shipped in Admin Products; it always renders `0` because no tracking source exists. Storefront `Item.visits` is live-wired but displayed nowhere. |

### 3.10 Backoffice navigation (§4.1) — 12 required sections, 5 missing outright

Have (mock unless noted): Overview/Dashboard, Catalog (Categories real, Products mock), Orders, Payments partial (Transactions), Customers, Notifications partial, Content & Design (builder/CMS, `localStorage` only), Monitoring, Reports, Users, Settings.

Missing entirely:

- **Marketplaces** — registry, type, status, domains, currencies, feature set, responsible manager. Nothing.
- **Sellers** — organizations, applications, roles, listings, integration health. Placeholder page only.
- **Payments & Finance** — refunds, reconciliation, unmatched events, settlements. `settlement*` = 0 hits.
- **Integrations** — external connectors, payment providers, FX sources, messaging. Nothing.
- **Domains & Releases** — DNS/SSL, staging, production, health checks, rollback. Nothing.
- **Audit & Security** — role changes, sensitive actions, login/security events, exports. `audit` appears only as display fields on mock models.

### 3.11 Roles and RBAC (§4.4, §10.1) — ⚠️ our most serious security gap

The plan specifies three scopes and 17 named roles (5 platform, 7 marketplace, 5 seller).

Our state: **the admin role model is decorative.** `AdminRole` and permissions exist as types, but nothing gates any button, page or action anywhere in the app. Anyone who passes admin authentication has full access. `AdminRole` is additionally defined twice with unrelated shapes.

Also missing from §10.1: idempotency keys, rate-limit handling (429 has zero client-side handling), step-up authentication for financial actions, audit log, PII minimisation policy.

### 3.12 External marketplace integrations (§5) — 0% built

Nothing in this section exists in any form: connector contract, webhook-preferred/polling-fallback ingestion, raw event storage, normalizer, SKU mapping, unmatched queue, exponential retry, dead-letter, manual replay, reconciliation, connector observability, and the proposed SLA (99% of webhook events processed under 60s, zero duplicate orders).

**Blocking unknown:** the plan never names which external marketplaces. Ozon? Wildberries? Yandex Market? Avito? Each is a separate connector with its own auth and rate limits. We cannot size this without the list.

### 3.13 Domains, publishing and tenant launch (§8)

| Plan requirement | Our state |
|---|---|
| Marketplace lifecycle `draft → configured → content_ready → domains_planned → staging_live → qa_passed → production_ready → live → paused/archived`, with the blocking item shown per transition | ❌ |
| DNS automation via Hostinger API (7 endpoints listed), snapshot + rollback, never touching MX/SPF/DKIM/DMARC/CAA, approval gate in production, propagation + SSL + health checks | ❌ Zero references. |
| Publish model: `draft → validation → preview → publish`, immutable published revision, rollback creates a new revision | 🟡 The builder edits an in-memory config and persists drafts to `localStorage`. "Publish" only promotes a local signal. No revisions, no server-side publish endpoint (`apiEndpoints.builder` is an empty placeholder). |
| Commerce data explicitly **not** part of content revisions | ✅ Structurally true today — orders/payments simply aren't in the revision at all. |

### 3.14 API boundaries (§9.3) — ⚠️ a naming migration we have not planned

Plan namespaces: `/api/v2/storefront/*`, `/api/admin/v2/*`, `/api/seller/v1/*`, `/api/identity/v1/*`, `/api/providers/v1/*`, `/api/integrations/v1/*`.

Ours: unversioned, flat — `/cart`, `/orders`, `/items`, `/category`, `/searchitems`, plus a separate `qrApiUrl` host. Our own reference says **"No API versioning scheme has been decided"**.

Adopting the plan's namespaces is a coordinated frontend+backend rename, not a config change. It should be sequenced *before* the new commerce endpoints are built, not after.

Also in §9: the plan's error model assumes a structured envelope. Ours is a proposal only — no interceptor inspects error bodies today; every error reaction happens at raw HTTP-status level.

### 3.15 Gorbushka as a tenant (§11)

The plan lists mall-directory content entities: `Shop`, `ShopCategory`, `Service`, `Floor`, `SchemePin`, `RentListing`, `News/Promo`, `StaticPage`, `Lead`, `MallSettings` — each with `marketplaceId`, audit, and publish/preview.

We have: static pages inside the bootstrap document. None of the other nine entity types exist, and CMS content has no backend write path at all.

Positive read: the plan explicitly says Gorbushka must **not** dictate platform architecture, and that the existing frontend is UX reference only. That matches our ADR-0001 constraint ("frontend must not contain marketplace-specific code"). No conflict here — just unbuilt scope.

### 3.16 Definition of Done (§13) — where we stand today

Of the 13 launch-checklist items, we can currently claim **zero** as green. Additionally, our own QA position makes their DoD hard to evidence:

- ~32% statement coverage, ~19% branch coverage, 11 spec files repo-wide.
- **Zero E2E tests** — no Playwright/Cypress config anywhere. The plan's acceptance criteria are all end-to-end by construction.
- Several past "verified live" claims were code-inspection only, because `/edit` and `/backoffice` require Telegram admin login that automated environments cannot complete.

---

## 4. What we have that the plan does not account for

Not gaps — assets and risks they should know about before sequencing:

1. **Project editor / builder** (~1.0 MB lazy chunk) — a full visual site builder. The plan's §8.3 publish model would replace its persistence layer entirely.
2. **Ed25519 challenge/response admin auth** — fully wired client-side, backend returns 404 today. The plan never mentions it; it assumes conventional RBAC.
3. **Widget manifest / dynamic renderer** — the mechanism that makes one storefront runtime serve many tenants. This is the part of the plan we have *already* solved and should defend.
4. **Super-admin Phase 1 design** (`docs/superpowers/specs/superuser.md`) — cross-tenant read-only view. Overlaps §4.3 Marketplace Registry and §10 audit. Worth re-scoping against the plan rather than building as specified.
5. **Three in-flight items already answer v3.0 P0s:** admin purchase notifications (§3.5), admin product views column (§3.1), email/phone OTP login (§3.4). Two of the three are inert until a real backend exists.

---

## 5. Manager's read — the honest framing

**Split of ownership.** Roughly 80% of this document is backend and platform-service work: Platform API, Workers/Event Processing, Integration Hub, Domain Automation, payment state machines, reconciliation, analytics pipeline. This repository is a frontend. Of the plan's ~14 sections, only Storefront Runtime (§6.1) is substantially delivered, and it is delivered *well*.

**The real message is trust, not features.** Every P0 in §3 is a variant of "we cannot explain your numbers." Sequencing should follow that: traceability first (money model, price snapshot, payment timeline, audit), feature breadth second. That happens to also be the plan's own P0-A ordering.

**The largest single risk is not scope — it is the frozen payment chain.** Cart is client-owned, price is client-supplied, orders are fire-and-forget, and the whole chain is marked "do not modify." Three P0s sit behind that freeze. Nothing else in this list can be honestly estimated until that decision is reversed or explained.

**Second risk: RBAC.** The plan assumes 17 enforced roles across three scopes. We enforce none. Any real admin backend going live before this is fixed hands full platform access to every authenticated operator.

---

## 6. Decisions — answered 2026-08-17

See [PRODUCT-PLAN-v3.1-DELIVERY-PLAN.md](PRODUCT-PLAN-v3.1-DELIVERY-PLAN.md) Sprint 0.1 for the full record and downstream consequences. Summary:

1. **Backend ownership — answered 2026-08-18.** A separate backend developer implements against `docs/backend/`. This repository owns the frontend and the contract set.
2. **Payment chain — unfrozen. Yes.** Phases 1, 6, 7 proceed.
3. **External marketplaces — no fixed list.** Connectors onboard partners as they arrive; build the Phase 4 framework config-driven/generic, not per-named-provider.
4. **FX rate source — ours, in-house, as a safety gate.** No external provider committed; backend computes FX authoritatively until/unless one is chosen later.
5. **§14 vs. OTP — VK ID first, then everything else** ("do all after vk"). Phase 8 resequenced.
6. **Multi-seller orders — unified.** One `Order` per checkout, seller-scoped `Fulfillment` groups internally. Resolves the three-document disagreement.
7. **"Fixed 5-second payment" — resolved as a non-issue.** `PAYMENT_POLL_INTERVAL_MS` is already `5000` — that's poll cadence against real provider status, not an artificial delay. Confirmed compliant, no change needed.
8. **API namespace — new endpoints only, no forced migration.** `/api/v2/...` used for all new Phase 1+ contracts; legacy endpoints stay as-is pending a dedicated migration sprint.
9. **Document version — v3.1 is canonical.** The source PDF's internal "3.0" version block is stale.

---

## 7. Suggested first slice (if they want a proposal back)

Following their own dependency order, restricted to what is buildable and provable:

1. **Money model + FX quote + price snapshot** (P0-A) — needs the payment freeze lifted. Removes client-supplied `amount`, kills the float math, gives every total an explainable formula. This one item closes §3.3, §3.8 and half of §13.1.
2. **Order canonical model + timeline + notification wiring** (P0-B) — the notification feature already exists and switches on for free.
3. **RBAC enforcement** — not on their P0 list, but it is the gate on everything else in the backoffice going live safely.
4. **Analytics event pipeline** (P0/§3.1) — long lead time, so start it in parallel rather than last.

Explicitly *not* in a first slice: Seller Portal, external connectors, domain automation, VK/MAX bots. All of them depend on the commerce core being real first, which is what the plan itself says in §12.1.
