console-merchant
À quoi ça sert
Section intitulée « À quoi ça sert »console-merchant/ est le back-office self-service du marchand. Une fois intégré à Titreo E-Pay, un marchand doit pouvoir gérer son intégration sans ticket de support : brancher son PSP (Stripe, PayPlug…), générer ses clés API, configurer son webhook, suivre ses transactions, rembourser, et inviter son équipe. Cette console est l’interface qui rend tout ça possible.
C’est une brique cliente : elle ne contient aucune logique métier de paiement. Elle est une façade web par-dessus l’API backend. Chaque écran lit ou écrit via les endpoints /v1/merchants/me* et /v1/sessions* du Backend ; l’autorité reste côté API.
Concrètement le marchand y gère lui-même :
| Domaine | Ce qu’il peut faire | Endpoint API sous-jacent |
|---|---|---|
| PSP | Configurer / mettre à jour / supprimer ses clés Stripe, Adyen, PayPlug | /v1/merchants/me/psp-configs[/:provider] |
| Clés API | Lister, créer une clé (avec scopes) | /v1/merchants/me/api-keys |
| Webhook | URL + secret de notification | /v1/merchants/me (champs webhookUrl, webhookSecret) |
| Livraisons webhook | Lister, inspecter le payload, rejouer (replay) | /v1/merchants/me/webhook-deliveries[/:id][/replay] |
| Sessions | Lister, voir le détail, rembourser | /v1/sessions[/:id][/refund] |
| Apparence | Logo + tokens de thème (couleur d’accent…) | /v1/merchants/me/branding |
| Équipe | Inviter, changer de rôle, désactiver un membre | /v1/merchants/me/users[/invites] |
| Compte | Nom, TTL de session | /v1/merchants/me |
Stack et démarrage
Section intitulée « Stack et démarrage »Next.js 15 (App Router, React 19, Server Components + Server Actions), TypeScript strict, Tailwind, validation Zod, lint via Biome. Aucune dépendance HTTP custom : tout passe par fetch côté serveur dans console-merchant/lib/api.ts.
Scripts réels (console-merchant/package.json) :
| Script | Commande | Effet |
|---|---|---|
dev |
next dev -p 3500 |
Serveur de dev sur le port 3500 |
build |
next build |
Build de production |
start |
next start -p 3500 |
Serveur de production sur le port 3500 |
typecheck |
tsc --noEmit |
Vérification de types |
lint |
biome check . |
Lint |
lint:fix |
biome check --write . |
Lint + autofix |
Configuration (console-merchant/.env.example) :
TITREO_API_BASE_URL=http://localhost:3000NODE_ENV=developmentTITREO_API_BASE_URL est l’URL du backend. Lue à un seul endroit (console-merchant/lib/config.ts), valeur par défaut http://localhost:3000. Voir Environnement de dev local.
Un Dockerfile multi-stage (deps / builder / runner) est présent : il s’appuie sur pnpm + un workspace racine (pnpm-workspace.yaml) et expose le port 3500. Lancement via pnpm start.
Comment l’authentification fonctionne
Section intitulée « Comment l’authentification fonctionne »Le marchand se connecte avec email + mot de passe. La console échange ces identifiants contre un cookie de session opaque qu’elle relaie ensuite à l’API à chaque appel.
sequenceDiagram
participant U as "Navigateur"
participant C as "Console (Server Action)"
participant A as "API Titreo E-Pay"
U->>C: "POST login (email, password)"
C->>A: "POST /v1/auth/login"
A-->>C: "Set-Cookie: tep_session=..."
C->>C: "Pose cookie tep_session (httpOnly, Secure, SameSite=strict)"
C-->>U: "Redirige /onboarding ou /dashboard"
Note over U,A: "Appels suivants : la console relaie tep_session vers l'API"
Points clés du flux d’auth (console-merchant/lib/actions/auth.ts, lib/api.ts, middleware.ts) :
- Le cookie
tep_sessionest la credential porteuse vers l’API. Il est poséhttpOnly,SameSite=strictet toujoursSecure(même en dev surlocalhost, qui est un contexte sécurisé) — jamais conditionné àNODE_ENV. middleware.tsprotège tout sauf les chemins publics (/login,/signup,/verify-email,/accept-invite) : en l’absence du cookie, redirection vers/login.- Le même middleware émet un en-tête CSP (Content-Security-Policy) avec nonce par requête, en mode
Report-Onlypar défaut (CSP_REPORT_ONLY=falsepour passer en enforce). getCurrentUser()(lib/auth.ts) appelle/v1/auth/me;requireMerchant()redirige vers/loginsi l’utilisateur n’est pas un marchand. Voir Sécurité.
Les appels API sont toujours faits côté serveur (Server Actions / RSC). Le navigateur ne parle qu’à la console ; il ne voit jamais l’URL de l’API ni le token en clair côté client.
Structure des routes
Section intitulée « Structure des routes »Le routing App Router se découpe en trois zones, dont deux groupes de routes ((authed), (onboarding)) qui partagent chacun un layout sans apparaître dans l’URL.
app/ page.tsx -> redirige vers /dashboard login/ public signup/ public verify-email/ public (vérification email post-signup) accept-invite/ public (acceptation d'invitation équipe) (authed)/ layout protégé + Sidebar dashboard/ sessions/ [id]/ liste + détail + bouton remboursement psp-configs/ [provider]/ api-keys/ webhooks/ deliveries/ [id]/ réglage + livraisons + replay branding/ users/ équipe (invitations, rôles) settings/ (onboarding)/ onboarding/ wizard + layout barre de progression psp/ api-key/ webhook/ branding/ plugin/ done/Le (authed)/layout.tsx impose requireMerchant() et affiche le Sidebar. La navigation est filtrée par rôle : chaque entrée du menu porte un scope requis, masqué si le rôle ne le possède pas (components/Sidebar.tsx + lib/permissions.ts).
Rôles et permissions
Section intitulée « Rôles et permissions »Trois rôles marchands, avec des scopes synthétisés côté UI pour masquer/désactiver les contrôles. C’est un miroir de la logique d’autorisation de l’API (api/src/http/plugins/auth.ts:scopesForMerchantRole) ; l’API reste l’autorité, la console ne fait que cacher ce qui serait de toute façon refusé.
| Rôle | Libellé | Périmètre |
|---|---|---|
owner |
Propriétaire | Tout : business + technique |
developer |
Développeur | Sessions, PSP, webhooks, clés API, branding (lecture) |
ops |
Opérations | Sessions (dont remboursement), webhooks, équipe (lecture), branding (lecture) |
Le wizard d’onboarding
Section intitulée « Le wizard d’onboarding »C’est la pièce centrale du parcours marchand. Au premier login, si l’onboarding n’est pas complet (/v1/merchants/me/onboarding renvoie complete: false), la console redirige vers /onboarding au lieu du dashboard.
flowchart LR
PSP["1. PSP"] --> KEY["2. Clé API"]
KEY --> WH["3. Webhook"]
WH --> BR["4. Apparence"]
BR --> PL["5. Plugin CMS"]
PL --> DONE["Terminé"]
L’ordre des étapes est défini en dur dans console-merchant/lib/onboarding-steps.ts (WIZARD_STEPS = ['psp', 'api-key', 'webhook', 'branding', 'plugin']). La page index (onboarding/page.tsx) interroge l’état et redirige vers la première étape non terminée, ce qui permet de reprendre l’onboarding où il s’était arrêté. La barre de progression ((onboarding)/onboarding/layout.tsx) calcule l’avancement depuis les drapeaux done renvoyés par l’API.
| Étape | Slug | Ce que fait l’action serveur | État API |
|---|---|---|---|
| PSP | psp |
PUT /v1/merchants/me/psp-configs/:provider (clé secrète + secret webhook) |
pspConfigured |
| Clé API | api-key |
POST /v1/merchants/me/api-keys (scopes sessions:read/write, préfixe tep_live_) — le token n’est montré qu’une fois |
apiKeyCreated |
| Webhook | webhook |
PUT /v1/merchants/me (webhookUrl + webhookSecret) |
webhookConfigured |
| Apparence | branding |
PUT /v1/merchants/me/branding (logo, token --accent) |
brandingSet |
| Plugin CMS | plugin |
Aucune écriture : instructions d’installation par CMS | pluginInstalled |
Esquisse vs réel
Section intitulée « Esquisse vs réel »Pour une passation honnête, voici la distinction sans fard.
Réel et branché sur l’API :
- Auth complète : login, signup (avec lien de vérification), vérification email, accept-invite, logout — toutes via
/v1/auth/*. - CRUD PSP, clés API, webhook, branding, équipe (invitations / rôles / désactivation) — vrais endpoints
/v1/merchants/me*. - Sessions : liste paginée par curseur, détail, remboursement (
POST /v1/sessions/:id/refund) avec garde de statut remboursable (lib/sessions-helpers.ts). - Livraisons webhook : liste, détail du payload, replay (
POST .../replay). - Wizard d’onboarding piloté par l’état réel renvoyé par l’API.
- Garde de sécurité côté serveur sur le branding : ré-validation Zod (tokens CSS, logo) car les Server Actions sont des POST directement appelables (
lib/actions/branding.ts).
Esquisse / non finalisé :
- Aucun test (ni unitaire ni e2e).
- Design system réduit à
components/ui.tsx; pas de bundle design intégré. - Confirmation « plugin installé » non vérifiée (lien manuel).
- Branding produit ici mais pas encore consommé par une surface de checkout dans cette brique.
- Pas de gestion d’erreur réseau riche (états de chargement / retry minimalistes).
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Backend — la source des endpoints
/v1/merchants/me*et de l’autorisation par scopes. - Contrats inter-briques — pourquoi la console est une façade jetable.
- Sécurité — cookie de session, CSP, frontière de confiance des Server Actions.
- Environnement de dev local — lancer la console (port 3500) contre l’API (port 3000).
- Vue d’ensemble de l’écosystème — où se place cette brique.