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 método asíncrono terminar más tarde.

Eventos principales

EventoSignificadoConfirmar compra
checkout_session.completedTerminó el flujo; el pago puede seguir en procesamientoNo
checkout_session.expiredLa sesión ya no puede usarseNo
invoice.paidLa obligación de pago quedó pagada de forma autoritativaSí, después de verificar y deduplicar
credit_note.created, credit_note.updated, credit_note.voidedCiclo de vida de una nota de créditoNo

invoice.paid es la señal autoritativa de fulfillment. checkout_session.completed puede llegar mientras el pago sigue en procesamiento.

Las líneas de factura vinculadas al catálogo incluyen productId y productPlanPriceId en data.related.invoice.items, para que tu sistema de fulfillment pueda identificar lo comprado. Las líneas manuales omiten esos campos.

Configurar y recibir

Configura un endpoint en los ajustes de integración y verifica su activación para la cuenta 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 Pharos-Signature con el secreto del endpoint.
  3. Interpreta sólo el evento verificado y comprueba si ya procesaste su id.
  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 envía Pharos-Signature: t=<unix>,v1=<hmac_sha256>. Calcula HMAC SHA-256 sobre <timestamp>.<raw_body>, rechaza timestamps con más de cinco minutos y compara en tiempo constante.

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

export function verifyPharosSignature(
  rawBody: Buffer,
  signatureHeader: string,
  signingSecret: string,
) {
  const fields = Object.fromEntries(signatureHeader.split(',').map(part => part.split('=', 2)))
  const timestamp = Number(fields.t)
  if (!Number.isSafeInteger(timestamp) || Math.abs(Date.now() / 1000 - timestamp) > 300) return false
  const received = fields.v1 ?? ''
  const expected = createHmac('sha256', signingSecret)
    .update(`${timestamp}.`)
    .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 id 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.id)) return

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

    if (event.type === 'invoice.paid') {
      await tx.orders.markFulfilled({ invoiceId: event.data.object.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 mientras el pago sigue procesándose. Mantén el pedido pendiente hasta recibir invoice.paid; 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