2026-05-05
v0.7.0 — Cobranças recorrentes com cartão e período de teste
A próxima etapa das cobranças agendadas chegou: agora você pode cobrar mensalmente (ou em qualquer outra cadência) com cartão de crédito, período de teste, e ações de ciclo de vida que cabem em um SaaS — tudo integrado ao mesmo wizard que você já usa para cobranças avulsas.
Crie uma cobrança recorrente em uma de sete cadências (semanal, quinzenal, mensal, bimestral, trimestral, semestral ou anual). No primeiro ciclo, o cliente abre o link de pagamento e cadastra o cartão uma única vez — a Garu salva o token via Celcoin (sem guardar PAN nem CVV). Nos ciclos seguintes a cobrança roda automaticamente, sem e-mail, sem clique, e o cliente só recebe contato se o cartão falhar. Funcionou: webhook scheduled_charge.paid com o número do ciclo. Falhou: sistema de retry assume.
Se a cobrança silenciosa for negada, a Garu tenta novamente em até 4 cadências (+6h, +24h, +48h, mais o erro inicial), usando o endpoint nativo de retry do Celcoin. Se esgotar:
overdue e o time recebe a sequência de dunning (D+1 / D+2 / D+3).scheduled_charge.cycle_failed com fallbackEmailSent: true.POST /api/scheduled-charges aceita trialDays (1 a 365). A Garu reagenda o ciclo 1 para hoje + N dias e dispara customer.trial_started na hora. No vencimento, se o pagamento converter, sai customer.trial_converted. Se passar 24h sem pagar, customer.trial_lapsed — sempre idempotente (o webhook só dispara uma vez por trial).
Cinco endpoints novos para administrar a série depois de criada:
POST /:id/cancel-recurrence — interrompe ciclos futuros. O ciclo em curso ainda pode ser pago; só depois disso a série vira recurrence_canceled. Final.POST /:id/cancel-at-period-end { enabled: boolean } — soft-cancel estilo Stripe. Com true, depois do próximo ciclo pago a série encerra. Reversível.POST /:id/payment-method { paymentMethodId } — troca o cartão salvo. Validação: o cartão precisa pertencer ao mesmo cliente.DELETE /:id/payment-method — limpa o cartão salvo. Próximos ciclos voltam para o fluxo de e-mail-com-link.POST /:id/mark-paid agora aceita cycleNumber opcional: para recorrentes é obrigatório, e marca apenas aquele ciclo como pago — futuros ciclos seguem normalmente.A etapa Detalhes ganhou:
productId).POST /api/scheduled-charges agora aceita:
{
"customerId": 42,
"productId": 17,
"amount": 49.9,
"type": "recurring",
"dueDate": "2026-06-01",
"methods": ["card", "pix"],
"recurrence": { "interval": "monthly" },
"trialDays": 7
}
A resposta inclui trialEndsAt, paymentMethodId, cancelAtPeriodEnd, e o ciclo 1 já fica gravado em scheduled_charge_cycle com dueDate = today + trialDays.
Eventos da série:
scheduled_charge.recurrence_canceledscheduled_charge.cancel_at_period_end_set / .cancel_at_period_end_clearedscheduled_charge.payment_method_attached / .payment_method_changed / .payment_method_clearedscheduled_charge.cycle_failedEventos de cliente (trial):
customer.trial_started / .trial_converted / .trial_lapsedTodos os webhooks de eventos por ciclo carregam seriesId + cycleNumber no payload, para você rotear sem precisar de uma segunda chamada.
Se você já roda v0.6.0 com cobranças avulsas, nada quebra: a coluna trialEndsAt aceita null, paymentMethodId aceita null, e o cron de billing antigo continua funcionando. Recorrentes ficaram atrás de uma flag (seller_config.scheduled_charges_enabled) — se você quer testar, fala com a gente que liberamos.