Todos los artículos

Pagos e integraciones

Webhooks de Stripe: evita efectos duplicados, no solo eventos repetidos

Guardar un identificador de evento ayuda a reconocer una entrega repetida. Para proteger un pedido necesitas además decidir qué efecto de negocio puede ocurrir, en qué estado y cómo comprobarlo si falla un paso.

Equipo editorial de KIURO · Publicado: · Revisado:

Stripe advierte de entregas duplicadas y sin orden garantizado. Su guía exige verificar la firma con el cuerpo original y responder con rapidez. Es documentación de referencia, no un lanzamiento de esta semana. (Stripe)

Dos claves para dos problemas

Una clave de recepción identifica qué mensaje has guardado. Una clave de efecto identifica qué operación quieres permitir sobre el negocio. Pueden coincidir en algún caso simple, pero no conviene diseñar el sistema suponiendo que son siempre equivalentes.

Matriz propuesta para diseñar la integración.
CasoQué no resuelve por sí solo event.idControl que debes definir
Mismo evento entregado otra vezLa recuperación si el primer procesamiento quedó a mediasEstado de la recepción y reintento seguro
Eventos distintos sobre un objetoLa unicidad del efecto comercialClave de efecto y transición permitida
Evento recibido tardeLa vigencia del estado que proponeReglas de estado y evidencia autorizada
Petición saliente con resultado inciertoLo ocurrido en el proveedorIdempotencia saliente y reconciliación

Aceptar significa haber conservado el trabajo

Nuestra propuesta es validar la petición y guardar una entrada durable antes de confirmar que se ha recibido. Una cola solo en memoria puede perder el trabajo al reiniciar. Si utilizas una base de datos y una cola separadas, define cómo recuperar una entrada guardada que todavía no llegó al trabajador.

pseudocódigo de diseño; no implementación lista para producción:
recibir(cuerpo_original, firma):
  verificar firma con el secreto del endpoint
  guardar en inbox con unicidad por cuenta + id de evento
  si ya existe, mantener su estado de procesamiento
  confirmar recepción solo después del guardado durable

recuperador:
  localizar entradas pendientes que no estén en la cola
  volver a ofrecerlas al trabajador de forma segura

La firma acredita una propiedad de la entrega, no que tu aplicación haya interpretado bien el evento. Necesitas también elegir los tipos que atiendes, comprobar los datos requeridos y utilizar una lógica compatible con la versión de la integración. Un evento no relevante no debería producir una escritura comercial por caer en una rama genérica.

Procesar el efecto dentro de una frontera clara

Supongamos que el efecto consiste en confirmar un pedido tras comprobar un pago. La operación debería relacionar cuenta, objeto de pago y pedido, y comprobar la transición permitida. Si otro evento conduce a la misma confirmación, una restricción de unicidad y una transacción deben impedir crearla de nuevo.

Un ejemplo de clave de efecto podría combinar «confirmar pedido», el identificador del pedido y el objeto de pago correspondiente. Es una propuesta ilustrativa: las reglas concretas dependen de cobros parciales, devoluciones u otras operaciones. Una clave demasiado amplia puede suprimir trabajo legítimo; una demasiado estrecha puede duplicarlo.

trabajador(inbox_id):
  cargar la evidencia necesaria para el evento
  iniciar transacción
  comprobar estado del pedido y transición permitida
  registrar efecto único y cambio de datos de forma atómica
  guardar tareas externas en outbox
  marcar la entrada procesada
  confirmar transacción

outbox:
  entregar cada tarea con control propio de reintento y estado

La transacción no se extiende mágicamente al exterior

Un correo o una petición a otro servicio puede completarse aunque tu proceso no reciba su respuesta. Por eso el outbox necesita sus propios estados y una vía para comprobar resultados ambiguos. No describimos el diseño como «exactly once»: los límites entre sistemas exigen recuperar y reconciliar.

La API de Stripe dispone de claves de idempotencia para solicitudes salientes compatibles. Ese mecanismo pertenece a las peticiones que envías; no sustituye la lógica de recepción de webhooks. (Stripe)

Después de un timeout, conserva la identidad de la operación y consulta la documentación de la API y versión utilizadas antes de repetir. Crear una clave nueva por cada reintento puede tratar la repetición como otra operación. Tampoco reutilices una clave para una intención comercial diferente.

Pruebas que deberían preceder a la conexión real

  • Entregar dos veces el mismo evento y comprobar tanto el estado de recepción como el del pedido.
  • Presentar eventos distintos que proponen el mismo efecto y verificar que la regla de negocio se conserva.
  • Invertir un orden esperado y observar si el sistema retiene, consulta o reconcilia sin perder trazabilidad.
  • Interrumpir un trabajador después de guardar datos y antes de una tarea externa.
  • Simular un resultado incierto del proveedor y comprobar la recuperación autorizada.

Estas pruebas son una propuesta y no se han ejecutado en esta pieza. Deben utilizar datos y entornos de prueba, sin simular ventas o contactos reales. El resultado aceptable incluye errores visibles y trabajo recuperable, no solo la ausencia de excepciones.

Si encargas una integración de pagos, acuerda estas fronteras de recepción, efecto y reconciliación como parte de la aceptación. La entrega de un webhook y la finalización de un proceso comercial son estados distintos.

Fuentes

Privacidad y analítica

Con tu permiso, Kiuro utiliza Google Analytics 4 para analizar visitas e interacciones, como navegar entre páginas, profundidad de lectura, proyectos, clics de contacto e inicio y envío correcto del formulario. Google también procesa datos de navegador, dispositivo y ubicación aproximada. El proveedor es Google Ireland Limited, en Irlanda; también puede existir tratamiento por Google LLC en Estados Unidos.

No enviamos tu nombre, email ni el contenido de tus mensajes. El almacenamiento publicitario, la publicidad personalizada y Google Signals están desactivados. Google Analytics no se carga antes de tu aceptación.

Tras aceptar, el registro temporal de navegación de esta pestaña es válido durante 30 segundos y se consume al llegar al destino. Tu elección se guarda durante seis meses en este navegador. Las cookies de analítica también se configuran para caducar a los seis meses. Puedes cambiar tu decisión en cualquier momento; rechazar detiene la medición y elimina las cookies de Analytics accesibles de esta web.

Las preferencias de tema claro u oscuro y de animaciones se guardan por separado en tu dispositivo. Contacto: hello@kiuro.ch.