Webhooks y conciliación
Los eventos de suscripciones y notas de venta, cómo reconocer sus pagos y cómo conciliar tu balance.
Webhooks y conciliación
Los webhooks de suscripciones usan la misma infraestructura de webhooks que ya conoces: mismo formato, misma firma, mismos reintentos.
Eventos de suscripción
Se emiten al crearse la suscripción y en cada cambio de estado:
| Evento | Cuándo |
|---|---|
subscription.created | Se creó la suscripción (estado incomplete, o trialing si viene de un plan con prueba). |
subscription.trialing | Entró en período de prueba. |
subscription.activated | Pasó a active: primera autorización exitosa, reintento exitoso o reactivación tras pausa. |
subscription.past_due | Un cobro falló; empiezan los reintentos. |
subscription.suspended | Fue pausada. |
subscription.unpaid | Los reintentos se agotaron (acción mark_unpaid). Terminal. |
subscription.canceled | Fue cancelada. status_reason indica el motivo (canceled_by_customer, canceled_by_merchant, canceled_due_payment, reached_cycles_duration). |
El payload incluye la suscripción con su cliente, productos e impuestos expandidos.
Además de tus webhooks de comercio, cada suscripción acepta unnotification_url(ynotification_events) propio: útil para enrutar los eventos de una integración específica.
Eventos de nota de venta
| Evento | Cuándo |
|---|---|
invoice.created | Se creó una nota de venta — una por ciclo en suscripciones, o al crearla tú. |
invoice.draft | Volvió a borrador (solo notas de venta manuales). |
invoice.open | Quedó cobrable: finalización manual, o un intento de cobro fallido que la mantiene abierta. |
invoice.paid | Se pagó (o la marcaste paid_out_of_band). |
invoice.uncollectible | Quedó incobrable: reintentos agotados o marcada por ti. |
invoice.void | Fue anulada. |
invoice.retrying | Reservado; en la práctica los reintentos mantienen la nota de venta en open. |
Reconocer los pagos de suscripción
Cada cobro exitoso también dispara los eventos payment.* estándar. Un pago de suscripción se reconoce por sus campos:
{
"id": "pay_Ts3wLq9KvM6xR8pN2cJhY4fZ",
"object": "payment",
"status": "succeeded",
"amount": 12990,
"subscription_id": "sub_Mh6tKq2WvR9pX4nL8sJcZ3fB",
"invoice_id": "inv_Qp8vNm4XwK7rT2sLcJ9hZf3G",
"automatic": true
}subscription_id+invoice_id→ cobro recurrente de esa suscripción y ese período.invoice_idsinsubscription_id→ pago de una nota de venta suelta.automatic: true→ lo originó el motor de cobro, no una acción del cliente.
Conciliación
| Evento de negocio | Objetos | Efecto en tu balance |
|---|---|---|
| Ciclo cobrado con éxito | invoice.paid + payment.* (subscription_id presente) | Abono del monto menos comisiones estándar |
| Nota de venta suelta pagada por link o cargo | invoice.paid + payment.* | Abono del monto menos comisiones estándar |
Nota de venta marcada paid_out_of_band | invoice.paid (sin successful_payment_id) | Sin impacto — el dinero se movió fuera de Venti |
Período en $0 (prueba, pausa, prorrateo none) | invoice.paid con amount: 0 | Sin impacto |
| Cobro fallido | invoice.open + subscription.past_due | Sin impacto |
| Reintentos agotados | invoice.uncollectible + subscription.unpaid o subscription.canceled | Sin impacto |
| Reembolso de un cobro | payment.refunded | Cargo por el monto reembolsado |
En la práctica:
- Concilia por nota de venta: es el documento del período. Su
successful_payment_idte lleva al pago y de ahí a las transacciones de balance (abonopayment, cargosfee). - Tu ingreso recurrente facturado = notas de venta
paidconsubscription_iden el período (excluye lasamount: 0y laspaid_out_of_bandsi solo miras flujo por Venti). - Tu churn involuntario = eventos
subscription.unpaid+subscription.canceledconstatus_reason: canceled_due_payment.
Idempotencia y reintentos
- La nota de venta de cada período es única (una por suscripción y período): reprocesos del motor de renovación nunca duplican cobros.
- Los webhooks pueden llegar más de una vez y se reintentan hasta por 3 días: deduplica por el
iddel evento, como con cualquier webhook de Venti. - Los eventos de estado se emiten en cada cambio de estado; una suscripción que pasa dos veces por
past_duegenera dos eventossubscription.past_due.
Modo test
Todo funciona en modo test con tu API key de test: productos, planes, suscripciones y notas de venta. Usa las tarjetas de prueba para simular autorizaciones exitosas y fallidas — incluida la secuencia completa past_due → reintentos → unpaid.
Updated 1 day ago

