Nordlet

← Documentation / Glossaire

Qu'est-ce que l'idempotence ? Pourquoi les API de comptabilité et de paiement en ont besoin

Comment une requête rejouée crée une facture en double, ce qu'garantit une clé d'idempotence, les types de défaillance à gérer et comment l'implémenter correctement.

Une opération est dite idempotente si son exécution en double produit le même effet que son exécution unique. La lecture d'un enregistrement est naturellement idempotente. Ce n'est pas le cas de la création d'une facture: appelez cette fonction deux fois et vous obtenez deux factures, deux créances et un client qui demande pourquoi il a été facturé en double.

Cet écueil est critique, car les réseaux connaissent une défaillance spécifique et pernicieuse: la requête réussit, mais la réponse est perdue. Le client subit un délai d'attente (timeout) et ne peut distinguer si la requête « n'est jamais arrivée » ou « est arrivée, a fonctionné, mais la réponse s'est perdue ». Sa seule option prudente est de la rejouer, et c'est cette nouvelle tentative (retry) qui crée le doublon.

Pourquoi la comptabilité aggrave le problème

Dans la plupart des systèmes, un doublon n'est qu'une nuisance. Dans un grand livre (ledger), c'est un problème cumulatif:

  • Une facture en double consomme un numéro d'une séquence continue sans rupture (gapless sequence), ce qui exclut sa suppression. L'immutabilité implique que sa correction nécessite une note de crédit, soit un document visible par votre client.
  • Le doublon comptabilise des écritures, ce qui fausse le chiffre d'affaires, la TVA et les créances jusqu'à ce qu'un contrôle le détecte.
  • Il s'insère dans la déclaration de TVA et, en Lituanie, dans les registres i.SAF.
  • S'il a déclenché un paiement, des fonds ont réellement bougé.

« Il suffit de vérifier les doublons » ne fonctionne pas non plus, car deux factures distinctes adressées au même client pour le même montant le même jour sont tout à fait normales. Le système ne peut pas déduire l'intention à partir du contenu: le client doit l'expliciter.

Fonctionnement des clés d'idempotence

Le client génère une clé unique par opération logique et l'envoie avec la requête:

POST /v1/sales/invoices/create
Idempotency-Key: 8f14e45f-ea1b-4c2b-9f3a-2d7c1e0b5a91

Le serveur garantit alors ce qui suit:

  1. Première requête avec cette clé : traitement normal, stockage du résultat associé à la clé.
  2. Nouvelle tentative avec la même clé et la même charge utile (payload) : pas de ré exécution, renvoi de la réponse stockée.
  3. Même clé, charge utile différente : rejet. La clé atteste qu'il s'agit de la même opération. Un corps différent signifie le contraire, et renvoyer l'ancien résultat en silence serait pire qu'une erreur.
  4. Nouvelle tentative alors que la requête initiale est toujours en cours : rejet avec une erreur de type réitérable, plutôt qu'une exécution simultanée.

Le point 4 est celui que les implémentations naïves oublient. Un client dont le délai expire après 5 secondes et qui réessaie à 6 secondes entrera en collision avec sa propre requête en cours (in-flight). Sans protection contre la concurrence, les deux s'exécutent et génèrent le doublon que le mécanisme était censé empêcher.

Générer les clés correctement

La clé doit identifier l'opération, et non la tentative:

  • Correct: un UUID généré une seule fois lorsque l'utilisateur clique sur « émettre la facture », réutilisé pour chaque nouvelle tentative liée à ce clic.
  • Correct: une clé déterministe issue de votre propre domaine, par exemple order-1041-invoice.
  • Incorrect: un nouvel UUID à chaque tentative HTTP. Chaque nouvel essai obtient une nouvelle clé, constituant ainsi une nouvelle opération. Le mécanisme n'est alors pas mis en œuvre.
  • Incorrect: un hachage de la charge utile. Deux opérations parfaitement légitimes entreraient en collision et la seconde serait ignorée en silence.

Les clés requièrent une période de rétention suffisamment longue pour couvrir les fenêtres de réessai réalistes et les pannes côté client, mais pas indéfinie.

Ce que l'idempotence ne résout pas

