Aller au contenu

ADR-0005 — Patron API B2B « façon Stripe » comme standard DYNORS

Date : 2026-05-07 (rédigée rétroactivement 2026-06-30 — backfill ; implémentation FISCAL livrée 2026-04-29) Catégorie : A (convention d'exposition B2B transverse) Décideur : Lead architecte DYNORS Statut : Acceptée (référence FISCAL opérationnelle ; à reproduire pour BOOKS, TRACIUM…) S'appuie sur : [[0004-perimetre-fige-fiscal-books-paiement]] (FISCAL pilier B2B), [[0018-exposition-hors-prod-sly-edge-chemin]] (exposition par chemin SLY) Source de vérité : docs/INTEGRATIONS_FACON_STRIPE.md §6 ; fiscal-architecture-cible-vs-existant.md §7 (Décision 5)


Contexte

DYNORS veut exposer ses services (FISCAL d'abord, puis BOOKS, TRACIUM…) comme des produits intégrables par des tiers : un intégrateur s'authentifie avec une clé, appelle l'API, reçoit des webhooks — sans déployer DYNORS. Si chaque app réinvente son propre schéma d'API publique (format de clé, route, filtre d'auth, webhooks), on obtient une surface incohérente, difficile à documenter et à sécuriser. FISCAL a déjà implémenté un patron complet et opérationnel (clés fsk_*, route SLY /v1/fiscal/**, webhooks whsec_*). Il faut le figer comme standard plutôt que de le laisser diverger.

Décision

Le patron implémenté dans FISCAL est la règle standard pour toute app DYNORS exposant un accès développeur externe. FISCAL est la référence ; les nouvelles surfaces B2B le reproduisent sans le réinventer.

  1. Contrat SLY (gateway) pour chaque app <app> : route Path=/v1/<app>/** + RewritePath=/v1/<app>/(?<s>.*), /api/v1/<app>/external/${s} ; SlyIdentificationFilter reconnaît Bearer <prefix>_live_* / <prefix>_test_* → marque B2B_API_KEY et forwarde sans valider (validation déléguée au backend). L'architecture interne reste opaque pour l'intégrateur.

  2. Contrat backend pour chaque app : tables <app>_api_keys (hash SHA-256, prefix, scopes CSV, mode live/test, statut) + <app>_webhook_registrations ; filtre <App>ApiKeyAuthFilter @Order(1) sur /api/v1/<app>/external/** ; <App>ExternalApiController ; <App>ApiKeyController (admin) ; <App>WebhookService (dispatch signé via dynors-webhooks après chaque event bus, auto-disable après N échecs).

  3. Format de clé invariant : <prefix>_live_<32hex> / <prefix>_test_<32hex> (fsk_ FISCAL, bsk_ BOOKS, tsk_ TRACIUM…). Clé en clair retournée une seule fois, hash stocké.

  4. Vue développeur invariante : POST https://api.dynors.com/v1/<app>/<resource> + Authorization: Bearer <prefix>_live_<32hex>. Versioning par préfixe /v1/. Réponses et erreurs homogènes (code, message, request_id). Idempotence via Idempotency-Key.

Conséquences

Positif : expérience développeur homogène sur tout l'écosystème ; sécurité éprouvée réutilisée (pas de filtre d'auth réécrit par app) ; doc et SDK factorisables ; SLY masque les détails internes ; onboarding d'une nouvelle app B2B = appliquer une checklist, pas concevoir.

Coût : chaque app B2B doit créer ses tables, son filtre, son controller externe (boilerplate, mais cadré) ; le préfixe de clé doit être réservé sans collision ; discipline de revue pour refuser toute variante maison.

Composants impactés : dynors-platform/selebeyone (routes), apps exposant du B2B (FISCAL livré ; BOOKS, TRACIUM candidats), dynors-webhooks, doc developers.dynors.com.

Alternatives écartées

  • Chaque app invente son API publique : surface incohérente, sécurité hétérogène, doc multipliée. Écartée.
  • Un unique service passerelle d'API qui absorbe toutes les apps : recrée un point de couplage et contredit l'autonomie des domaines ([[0004-perimetre-fige-fiscal-books-paiement]]). Écartée au profit du patron répliqué + SLY comme front commun.

Références

  • docs/INTEGRATIONS_FACON_STRIPE.md §6 (règle globale, contrats SLY et backend)
  • dynors-docs/docs/05-architecture-si/fiscal-architecture-cible-vs-existant.md §2.1, §7 (Décision 5)
  • ADR-0004 (périmètre figé), ADR-0018 (exposition SLY par chemin)