Webhooks
Recibe notificaciones HTTP POST inmediatas en tu servidor cuando se confirme un pago móvil, una transferencia bancaria o una renovación de suscripción.
Firma de webhook: HMAC-SHA256
Los webhooks salientes de enlaces de pago, incluidas las pruebas, llevan X-Pabilo-Timestamp (segundos Unix) y X-Pabilo-Signature (prefijo sha256= y 64 caracteres hexadecimales en minúsculas).
Calcula HMAC-SHA256 usando como clave el texto del secreto y como mensaje el timestamp, un punto y los bytes exactos del cuerpo HTTP. No decodifiques el secreto como hexadecimal ni vuelvas a serializar el JSON.
curl https://api.pabilo.app/me/webhook-secret -H "appKey: TU_API_KEY"{
"message": "Webhook secret fetched successfully",
"webhook_secret": "SECRETO_DEL_TITULAR"
}Requiere lectura de integrations. El secreto pertenece al titular; los clientes no pueden consultarlo. Consérvalo únicamente en el servidor receptor. Rechaza firmas ausentes o inválidas, compara en tiempo constante y aplica una ventana de antigüedad (por ejemplo, cinco minutos con el reloj sincronizado).
Una entrega 2xx se considera exitosa. El emisor registra los fallos y avisa al dueño; no se garantiza una política automática de reintentos. La firma no sustituye la idempotencia: registra la identidad del pago y su estado para evitar entregar dos veces una orden.
Pabilo intenta entregar el evento una vez que el banco valida la transferencia, sin necesidad de polling.
Cada evento incluye identificadores únicos de pago y referencia bancaria para procesar cada cobro una sola vez.
El webhook se propaga automáticamente a cada cobro y ciclo recurrente de tus suscripciones activas.
1. ¿Cómo Funciona la Configuración?
Puedes configurar tu webhook_url al crear un Link de Pago (vía API o Dashboard) o al registrar una Suscripción:
- Creas una orden o enlace indicando tu endpoint:
"webhook_url": "https://tu-tienda.com/api/webhooks/pabilo". - El cliente realiza el pago móvil o transferencia e ingresa su referencia en el checkout.
- Pabilo verifica los fondos directamente con la entidad bancaria en tiempo real.
- Pabilo despacha una petición HTTP
POSTcon el cuerpo JSON a tu servidor con todos los detalles de la operación. - Tu servidor verifica primero la firma y después valida el estado
status: "paid", despacha la orden y responde con un código HTTP200 OK.
2. Estructura del Payload
La petición enviada por Pabilo a tu URL contiene los datos completos de la transacción bancaria y el enlace correspondiente.
Evento: Pago Confirmado (status: "paid")
{
"payment_link_id": "675725869e6febc736848bf1",
"status": "paid",
"credit_balance": 150.5,
"payment_link": {
"id": "675725869e6febc736848bf1",
"name": "Pago de Orden #1042",
"amount": 50,
"currency": "USD",
"description": "2x Camisas Premium + Envío",
"webhook_url": "https://miapp.com/webhook/pabilo",
"status": "paid",
"url": "https://pabilo.app/pay/675725869e6febc736848bf1",
"redirect_url": "https://miapp.com/gracias",
"with_subscription_id": null,
"type": "api"
},
"user_bank_payment": {
"id": "675725869e6febc736848bf4",
"bank_reference_id": "0025513902095",
"amount": 3122.5,
"status": "paid",
"credit_cost": 1,
"created_at": "2026-08-03T15:30:00Z"
}
}Descripción de Campos Clave
| Campo | Tipo | Descripción |
|---|---|---|
| payment_link_id | string | ID único del link de pago que originó el cobro. |
| status | string | Estado final de la transacción (paid o failed). |
| credit_balance | number | Saldo de créditos restante en tu cuenta de Pabilo tras la validación. |
| payment_link | object | Objeto completo del link de pago con monto, moneda, descripción y URLs de retorno. |
| user_bank_payment | object | null | Información de la transacción bancaria validada (referencia bancaria emitida, monto real en Bs acreditado y costo en créditos). |
Evento: Pago Rechazado o Fallido (status: "failed")
{
"payment_link_id": "675725869e6febc736848bf1",
"status": "failed",
"credit_balance": 149.5,
"payment_link": {
"id": "675725869e6febc736848bf1",
"status": "failed",
"status_detail": "payment not found: movement not found with bank reference 00123"
},
"user_bank_payment": null
}3. Ejemplo de Receptor en tu Servidor (Node.js / Express)
Asegúrate de responder rápidamente con código 200 OK después de verificar la firma y persistir el evento; procesa las tareas prolongadas desde tu cola:
const express = require("express");
const { createHmac, timingSafeEqual } = require("node:crypto");
// saveEventOnce debe persistir el evento de forma durable e idempotente.
// Monta este router ANTES de cualquier express.json() global.
function webhookRouter(saveEventOnce) {
const router = express.Router();
const secret = process.env.PABILO_WEBHOOK_SECRET;
if (!secret) throw new Error("Falta PABILO_WEBHOOK_SECRET");
router.post("/api/webhooks/pabilo", express.raw({ type: "application/json" }), async (req, res) => {
const timestamp = req.get("X-Pabilo-Timestamp") || "";
const signature = req.get("X-Pabilo-Signature") || "";
if (!/^\d+$/.test(timestamp) || !/^sha256=[a-f0-9]{64}$/.test(signature) || !Buffer.isBuffer(req.body)) {
return res.sendStatus(401);
}
// 300 segundos es una política del receptor, no un valor enviado por Pabilo.
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return res.sendStatus(401);
const expected = createHmac("sha256", secret)
.update(timestamp + ".").update(req.body).digest();
const received = Buffer.from(signature.slice(7), "hex");
if (received.length !== expected.length || !timingSafeEqual(received, expected)) return res.sendStatus(401);
let event;
try { event = JSON.parse(req.body.toString("utf8")); }
catch { return res.sendStatus(400); }
try {
await saveEventOnce(event);
return res.sendStatus(204);
} catch {
return res.sendStatus(500);
}
});
return router;
}
module.exports = webhookRouter;4. Reglas de Entrega y Buenas Prácticas
Tiempo de Respuesta
Tu endpoint debe responder con código HTTP 200 OK en menos de 10 segundos (timeout del emisor). Recomendamos responder inmediatamente y procesar envíos o correos en segundo plano.
Manejo de Idempotencia
Registra payment_link_id y bank_reference_id en tu base de datos para asegurar que reintentos de red no procesen la entrega de un pedido más de una vez.