VexorpayDocs
VexorpayDocs
APIDocumentación de VexorpayPrimeros pasosVenta de producto digital

Core API

Referencia de APICrear link de pago únicoCrear link de pago recurrenteListar y obtener linksSuscripcionesCheckout por compraCobrosSaldoRetirosAcreditacionesEventos de reconciliaciónWebhooks salientesAutenticación

Recursos

PostmanComunidadModo sandbox

Webhooks salientes

Recibí notificaciones firmadas cuando ocurren eventos de negocio en tu cuenta.

Vexorpay te notifica por HTTP cuando pasan eventos de negocio en tu cuenta. Configurás los webhooks desde Configuración → Webhooks en el dashboard: creás un endpoint con la URL de tu servidor, elegís a qué eventos suscribirte y Vexorpay genera un secreto de firma (mostrado una sola vez).

Eventos

EventoCuándo ocurre
payment.completedUn cobro se confirmó y fue acreditado a tu cuenta.
payment.failedUn cobro pendiente falló.
payment.refundedUn cobro fue reembolsado.
payment.disputedUn cobro fue disputado (chargeback).
dispute.resolvedUna disputa se resolvió (ganada o perdida).
payout.paidUn retiro manual se acreditó a tu cuenta Express.
subscription.createdSe creó una suscripción nueva sobre un link recurrente.
subscription.payment.completedSe cobró un ciclo de la suscripción y se transfirió el neto.
subscription.payment.failedFalló el cobro de un ciclo de la suscripción.
subscription.canceledSe canceló una suscripción.
subscription.trial_will_endEstá por terminar el período de prueba de una suscripción.

Podés suscribir cada endpoint a todos o a un subconjunto de estos eventos.

Entrega

  • Cada evento se envía por POST al endpoint, con Content-Type: application/json.
  • Sin confirmación 2xx, Vexorpay reintenta con backoff (~5 min, ~30 min, ~3 h) hasta 3 intentos y lo marca como fallido. En Configuración → Webhooks ves el log de entregas (intentos, estado, último error).
  • Un mismo evento enviado a varios endpoints y en varios intentos comparte el mismo evt_ (id idempotente para el receptor).

Cuerpo del evento

{
  "id": "evt_6c2314a4fb4a408c07c2",
  "type": "payment.completed",
  "created": "2026-01-02T12:05:00.000Z",
  "sandbox": false,
  "data": {
    "charge": {
      "id": "67f1c1e2c8a4b0d1f2e3a4b5",
      "reference": "ORD-1001",
      "amountCents": 1900,
      "currency": "usd",
      "status": "paid",
      "sandbox": false,
      "paidAt": "2026-01-02T12:05:00.000Z"
    },
    "payment_link": {
      "id": "66f1c1e2c8a4b0d1f2e3a4b5",
      "slug": "a1b2c3d4e5",
      "title": "Ebook UX en 30 días",
      "reference": "CATALOGO-UX"
    }
  }
}

sandbox viene a nivel raíz y, para payment.* y payout.paid, también dentro del objeto. Los eventos de cobro agregan bloques extra según el caso:

  • payment.refunded → data.refund (id, amountCents, recoveredCents, debtAfterCents).
  • payment.disputed → data.dispute (status, reason, evidenceDeadline, amountCents).
  • dispute.resolved → data.dispute (status: won/lost, amountCents, recoveredCents).

Eventos de suscripción

Los eventos subscription.* traen el bloque subscription y payment_link:

{
  "id": "evt_9a1b2c3d4e5f6a7b8c9d",
  "type": "subscription.payment.completed",
  "created": "2026-02-02T12:05:00.000Z",
  "sandbox": false,
  "data": {
    "subscription": {
      "id": "68f1c1e2c8a4b0d1f2e3a4b5",
      "status": "active",
      "amountCents": 900,
      "currency": "usd",
      "interval": "month",
      "intervalCount": 1,
      "cancelAtPeriodEnd": false,
      "customerEmail": "cliente@mail.com",
      "sandbox": false,
      "createdAt": "2026-01-02T12:00:00.000Z"
    },
    "payment_link": {
      "id": "66f1c1e2c8a4b0d1f2e3a4b7",
      "slug": "b2c3d4e5f6",
      "title": "Membresía mensual",
      "reference": null
    },
    "charge": {
      "id": "67f1c1e2c8a4b0d1f2e3a4c9",
      "amountCents": 900,
      "currency": "usd",
      "status": "paid",
      "paidAt": "2026-02-02T12:05:00.000Z",
      "sandbox": false
    }
  }
}

subscription.payment.failed agrega data.payment_failure (amountCents, nextPaymentAttempt). Para payout.paid el data contiene payout en vez de charge/payment_link.

Verificación de firma

Cada request incluye:

HeaderValor
Vexorpay-SignatureHMAC-SHA256 del cuerpo crudo (exactamente como llega) con tu secreto, en hex.
Vexorpay-Event-IdId del evento (evt_...), igual al id del body.

Ejemplo

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

async function verify(req, secret) {
  const raw = await text(req)              // cuerpo crudo, SIN parsear
  const sig = req.headers['vexorpay-signature']
  const expected = createHmac('sha256', secret).update(raw).digest('hex')
  const a = Buffer.from(sig ?? '', 'hex')
  const b = Buffer.from(expected, 'hex')
  const ok = a.length === b.length && timingSafeEqual(a, b)
  if (ok) req.event = JSON.parse(raw)      // recién acá parseás
}

// Uso:
// 1) verificás la firma
// 2) deduplicás por body.id (guardás el evt_ procesado)
// 3) recién entonces procesás y respondés 2xx
  • Nunca confíes en un POST sin firma válida.
  • Compará en tiempo constante (timingSafeEqual).
  • Parseá el body solo después de verificar; el hash usa el body crudo.

Verificación a demanda (reconciliación)

Si perdiste un webhook, podés consultar el mismo evento en el histórico:

GET /v1/events

Cada evento del histórico tiene el mismo evt_ que se envía por webhook (mismos id, type y data) → podés deduplicar y re-sincronizar lo que faltó.

Rotación y seguridad

  • El secreto solo se muestra una vez al crearlo; si lo perdés, rotalo desde el dashboard (el endpoint deja de funcionar hasta que actualices tu verificación).
  • Podés pausar un endpoint sin borrarlo (no recibe eventos mientras está pausado).
  • Guardá el secreto como variable de entorno, nunca en el frontend ni en repositorios.

Eventos de reconciliación

Consulta el histórico de eventos para reconciliar tus webhooks con la API.

Autenticación

Cómo autenticarte en la API de Vexorpay con tu clave.

On this page

EventosEntregaCuerpo del eventoEventos de suscripciónVerificación de firmaEjemploVerificación a demanda (reconciliación)Rotación y seguridad