Skip to main content

Configuración

Los webhooks se configuran en el panel de administración de Cobrix:
  1. Ve a ConfiguraciónWebhooks
  2. Crea un nuevo webhook con tu URL
  3. Guarda el signing secret (whsec_...)

Headers

Cada webhook incluye estos headers:
HeaderDescripción
X-Cobrix-SignatureFirma HMAC-SHA256
X-Cobrix-TimestampUnix timestamp
Content-Typeapplication/json

Estructura del Payload

{
  "id": "evt_1234567890abcdef",
  "event": "payment.succeeded",
  "created_at": "2026-01-22T15:30:00.000Z",
  "api_version": "2025-01-21",
  "data": {
    // Datos específicos del evento
  }
}
CampoTipoDescripción
idstringID único del evento (para idempotencia)
eventstringTipo de evento
created_atstringTimestamp ISO 8601
api_versionstringVersión de la API
dataobjectDatos del evento

Eventos

checkout.session.created

Se dispara cuando se crea una sesión de checkout.
{
  "id": "evt_xxx",
  "event": "checkout.session.created",
  "data": {
    "session": {
      "id": "cs_xxx",
      "status": "active",
      "amountMinor": 5000,
      "currency": "USD",
      "expiresAt": "2026-01-22T11:00:00.000Z"
    },
    "product": {
      "id": "prod_xxx",
      "sku": "PROD-001",
      "name": "Membresía Premium"
    },
    "customer": {
      "id": "cust_xxx",
      "email": "[email protected]"
    },
    "metadata": {
      "orderId": "ORD-12345"
    }
  }
}

checkout.session.completed

Se dispara cuando el checkout se completa exitosamente.
{
  "id": "evt_xxx",
  "event": "checkout.session.completed",
  "data": {
    "session": {
      "id": "cs_xxx",
      "status": "completed",
      "completedAt": "2026-01-22T10:30:00.000Z"
    },
    "payment": {
      "transactionId": "txn_xxx",
      "method": "debito_inmediato",
      "amountMinor": 5000,
      "currency": "USD"
    },
    "metadata": {
      "orderId": "ORD-12345"
    }
  }
}

checkout.session.expired

Se dispara cuando una sesión expira sin completarse.
{
  "id": "evt_xxx",
  "event": "checkout.session.expired",
  "data": {
    "session": {
      "id": "cs_xxx",
      "status": "expired",
      "expiredAt": "2026-01-22T11:00:00.000Z"
    },
    "metadata": {
      "orderId": "ORD-12345"
    }
  }
}

payment.processing

Se dispara cuando un pago está siendo procesado.
{
  "id": "evt_xxx",
  "event": "payment.processing",
  "data": {
    "payment": {
      "transactionId": "txn_xxx",
      "checkoutSessionId": "cs_xxx",
      "status": "processing",
      "method": "debito_inmediato",
      "amountMinor": 5000,
      "currency": "USD"
    },
    "metadata": {
      "orderId": "ORD-12345"
    }
  }
}

payment.succeeded

Se dispara cuando un pago se completa exitosamente.
{
  "id": "evt_xxx",
  "event": "payment.succeeded",
  "data": {
    "payment": {
      "transactionId": "txn_xxx",
      "checkoutSessionId": "cs_xxx",
      "status": "succeeded",
      "method": "debito_inmediato",
      "amountMinor": 5000,
      "currency": "USD",
      "paidAt": "2026-01-22T15:30:00.000Z"
    },
    "product": {
      "id": "prod_xxx",
      "sku": "PROD-001",
      "name": "Membresía Premium"
    },
    "customer": {
      "id": "cust_xxx",
      "email": "[email protected]",
      "name": "Juan Pérez"
    },
    "metadata": {
      "orderId": "ORD-12345"
    }
  }
}

payment.failed

Se dispara cuando un pago falla.
{
  "id": "evt_xxx",
  "event": "payment.failed",
  "data": {
    "payment": {
      "transactionId": "txn_xxx",
      "checkoutSessionId": "cs_xxx",
      "status": "failed",
      "method": "debito_inmediato",
      "failureReason": "insufficient_funds",
      "failedAt": "2026-01-22T15:30:00.000Z"
    },
    "metadata": {
      "orderId": "ORD-12345"
    }
  }
}

Verificación de Firma

Formato del Header

X-Cobrix-Signature: t=1706023800,v1=5257a869e7ecebeda32affa62cdca3fa...

Algoritmo

  1. Extraer t (timestamp) y v1 (firma)
  2. Construir: {timestamp}.{raw_body}
  3. Calcular: HMAC-SHA256(mensaje, secret)
  4. Comparar con v1 (timing-safe)
  5. Verificar timestamp < 5 minutos
const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const [tPart, vPart] = signature.split(',');
  const timestamp = tPart.replace('t=', '');
  const sig = vPart.replace('v1=', '');

  // Verificar timestamp
  if (Math.abs(Date.now() / 1000 - parseInt(timestamp)) > 300) {
    return false;
  }

  // Calcular firma esperada
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${payload}`)
    .digest('hex');

  // Comparación timing-safe
  return crypto.timingSafeEqual(
    Buffer.from(sig),
    Buffer.from(expected)
  );
}

Reintentos

IntentoDelay
1Inmediato
21 min
35 min
430 min
52 horas
624 horas

Respuesta Esperada

Tu endpoint debe responder:
  • 200-299: Webhook procesado correctamente
  • 4xx/5xx: Se reintentará
{
  "received": true
}