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:

EventoCuándo
subscription.createdSe creó la suscripción (estado incomplete, o trialing si viene de un plan con prueba).
subscription.trialingEntró en período de prueba.
subscription.activatedPasó a active: primera autorización exitosa, reintento exitoso o reactivación tras pausa.
subscription.past_dueUn cobro falló; empiezan los reintentos.
subscription.suspendedFue pausada.
subscription.unpaidLos reintentos se agotaron (acción mark_unpaid). Terminal.
subscription.canceledFue 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 un notification_url (y notification_events) propio: útil para enrutar los eventos de una integración específica.

Eventos de nota de venta

EventoCuándo
invoice.createdSe creó una nota de venta — una por ciclo en suscripciones, o al crearla tú.
invoice.draftVolvió a borrador (solo notas de venta manuales).
invoice.openQuedó cobrable: finalización manual, o un intento de cobro fallido que la mantiene abierta.
invoice.paidSe pagó (o la marcaste paid_out_of_band).
invoice.uncollectibleQuedó incobrable: reintentos agotados o marcada por ti.
invoice.voidFue anulada.
invoice.retryingReservado; 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_id sin subscription_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 negocioObjetosEfecto en tu balance
Ciclo cobrado con éxitoinvoice.paid + payment.* (subscription_id presente)Abono del monto menos comisiones estándar
Nota de venta suelta pagada por link o cargoinvoice.paid + payment.*Abono del monto menos comisiones estándar
Nota de venta marcada paid_out_of_bandinvoice.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: 0Sin impacto
Cobro fallidoinvoice.open + subscription.past_dueSin impacto
Reintentos agotadosinvoice.uncollectible + subscription.unpaid o subscription.canceledSin impacto
Reembolso de un cobropayment.refundedCargo por el monto reembolsado

En la práctica:

  • Concilia por nota de venta: es el documento del período. Su successful_payment_id te lleva al pago y de ahí a las transacciones de balance (abono payment, cargos fee).
  • Tu ingreso recurrente facturado = notas de venta paid con subscription_id en el período (excluye las amount: 0 y las paid_out_of_band si solo miras flujo por Venti).
  • Tu churn involuntario = eventos subscription.unpaid + subscription.canceled con status_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 id del 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_due genera dos eventos subscription.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.