Aller au contenu
Chargement des API keys…

Idempotence

Rejouer une création sans risque grâce à l'en-tête Idempotency-Key.

Un délai dépassé côté réseau ne vous dit rien sur ce que le serveur a fait. Rejouer un POST /transfers à l'aveugle peut payer deux fois un bénéficiaire. Une clé d'idempotence lève l'ambiguïté : le second appel renvoie la réponse du premier au lieu de refaire le travail.

Deux pages, deux questions

Cette page décrit la mécanique : l'en-tête, son format, où il fonctionne, combien de temps il dure. Pour choisir vos clés, structurer vos nouvelles tentatives et dédupliquer les webhooks dans votre propre code, consultez Bonnes pratiques → Idempotence.

L'en-tête

Envoyez Idempotency-Key sur la requête. Sans préfixe X-.

En-têtes de la requête
Authorization: sk.xxxxxxxxxxxxxxxx...
Idempotency-Key: 8f14e45f-ceea-467a-9bd1-2c2bd7a1e0a4
Content-Type: application/json
Idempotency-Keystringfacultatif
Votre propre identifiant, de 1 à 128 caractères correspondant à [A-Za-z0-9._:-]+. Un UUID v4 est le choix habituel. Tout caractère hors de cet alphabet renvoie un 422 sur idempotency_key.

Les clés sont rattachées au compte, à l'environnement et à l'opération, et celles d'une clé publique à part de celles d'une clé secrète ou restreinte : sur POST /payments, une clé que l'une a utilisée est refusée à l'autre avec un 422. La même chaîne utilisée en sandbox et en live correspond à deux clés indépendantes, la même chaîne sur POST /customers et sur POST /links aussi, et aucun autre compte ne peut entrer en collision avec les vôtres.

Où ça fonctionne

EndpointProtection
POST /paymentsPermanente
POST /transfersPermanente
POST /transfers/batchObligatoire, chaque ligne a pour clé celle du lot et sa reference, permanent
POST /refundsPermanente
POST /payments/{id}Réponse enregistrée, 24 heures
POST /payments/{id}/splits, /confirm, /closeRéponse enregistrée, 24 heures
POST /customersRéponse enregistrée, 24 heures
POST /beneficiariesRéponse enregistrée, 24 heures
POST /invoicesRéponse enregistrée, 24 heures
POST /linksRéponse enregistrée, 24 heures
POST /webhooksRéponse enregistrée, 24 heures
POST /accountsRéponse enregistrée, 24 heures
POST /customers/{id}/tax_idsRéponse enregistrée, 24 heures
POST /tax/registrationsRéponse enregistrée, 24 heures
POST /disputes/{id}/messagesRéponse enregistrée, 24 heures

Les deux protections se comportent de la même façon vues de l'extérieur, et diffèrent par la durée pendant laquelle elles se souviennent.

Les paiements, les transferts et les remboursements gardent la clé pour toujours. La protection n'expire jamais : rejouer cette clé dans un an renvoie toujours le paiement, le transfert ou le remboursement d'origine au lieu d'en créer un second. Deux requêtes simultanées avec la même clé ne créent jamais deux ressources.

La première réponse de ces trois opérations est aussi enregistrée pendant 24 heures. Dans ce délai, un rejeu renvoie cette réponse, inchangée, avec Idempotent-Replayed: true. Passé ce délai, le paiement, le transfert ou le remboursement d'origine est renvoyé dans un nouveau 201, sans l'en-tête, et pour un paiement avec un nouvel authorization_url et un nouvel authorization_token, car une session de paiement ne sert qu'une fois.

Tout le reste se souvient de la première réponse pendant 24 heures, et rien d'autre. Passé ce délai, la clé est oubliée et le même appel crée une nouvelle ressource. C'est le bon compromis pour un client ou un lien de paiement, où un doublon est une gêne plutôt qu'une perte.

