Skip to content
StableAudienceDevOpsAudit banqueComplianceOwner@architecture-teamDernière revue2026-05-22

Transactions — Procédures, audit, gouvernance et limites

Procédures de gestion d'une transaction (règle d'or, étapes implémenteur, gestion d'erreurs, tests), audit et contrôles continus (DB, applicatif, intégrité nocturne, ponctuel), gouvernance et rôles (permissions, SoD, maker-checker), limites connues et feuille de route. Pour la vue d'ensemble, voir Système de transactions — overview.

7. Procédures de gestion d'une transaction

7.1 La règle d'or : un unique chemin d'écriture

Toute transaction financière Nex doit être initiée par la création d'un intent.

Cette règle est constitutive de la nouvelle architecture. Elle implique :

  • Aucun autre endpoint que le flow intent ne doit être utilisé pour créer de nouveau paiement.
  • Les endpoints directs historiques (POST /v1/transactions et ses dérivés) sont en cours de dépréciation. Ils continuent de fonctionner mais seront supprimés dans une itération ultérieure.
  • Toute nouvelle fonctionnalité transactionnelle se branche sur le flow intent en créant une nouvelle stratégie de confirmation dédiée.

7.2 Étape par étape pour un implémenteur

1. Créer l'intent

Le client envoie POST /v1/intents avec un corps contenant au minimum :

json
{
  "type": "TRANSFER" | "CASH_IN" | "CASH_OUT",
  "amount": <nombre entier en XAF>,
  "idempotencyKey": "<UUID v4 généré côté client>",
  "clientPhone": "<numéro destinataire>",
  "clientCountryCode": "<préfixe pays>",
  "description": "<libellé optionnel>"
}

Le serveur :

  • Valide la structure du corps.
  • Résout le destinataire.
  • Évalue le risque auprès du risk-engine.
  • Persiste l'intent avec un verrou d'unicité sur idempotencyKey.

Réponse : { intentId, status: "CREATED", requiresOtp, expiresAt }.

2. (Si OTP requis) Envoyer et vérifier l'OTP

POST /v1/intents/:id/send-otp puis POST /v1/intents/:id/verify-otp avec { otpCode }. Transitions internes : OTP_SENTOTP_VERIFIED.

3. Vérifier le PIN de l'initiateur

POST /v1/intents/:id/verify-pin avec { pin }. L'orchestrator valide le PIN auprès du service auth. Transition : PIN_VERIFIED.

4. Confirmer et exécuter

POST /v1/intents/:id/confirm. L'orchestrator :

  • Recalcule et valide les frais.
  • Évalue le risque une seconde fois (confirmation).
  • Construit l'executionContext (comptes, montants, frais, commission, métadonnées).
  • Appelle en un seul appel HTTP le ledger : POST /v1/internal/intents/:id/transition avec toStatus: SUCCEEDED et le contexte complet.
  • Le ledger exécute alors la transaction atomique décrite au §5.3.

Réponse : { intentId, status: "SUCCEEDED", transactionId, referenceId, amount, fees }.

7.3 Gestion des erreurs

Tous les cas d'erreur sont mappés à des codes HTTP explicites et des exceptions de domaine nommées :

SituationException (domaine)Code HTTP
Solde insuffisantInsufficientBalanceException422
Conflit de concurrence non résolu après retriesConcurrencyConflictException409
Idempotency-Key réutilisée avec corps différentIdempotencyKeyConflictException422
Idempotency-Key manquante sur endpoint obligatoireIdempotencyKeyMissingException400
Violation d'intégrité du grand livreLedgerIntegrityViolationException500 (+ alerte compliance)
Transition d'intent invalideBadRequestException400
Intent introuvableNotFoundException404

Chaque erreur contient un code métier stable (par exemple INSUFFICIENT_BALANCE, CONCURRENCY_CONFLICT) qui permet au client de traiter le cas de façon structurée, plutôt que de parser un message texte.

7.4 Tests et preuves

La robustesse du système est garantie par trois niveaux de tests automatisés :

  • Tests unitaires : couvrent chaque méthode métier en isolation avec des doublures de test. Au 17 avril 2026, 2 170 tests sont verts (ledger-wallets 787, orchestrator 1 383).
  • Tests d'intégration (à venir en MR5) : utilisent une vraie base PostgreSQL éphémère via Testcontainers. Couvrent les contraintes DB, le trigger append-only, les cas de concurrence réelle avec Promise.all.
  • Tests de concurrence (à venir en MR5) : reproduisent les scénarios de double-spend avec des requêtes simultanées sur la même ressource. Chaque scénario est exécuté 20 fois pour prouver l'absence de comportement aléatoire.

