Webhooks

Recibe notificaciones en tiempo real sobre actividad en tu cuenta.

Venti te permite recibir notificaciones de eventos ocurridos en tu cuenta de comercio utilizando webhooks.

Los webhooks son particularmente útiles cuando necesitas tomar acciones asincrónicas en tu sistema en base a un evento, por ejemplo, cuando se renueva exitosamente una suscripción.

Eventos disponibles

Checkout

EventoTipo
Cuando una orden se creacheckout.created
Cuando una orden se autorizacheckout.paid
Cuando una orden se cancelacheckout.canceled
Cuando una orden se reembolsacheckout.refunded

Subscriptions

EventoTipo
Cuando una suscripción se creasubscription.created
Cuando una suscripción comienza el periodo de trialsubscription.trialing
Cuando una suscripción se activa o renuevasubscription.activated
Cuando una suscripción se atrasasubscription.past_due
Cuando una suscripción se marca como impaga (estado terminal)subscription.unpaid
Cuando una suscripción se cancela (estado terminal)subscription.canceled

Invoices

EventoTipo
Cuando una nota de venta se creainvoice.created
Cuando una nota de venta pasa a estado draftinvoice.draft
Cuando una nota de venta pasa a estado openinvoice.open
Cuando una nota de venta se autorizainvoice.paid
Cuando una nota de venta se marca como incobrableinvoice.uncollectible
Cuando una nota de venta se encuentra en modo de reintento automático de cobroinvoice.retrying
Cuando una nota de venta se marca como anuladainvoice.void

Setup Intents

EventoTipo
Cuando una intención de inscripción se creasetup_intent.created
Cuando una intención de inscripción se completa exitosamentesetup_intent.succeeded
Cuando una intención de inscripción se cancelasetup_intent.canceled
Cuando una intención de inscripción expirasetup_intent.expired

Charges

EventoTipo
Cuando un cargo se creacharge.created
Cuando un cargo se autorizacharge.succeeded
Cuando un cargo fallacharge.failed

Cómo crear un webhook

El primer paso para agregar un webhook a tu integración con Venti, es crear un endpoint en tu sistema que reciba y procese la notificación.

Este endpoint es como cualquier ruta o página existente en tu sistema. Si utilizas, por ejemplo, Express debes crear una nueva ruta, si utilizas PHP debes crear un nuevo archivo .php, etc.

Por cada evento ocurrido, Venti realizará a tu nuevo endpoint una solicitud tipo POST en la que se incluirá data asociada al evento.

Qué data recibes

Al ser notificado del evento, recibirás un payload de tipo JSON (application/json) con los siguientes atributos:

  • id: Representa un ID único del evento (string).
  • type: Representa el tipo de evento recibido (string).
  • live: Indica si el evento ocurrió en modo live o test (boolean).
  • data: Objeto relacionado al evento, por ejemplo una suscripción o un pago (object).

Payload de ejemplo

{
  "id": "evt_aKf81A82qOa0wJaHquPqo",
  "type": "checkout.created",
  "live": true,
  "data": {
    "url": "https://ventipay.com/checkout/chk_Lak10FuaS3aYk",
    "object": "checkout",
    "id": "chk_Lak10FuaS3aYk",
    "amount": 10000,
    "authorize": true,
    "cancel_url": null,
    "cancel_url_method": "get",
    "canceled_at": null,
    "currency": "clp",
    "customer_id": "cus_Ka0197aJa1Oq",
    "description": null,
    "discount": 0,
    "discount_amounts": [],
    "external_id": null,
    "installments_plan": null,
    "items": [
      {
        "sku": "",
        "name": "T-Short",
        "total": 10000,
        "quantity": 1,
        "unit_price": 10000
      }
    ],
    "live": true,
    "merchant_id": "mer_qOaMKf82JaHafg1",
    "original_amount": 10000,
    "paid_at": null,
    "payment_button_id": null,
    "payment_method_id": null,
    "refunded": false,
    "refunded_amount": 0,
    "refunded_at": null,
    "session": null,
    "settlement_amount": 0,
    "settlement_currency": null,
    "source": null,
    "status": "unpaid",
    "status_reason": null,
    "subtotal": 10000,
    "success_url": null,
    "success_url_method": "post",
    "successful_object": null,
    "successful_object_id": null,
    "tax": 0,
    "tax_amounts": [],
    "created_at": "2025-01-01T00:00:00.000Z",
    "updated_at": "2025-01-01T00:00:00.000Z",
    "customer": null,
    "merchant": {
      "logo_url": null,
      "url": "https://ventipay.com/merchant/mer_KaO19t7aJa1Oq",
      "object": "merchant",
      "id": "mer_KaO19t7aJa1Oq",
      "billing_email": "[email protected]",
      "country": "cl",
      "default_settlement_currency": "clp",
      "disabled": false,
      "entity_type": "business",
      "legal_name": "WALLACE CORP",
      "mcc": null,
      "name": "Wallace Corp",
      "payment_category": "services",
      "quick_code": "MKD637",
      "subscription_allow_pause": true,
      "subscription_allow_unsubscription": true,
      "subscription_retry_days": 4,
      "subscription_retry_failed_action": "mark_unpaid",
      "support_email": "[email protected]",
      "taxid": "TIN00100001010",
      "timezone": "America/Santiago",
      "ui_primary_color": "#f6f6f6",
      "website": "https://www.wallacecorp.com/",
      "created_at": "2020-01-201T00:00:00.000Z",
      "updated_at": "2020-01-201T00:00:00.000Z",
    },
    "loan": null,
    "payment": null,
    "payment_method": null
  }
}

