ArchitectureMarketplace — Roadmap technique détaillée

Marketplace — Roadmap technique détaillée

Pendant éditorial de marketplace.md. Trace ce qui est livré, ce qui manque, et l'ordre d'attaque. Mis à jour : 2026-09.

Pendant éditorial de marketplace.md. Trace ce qui est livré, ce qui manque, et l'ordre d'attaque. Mis à jour : 2026-09.

✅ V1 (déjà en prod)

  • Prisma schema complet : MarketplaceListing, VerifiedRevenue, Offer, DealThread, DealMessage, Order, Review, SavedListing. (BuyerProfile figurait ici : il a été retiré du schéma en juin 2026, cf. marketplace.md section 3.)
  • 11 API publiques + 2 admin.
  • Queue moderation admin (Upstash Redis zset + hash) + decision form.
  • Routes publiques /marketplace, /marketplace/[type], /marketplace/[type]/[slug], /marketplace/hire.
  • Composants shared (19 fichiers).
  • Email Resend offer OTP + offer received + lead.
  • Sitemap + robots + JSON-LD basique.
  • Stripe Checkout one-time + Stripe Tax + 3% fee.
  • Privacy mode (anonymous listings).
  • Token de preview privé pour partager paused listings.

✅ V1.1 (cette PR)

Schema Prisma

  • Indexes composites (type, status, publishedAt DESC) x2.
  • Indexes composites (sellerId, status, publishedAt DESC).
  • Indexes composites DealThread (sellerId, status, createdAt DESC) + buyer.
  • Index Review (listingId, createdAt DESC) + (listingId, verifiedBuyer, createdAt DESC).
  • Index Order (buyerId, status, createdAt DESC) + (listingId, status).
  • Index VerifiedRevenue (source, refreshFailedAt).
  • Unique constraint Offer (listingId, buyerEmail, status) : anti-doublon.
  • Index Offer (expiresAt, status) pour cron expiration.

Constants

  • src/types/marketplace-constants.ts — source unique pour TYPE_TO_PLURAL, PLURAL_TO_TYPE, LOWER_TO_TYPE, TYPE_TO_LOWER, TYPE_COPY, TYPE_ORDER, BUYABLE_TYPES, HIRE_TYPES, APPLY_TYPES, REVIEWABLE_TYPES, contactIntentFor(), categoryPathFor(), listingPathFor(), resolveTypeFromPlural(), resolveTypeFromLower().
  • Pages /marketplace, /marketplace/[type] migrées vers ces constants.

Sécurité & idempotence

  • Rate limit /api/marketplace/save : 50/min/user.
  • Rate limit /api/marketplace/recommendations : 30/min/IP + cursor pagination.
  • Rate limit routes admin POST/PATCH/DELETE (10-50/min/admin).
  • AdminAuditLog sur PATCH (avec diff before/after) + DELETE.
  • Checkout idempotent : réutilise Order PENDING + Stripe idempotencyKey.
  • Offers : dedup findFirst pre-check + catch P2002 → 409.
  • Reviews : upsert + agg refresh dans une $transaction.
  • Save : rateLimit + 404 explicite si listing introuvable.
  • Leads : dedup hash SHA-256 60s + Resend fail loggé structuré.
  • Slug uniqueness admin : catch P2002 + retry suffixe au lieu de boucle 1000× findUnique.

Frontend

  • force-dynamic → revalidate (1h index, 5min type+detail).
  • error.tsx + loading.tsx sur /marketplace.
  • Empty state CTA enrichi.
  • Pagination rel="prev" / rel="next" pour le crawl ranking.
  • og:image masqué en mode anonyme.

SEO

  • productJsonLd enrichi avec :
    • offers (price + priceCurrency + priceLabel) si prix ONE_TIME/SUBSCRIPTION.
    • aggregateRating si reviewsCount > 0.

Service unified

  • Cache Upstash Redis 5min sur listUnified() (clé anonyme, viewer flags joints au runtime).
  • Fusion findUnifiedBySlug + loadLegacyDetail dans le service : retourne { card, source, legacy }.
  • Structured logging sur fallback DB→JSON (monitoring migration).
  • trackListingView : catch + log silencieux.

Notifications

  • Template email marketplace-decision.tsx (approved/rejected).
  • Service marketplace-decisions.ts.
  • Wired dans decideDraft (queue admin) : best-effort, n'échoue jamais.
  • Resolve submitter email : User.email → details.email → noop.