L'idempotence protège contre l'exécution en double d'une même requête. Elle ne protège pas contre:

  • Deux utilisateurs distincts créant la même facture depuis des sessions différentes, ce qui relève d'une règle métier résolue par des contraintes d'unicité, telles qu'une facture par commande.
  • Des doublons logiques dotés de clés différentes.
  • Le non-déterminisme au sein d'une opération : si le gestionnaire génère un horodatage ou un numéro, la réponse stockée conserve le premier, ce qui constitue le comportement souhaité, mais mérite d'être compris.

Elle complète les contraintes de base de données plutôt de ne pas les remplacer. Le grand livre de Nordlet emploie les deux: les clés d'idempotence bloquent les requêtes rejouées, tandis que les index uniques stoppent les lots de règlements en double par ID de décaissement, les factures d'achat en double par fournisseur et numéro de document, ainsi que les calculs d'amortissement en double par actif et période.

Comment Nordlet l'implémente

Envoyez un en-tête Idempotency-Key pour toute requête modifiant l'état, sous forme de chaîne non vide de 255 caractères au maximum.

La première requête dotée d'une clé s'exécute et son résultat est stocké. Une nouvelle tentative utilisant la même clé et la même charge utile renvoie la réponse enregistrée sans réexécution. La facture est ainsi créée exactement une seule fois, quel que soit le nombre de répétitions de l'appel.

Deux types de défaillance sont explicités au lieu d'être ignorés:

  • Même clé, charge utile différente → rejet: "Idempotency-Key was already used with a different request payload." Cela intercepte le cas critique où un client réutilise une clé tout en modifiant le montant.
  • Même clé, requête initiale toujours en cours → rejet avec possibilité de réessai: "A request with this Idempotency-Key is still being processed, retry shortly." Aucune double exécution concurrente n'est possible.

Les clés sont délimitées par entreprise (company), ce qui évite toute collision entre deux locataires (tenants) générant le même UUID.

La garantie complémentaire s'applique dans l'autre sens. La livraison des webhooks est assurée au moins une fois (at-least-once), ce qui signifie que votre point de terminaison recevra parfois le même événement deux fois, suite à un délai d'expiration alors que la livraison avait réussi, ou lors d'une relivraison. Chaque événement comporte un identifiant, et votre gestionnaire doit enregistrer les identifiants traités tout en ignorant les doublons. L'idempotence est une propriété requise des deux côtés d'une intégration. Le guide des conventions d'API détaille l'approche côté requêtes.

FAQ

Qu'est-ce qu'une clé d'idempotence ?

Il s'agit d'un identifiant généré par le client et transmis avec une requête, permettant au serveur d'identifier qu'il s'agit d'une nouvelle tentative pour la même opération logique. La première requête s'exécute, tandis que les essais suivants avec la même clé renvoient le résultat stocké au lieu d'exécuter à nouveau l'action.

Pourquoi les API de comptabilité ont-elles besoin de l'idempotence ?

Parce qu'une écriture dupliquée génère un document réel aux conséquences bien réelles : une seconde facture consommant un numéro séquentiel, s'inscrivant dans le grand livre et alimentant une déclaration de TVA. Contrairement à une lecture dupliquée, il est impossible de simplement la supprimer ; elle exige une correction par note de crédit.

Dois-je générer une nouvelle clé pour chaque nouvelle tentative ?

Non, cela neutralise complètement le mécanisme. Générez une seule clé par opération logique et réutilisez-la pour chaque nouvel essai de cette même opération. Une clé inédite par tentative équivaut à traiter chaque tentative comme une opération nouvelle.

Que se passe-t-il si je réutilise une clé avec une charge utile différente ?

La requête est rejetée. La clé affirme qu'il s'agit de la même opération, de sorte qu'un corps modifié indique une anomalie, très probablement un bogue de réutilisation de clé côté client. Renvoyer le résultat précédent en silence aggraverait la situation.

L'idempotence prévient-elle tous les doublons ?

Non. Elle prévient l'exécution en double d'une même requête. Deux utilisateurs distincts créant indépendamment la même facture formulent deux requêtes différentes. Il s'agit d'un problème de règle métier lié aux contraintes d'unicité, que la clé d'idempotence ne peut pas détecter.