Le plan de tests complet, organisé en onze sections et une centaine de cas, est maintenu dans le document _bmad-output/implementation-artifacts/NEX-180-test-plan.md.


8. Audit et contrôles continus

8.1 Contrôles au niveau base de données

Les contraintes PostgreSQL détaillées au §4 sont actives à chaque écriture. Toute violation est rejetée immédiatement, avec un code d'erreur standardisé. Les contraintes d'intégrité ne peuvent pas être contournées par le code applicatif.

8.2 Traçabilité applicative

Chaque opération critique produit un journal structuré contenant :

  • Un identifiant de corrélation (correlationId), UUID v4 généré par l'orchestrator, propagé vers le ledger via header HTTP X-Correlation-Id. Il permet de retrouver toutes les traces d'une même transaction cross-service.
  • L'identifiant de l'intent et de la transaction.
  • Les identifiants de comptes, masqués pour la confidentialité (format XXXX***YYYY).
  • Le montant, l'opération type, la devise.
  • La durée d'exécution.

Aucun secret (PIN, OTP, token d'authentification) n'est jamais journalisé, même en mode debug. Les corps de requête sensibles sont filtrés avant hash lors de la canonicalisation d'idempotence.

8.3 Contrôle d'intégrité nocturne

Un job automatique (à livrer en MR5) s'exécute chaque nuit à 02h00 UTC. Il parcourt l'ensemble des comptes actifs et compare, pour chacun :

  • La valeur stockée dans accounts.balance.
  • La valeur recalculée à partir des écritures : SUM(credits) - SUM(debits) depuis ledgers.

Tout écart est :

  1. Enregistré dans la table balance_integrity_checks (rétention indéfinie).
  2. Loggué en niveau ERROR avec correlationId.
  3. Exposé via une métrique Prometheus ledger_integrity_gap_count.
  4. Envoyé en alerte Slack sur le canal #compliance via webhook (URL stockée de manière sécurisée dans Doppler).

Le seuil d'alerte est zéro. Toute anomalie est un incident traité en priorité par le département compliance.

Le job ne modifie jamais aucune donnée. La correction d'un écart, si elle est requise, fait l'objet d'un workflow manuel supervisé, sous la responsabilité du département compliance, et documenté dans un runbook dédié.

8.4 Audit ponctuel à la demande

Pour un besoin de vérification ponctuelle sur un compte spécifique, une commande en ligne est disponible :

pnpm --filter @nex/service-ledger-wallets run audit:account <numéro de compte>

Elle produit un rapport JSON détaillant :

  • Les informations du compte (titulaire, devise, type, date de création).
  • Le solde stocké et le solde recalculé, avec l'écart éventuel.
  • La symétrie des écritures (chaque débit a bien un crédit correspondant).
  • L'existence éventuelle d'écritures orphelines.
  • Un indicateur de cohérence global : OK, WARNING ou CRITICAL.

Un runbook (infrastructure/docs/runbooks/audit-account.md, à livrer en MR5) décrit l'interprétation des résultats et les actions associées.


9. Gouvernance et rôles

9.1 Permissions et séparation des tâches

Les endpoints publics de l'orchestrator sont protégés par un système de permissions granulaire (RequirePermission), distinguant :

  • transactions.create : initier une transaction.
  • transactions.read : consulter les transactions.
  • transactions.approve (pour les workflows maker-checker) : approuver une opération initiée par un autre utilisateur.
  • reports.read : consulter les rapports d'historique de compte.
  • compliance.* : actions réservées au département compliance (consulter les intégrité checks, examiner les anomalies).

9.2 Rôles métier

Les rôles applicatifs suivants sont définis, avec leur matrice de permissions associée (détaillée dans le système RBAC du service auth) :

  • Utilisateur final : peut initier un P2P, consulter son propre historique.
  • Agent : peut initier un cash-in ou cash-out, consulter les transactions de ses clients.
  • Master Agent : peut en plus consulter les transactions de tous ses agents affiliés.
  • Opérateur support : accès en lecture sur les transactions pour traiter les réclamations.
  • Finance / Compliance Officer : accès en lecture étendu, peut consulter les balance_integrity_checks, initier des audits ponctuels.
  • Tech Lead / Admin : droits d'administration du système.

9.3 Workflow maker-checker

Les opérations sensibles hors flux courant (paiements en masse, rappels de trésorerie) requièrent la validation de deux utilisateurs distincts :

  • Un maker qui initie et soumet l'opération.
  • Un checker qui examine, valide ou rejette.

La table bulk_payments porte les champs makerId et checkerId distincts. La contrainte applicative makerId != checkerId est en cours de renforcement (dette compliance identifiée, hors périmètre NEX-180).


10. Limites connues et feuille de route

10.1 Périmètre couvert par NEX-180

Sont couverts et conformes au nouveau modèle atomique :

  • Transfert P2P entre utilisateurs (type TRANSFER).
  • Cash-in agent et master agent (type CASH_IN).
  • Cash-out agent et master agent (type CASH_OUT).

10.2 Chantiers en cours (tickets de suivi)

Quatre tickets complémentaires sont créés et suivis dans Jira, reliés à NEX-180 :

  • NEX-331 — Pattern outbox pour notifications et commissions. Les effets de bord non-critiques (envoi de SMS, calcul de commission différée) sont actuellement en mode fire-and-forget. L'ajout d'un pattern outbox transactionnel garantira leur publication au-moins-une-fois, même en cas de crash du service.
  • NEX-332 — Observabilité temps réel des invariants comptables. Métriques Prometheus live, tableaux Grafana, alerting PagerDuty en cas de détection immédiate d'une incohérence — en complément du contrôle nocturne.
  • NEX-333 — Migration des flux résiduels vers le modèle intent. Les opérations de recharge mobile, paiements en masse, rappels de trésorerie et distribution de commission n'utilisent pas encore le flow intent. Elles seront migrées pour bénéficier du même niveau de garanties.
  • NEX-334 — Cantonnement EME et réconciliation bancaire. Dépend du statut officiel d'EME et des accords bancaires. Hors périmètre technique immédiat.

10.3 Dettes compliance identifiées

Trois dettes ont été documentées dans l'audit NEX-180 et restent à traiter dans des stories dédiées :

  1. Endpoints d'administration PUT /v1/accounts/:id/balance/* (ajout, soustraction, gel, dégel de solde) : ces endpoints modifient accounts.balance sans créer d'écriture dans ledgers. Ils constituent la cause racine la plus probable des écarts historiques observés. Recommandation : retrait ou refactorisation pour forcer la création systématique d'une écriture d'ajustement signée.
  2. Absence d'enforcement makerId != checkerId sur les paiements en masse. La structure maker-checker existe mais la validation applicative empêchant l'auto-approbation n'est pas en place.
  3. Absence de correlation_id natif sur les entités financières — contourné partiellement par un mécanisme de propagation dans NEX-180, à consolider avec un vrai middleware d'identification de requête (NEX-332).

10.4 Gouvernance post-déploiement

La mise en production de NEX-180 est conditionnée à :

  • Un pré-check d'intégrité sur l'environnement cible confirmant l'absence de compte en violation des nouvelles contraintes.
  • L'approbation de la merge request par au moins deux développeurs backend seniors distincts.
  • Une revue adversariale automatisée (bmad-code-review) attachée à la merge request.
  • Un benchmark de performance avant/après attestant d'une dégradation p95 inférieure à 20 %.
  • Un smoke test sur l'environnement de staging portant sur au moins trente transactions représentatives des trois types couverts.
  • Une vérification manuelle des rapports du contrôle nocturne pendant au moins 48 heures post-déploiement.

11. Références

11.1 Documents internes

  • infrastructure/docs/compliance-audit-2026.md — audit compliance détaillé, 5 piliers A1 à A5, identifiant les conformités et les écarts.
  • _bmad-output/implementation-artifacts/NEX-180-ledger-concurrency-hardening.md — spécification complète de la story NEX-180.
  • _bmad-output/implementation-artifacts/NEX-180-test-plan.md — plan de tests exhaustif organisé en 11 sections.
  • infrastructure/docs/INTENT_CASHOUT_FLOW.md — description détaillée du flow cash-out avec QR.
  • infrastructure/docs/algo-reporting-historique-compte.md — spécification de l'algorithme de rapport d'historique de compte (NEX-179).
  • CLAUDE.md — conventions de développement du monorepo.

11.2 Code source clé

  • services/ledger-wallets/src/intents/intents.service.ts — service de gestion des intents, point d'entrée du chemin atomique.
  • services/ledger-wallets/src/transactions/orchestrators/transaction-creation.orchestrator.ts — orchestrateur transactionnel au sens SQL.
  • services/ledger-wallets/src/common/utils/lock-accounts-in-order.ts — helper de verrouillage pessimiste ordonné.
  • services/ledger-wallets/src/common/decorators/retry-on-serialization-failure.decorator.ts — décorateur de rejeu sur erreurs de sérialisation.
  • services/ledger-wallets/src/idempotency/interceptors/idempotency.interceptor.ts — interception d'idempotence transversale.
  • services/ledger-wallets/migrations/000-baseline.sql à 025-create-balance-integrity-checks-table.sql — schéma et évolutions de la base de données.
  • services/orchestrator/src/application/use-cases/intents/confirm-intent.use-case.ts — use-case orchestrateur de la confirmation d'un intent.

11.3 Réglementation et standards


Annexe A — Glossaire

  • ACID : propriétés d'une transaction base de données — Atomicité, Cohérence, Isolation, Durabilité.
  • Agent : partenaire physique de Nex qui accueille les clients pour les dépôts et retraits d'espèces.
  • Append-only : se dit d'une structure dans laquelle on ne peut qu'ajouter des éléments, jamais modifier ni supprimer.
  • Cash-in : dépôt d'argent liquide par un agent sur le compte d'un client.
  • Cash-out : retrait d'argent liquide par un client auprès d'un agent.
  • Cantonnement (safeguarding) : obligation réglementaire de conserver les fonds des clients sur un compte bancaire ségrégué du patrimoine de l'émetteur.
  • COBAC : Commission Bancaire d'Afrique Centrale, régulateur de la zone CEMAC.
  • Correlation ID : identifiant unique d'une requête traversant plusieurs services, permettant la traçabilité de bout en bout.
  • Deadlock : situation où deux transactions s'attendent mutuellement et ne peuvent avancer.
  • Double-entry : comptabilité en partie double, principe selon lequel toute écriture de débit est compensée par une écriture de crédit équivalente.
  • Double-spend : défaut qui permet à deux opérations concurrentes de dépenser les mêmes fonds, produisant un écart comptable.
  • EME : Établissement de Monnaie Électronique, statut réglementaire d'un émetteur de monnaie électronique.
  • Idempotence : propriété d'une opération qui, rejouée plusieurs fois, produit le même résultat que si elle était jouée une seule fois.
  • Intent (TransactionIntent) : objet métier représentant l'intention d'exécuter une transaction, porteur de tous ses paramètres et de son cycle de vie.
  • Ledger : grand livre comptable, collection d'écritures individuelles de débit et de crédit.
  • Lock pessimiste : verrouillage qui retient une ressource pour la durée d'une transaction, empêchant tout accès concurrent.
  • Maker-checker : principe de séparation des tâches, selon lequel l'initiateur d'une opération ne peut pas être son propre approbateur.
  • Master Agent : agent associé à une organisation partenaire, disposant d'un portefeuille corporate.
  • OTP (One-Time Password) : code à usage unique envoyé au client pour valider une opération sensible.
  • P2P : Peer-to-Peer, transfert d'argent directement entre deux utilisateurs.
  • PostgreSQL : système de gestion de base de données relationnelle utilisé par Nex.
  • Race condition : situation où le résultat d'une opération dépend de l'ordre d'exécution de plusieurs processus concurrents.
  • SERIALIZABLE : niveau d'isolation transactionnelle le plus strict, garantissant un résultat équivalent à une exécution séquentielle.
  • SERIALIZATION_FAILURE : erreur PostgreSQL levée lorsque deux transactions concurrentes ne peuvent pas être sérialisées sans conflit.
  • SoD (Segregation of Duties) : séparation des tâches, principe de gouvernance interne.
  • Trésorerie (Treasury) : compte technique de la plateforme, distinct des comptes clients.
  • Trigger : procédure automatique exécutée par la base de données en réaction à un événement (insertion, modification, suppression).
  • XAF / XOF : codes ISO 4217 des devises Franc CFA d'Afrique centrale (XAF) et Franc CFA d'Afrique de l'Ouest (XOF).

Fin du document.

Version 1.0 — 2026-04-17. Révisions et mises à jour à venir à chaque palier de livraison de NEX-180 et de ses tickets de suivi.

Nex — Plateforme fintech CEMAC