CoreInnovateCoreInnovate
← Blog·Engineering

Construire des APIs de paiement idempotentes

5 février 2025·7 min read

L'idempotence est un prérequis pour des systèmes de paiement fiables, pas une fonctionnalité optionnelle. Ce guide couvre la conception des clés d'idempotence, les schémas de stockage et les cas limites qui font échouer les implémentations naïves.

L'idempotence dans les APIs de paiement n'est pas un atout optionnel. C'est le mécanisme qui prévient les doubles débits, les remboursements en doublon et les transferts dupliqués - autant de situations catastrophiques pour la confiance des clients et coûteuses à corriger. Pourtant, l'idempotence est fréquemment implémentée de façon incorrecte, partielle, ou pas du tout.

Pourquoi l'idempotence est nécessaire dans les systèmes distribués

Dans tout système distribué, les requêtes peuvent échouer de façon à laisser l'appelant incertain quant au résultat. Un client envoie une requête de paiement. La requête est traitée avec succès. La réponse est perdue en transit. Le délai d'attente du client expire. Le client réessaie.

Sans idempotence : la nouvelle tentative crée un second paiement. Le client est débité deux fois.

Avec idempotence : la nouvelle tentative retourne le résultat de la requête originale. Le client n'est débité qu'une seule fois.

Il ne s'agit pas d'un mode de défaillance théorique. Les partitions réseau, les redémarrages d'applications et les pannes DNS se produisent régulièrement. Votre API doit être conçue en supposant que les clients vont réessayer, et le résultat d'une nouvelle tentative doit être identique au résultat d'une première requête réussie.

Conception des clés d'idempotence

La clé d'idempotence est un identifiant unique généré par le client pour une requête. Le client est responsable de sa génération, de son envoi avec la requête, et de l'utilisation de la même clé pour toutes les nouvelles tentatives de la même opération logique.

Propriétés essentielles :

  • Unique par opération : la clé doit identifier de façon unique une opération spécifique intentionnelle, pas simplement la requête. Une clé dérivée uniquement du montant et de la devise entrera en collision pour deux transactions légitimes ayant le même montant.
  • Contrôlée par le client : le serveur ne doit pas générer de clés d'idempotence. Seul le client sait ce qui constitue une nouvelle tentative par rapport à une nouvelle opération.
  • Délimitée au client : les clés d'idempotence doivent être rattachées au client API (par clé API ou identifiant client) afin que deux clients puissent utiliser la même clé sans conflit.

Un format raisonnable : des UUID générés au moment où l'utilisateur initie l'action. Un nouvel UUID pour chaque nouvelle action initiée par l'utilisateur ; le même UUID pour les nouvelles tentatives.

Voir aussi : Principes de conception du registre pour les plateformes de portefeuille

Implémentation côté serveur

À la réception d'une requête avec une clé d'idempotence :

  1. Vérifier le store d'idempotence pour la clé. Si trouvée, retourner la réponse stockée.
  2. Si non trouvée, exécuter l'opération.
  3. Stocker la clé avec le résultat avant de retourner la réponse.
  4. Retourner la réponse.

L'étape 3 doit se produire avant l'étape 4. Si la réponse est retournée avant que la clé soit stockée et que l'application plante, la prochaine requête avec la même clé retraitera l'opération.

Le store d'idempotence doit être durable et cohérent. Un cache en mémoire n'est pas suffisant - un redémarrage perdrait toutes les clés, rendant les nouvelles tentatives non sécurisées. Utilisez un store persistant avec les mêmes garanties de durabilité que vos données de transaction.

Le problème des requêtes concurrentes

Une implémentation naïve présente une condition de course : deux requêtes concurrentes avec la même clé ratent toutes les deux la vérification d'idempotence et procèdent toutes les deux à l'exécution de l'opération.

Gérez cela avec un verrou distribué sur la clé d'idempotence. Avant de vérifier le store, acquérez un verrou. Après avoir stocké le résultat, libérez le verrou. Les requêtes concurrentes avec la même clé seront bloquées jusqu'à ce que la première requête soit complète, puis retourneront le résultat stocké.

Les verrous distribués sont délicats. Si l'application plante pendant qu'elle détient le verrou, le verrou doit expirer automatiquement. Choisissez un TTL de verrou plus long que la durée maximale de votre opération pour éviter l'expiration du verrou pendant un traitement normal.

Gestion de l'idempotence PSP

Votre couche d'idempotence doit s'étendre aux appels PSP. Si votre application traite un débit puis plante avant d'avoir enregistré le résultat, la prochaine tentative ne doit pas débiter la carte à nouveau.

La plupart des PSPs acceptent une clé d'idempotence sur les requêtes de débit. Passez une clé dérivée de votre clé d'idempotence interne lors de l'appel au PSP. Si l'appel est réessayé, le PSP retourne le résultat du débit original. Votre application enregistre ce résultat et le retourne au client.

Voir aussi : Les erreurs d'architecture les plus courantes dans les passerelles de paiement

Expiration des clés

Les clés d'idempotence doivent expirer après une période raisonnable - 24 heures est courant. Après expiration, la même clé peut être réutilisée et sera traitée comme une nouvelle requête. Cela empêche le store d'idempotence de croître indéfiniment tout en couvrant tous les scénarios de nouvelle tentative pratiques (les clients réessaient généralement en quelques minutes, pas en quelques jours).

Tester l'idempotence

L'idempotence doit être testée explicitement. Votre suite de tests doit inclure :

  • Deux requêtes identiques avec la même clé : une seule opération est exécutée, les deux retournent la même réponse
  • Deux requêtes identiques concurrentes avec la même clé : une seule opération est exécutée
  • Une requête où l'application plante après l'exécution mais avant le retour : la nouvelle tentative retourne le résultat correct sans réexécution
  • Une requête après expiration de la clé : traitée comme une nouvelle opération

L'idempotence est l'une de ces propriétés qu'il est facile d'avoir presque juste, mais difficile d'avoir complètement juste. Si votre API de paiement n'a pas d'idempotence de bout en bout - y compris au niveau du PSP - une évaluation de plateforme identifiera les lacunes.

CoreInnovate

Working on a payment platform challenge?

Our specialist engineers work directly with payment gateways, wallet providers, and fintech platforms. Start with a scoped architecture assessment.