Saltar al contenido principal
Esta página es conceptual: explica cómo el motor de recurrencia decide cuándo nace cada factura y cuándo se cobra. Los campos mencionados aquí son los mismos que envías al crear una suscripción — consulta Suscripciones para el request completo y Facturas para el ciclo de vida de cada factura.
Todas las fechas internas del motor se normalizan en UTC. El cálculo de ciclo es determinístico: depende únicamente de la fecha de inicio y de la regla de recurrencia (recurrence), sin reloj externo. En transport (body/query), las fechas siempre viajan en ISO 8601 con offset — consulta Convenciones.

La regla de recurrencia

Cada suscripción tiene una recurrence (heredada del precio del plan o informada inline). Ella es la que define el tamaño del ciclo y el punto de alineación de las facturas.
interval
integer
requerido
Multiplicador de la unidad. Entero positivo. Ej.: interval=3 + unit=month → cada 3 meses.
unit
string
requerido
Unidad del ciclo. Uno de: day, week, month, year.
anchor
string
requerido
Cómo se alinea el ciclo a un punto de referencia. Uno de: subscription_start, day_of_month, end_of_month. Detallado a continuación.
anchorDay
integer
Obligatorio cuando anchor='day_of_month'. Día del mes, rango de 1 a 31. Ignorado en las demás anclas.
collectionTiming
string
predeterminado:"prepaid"
Cuándo cobrar dentro del ciclo: prepaid (inicio) o postpaid (fin). Detallado en Prepaid vs Postpaid.
Las anclas day_of_month y end_of_month solo tienen sentido con unit='month' o unit='year'. Combinarlas con day/week es rechazado en la validación (“cada 1 día, el día 15” no tiene semántica).

Anclas

El ancla decide en qué fecha cae el fin del ciclo actual — que es también el inicio del siguiente ciclo y, por defecto, la fecha de la próxima factura.
El siguiente ciclo es simplemente inicio + intervalo. Preserva la hora (hora/min/seg) de la fecha de inicio.Ejemplo (unit=month, interval=1, inicio el 15/ene/2026 10:00):
CicloInicioFin (= próxima factura)
115/ene 10:0015/feb 10:00
215/feb 10:0015/mar 10:00
315/mar 10:0015/abr 10:00
Para los meses cortos aplica el clamp de fin de mes (ver abajo): inicio el 31/ene → el siguiente fin cae el 28/feb (o 29/feb en año bisiesto). A diferencia de day_of_month, el subscription_start no re-ancla en el día original: cada paso avanza desde el fin del ciclo anterior (ya clampado), así que después del 28/feb el ciclo siguiente es el 28/mar, luego el 28/abr — el día “se encoge” al mes más corto que atraviesa y no vuelve a 31.
Cobra siempre en el día anchorDay. La primera factura recurrente cae al menos un intervalo adelante — nunca en el mes corriente de la suscripción. Esto evita ciclos cortos al arranque.Ejemplo (unit=month, interval=1, anchorDay=10):
Inicio de la suscripciónFin del 1er ciclo (1ª factura recurrente)
05/abr10/may
19/abr10/may
10/abr (ya en el día del ancla)10/may
Es decir: si compras el día 5 con ancla en el día 10, no se cobra el día 10 del mismo mes — se posterga al mes siguiente.
Si anchorDay es mayor que el número de días del mes objetivo, el motor ajusta al último día del mes (compatible con Stripe). anchorDay=31 en febrero se convierte en 28/feb (o 29/feb en año bisiesto). Y como la alineación re-ancla en el día original en cada paso, no hay drift: después del 28/feb (31 clampado), el ciclo siguiente vuelve al 31/mar.
Cobra siempre en el último día de cada mes del ciclo, sin importar su duración.Ejemplo (unit=month, interval=1, inicio el 10/ene):
CicloFin (= próxima factura)
128/feb (o 29/feb bisiesto)
231/mar
330/abr
Al igual que day_of_month, la primera factura recurrente cae un intervalo adelante, nunca en el mes de inicio.
La hora (hora/minuto/segundo) de la fecha de inicio se preserva al avanzar los ciclos. El clamp solo afecta el día, nunca el reloj.

