Idempotencia en pagos: por qué un doble clic no debe cobrar dos veces

Trabajando en el equipo de pagos aprendí una lección que parece pequeña pero lo es todo: una operación de pago debe poder repetirse sin cobrar dos veces. A eso lo llamamos idempotencia.

El problema

El usuario pulsa "Pagar", la red va lenta, no ve respuesta y vuelve a pulsar. O tu propio sistema reintenta una petición que falló a medias. Sin protección, acabas de cobrar dos veces. En pagos eso no es un bug, es un incidente.

La idea: la clave de idempotencia

Cada intento de pago lleva una clave de idempotencia única que genera el cliente. El servidor guarda el resultado asociado a esa clave:

  • Si la clave es nueva → procesa el pago y guarda el resultado.
  • Si la clave ya existe → devuelve el resultado guardado, sin volver a cobrar.

Pasarelas como Stripe o Adyen lo soportan de serie con una cabecera Idempotency-Key. Pero tu propio backend también debe ser idempotente de punta a punta.

Cómo lo implemento

  • Genera la clave antes de mostrar el botón de pago, no después.
  • Persiste (clave → estado, resultado) de forma atómica: una restricción UNIQUE en base de datos es tu mejor amiga.
  • Trata los webhooks como idempotentes también: el mismo evento puede llegarte varias veces y en distinto orden.
  • Distingue "en curso" de "completado" para no procesar dos peticiones concurrentes con la misma clave.

Lo que me llevo

La idempotencia no es solo para pagos: reintentos, colas de mensajes, jobs… cualquier cosa que pueda ejecutarse "más de una vez" la necesita. En pagos, simplemente, no es negociable.

¿Montando una pasarela o un sistema de cobros? Escríbeme desde la página de inicio.

← Volver al blog