Pharos Docs
Cobros

Webhooks de Collections

Verifica eventos de checkout y confirma compras sólo después del éxito de pago autorizado.

Los webhooks notifican a tu backend los resultados de enlaces de pago y Cart Checkout. Una redirección no prueba un pago: el navegador puede cerrarse o un medio asíncrono terminar más tarde.

Eventos principales

EventoSignificadoConfirmar compra
checkout_session_completedTerminó el flujo; el pago puede seguir processingNo
checkout_session_expiredLa sesión ya no puede usarseNo
purchase_succeededLa compra alcanzó el éxito de pago autorizadoSí, después de verificar y deduplicar

purchase_succeeded puede incluir checkoutSession.id. Úsalo para reconciliar sesión, clientReferenceId, factura, pago, suscripción y carrito original.

Configurar y recibir

Configura un endpoint en los ajustes de integración y verifica su activación para la empresa y entorno elegidos. Necesitas acceso a esa configuración y un receptor en tu servidor.

  1. Pharos guarda el estado comercial y envía el evento JSON al endpoint configurado.
  2. Tu servidor lee el cuerpo original y verifica x-pharos-signature con el secreto de firma.
  3. Interpreta sólo el evento verificado y comprueba si ya procesaste su eventId o idempotencyKey.
  4. Registra la identidad y ejecuta la acción comercial de forma atómica o mediante un flujo idempotente equivalente.
  5. Responde exitosamente sólo después de aceptar la entrega. Las entregas fallidas pueden reintentarse.

Verificar la firma

Pharos firma los bytes exactos del cuerpo con HMAC SHA-256 y envía x-pharos-signature: sha256=.... Calcula la firma antes de parsear JSON y compara con una función de tiempo constante:

import { createHmac, timingSafeEqual } from 'node:crypto'

export function verifyPharosSignature(
  rawBody: Buffer,
  signatureHeader: string,
  signingSecret: string,
) {
  const received = signatureHeader.replace(/^sha256=/, '')
  const expected = createHmac('sha256', signingSecret)
    .update(rawBody)
    .digest('hex')

  const receivedBuffer = Buffer.from(received, 'hex')
  const expectedBuffer = Buffer.from(expected, 'hex')

  return receivedBuffer.length === expectedBuffer.length
    && timingSafeEqual(receivedBuffer, expectedBuffer)
}

Rechaza firmas ausentes, malformadas o inválidas. No verifiques un JSON serializado de nuevo: espacios y orden de claves pueden cambiar los bytes.

Evitar efectos duplicados

Trata las entregas como al menos una vez, no exactamente una vez. Guarda eventId o la idempotencyKey estable con una restricción única. Una entrega repetida debe responder éxito sin duplicar pedidos, acceso al servicio, emails ni otras acciones.

async function handleVerifiedEvent(event: PharosEvent) {
  if (await events.has(event.eventId)) return

  await database.transaction(async (tx) => {
    await tx.events.insert({
      eventId: event.eventId,
      idempotencyKey: event.idempotencyKey,
    })

    if (event.eventKey === 'purchase_succeeded') {
      await tx.orders.markFulfilled({
        checkoutSessionId: event.payload.checkoutSession?.id,
      })
    }
  })
}

Este ejemplo es ilustrativo: adapta tipos, transacciones y almacenamiento a tu sistema. La identidad única debe proteger también frente a dos entregas concurrentes.

Medios asíncronos

Transferencias, tickets, efectivo o redirecciones pueden completarse después del checkout. Una sesión puede estar complete con paymentStatus=processing. Mantén el pedido pendiente hasta recibir purchase_succeeded; no deduzcas éxito por tiempo transcurrido.

Si falla después, actualiza el pedido con el evento verificado correspondiente. Reutiliza la sesión sólo si su estado permite otro intento.

Historial y reintentos

Los endpoints deben usar HTTPS público, sin credenciales en la URL. Se rechazan destinos privados o inseguros y no se siguen redirecciones. La entrega tiene un plazo de diez segundos.

La entrega automática admite hasta ocho intentos en un máximo de 24 horas. Los reintentos conservan ID y cuerpo firmado. La configuración de integración enlaza al historial; un reintento manual afecta esa entrega y no reenvía el evento a email o analítica.

Las notificaciones comerciales requieren activación. Reconciliar pagos antiguos no envía automáticamente notificaciones históricas. Verifica activación y configuración antes de depender de eventos en producción.

Verificar y resolver problemas

  • Prueba éxito inmediato y demorado, fallo, vencimiento y entregas duplicadas en Sandbox.
  • Conserva el secreto fuera del código y usa la configuración compatible para rotarlo.
  • Si falla la firma, comprueba cuerpo original, header y secreto antes de procesar.
  • Si no llegan eventos, revisa activación, historial y respuesta de tu endpoint.
  • Responde rápido y delega trabajo lento a un flujo idempotente; alerta por fallos repetidos.
  • Guarda identificadores de evento y correlación sin registrar secretos ni datos sensibles de pago.

Continuar