Aller au contenu

Machines à états

Tout le comportement de paiement est gouverné par deux machines à états : celle d’un leg (une jambe) et celle d’une session. Le statut de la session est déduit de l’état de ses legs.

  • Logique des legs : api/src/domain/session/leg-status.ts
  • Logique des sessions : api/src/domain/session/session-status.ts

Une carte cadeau est débitée directement (pas de phase « hold » séparée) : un débit réussi la place en CAPTURED. L’annulation et le remboursement passent par un état transitoire.

stateDiagram-v2
  [*] --> PENDING
  PENDING --> CAPTURED: débit réussi
  PENDING --> DEBIT_FAILED: débit refusé
  CAPTURED --> CANCELING: rollback demandé
  CAPTURED --> REFUNDING: remboursement demandé
  CANCELING --> CANCELED
  REFUNDING --> REFUNDED
  DEBIT_FAILED --> [*]
  CANCELED --> [*]
  REFUNDED --> [*]

Un PSP suit un cycle autorisation (hold) → capture. Entre les deux, un hold peut être relâché (void) lors d’un rollback. Le 3DS introduit un état d’attente d’action client.

stateDiagram-v2
  [*] --> PENDING
  PENDING --> HELD: autorisation réussie
  PENDING --> HOLD_FAILED: autorisation refusée
  HELD --> CAPTURED: capture réussie
  HELD --> CAPTURE_FAILED: capture refusée
  HELD --> RELEASING: rollback (void du hold)
  RELEASING --> RELEASED
  CAPTURED --> REFUNDING: remboursement
  REFUNDING --> REFUNDED
  HOLD_FAILED --> [*]
  CAPTURE_FAILED --> [*]
  RELEASED --> [*]
  REFUNDED --> [*]

Les états terminaux d’un leg sont : DEBIT_FAILED, HOLD_FAILED, CAPTURE_FAILED, CANCELED, RELEASED, REFUNDED. Une fois là, plus aucune transition n’est possible.

Une session naît en CREATED, collecte ses legs, puis est traitée. Son statut « avancé » (COMPLETED, ROLLING_BACK, FAILED, remboursements) est déduit de l’état des legs.

stateDiagram-v2
  [*] --> CREATED
  CREATED --> COLLECTING: ajout de legs
  COLLECTING --> PROCESSING: submit (montant restant = 0)
  CREATED --> PROCESSING: submit
  PROCESSING --> COMPLETED: tous les legs CAPTURED
  PROCESSING --> ROLLING_BACK: un leg échoue
  ROLLING_BACK --> FAILED: legs annulés / relâchés
  COMPLETED --> PARTIALLY_REFUNDED: un leg remboursé
  COMPLETED --> FULLY_REFUNDED: tous les legs remboursés
  PARTIALLY_REFUNDED --> FULLY_REFUNDED
  CREATED --> EXPIRED: TTL dépassé
  COLLECTING --> EXPIRED: TTL dépassé
  FAILED --> [*]
  FULLY_REFUNDED --> [*]
  EXPIRED --> [*]

deriveSessionStatus applique, en simplifié, cette cascade de règles sur l’ensemble des legs :

  1. Tous les legs REFUNDEDFULLY_REFUNDED
  2. Au moins un leg REFUNDEDPARTIALLY_REFUNDED
  3. Un leg en échec / en cours de rollback → ROLLING_BACK
  4. Tous les legs CANCELED/RELEASEDFAILED
  5. Tous les legs CAPTUREDCOMPLETED
  6. Sinon → statut « de collecte » (CREATED, COLLECTING, PROCESSING) ou EXPIRED

Ces transitions ne se produisent pas « en direct » dans la requête HTTP : elles sont appliquées par l’orchestrateur (process-outbox-entry.ts) au fil du traitement asynchrone. Voir Outbox & orchestration puis L’orchestrateur.

Source de vérité métier complémentaire : doc/site/content/state-machines.mdx.