Prepaid vs Postpaid

collectionTiming define en qué momento, dentro del ciclo, ocurre el cobro.

prepaid (predeterminado)

Cobra al inicio de cada período. Es el modelo clásico de SaaS/streaming: pagas por el período que está comenzando.

postpaid

Cobra al final de cada período. Modelo de consumo/utility: pagas por el período que ya transcurrió.
En el request de creación de suscripción hay dos lugares que gestionan el timing:
  • collectionTiming a nivel de la suscripción — prepaid (default) o postpaid.
  • collectionTiming dentro de la propia recurrence — cuando está ausente, asume prepaid (retrocompatibilidad con precios antiguos que no tenían el campo).
La ausencia del campo siempre significa prepaid. Si no defines nada, tus suscripciones cobran al inicio del ciclo.

Generación de facturas: just_in_time vs upfront

invoiceGenerationMode controla cuántas facturas se crean y cuándo.
ModoCuándo nacen las facturasPara qué sirve
just_in_time (predeterminado)1 factura por ciclo, generada por el programador en cada cambioSuscripciones “infinitas” (SaaS/streaming) y contratos sin necesidad de ver ciclos futuros
upfrontTodas las maxCycles facturas en el momento de la creación, cada una con dueAt distintoContratos finitos de plazo fijo (EAD, fraccionamiento) donde el comerciante quiere previsibilidad de flujo de caja
invoiceGenerationMode='upfront' exige maxCycles informado (no puede ser nulo). Sin él, la creación de la suscripción es rechazada en la validación.
En just_in_time, el número total de ciclos está limitado por maxCycles (cuando se informa); sin maxCycles, la suscripción se considera perpetua y el programador continúa generando una factura por ciclo indefinidamente.

Trial (período de prueba)

trialSpec define un período inicial sin cobro.
trialSpec.durationDays
integer
Duración del trial en días. Entero >= 0.
trialSpec.requiresPaymentMethod
boolean
Si el trial requiere un medio de pago adjunto para comenzar.
Durante el trial la suscripción permanece en estado trialing y no se cobra ninguna factura. Al final del trial, el motor genera la primera factura y la suscripción entra en cobro normal.
trialSpec y bootstrapPayment son mutuamente exclusivos. El trial implica que el ciclo 1 aún no fue cobrado (es el fin del trial lo que genera la 1ª factura); bootstrapPayment significa que el ciclo 1 ya fue pagado externamente. Enviar ambos juntos es rechazado.
El trialSpec a nivel de la suscripción tiene precedencia sobre el trial derivado de ítems basados en plan (que proviene del precio). Utiliza el override a nivel de la suscripción cuando el trial se decide fuera del plan — por ejemplo, checkout inline con ítems sin precio asociado.

chargeLeadTimeDays — anticipación del cobro

chargeLeadTimeDays es la anticipación, en días, entre el momento en que la factura es disparada para cobro (chargeAt — cuando la factura se abre y el boleto/PIX se crea en el PSP) y su vencimiento (dueAt):
chargeAt = dueAt − chargeLeadTimeDays
chargeLeadTimeDays
integer
Rango aceptado: 0 a 30 días. Cuando se omite, se deriva automáticamente del tipo del medio de pago predeterminado (defaultPaymentMethodRef.type).
Los valores predeterminados derivados por tipo de medio de pago:
Medio de pagoLead time predeterminadoPor qué
card0 díasCobro instantáneo
pix1 díaTiempo para enviar el QR Code al cliente
boleto2 díasTiempo de la red bancaria para registrar el boleto
other0 díasSin anticipación
La regla de resolución es: el valor explícito siempre prevalece; de lo contrario, se deriva del tipo del medio de pago; si no hay ninguno de los dos, el predeterminado es 0. Se persiste en la suscripción como chargeLeadTimeDays.
Ejemplo: una factura de boleto que vence el 20/jun (dueAt), con lead time 2, es disparada para cobro el 18/jun (chargeAt) — dando 2 días para que el boleto circule antes del vencimiento.