Ce qui est enregistré, c'est ce que l'opération a répondu en dessous de 500, refus compris. Une charge refusée par un 402 sur POST /payments/{id} est rejouée comme refusée pendant 24 heures : réessayer une fois le compte du client rechargé demande une nouvelle clé. Un 5xx n'est jamais enregistré, pas plus qu'une requête rejetée à la validation : la clé est libérée et la tentative suivante s'exécute de nouveau.

Rejouer une requête

Générez la clé une seule fois, au moment de l'intention métier, et réutilisez-la pour chaque tentative réseau de cette même intention.

curl https://api.wajub.com/transfers \
-H "Authorization: $WAJUB_API_KEY" \
-H "Idempotency-Key: payout-invoice-4172" \
-H "Content-Type: application/json" \
-d '{
  "amount": 100000,
  "currency": "XAF",
  "beneficiary": {
    "phone": "237670000000",
    "channel": "cm.mtn",
    "name": "Amina Nkeng"
  }
}'

Envoyez-la une seconde fois et vous recevez la première réponse, avec le même id de transfert et un en-tête Idempotent-Replayed: true. Rien n'a été créé, aucun argent n'a bougé.

Réutiliser une clé avec un corps différent

Rejouer n'est sûr que s'il s'agit de la même requête. Si vous réutilisez une clé avec un payload différent, Wajub refuse au lieu de renvoyer en silence une ressource que vous n'avez pas demandée :

Réponse · 422 Unprocessable Content
{
"code": 422,
"status": "Unprocessable Content",
"message": "This Idempotency-Key was already used with a different request payload.",
"errors": {
"idempotency_key": [
"This Idempotency-Key was already used with a different request payload."
]
}
}

Ce qui est comparé, et pendant combien de temps, dépend de l'endpoint.

EndpointCe qui est comparéPendant combien de temps
POST /paymentsLe corps entierToute la vie du paiement
POST /transfersamount, currency, beneficiary, description, reason, reference, metadata24 heures
POST /refundsLe corps entier pendant 24 heures, puis le paiement et amountToute la vie du remboursement
POST /payments/{id} et /splitschannel, data, split_count, split_amounts24 heures
Tout le resteLe corps entier24 heures

Sur POST /transfers, plus rien n'est comparé une fois les 24 heures passées : la clé renvoie le transfert d'origine, quoi que dise le nouveau corps.

Sur POST /customers/{id}/tax_ids et POST /disputes/{id}/messages, le client ou le litige du chemin est comparé aussi : la même clé sur un autre répond ce 422. Les pièces jointes d'un message sont comparées par leur contenu.

Une clé de remboursement réutilisée après 24 heures pour un autre paiement ou un autre montant est refusée par le même 422, avec le message This Idempotency-Key was already used with a different refund request.

Un en-tête mal formé arrive sous la forme du même 422 avec un autre message, The Idempotency-Key header format is invalid.

Concurrence

Une requête qui arrive pendant que la première avec la même clé est encore en cours n'attend pas. Elle reçoit tout de suite un 409, et le même appel quelques instants plus tard reçoit la réponse enregistrée. Vous n'avez jamais deux créations en cours pour une même clé, ni une ressource écrite à moitié.

Réponse · 409 Conflict
{
"code": 409,
"status": "Conflict",
"message": "A request with this Idempotency-Key is still being processed.",
"errors": {
"idempotency_key": [
"A request with this Idempotency-Key is still being processed."
]
}
}

Les SDK serveur relancent ce 409 pour vous avec la même clé et renvoient la réponse enregistrée.

Une clé par intention métier, pas par tentative

Dérivez la clé de quelque chose de déjà unique dans votre propre domaine : un ID de commande, un numéro de facture, une ligne de paie. payout-invoice-4172 survit à un redémarrage du processus, à un redéploiement et à une nouvelle tentative de la file d'attente. Un UUID généré dans la boucle de nouvelles tentatives ne survit à aucun des trois.

Repérer une requête rejouée

Dans Konsole → Journaux API, une réponse servie par la protection d'idempotence porte un badge replayed, ce qui vous permet de distinguer un appel en double d'une ressource en double.

Que pensez-vous de ce contenu ?