Vendor dashboard skeleton

  • /[orgSlug]/~/listings — index + KPI rail + table.
  • /[orgSlug]/~/listings/[id] — detail + offres en attente + deals.

Docs

✅ V1.2 — Vendor edit + search + audit filters (livré cette PR)

  • ✅ Vendor edit form /[orgSlug]/~/listings/[id]/edit complet : rhf + zod + 3 sections (Identity / Pricing / Privacy) + delete dialog.
  • ✅ API vendor CRUD /api/vendor/marketplace/listings (GET/POST/PATCH/DELETE)
    • offer actions accept/reject.
  • ✅ Subscription pricing model supporté en checkout (mode: subscription, monthly interval).
  • ✅ Postgres tsvector full-text search via services/marketplace/search.ts
    • API /api/marketplace/search. Pondération A/B/C/D, cache Redis 5min.
  • ✅ Admin audit log filtres : actor / action / resource + cursor pagination.

✅ V1.3 — Deal management + queue filters + markdown preview + search UI + analytics (livré cette PR)

  • ✅ Deal management vendor :
    • /[orgSlug]/~/deals index + /[orgSlug]/~/deals/[id] detail.
    • Pipeline visualizer 7 stages + asset checklist + messages chat.
    • Stage actions (advance + sign nda/loi/apa) seller-only.
    • API : POST /api/vendor/marketplace/deals/[id]/messages|advance|sign.
  • ✅ Admin queue filters : type / boost / q via GET-form + cap 200 drafts loaded (vs 50 avant) pour absorber les filtres.
  • ✅ Bulk reject server action + UI sticky bar.
  • ✅ Markdown editor avec live preview toggle (rendu client-side via react-markdown + remark-gfm).
  • ✅ Public marketplace search UI : autocomplete debounced 250ms, keyboard nav (↑↓/Enter/Esc/), keyboard shortcut /, hits MarketplaceSearch.
  • ✅ Vendor analytics page /[orgSlug]/~/listings/analytics : totals all-time + KPIs 30j (offers / deals / orders / revenue / fee)
    • top 5 listings par saves.

✅ V2 — Cancel deal + buyer dashboard + screenshots + privacy preview UI (livré cette PR)

  • ✅ Cancel deal action (service + API + UI dialog avec warning escrow).
  • ✅ Buyer dashboard E2E :
    • /account/orders index avec status filter + total spent
    • /account/offers index avec status filter + dealThread link
    • /account/deals index buyer-side + /account/deals/[id] (non-org-scoped)
  • ✅ Screenshots :
    • Schema MarketplaceListing.screenshots: String[] (max 8).
    • ScreenshotsGallery component avec lightbox zoom.
    • ScreenshotsEditor + Vercel Blob upload (kind="screenshot", 10MB).
    • Wired dans vendor edit form + detail page (skip si anonyme).
  • ✅ Privacy preview token UI :
    • PreviewTokenCard component (rotate / revoke / copy share URL).
    • API /api/vendor/marketplace/listings/[id]/preview-token.
    • Audit log per action.
  • ✅ Dynamic OG images per listing :
    • /api/og étendu (subtitle + icon params).
    • buildMarketingMetadata ogSubtitle + ogIcon props.
    • Detail page passe listing tagline + iconUrl au générateur OG (mieux que iconUrl raw comme og:image 1200×630).

⛔ V2.5 — In-app notifications + bell (retiré)