Dunning — qué ocurre cuando el cobro falla

Cuando un cobro recurrente falla, el motor entra en dunning (régimen de incumplimiento): decide si reprograma un nuevo intento o si agota los intentos y aplica la política final. El comportamiento es configurable por comerciante mediante BillingSettings.

Configuración (BillingSettings)

CampoDefaultRango / valoresSignificado
retryIntervalsDays[3, 5, 7]array de enteros >= 0 (hasta 10)Espera, en días, antes de cada reintento. El índice extrapolado reutiliza el último intervalo
maxRetries30 a 10Número máximo de reintentos antes de agotar
dunningFinalPolicymark_unpaidmark_unpaid o cancelQué hacer al agotar el dunning
hardDeclineCategories['hard_decline', 'authentication_required']subconjunto de soft_decline, hard_decline, insufficient_funds, authentication_required, otherCategorías de fallo que saltan el retry y van directamente a la política final

Cómo funciona la régimen

El conteo sigue el modelo de Stripe: el intento nº 1 es el cobro inicial; los reintentos comienzan en 2. Por lo tanto, retryCountSoFar = attemptNumber − 1.
1

El cobro falla

La factura sale de open y va a past_due. El intento se registra como failed con la categoría de fallo.
2

¿Aún hay reintentos?

Si retryCountSoFar < maxRetries y el fallo no es hard decline, el motor programa el próximo intento: nextRetryAt = momento del fallo + retryIntervalsDays[índice]. La suscripción permanece activa y la factura continúa en past_due.
3

Dunning agotado

Se agota cuando retryCountSoFar >= maxRetries o el fallo cae en una categoría de hardDeclineCategories (que salta el retry de inmediato). Entonces nextRetryAt queda nulo y la política final se aplica.
4

Política final

Con dunningFinalPolicy = mark_unpaid (default), la suscripción se marca como unpaid. Con cancel, la suscripción se cancela automáticamente.
La transición predeterminada de la suscripción cuando el cobro falla es active/cobrando → past_due (en reintento) → unpaid (dunning agotado con política mark_unpaid). Con la política cancel, el desenlace final es canceled en lugar de unpaid.
Ejemplo concreto (defaults: retryIntervalsDays=[3,5,7], maxRetries=3, dunningFinalPolicy=mark_unpaid), cobro inicial fallando el 01/jun:
IntentoattemptNumberretryCountSoFarFechaResultado
Inicial1001/junFallo → past_due, programa retry +3d
Retry 12104/junFallo → programa retry +5d
Retry 23209/junFallo → programa retry +7d
Retry 34316/junFallo → retryCountSoFar (3) >= maxRetries (3): agotado → suscripción pasa a unpaid
Las facturas de adhesión (kind=enrollment) no entran en dunning: el fallo es terminal, sin reintento. La factura pasa de open a past_due (para no ser recobrada hora a hora) y el flujo de adhesión decide la cancelación/cierre de la suscripción.
Para los estados detallados de la suscripción (incomplete, trialing, active, past_due, unpaid, paused, canceled, completed) consulta Suscripciones. Para los códigos de error de llamadas a la API consulta Errores.

Ver también

Suscripciones

Crear y gestionar suscripciones — request completo con recurrence, trialSpec, collectionTiming y demás campos.

Planes y precios

De dónde se hereda la recurrence cuando la suscripción está basada en un plan.

Facturas

Ciclo de vida de cada factura — open, past_due, paid y los campos chargeAt/dueAt.

Visión general de suscripciones

Conceptos del módulo de recurrencia de Z2Pay.