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
Evento | Tipo |
---|---|
Cuando una orden se crea | checkout.created |
Cuando una orden se autoriza | checkout.paid |
Cuando una orden se cancela | checkout.canceled |
Cuando una orden se reembolsa | checkout.refunded |
Subscriptions
Evento | Tipo |
---|---|
Cuando una suscripción se crea | subscription.created |
Cuando una suscripción comienza el periodo de trial | subscription.trialing |
Cuando una suscripción se activa o renueva | subscription.activated |
Cuando una suscripción se atrasa | subscription.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
Evento | Tipo |
---|---|
Cuando una nota de venta se crea | invoice.created |
Cuando una nota de venta pasa a estado draft | invoice.draft |
Cuando una nota de venta pasa a estado open | invoice.open |
Cuando una nota de venta se autoriza | invoice.paid |
Cuando una nota de venta se marca como incobrable | invoice.uncollectible |
Cuando una nota de venta se encuentra en modo de reintento automático de cobro | invoice.retrying |
Cuando una nota de venta se marca como anulada | invoice.void |
Setup Intents
Evento | Tipo |
---|---|
Cuando una intención de inscripción se crea | setup_intent.created |
Cuando una intención de inscripción se completa exitosamente | setup_intent.succeeded |
Cuando una intención de inscripción se cancela | setup_intent.canceled |
Cuando una intención de inscripción expira | setup_intent.expired |
Charges
Evento | Tipo |
---|---|
Cuando un cargo se crea | charge.created |
Cuando un cargo se autoriza | charge.succeeded |
Cuando un cargo falla | charge.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.
Updated about 2 hours ago