Recurso técnico

Webhooks de pago: confirmación, idempotencia y conciliación

La forma en que tu sistema se entera de que un pago fue exitoso determina si tendrás pedidos duplicados, cobros sin pedido o diferencias al cierre del día. Esta guía explica cómo hacerlo de forma confiable.

En casi todas las integraciones de pago existe el mismo momento de riesgo: el cliente completa el pago en la pasarela y vuelve al sitio del comercio. Es tentador marcar el pedido como pagado en ese instante, cuando el navegador llega a la «página de éxito». Es también la causa más frecuente de inconsistencias.

Por qué la redirección no es una confirmación

La redirección depende del navegador del cliente, y el navegador no es un canal confiable para confirmar dinero:

  • El cliente puede cerrar la pestaña, quedarse sin batería o perder la conexión justo después de pagar. El pago existe, pero tu sistema nunca se entera.
  • Los parámetros de la URL de retorno pueden ser manipulados si no están firmados y verificados en el servidor.
  • Algunos medios de pago no se confirman de inmediato: el estado puede quedar pendiente y resolverse minutos u horas después.

La página de retorno sirve para la experiencia del usuario («estamos procesando tu pago»), pero el estado del pedido debe cambiar a partir de una fuente verificable del lado del servidor.

Navegador del clienteRedirección visual (solo experiencia UI)
Servidor del comercioMáquina de estados + Idempotencia
Pasarela de pagosProcesamiento del cobro
Canal 1: Webhook servidor a servidorNotificación asíncrona oficial (fuente principal)
Canal 2: Consulta por APISondeo programado / Conciliación (red de respaldo)
Esquema de confirmación robusta: el estado financiero depende de canales servidor a servidor, nunca del navegador del cliente.

Las dos fuentes confiables

Las pasarelas suelen ofrecer dos mecanismos que se complementan:

Fuentes para conocer el estado de un pago
MecanismoCómo funcionaPara qué sirve
Notificación servidor a servidor (webhook)La pasarela llama a una URL de tu servidor cuando cambia el estado de un pago.Enterarse rápido de los cambios sin preguntar constantemente.
Consulta de estado por APITu servidor pregunta a la pasarela el estado de un pago concreto.Verificar, recuperar notificaciones perdidas y resolver dudas.

Lo recomendable es usar ambos: el webhook como mecanismo principal y la consulta por API como verificación y como red de seguridad cuando una notificación no llega. Los nombres y la disponibilidad exacta de cada mecanismo dependen de la pasarela, por lo que siempre hay que revisar su documentación.

Modela el pago como una máquina de estados

Cada pasarela usa sus propios nombres de estado. Conviene traducirlos a un conjunto interno pequeño y definir qué transiciones están permitidas. Así, una notificación que intenta llevar un pago de «pagado» a «pendiente» se detecta como anómala en lugar de aplicarse a ciegas.

Ejemplo de estados internos y transiciones permitidas
Estado internoSignificadoPuede pasar a
pendienteSe inició el pago y aún no hay resultado.autorizado, pagado, rechazado, expirado
autorizadoFondos reservados, aún no cobrados (si la pasarela separa ambos pasos).pagado, anulado
pagadoCobro confirmado.reembolsado, reembolsado_parcial
rechazadoEl pago no fue aprobado.(estado final)
expiradoEl cliente no completó el pago a tiempo.(estado final)
anuladoAutorización cancelada antes del cobro.(estado final)
reembolsadoSe devolvió el total.(estado final)

Los estados de la tabla son un ejemplo: el conjunto real depende de los medios de pago y de lo que reporte cada pasarela. Lo importante es que la tabla de transiciones exista y que el código la respete.

Cómo recibir un webhook correctamente

  1. Verificar autenticidadFirma, token o mecanismo que provea la pasarela.
  2. Guardar el eventoRegistrar el contenido recibido antes de procesarlo.
  3. Responder rápidoDevolver un código de éxito en pocos segundos.
  4. Procesar en segundo planoActualizar pedido, stock y notificaciones.
  5. Descartar duplicadosUsar el identificador del evento o del pago.

Responder rápido es importante: si tu servidor tarda, la pasarela puede considerar que la entrega falló y reenviarla, generando más duplicados. Por eso el trabajo pesado (actualizar inventario, enviar correos, registrar en el ERP) se hace después de responder.

Un esquema simplificado, en pseudocódigo:

recibirWebhook(solicitud):
    si no firmaValida(solicitud): responder 401 y terminar
    evento = parsear(solicitud.cuerpo)

    // la restricción única en id_evento evita registrar dos veces lo mismo
    insertado = guardarSiNoExiste(tabla_eventos, evento.id, solicitud.cuerpo)
    responder 200
    si insertado: encolar(procesarEvento, evento.id)