Cómo responder a un webhook

Es importante que respondas de manera exitosa lo más rápido posible a la solicitud enviada por Venti.

Se considera una respuesta exitosa cualquiera con un código de estado HTTP del rango 2xx, sin importar el contenido enviado. Todo otro estado, incluidas las redirecciones, serán consideradas como erroneas.

Si tu endpoint no responde exitosamente seguiremos intentando enviar la notificación de manera automática, sin embargo si no recibimos una respuesta exitosa al 3er día, la notificación se considerará fallida y no intentaremos nuevamente. Usualmente el espacio entre cada intento es de 1 hora.

Cómo validar un webhook

Opcionalmente podrás validar que el mensaje recibido haya sido enviado por Venti y no por terceros. Para esto, cada notificación incluye una firma que permite verificar la validez del mensaje.

La firma del mensaje la podrás encontrar en la cabecera venti-signature dentro de la solicitud recibida en tu endpoint.

Antes de verificar la validez, primero deberás rescatar el secreto de firma del webhook desde el Dashboard.

Este secreto de firma se utiliza para recrear la firma y compararla con la firma recibida en tu endpoint. Debes considerar de que cada webhook tiene su propio secreto de firma.

La cabecera de firma es un string que contiene un timestamp y una firma, ambos separados por una coma (,). El timestamp (UNIX epoch) se indica con el prefijo t= y la firma con el prefijo v1=. Este prefijo indica la versión del schema utilizado. Actualmente solo existe el schema v1, pero podrían existir otros en el futuro.

t=1608681600,
v1=ba6773cda175388cc23156970add41ac4bb1254629dfd465b540e839c8690696

📘

Para facilitar la lectura en esta documentación, cada valor se ha separado con una línea nueva, sin embargo la firma real viene en una única línea.

VentiPay genera la firma utilizando un código de autentificación de mensajes en clave-hash (HMAC) con SHA-256.

Pasos para verificar la validez del mensaje

Paso 1: Primero debes separar la cabecera en ítems por coma (,) y luego separar cada ítem por el signo igual (=). Esto te entregará el timestamp en la llave t y la firma en la llave v1.

Paso 2: Luego debes construir un string de verificación concatenando los siguientes valores: el timestamp (como string), el carácter ., y finalmente la representación como string del payload JSON recibido. El string resultante tendrá la forma <timestamp>.<payload>.

Paso 3: Luego debes calcular el HMAC del string de verificación utilizando SHA-256 y el secreto de firma del webhook como secreto de tu función de hashing.

Paso 4: Finalmente debes comparar la igualdad de la firma recibida en la solicitud y la firma calculada. Si ambas son iguales, puedes considerar que el mensaje es válido.

Evitar ataques de replay

Un ataque de replay ocurre cuando un atacante intercepta el payload y firma de una solicitud para luego repetirla. Para mitigar este tipo de ataques es que se incluye el timestamp en la cabera de firma. Ya que el timestamp también es parte del mensaje firmado, cualquier cambio a este valor invalida el mensaje completo.

Cada vez que se realiza la llamada a tu endpoint se genera una nueva firma con el timestamp del momento en el que se creó la llamada, por lo tanto si un mensaje es válido pero el timestamp muy antiguo, tu sistema debiera ignorarlo. Te recomendamos considerar una tolerancia de 5 minutos.