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
| Evento | Significado | Confirmar compra |
|---|---|---|
checkout_session.completed | Terminó el flujo; el pago puede seguir en procesamiento | No |
checkout_session.expired | La sesión ya no puede usarse | No |
invoice.paid | La obligación de pago quedó pagada de forma autoritativa | Sí, después de verificar y deduplicar |
credit_note.created, credit_note.updated, credit_note.voided | Ciclo de vida de una nota de crédito | No |
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.
- Pharos guarda el estado comercial y envía el evento JSON al endpoint configurado.
- Tu servidor lee el cuerpo original y verifica
Pharos-Signaturecon el secreto del endpoint. - Interpreta sólo el evento verificado y comprueba si ya procesaste su
id. - Registra la identidad y ejecuta la acción comercial de forma atómica o mediante un flujo idempotente equivalente.
- 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.