procesarEvento(idEvento):
    evento = leer(tabla_eventos, idEvento)
    pago   = buscarPago(evento.referencia)       // con bloqueo de fila
    nuevo  = traducirEstado(evento.estado)
    si transicionPermitida(pago.estado, nuevo):
        actualizarEstado(pago, nuevo)
        ejecutarEfectos(pago, nuevo)             // confirmar pedido, stock, aviso
    si no:
        registrarAnomalia(pago, evento)          // revisar o consultar por API
    marcarProcesado(idEvento)

Idempotencia: en ambos sentidos

Al recibir

Las pasarelas pueden enviar la misma notificación más de una vez, y es su comportamiento esperado. La defensa es guardar el identificador del evento con una restricción única en la base de datos y hacer que los efectos sean idempotentes: confirmar un pedido que ya está confirmado no debe hacer nada.

Al enviar

Cuando tu sistema crea un cobro o un reembolso y la respuesta no llega (por ejemplo, por un tiempo de espera), no sabes si la operación se realizó. Reintentar a ciegas puede cobrar dos veces. Si la pasarela admite una clave de idempotencia, envía siempre la misma clave al reintentar la misma operación. Si no la admite, consulta el estado por API antes de reintentar.

Error frecuente

Generar la clave de idempotencia en cada intento. La clave debe identificar la operación de negocio (por ejemplo, «reembolso del pedido 1042») y guardarse antes del primer intento, para reutilizarla en los siguientes.

Eventos desordenados

No hay garantía de que las notificaciones lleguen en el orden en que ocurrieron. Un «pagado» puede llegar antes que un «autorizado». Con la máquina de estados, el evento atrasado simplemente no produce una transición válida y se ignora o se registra. Cuando haya dudas, la regla es simple: consultar el estado actual por API y usar esa respuesta como referencia.

Cuando el webhook no llega

Las notificaciones pueden perderse por una caída de tu servidor, un certificado vencido o un cambio de configuración. Un proceso programado debe buscar pagos que siguen en pendiente más allá de un tiempo razonable y consultar su estado en la pasarela. Este proceso convierte una notificación perdida en un retraso, no en un pedido huérfano.

Conciliación

Aun con webhooks e idempotencia bien implementados, conviene comparar periódicamente lo que tu sistema registró con los reportes de la pasarela. La conciliación detecta problemas que ningún mecanismo automático ve: configuraciones erróneas, cambios en la pasarela o errores humanos.

Diferencias típicas en una conciliación y qué indican
DiferenciaPosible causaAcción
Pago aprobado en la pasarela, pedido pendienteWebhook perdido o procesamiento fallido.Consultar estado y reprocesar.
Pedido pagado, sin pago en la pasarelaConfirmación basada en la redirección o error de código.Revisar urgente: posible entrega sin cobro.
Montos distintosErrores de redondeo, moneda o cargos parciales.Revisar cálculo en unidades mínimas de la moneda.
Reembolso en la pasarela no registradoReembolso hecho desde el panel de la pasarela.Registrar y definir el flujo oficial de reembolsos.

Errores frecuentes

  • Confirmar el pedido en la página de retorno.
  • No verificar la firma o la autenticidad del webhook.
  • Procesar todo antes de responder a la pasarela, provocando reintentos.
  • No guardar el contenido original de los eventos, lo que impide investigar después.
  • Usar números decimales de punto flotante para montos.
  • Registrar datos de tarjeta o credenciales en los logs.
  • No tener un proceso para pagos que quedan pendientes.

Lista de verificación

  • El estado del pedido cambia solo por fuentes del lado del servidor.
  • Los webhooks se verifican, se guardan y se responden rápido.
  • Existe una restricción única sobre el identificador de evento.
  • Hay una tabla de transiciones de estado y se respeta.
  • Las operaciones salientes usan claves de idempotencia o consulta previa.
  • Un proceso programado revisa los pagos pendientes.
  • Se concilia periódicamente con los reportes de la pasarela.

Preguntas frecuentes

¿Puedo usar solo la consulta por API, sin webhooks?

Es posible, consultando periódicamente los pagos pendientes, pero introduce demoras y más llamadas a la pasarela. Los webhooks permiten reaccionar rápido; la consulta sirve como verificación y respaldo.

¿Qué código debo responder si el evento ya fue procesado?

Un código de éxito. Para la pasarela, la entrega fue correcta; que tu sistema ya conociera el evento es un detalle interno.

¿Qué hago si la firma no es válida?

Rechazar la solicitud, no procesarla y registrar el intento. Si ocurre con frecuencia, revisar la configuración del secreto o la posibilidad de solicitudes falsificadas.

Este tema se complementa con cómo consumir servicios externos sin detener tu operación, que explica tiempos de espera y reintentos en las llamadas salientes.

¿Quieres revisar tu caso con nosotros?

Si este tema afecta a un sistema que operas o estás construyendo, escríbenos con el contexto y lo conversamos.