Files
marketplaces/docs/backend/PHASE-8-IDENTITY-MESSAGING-CONTRACT.md
sdarbinyan ec6760ac65 docs: backend contracts for Phases 8-10 (identity/messaging, tenant registry, content modules)
- Phase 8: Customer/ExternalIdentity/ContactChannel, VK ID OAuth 2.1/PKCE
  built first per Sprint 0.1 ("do all after vk"), then OTP, then MAX/
  Telegram bot linking, then the Notification Orchestrator + Delivery
  Conversation State Machine. Hard rule carried through: bots never touch
  financial statuses, only delivery fields via a dedicated Delivery Service.
- Phase 9: Marketplace/MarketplaceDomain/MarketplaceFeatureSet/
  MarketplaceRevision, full Hostinger DNS automation sequence (snapshot
  before change, never touch MX/SPF/DKIM/DMARC/CAA), lifecycle state
  machine that must expose its own blocker on every transition, publish
  model with orders/payments/inventory explicitly excluded from revisions.
- Phase 10: Gorbushka-class content entities (Shop/Service/Floor/
  SchemePin/RentListing/News/Lead/MallSettings), explicitly lowest
  priority and gated on Commerce Core being real first.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 23:05:39 +04:00

5.8 KiB
Raw Blame History

Phase 8 Backend Contract — Customer Identity, VK ID, MAX/Telegram Messaging

Companion to PRODUCT-PLAN-v3.1-DELIVERY-PLAN.md Phase 8 (Sprints 8.18.5). Covers plan §2.9, §3.4, and all of §14 (the v3.1-only addition).

Status: ready to build. Sprint order fixed by Sprint 0.1 decision: VK ID first, then everything else ("do all after vk"). Sequence below follows that: identity core → VK ID → email/phone OTP → MAX/Telegram → Notification Orchestrator.


1. Entities

interface Customer {
  id: string;
  marketplaceId: string;      // or global identity strategy, tenant-configurable
  name?: string;
  email?: string;
  phone?: string;
  status: 'active' | 'suspended';
  createdAt: string;
}

interface ExternalIdentity {
  customerId: string;
  provider: 'vk_id' | 'telegram' | 'max';
  providerUserId: string;
  verifiedAt: string;
  metadata: Record<string, unknown>;
  lastUsedAt: string;
}

interface ContactMethod {
  customerId: string;
  type: 'email' | 'phone';
  value: string;
  verifiedAt?: string;
}

interface ContactChannel {
  customerId: string;
  provider: 'telegram' | 'vk' | 'max';
  chatId: string;
  verified: boolean;
  notificationsEnabled: boolean;
  deliveryEnabled: boolean;
}

interface MessagingConsent {
  customerId: string;
  channel: string;
  purpose: 'marketing' | 'order_service_messages';
  grantedAt?: string;
  revokedAt?: string;
}

Telegram is demoted from sole identity to one ExternalIdentity provider among several — it must remain fully functional, just no longer the only path.

2. Sprint 8.2 — VK ID (build first)

GET  /api/identity/v1/vk/authorize     -> redirects into VK's OAuth 2.1/PKCE flow
POST /api/identity/v1/vk/callback      { code, codeVerifier } -> completes OAuth **backend-side**,
                                          links ExternalIdentity, returns session

Invariants:

  • OAuth completion happens entirely backend-side; the VK client secret never reaches the frontend.
  • A repeat login for the same providerUserId must resolve to the same Customer, never create a duplicate.
  • If providerUserId is already linked to a different Customer than the one currently authenticated (or none), this is an identity conflict — route to controlled resolution, never silently overwrite the existing binding (plan §14.3).

3. Sprint 8.3 — Email/phone OTP (after VK ID)

Implements the already-approved email/phone login spec. Per v3.1 §14, position this as recovery/fallback when a messenger channel is unavailable — not the primary login path. No new contract beyond that spec; this section exists only to fix its place in the build order relative to VK ID.

4. Sprint 8.4 — MAX + Telegram bot channels

interface BotConversationBinding {
  customerId: string;
  marketplaceId: string;
  provider: 'telegram' | 'max';
  chatId: string;
  state: string;              // see §5 state machine
  orderId?: string;
  lastMessageAt: string;
}

MAX linking flow (bot-assisted, one-time code):

POST /api/identity/v1/max/link-code       -> { code, expiresAt }  (TTL, single-use, bound to marketplace + browser session)

User opens the MAX bot, sends the code; a confirmed bot update on the backend calls:

POST /api/providers/v1/max/bot-webhook    -- idempotent; a repeated update must not create a duplicate binding

which links the pending Customer session to the MAX chatId.

All three providers' incoming bot updates (VK, MAX, Telegram) normalize into one shape:

interface MessagingEvent {
  provider: 'telegram' | 'vk' | 'max';
  chatId: string;
  orderId?: string;
  text?: string;
  receivedAt: string;
}

Provider bot tokens/secrets never reach the frontend, ever — only the backend calls each provider's Bot API.

5. Sprint 8.5 — Notification Orchestrator + Delivery Conversation State Machine

On order.paid (Phase 2 event bus), the orchestrator picks the customer's chosen channel (captured at checkout, see Phase 6 and OrderContactSnapshot in Phase 2) and drives:

not_started -> awaiting_customer -> details_received -> manager_assigned/auto_confirmed -> shipment_planned -> completed
interface DeliveryDetailsSnapshot {
  orderId: string;
  city?: string;
  address?: string;
  recipientName?: string;
  phone?: string;
  timeWindow?: string;
  comment?: string;
  receivedAt: string;
}

Hard rules:

  • The bot never changes financial statuses. It can only write DeliveryDetailsSnapshot fields via a dedicated Delivery Service — no bot code path touches Order.paymentStatus/orderStatus.
  • The backoffice Notification (Phase 2 §6) fires unconditionally on order.paid, independent of whether the customer's messenger channel is reachable.
  • If the chosen channel is unavailable, log a DeliveryAttempt error (Phase 2 §6) and fall back per tenant-configured policy (e.g. email/SMS) — never block the order itself.
  • Follow-up messages are rate-limited per tenant policy; after the configured attempt limit, hand off to a human manager instead of continuing to message.
POST /api/providers/v1/{provider}/bot-webhook   -- generic entrypoint for all three providers
GET  /api/admin/v2/orders/{orderId}/conversation -- message history + current state, for manager handoff
POST /api/admin/v2/orders/{orderId}/conversation/handoff

6. What the frontend will start doing once this ships

  • VK ID login button + OAuth redirect flow on storefront (primary social login).
  • MAX/Telegram linking UI (one-time code flow).
  • Checkout channel-choice step ("where should we send confirmation?") — VK / MAX / Telegram / email/SMS fallback.
  • Manager-facing conversation view (message history, current delivery state, accept handoff).