Plataforma

Webhooks

Suscríbete a eventos de pago y checkout, verifica la firma y maneja los reintentos. Una sola integración cubre pagos en línea y presenciales.

Los webhooks avisan a tu backend en el momento en que algo ocurre: un pago se aprueba, se rechaza, se reembolsa o se disputa. Son la forma recomendada de conocer el resultado de un pago, tanto en línea como presencial.

Registrar un endpoint

Crea un endpoint de webhook como recurso de tu comercio. El secreto de firma (whsec_…) se devuelve una sola vez, al crearlo: guárdalo para verificar las entregas.

curl -X POST https://api.vecnet.io/api/webhooks \
  -H "Authorization: Token vk_live_1a2b3c4d5e6f7a8b9c0d" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://tienda.example.com/webhooks/vecnet",
    "events": ["payment.approved", "payment.refunded", "payment.chargeback"]
  }'
{
  "id": "whk_4d9c2b",
  "url": "https://tienda.example.com/webhooks/vecnet",
  "events": ["payment.approved", "payment.refunded", "payment.chargeback"],
  "status": "active",
  "secret": "whsec_test_0000000000000000000000000000abcd"
}

El secret (whsec_…) solo aparece en esta respuesta. Guárdalo ahora: lo necesitas para verificar cada entrega y no se puede recuperar después. Si lo pierdes, rótalo con POST /api/webhooks/:id/rotate.

El sobre del evento

Cada entrega es un sobre JSON. data contiene el objeto del que trata el evento (la transacción, la sesión o la liga).

{
  "id": "evt_0d1e2f3a",
  "object": "event",
  "event": "payment.approved",
  "merchant_id": "mrc_3f9a2c1b",
  "created_at": "2026-06-22T14:30:00.000Z",
  "data": {
    "id": "txn_9f3c2a71",
    "rail": "online_cnp",
    "amount_cents": 49900,
    "currency": "MXN",
    "status": "approved",
    "correlation_id": "corr_b8e21f3a4c5d6e7f"
  }
}

Encabezados de la entrega

EncabezadoDescripción
X-Vecnet-EventEl tipo de evento, por ejemplo payment.approved.
X-Vecnet-DeliveryUn id único de entrega (whd_…), estable entre reintentos.
X-Vecnet-Signature-TimestampSegundos Unix en que se firmó la entrega.
X-Vecnet-Signature-V1Firma HMAC de la entrega (ve abajo).

Verificar la firma

Cada entrega se firma con el secreto whsec_… de tu endpoint. La firma es el HMAC-SHA256 en hexadecimal de "{timestamp}.{rawBody}", donde rawBody son los bytes exactos del cuerpo de la petición:

X-Vecnet-Signature-V1 = hex( HMAC_SHA256( whsec, `${timestamp}.${rawBody}` ) )

Verifica sobre el cuerpo crudo (no sobre un objeto re-serializado), compara en tiempo constante y rechaza las entregas con una marca de tiempo demasiado antigua.

import crypto from "node:crypto";

function verify(req, secret) {
  const ts = req.headers["x-vecnet-signature-timestamp"];
  const sig = req.headers["x-vecnet-signature-v1"];
  const raw = req.rawBody; // los bytes exactos recibidos

  // Rechaza entregas viejas (ventana de 5 minutos).
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${ts}.${raw}`)
    .digest("hex");

  return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}

Captura el cuerpo crudo de la petición antes de que cualquier middleware de JSON lo re-serialice. Una firma calculada sobre un cuerpo re-codificado no va a coincidir.

Responder y reintentos

Devuelve un 2xx rápido para confirmar la entrega. Cualquier respuesta que no sea 2xx (o un tiempo de espera agotado) se trata como falla y se reintenta con espera creciente:

30 s  →  2 min  →  10 min  →  1 h      (hasta 5 intentos)

Cada intento tiene un tiempo de espera corto, así que haz el trabajo lento de forma asíncrona: confirma primero, procesa después.

Las entregas son idempotentes. El id X-Vecnet-Delivery es estable entre reintentos del mismo evento, así que deduplica por él antes de actuar.

Catálogo de eventos

EventoCuándo se dispara
payment.approvedUn pago fue aprobado (fondos capturados).
payment.pendingUn pago está pendiente (por ejemplo, en espera de un pago en efectivo).
payment.declinedUn pago fue rechazado.
payment.failedUn pago falló por un error.
payment.expiredUna sesión o liga expiró.
payment.cancelledUn pago fue cancelado o anulado.
payment.refundedUn pago fue reembolsado por completo.
payment.chargebackUn pago fue disputado / tuvo un contracargo.
payment.duplicateUn intento repetido de un pago ya procesado.
payment_link.createdSe creó una liga de pago.
payment_link.paidSe pagó una liga de pago.
checkout_session.createdSe creó una sesión de checkout alojado.
checkout_session.completedUna sesión de checkout alojado llegó a un estado final.

El catálogo vigente siempre está disponible en GET /api/webhooks/events.

Un solo resultado real puede disparar más de un evento. Por ejemplo, una sesión expirada dispara payment.expired y payment.failed. Suscríbete exactamente a los eventos que manejas.

Probar un endpoint

Envía una entrega firmada de prueba (webhook.test) para validar tu verificación de firma antes de recibir tráfico real:

curl -X POST https://api.vecnet.io/api/webhooks/whk_4d9c2b/test \
  -H "Authorization: Token vk_live_1a2b3c4d5e6f7a8b9c0d"

Inspeccionar entregas

Las entregas pasadas y su historial de intentos están disponibles como recurso de tu comercio. Cada entrega guarda el payload exacto, el código HTTP de tu respuesta y los primeros caracteres de su cuerpo:

curl https://api.vecnet.io/api/webhook-deliveries \
  -H "Authorization: Token vk_live_1a2b3c4d5e6f7a8b9c0d"

Si tu endpoint estuvo caído, vuelve a enviar una entrega con su payload original:

curl -X POST https://api.vecnet.io/api/webhook-deliveries/whd_9a41bc/redeliver \
  -H "Authorization: Token vk_live_1a2b3c4d5e6f7a8b9c0d"
¿Te fue útil esta página?

En esta página