Schéma Notification, service services/notifications, routes /api/notifications/*, composant NotificationsBell et SSE stream ont été retirés au profit d'une stratégie 100 % email (Resend). Les call sites in-app (notify(…)) ont été supprimés des services marketplace ; seuls les emails Resend restent.

✅ V3 — Bulk actions + cancel email + FTS index (livré cette PR)

  • ✅ Bulk vendor actions :
    • POST /api/vendor/marketplace/listings/bulk (pause / unpause / delete, max 50 IDs, owner-gated via filter sellerId).
    • BulkActionsBar sticky-bottom + ListingsTable client wrapper avec checkboxes Select all / per-row.
  • ✅ Cancel deal email Resend :
    • Template marketplace-deal-cancelled.tsx (eyebrow + raison + warning special escrow funded).
    • Service marketplace-deal-events.ts sendDealCancelledEmail.
    • Hook : cancelDeal service envoie l'email (best-effort).
  • ✅ FTS index helper :
    • Script pnpm db:marketplace:fts-index crée GIN expression-index avec setweight A/B/C/D matching le service search.ts.
    • Partial index supplémentaire WHERE status='LIVE' pour query principale.
    • Idempotent (CREATE INDEX IF NOT EXISTS).
    • À run quand volume dépasse ~10k listings.

✅ V4 — Stripe Connect + disputes + refund automation + timeline events (livré cette PR)

Stripe Connect (Express)

  • ✅ Schema StripeConnectAccount + StripeConnectStatus enum (PENDING / RESTRICTED / ENABLED / REJECTED).
  • ✅ Service services/stripe/client.ts (centralized Stripe factory).
  • ✅ Service services/stripe/connect.ts :
    • createConnectAccount (idempotent, Express type, embeds boostecom_user_id metadata).
    • createOnboardingLink (account_onboarding).
    • createDashboardLink (Express Dashboard login).
    • syncFromAccount (mirror flags + requirements + auto-notify ENABLED/RESTRICTED).
    • transferToConnectAccount (Stripe Transfer, idempotencyKey transfer:${orderId}).
  • ✅ API : POST /api/vendor/stripe/connect/onboard, GET /api/vendor/stripe/connect/status (with ?refresh=1 for live Stripe pull), POST /api/vendor/stripe/connect/dashboard.
  • ✅ Webhook : account.updated → syncFromAccount, payout.failed → notify seller.
  • ✅ Payout vendeur en escrow : le transfert net (amount - fee) part du cron marketplace-payouts une fois la fenêtre de dispute de 14j fermée et aucune dispute ouverte, puis notify MARKETPLACE_PAYOUT_SENT. Cette ligne a décrit un transfert immédiat depuis handleCheckoutCompleted, ce que la V4 a justement remplacé.
  • ✅ UI : /account/settings/payouts (status card + onboarding CTA + refresh button + FAQ), Payouts tab added to /account/settings layout.

Dispute resolution

  • ✅ Schema Dispute + DisputeStatus + DisputeReason enums.
  • ✅ Service services/marketplace/disputes.ts :
    • raiseDispute (14-day window enforcement, anti-doublon @unique).
    • sellerRespondToDispute (OPEN/AWAITING_SELLER → AWAITING_ADMIN).
    • adminResolveDispute (REFUND_FULL/PARTIAL/NO_ACTION + auto-fire Stripe refund).
    • withdrawDispute.
    • autoEscalateStaleDisputes (cron hook).
  • ✅ API : POST/GET /api/marketplace/disputes, GET/PATCH /api/marketplace/disputes/[id], POST /api/vendor/marketplace/disputes/[id]/respond, POST /api/admin/marketplace/disputes/[id]/resolve (admin-audit logged).
  • ✅ Email : MarketplaceDisputeEmail template (3 events × 2 audiences) + service services/email/marketplace-disputes.ts hooked dans raise / respond / resolve.
  • ✅ UI buyer : /account/disputes (index avec status filters) + /account/disputes/[id] (unified thread multi-role : seller form + admin form + buyer withdraw button selon le rôle du viewer).
  • ✅ UI vendor : /[orgSlug]/~/disputes (index avec deadline highlight).
  • ✅ UI admin : /admin/content/marketplace-disputes (queue avec priority sort) + Flag icon dans admin-routes.ts sous "Content" category.
  • ✅ Cron : marketplace-maintenance étendu avec auto-escalation des disputes OPEN > 7j (sellerDeadline trigger) → AWAITING_ADMIN.
  • ✅ RaiseDisputeDialog shared component (modal avec reason select + description + evidence URLs) wired in /account/orders/[id] (PAID + within 14d + no dispute) et /account/deals/[id] (escrow funded + buyer only).

Refund automation Stripe

  • ✅ Service services/stripe/refunds.ts refundOrder :
    • Crée stripe.refunds.create avec idempotencyKey unique.
    • Flip Order status REFUNDED + refundedAt.
    • Notify buyer (MARKETPLACE_REFUND_ISSUED).
  • ✅ adminResolveDispute branche refundOrder pour REFUND_FULL/PARTIAL (capture stripeRefundId).
  • ✅ Webhook charge.refunded sync local Order status (idempotent via updateMany filter status: { not: "REFUNDED" }, notify only on first flip).

Timeline events + foundation ML recos v2

  • ✅ Schema ListingEvent + ListingEventKind enum (VIEW, SAVE, UNSAVE, OFFER_CREATED, OFFER_ACCEPTED, OFFER_REJECTED, DEAL_STARTED, DEAL_CLOSED, CHECKOUT_STARTED, ORDER_PAID, REVIEW_POSTED, CONTACT_CLICK, EXTERNAL_CLICK).
  • ✅ Service services/marketplace/events.ts :
    • recordListingEvent (best-effort, VIEW dedup par visitor/day).
    • hashVisitor (sha256(ip + ua + day) : rotating daily salt).
    • getListingEventCounts (groupBy kind, last 30d).
    • getDailyListingEventTimeline (raw query Postgres date_trunc).
  • ✅ Hooked dans trackListingView (VIEW), toggleSave (SAVE/UNSAVE), /api/marketplace/offers POST (OFFER_CREATED), webhook checkout.session.completed (ORDER_PAID).

Helpers

  • isAdmin() soft check ajouté à @/lib/security/admin-guard (pour les routes qui doivent conditionnellement admin-gate sans redirect).
  • ✅ "Related listings" sur detail page : 6 cards same-type + tag overlap, service src/services/marketplace/related.ts.
  • ✅ Similarité Jaccard-like sur tags (Postgres hasSome) + tri tier / featured / reviewsAvg / savesCount.
  • ✅ Cache Redis 1h par (type, sourceId).
  • ✅ Consommé par /marketplace/[type]/[slug] (rail "Related", sous le rail "Customers also viewed" de la V5.1) et exposé par /api/marketplace/recommendations.

🚧 V2.3 — Filtres admin queue + batch actions (3 jours)

  • Filtres par type, status, date range, search query.
  • Bulk approve/reject avec mass-checkbox.
  • Notes internes (visible admin seulement).

✅ V5.1 — KYC pipeline + e-signature scaffold + admin metrics + sponsor (livré cette PR)

V5 polish (chantier précédent dans cette PR)

  • ✅ Vendor analytics timeline chart (recharts LineChart sur ListingEvent V4) : service getVendorEventTimeline() + composant EventTimelineChart wired dans /[orgSlug]/~/listings/analytics.
  • ✅ Per-listing analytics drill-down /[orgSlug]/~/listings/[id]/analytics avec window selector (7/14/30/60/90 days) + breakdown par kind.
  • ✅ Vendor payouts history + balance card via Stripe Connect API (getConnectPayoutsSummary + /api/vendor/stripe/connect/payouts).
  • ✅ Admin-only adminNotes field sur MarketplaceListing + AdminNotesPanel inline editor (rendered conditionnellement via isAdmin() server-side gate).
  • ✅ Collaborative filtering recommendations v2 : getCoOccurrenceRecommendations() SQL en 2 steps via ListingEvent V4 (WITH actors AS … FROM "ListingEvent" GROUP BY listingId). Cache Redis 1h. Rail "Customers also viewed" AVANT le rail "Related" sur detail page.
  • ✅ Public marketplace filters : freeOnly / featuredOnly / tier filters ajoutés dans ListingFilters UI + service unified.
  • ✅ Dispute badges sur /account/orders index.
  • ✅ Vendor dispute banner sur listing detail page (amber alert + Respond link vers /account/disputes/[id]).
  • ✅ Refund details bloc sur /account/orders/[id] avec "Refunded on" + délai fonds bancaires.

V5.1 (chantier supplémentaire dans cette PR)

  • ✅ KYC pipeline scaffold :
    • Schema KycVerification + enums KycStatus (PENDING/IN_REVIEW/VERIFIED/ REJECTED/CANCELLED) + KycProvider (STRIPE_IDENTITY/PERSONA/ONFIDO/MANUAL).
    • PII report stocké encryptedReport (AES-256-GCM via lib/security/crypto).
    • Service services/marketplace/kyc.ts : startVerification (idempotent, Stripe Identity hosted URL), syncFromProviderSession (webhook handler), getLatestKycForUser + isKycVerified (gate helpers).
    • API : POST /api/marketplace/kyc/start.
  • ✅ E-signature scaffold (NDA/LOI/APA) :
    • Schema LegalSignature + enums LegalDocKind / SignatureStatus / SignatureProvider (DOCUSIGN/HELLOSIGN/BOOSTECOM).
    • Audit hash sha256(dealId|kind|signerId|isoTs) pour BOOSTECOM internal.
    • Service services/marketplace/legal-signatures.ts : requestSignature, signSignature (auto-advance ndaSignedAt/loiSignedAt/apaSignedAt sur DealThread quand all signed), declineSignature, listSignaturesForDeal.
    • API : POST /api/marketplace/signatures/[id] (sign/decline), POST /api/vendor/marketplace/deals/[id]/request-signature.
    • UI SignaturesPanel (4 sections : my pending / their pending / signed / declined + request buttons NDA/LOI/APA). Wired dans /account/deals/[id] et /[orgSlug]/~/deals/[id].
  • ✅ Admin marketplace metrics dashboard :
    • Service getAdminMetrics() : all-time + windowed aggregates (listings, sellers, buyers, offers, deals, orders, revenue, fees, disputes, refunds) + top 10 sellers par saves.
    • Page /admin/content/marketplace-metrics avec KPI grid + window selector + top sellers table. Entry dans admin-routes.ts catégorie "Content".
  • ✅ Public seller profile/storefront /sellers/[id] :
    • Service getSellerProfile + listSellerListings.
    • Page avec avatar + KPIs (listings live, saves, deals closed, orders) + badges trust signals + grid ListingCard. ISR 10min. SEO complete.
  • ✅ Tests vitest : events.hashVisitor (6 cas) + legal-signatures audit hash determinism (4 cas) = 10 tests verts.

🚧 V6 — DocuSign/HelloSign live integration + KYC providers wiring (1-2 semaines)

  • ⚙️ Connect activation côté Stripe Dashboard : avant d'aller live, il faut activer Stripe Connect sur le compte BoostEcom (un click depuis dashboard.stripe.com/connect). Sans ça, createConnectAccount lèvera account_creation_disabled.
  • ⚙️ Branding Connect : configurer le logo + nom légal sur la page d'onboarding hosted (Stripe Connect → Settings → Branding).
  • ⚙️ Webhook endpoint : ajouter account.updated, payout.failed, charge.refunded aux events listenés sur le webhook existant (/api/webhooks/stripe). Pas de nouvelle URL.
  • ⚙️ Cron schedule : marketplace-maintenance déjà scheduled, pas d'action requise.
  • ⚙️ E-signature NDA/LOI/APA : DocuSign / HelloSign integration sur les deal stages LOI_SIGNED / APA_SIGNED. Multi-signataire workflow, audit trail légal. (Schema déjà préparé via ndaSignedAt, loiSignedAt, apaSignedAt columns sur DealThread.)

🚧 V5 — ce qui reste après V5.1 : embeddings, mobile, API publique (4-8 semaines)

Section placée après V5.1 volontairement : ce qui reste ici est le reliquat de V5, pas un chantier antérieur.

  • Recommendations engine ML : le collaborative filtering est ✅ livré par V5.1 (getCoOccurrenceRecommendations() sur les ListingEvent). Reste 🚧 : embeddings sémantiques, vector store pgvector, A/B testing infra.

  • KYC pipeline : ✅ livré par V5.1 (services/marketplace/kyc.ts, modèle KycVerification, Stripe Identity hosted). Reste 🚧 : Persona / Onfido, la reprise automatique du verdict provider (aujourd'hui tirée à la main depuis le panel admin, aucun webhook), les auto-tier upgrades selon trust score.

  • 🚧 Cross-marketplace integrations + mobile (rien de livré) :

  • Import depuis Shopify App Store / ThemeForest.

  • White-label marketplace pour partenaires.

  • React Native app : browse + search + save + push notif.

  • API publique OAuth 2.0 + SDK TypeScript/Python.

Métriques de succès

  • Adoption vendor : > 100 listings tiers en V2.
  • Conversion : > 3% (view → save), > 1% (save → offer).
  • Time to first lead : médiane < 24h après publication.
  • Trust : aggregateRating moyenne > 4.0 sur les 100 top listings.
  • Search : query latency p95 < 100ms.
  • Cache hit rate : > 70% sur listUnified.