Todas as datas internas do motor são normalizadas em UTC. O cálculo de ciclo é
determinístico: depende apenas da data de início e da regra de recorrência (
recurrence),
sem relógio externo. Em transport (body/query), datas sempre viajam em ISO 8601 com
offset — veja Convenções.A regra de recorrência
Cada assinatura tem umarecurrence (herdada do preço do plano ou informada inline). Ela é o
que define o tamanho do ciclo e o ponto de alinhamento das faturas.
Multiplicador da unidade. Inteiro positivo. Ex.:
interval=3 + unit=month → a cada
3 meses.Unidade do ciclo. Um de:
day, week, month, year.Como o ciclo se alinha a um ponto de referência. Um de:
subscription_start,
day_of_month, end_of_month. Detalhado abaixo.Obrigatório quando
anchor='day_of_month'. Dia do mês, faixa de 1 a 31. Ignorado
nas outras âncoras.Quando cobrar dentro do ciclo:
prepaid (início) ou postpaid (fim). Detalhado em
Prepaid vs Postpaid.Âncoras
A âncora decide em que data cai o fim do ciclo atual — que é também o início do próximo ciclo e, por padrão, a data da próxima fatura.subscription_start — alinha pela data de início
subscription_start — alinha pela data de início
O próximo ciclo é simplesmente
Para meses curtos vale o clamp de fim de mês (veja abaixo): início em 31/jan
→ próximo fim em 28/fev (ou 29/fev em ano bissexto). Diferente do
início + intervalo. Preserva o horário (hora/min/seg) da
data de início.Exemplo (unit=month, interval=1, início em 15/jan/2026 10:00):| Ciclo | Início | Fim (= próxima fatura) |
|---|---|---|
| 1 | 15/jan 10:00 | 15/fev 10:00 |
| 2 | 15/fev 10:00 | 15/mar 10:00 |
| 3 | 15/mar 10:00 | 15/abr 10:00 |
day_of_month, o
subscription_start não re-ancora no dia original: cada passo avança a partir do fim do
ciclo anterior (já clampado), então depois de 28/fev o ciclo seguinte é 28/mar, depois 28/abr
— o dia “encolhe” para o menor mês atravessado e não volta a 31.day_of_month — todo dia X do mês (com clamp)
day_of_month — todo dia X do mês (com clamp)
Cobra sempre no dia
Ou seja: compra dia 5 com âncora no dia 10 não cobra no dia 10 do mesmo mês — vai
para o mês seguinte.
anchorDay. A primeira fatura recorrente cai ao menos um
intervalo à frente — nunca no mês corrente da assinatura. Isso evita ciclos curtos no
arranque.Exemplo (unit=month, interval=1, anchorDay=10):| Início da assinatura | Fim do 1º ciclo (1ª fatura recorrente) |
|---|---|
| 05/abr | 10/mai |
| 19/abr | 10/mai |
| 10/abr (já no dia da âncora) | 10/mai |
Se
anchorDay for maior que o número de dias do mês alvo, o motor ajusta para o
último dia do mês (compatível com Stripe). anchorDay=31 em fevereiro vira 28/fev
(ou 29/fev em ano bissexto). E como o alinhamento re-ancora no dia original a cada
passo, não há drift: depois de 28/fev (31 clampado), o ciclo seguinte volta para
31/mar.end_of_month — sempre o último dia do mês
end_of_month — sempre o último dia do mês
Cobra sempre no último dia de cada mês do ciclo, qualquer que seja seu tamanho.Exemplo (
Assim como
unit=month, interval=1, início em 10/jan):| Ciclo | Fim (= próxima fatura) |
|---|---|
| 1 | 28/fev (ou 29/fev bissexto) |
| 2 | 31/mar |
| 3 | 30/abr |
day_of_month, a primeira fatura recorrente cai um intervalo à frente, nunca
no mês de início.Prepaid vs Postpaid
collectionTiming define onde, dentro do ciclo, a cobrança acontece.
prepaid (padrão)
Cobra no início de cada período. É o modelo clássico de SaaS/streaming: você paga
pelo período que está começando.
postpaid
Cobra no fim de cada período. Modelo de consumo/utility: você paga pelo período que
já passou.
collectionTimingno nível da assinatura —prepaid(default) oupostpaid.collectionTimingdentro da própriarecurrence— quando ausente, assumeprepaid(retrocompatibilidade com preços antigos que não tinham o campo).
Ausência do campo sempre significa
prepaid. Se você não definir nada, suas assinaturas
cobram no início do ciclo.Geração das faturas: just_in_time vs upfront
invoiceGenerationMode controla quantas faturas são criadas e quando.
| Modo | Quando as faturas nascem | Para que serve |
|---|---|---|
just_in_time (padrão) | 1 fatura por ciclo, gerada pelo agendador a cada virada | Assinaturas “infinitas” (SaaS/streaming) e contratos sem necessidade de ver ciclos futuros |
upfront | Todas as maxCycles faturas no momento da criação, cada uma com dueAt distinto | Contratos finitos de prazo fixo (EAD, parcelamento) onde o lojista quer previsibilidade de fluxo de caixa |
just_in_time, o número total de ciclos é limitado por maxCycles (quando informado);
sem maxCycles, a assinatura é considerada perpétua e o agendador continua gerando uma
fatura por ciclo indefinidamente.
Trial (período de teste)
trialSpec define um período inicial sem cobrança.
Duração do trial em dias. Inteiro
>= 0.Se o trial exige meio de pagamento anexado para começar.
trialing e nenhuma fatura é cobrada. Ao
final do trial, o motor gera a primeira fatura e a assinatura entra em cobrança normal.
O trialSpec no nível da assinatura tem precedência sobre o trial derivado de itens
baseados em plano (que vem do preço). Use o override no nível da assinatura quando o trial é
decidido fora do plano — por exemplo, checkout inline com itens sem preço associado.
chargeLeadTimeDays — antecedência da cobrança
chargeLeadTimeDays é a antecedência, em dias, entre o momento em que a fatura é disparada
para cobrança (chargeAt — quando a fatura abre e o boleto/PIX é criado no PSP) e o seu
vencimento (dueAt):
Faixa aceita: 0 a 30 dias. Quando omitido, deriva automaticamente do tipo do meio
de pagamento padrão (
defaultPaymentMethodRef.type).| Meio de pagamento | Lead time padrão | Por quê |
|---|---|---|
card | 0 dias | Cobrança instantânea |
pix | 1 dia | Tempo de enviar o QR Code ao cliente |
boleto | 2 dias | Tempo da rede bancária registrar o boleto |
other | 0 dias | Sem antecedência |
A regra de resolução é: valor explícito vence sempre; senão, deriva do tipo do meio de
pagamento; se não houver nenhum dos dois, o padrão é 0. Persistido na assinatura como
chargeLeadTimeDays.dueAt), com lead time 2, é
disparada para cobrança em 18/jun (chargeAt) — dando 2 dias para o boleto circular
antes do vencimento.
Dunning — o que acontece quando a cobrança falha
Quando uma cobrança recorrente falha, o motor entra em dunning (régua de inadimplência): ele decide se reagenda uma nova tentativa ou se esgota as tentativas e aplica a política final. O comportamento é configurável por lojista via BillingSettings.Configuração (BillingSettings)
| Campo | Default | Faixa / valores | Significado |
|---|---|---|---|
retryIntervalsDays | [3, 5, 7] | array de inteiros >= 0 (até 10) | Espera, em dias, antes de cada retentativa. O índice extrapolado reusa o último intervalo |
maxRetries | 3 | 0 a 10 | Número máximo de retentativas antes de esgotar |
dunningFinalPolicy | mark_unpaid | mark_unpaid ou cancel | O que fazer ao esgotar o dunning |
hardDeclineCategories | ['hard_decline', 'authentication_required'] | subconjunto de soft_decline, hard_decline, insufficient_funds, authentication_required, other | Categorias de falha que pulam o retry e vão direto para a política final |
Como a régua funciona
A contagem segue o modelo do Stripe: a tentativa nº 1 é a cobrança inicial; as retentativas começam em 2. Logo,retryCountSoFar = attemptNumber − 1.
Cobrança falha
A fatura sai de
open e vai para past_due. A tentativa é registrada como failed com
a categoria de falha.Ainda há retentativas?
Se
retryCountSoFar < maxRetries e a falha não é hard decline, o motor agenda a
próxima tentativa: nextRetryAt = momento da falha + retryIntervalsDays[índice]. A
assinatura permanece ativa e a fatura segue em past_due.Dunning esgotado
Esgota quando
retryCountSoFar >= maxRetries ou a falha cai em uma categoria de
hardDeclineCategories (que pula o retry de imediato). Aí nextRetryAt fica nulo e a
política final é aplicada.A transição padrão da assinatura quando a cobrança falha é
active/cobrando →
past_due (em retentativa) → unpaid (dunning esgotado com política mark_unpaid).
Com a política cancel, o desfecho final é canceled em vez de unpaid.retryIntervalsDays=[3,5,7], maxRetries=3,
dunningFinalPolicy=mark_unpaid), cobrança inicial falhando em 01/jun:
| Tentativa | attemptNumber | retryCountSoFar | Data | Resultado |
|---|---|---|---|---|
| Inicial | 1 | 0 | 01/jun | Falha → past_due, agenda retry +3d |
| Retry 1 | 2 | 1 | 04/jun | Falha → agenda retry +5d |
| Retry 2 | 3 | 2 | 09/jun | Falha → agenda retry +7d |
| Retry 3 | 4 | 3 | 16/jun | Falha → retryCountSoFar (3) >= maxRetries (3): esgotado → assinatura vira unpaid |
incomplete, trialing, active, past_due,
unpaid, paused, canceled, completed) veja
Assinaturas. Para os códigos de erro de chamadas à API
veja Erros.
Veja também
Assinaturas
Criar e gerenciar assinaturas — request completo com
recurrence, trialSpec,
collectionTiming e demais campos.Planos e preços
De onde a
recurrence é herdada quando a assinatura é baseada em plano.Faturas
Ciclo de vida de cada fatura —
open, past_due, paid e os campos chargeAt/dueAt.Visão geral de assinaturas
Conceitos do módulo de recorrência da Z2Pay.

