Aller au contenu principal
Tous les articles
Technique4 min de lecture

L'idempotence : ne facturez jamais deux fois

Le réseau coupe au mauvais moment, vous réessayez, le client est débité deux fois. Comment une seule en-tête HTTP règle le problème.

Votre serveur envoie une demande de paiement. La connexion tombe avant que la réponse n'arrive. Vous ne savez pas si l'opération est passée. Que faites-vous ?

Si vous réessayez, vous risquez de débiter deux fois. Si vous ne réessayez pas, vous risquez de perdre une vente. Ce dilemme a une solution, et elle tient en une en-tête HTTP.

Le problème n'est pas rare

On imagine volontiers que ce cas est théorique. Il ne l'est pas. Un délai d'attente dépassé, un redémarrage de conteneur au mauvais moment, un client mobile qui bascule du Wi-Fi à la 4G, une file de tâches qui rejoue un message : chacun de ces incidents produit exactement la même situation.

À l'échelle de quelques centaines de paiements par jour, cela se produit. À l'échelle de quelques milliers, cela se produit tous les jours.

La clé d'idempotence

Toute création accepte une en-tête Idempotency-Key. La règle est simple : deux appels portant la même clé produisent une seule ressource.

bash
curl -X POST 'https://api-psp.charipay.ma/v1/payment-links' \
  -H 'X-CHARI-PAY-API-KEY: chari_sk_live_...' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd-2026-1042' \
  -d '{ "amount": 249.00, "description": "Commande #1042" }'

Rejouez cet appel autant de fois que vous voulez : vous obtiendrez toujours le même lien de paiement. Pas un deuxième.

La clé doit être déterministe et propre à l'opération. La référence de votre commande est un excellent candidat : vous la connaissez, elle ne change pas, et elle est unique chez vous. Un identifiant aléatoire tiré à chaque tentative, en revanche, ne sert à rien — c'est exactement ce que la clé est censée éviter.

Les liens de paiement vont plus loin

Sur les liens de paiement, le champ externalId porte la même garantie, de façon persistante : il est unique par compte et par environnement.

Créez deux fois un lien avec le même externalId, et l'API renvoie le lien existant avec un code 200 au lieu de 201. La différence de code vous dit exactement ce qui s'est passé, sans que vous ayez à conserver le moindre état intermédiaire.

C'est particulièrement utile quand votre traitement est déclenché par une file de messages : le message peut être livré deux fois, votre code peut appeler deux fois, le résultat reste juste.

Pour les remboursements

La référence de remboursement joue le même rôle. Rejouer un remboursement avec la même référence ne rembourse pas deux fois — ce qui, sur un remboursement partiel, évite une classe entière d'incidents pénibles à démêler.

Ce que l'idempotence ne fait pas

Elle ne remplace pas la vérification de vos propres états. Si votre système peut créer deux commandes pour un même panier, aucune en-tête HTTP ne le corrigera.

Elle ne rend pas non plus vos webhooks idempotents. C'est un travail distinct, du même esprit : chaque livraison porte un identifiant d'événement, stockez-le, et ignorez ce que vous avez déjà traité. Une même notification peut arriver deux fois — c'est voulu, pour qu'aucune ne se perde.

Générer une bonne clé

La clé d'idempotence n'est pas un nombre aléatoire tiré au moment de l'appel — ce serait passer à côté de tout l'intérêt. Une bonne clé est déterministe : la même opération métier produit toujours la même clé.

Le motif le plus sûr : dérivez la clé de l'identifiant de l'objet métier qui déclenche le paiement. commande-8842-paiement pour l'encaissement de la commande 8842 ; abonnement-512-echeance-2026-09 pour une échéance d'abonnement ; remboursement-tx-77a1 pour un remboursement. Si votre code repasse par là — retry automatique, file de messages livrée deux fois, utilisateur pressé — la clé est identique, et l'opération unique.

À l'inverse, deux opérations réellement distinctes doivent produire deux clés distinctes : n'utilisez jamais l'identifiant du client seul, ou toutes ses commandes se partageraient une clé.

Une limite à connaître : la clé protège l'appel, pas votre logique. Si votre code crée deux commandes différentes pour un même achat, chacune portera sa clé et chacune sera encaissée. L'idempotence commence dans votre modèle de données.

L'idempotence côté webhooks

Le même principe s'applique dans l'autre sens. Une notification de paiement peut vous être livrée deux fois — c'est une garantie de fiabilité, pas un défaut. Votre traitement de webhook doit donc être idempotent lui aussi : l'identifiant d'événement joue le rôle de la clé, une table à contrainte d'unicité joue le rôle du garde-fou.

Les deux mécanismes se répondent : la clé d'idempotence empêche votre retry de créer deux paiements ; la déduplication d'événements empêche notre retry de vous faire expédier deux commandes. Une intégration robuste porte les deux, et le même incident réseau — le seul vrai coupable dans cette histoire — ne coûte plus rien à personne.

La règle en une phrase

Toute création qui engage de l'argent porte une clé d'idempotence dérivée de votre propre référence, et tout traitement de webhook déduplique par identifiant d'événement. Deux habitudes, dix lignes de code, et une classe entière de bugs qui disparaît.

Écrit par Équipe ChariPay.

À lire ensuite

Technique4 min de lecture

Réussir son intégration des webhooks

Cinq erreurs qui reviennent dans presque toutes les intégrations, et le traitement en quinze lignes qui les évite toutes.

Une question sur votre intégration ?

Notre équipe répond aux commerçants comme aux développeurs, du premier test jusqu'à la mise en production.