Garu

2026-05-05

v0.9.0 — Log por tentativa para auditoria de billing recorrente

scheduled-chargesrecorrenteswebhooksapisdkmcpb2b2c

A tabela scheduled_charge_cycle_attempt foi criada na v0.8.0 mas ficou sem ninguém escrevendo nela — uma tabela vazia em produção. A v0.9.0 fecha essa ponta e expõe os dados via API, SDK e MCP. Com isso, sellers podem auditar exatamente o que aconteceu em cada cobrança recorrente que falhou.

O que mudou

Escrita per-tentativa em todos os caminhos de billing

Toda tentativa lógica de cobrança gera uma linha em scheduled_charge_cycle_attempt:

  • Ciclo 1 interativo — sucesso, falha de tokenização, falha de cobrança após tokenização.
  • Silent charge (ciclo N≥2) — sucesso, recusa, cartão expirado (short-circuit antes da chamada ao gateway), método de pagamento ausente, cliente sem cadastro.
  • Card retry cron — sucesso (status pending aguardando webhook), recusa.

Cada linha captura: origem (source), método, paymentMethodId + snapshot de cardLast4/cardBrand (sobrevive a LGPD removal), status, failureCode canônico + failureReason + gatewayFailureCode raw, gatewayChargeId, transactionId. attemptNumber é monotônico por ciclo.

A escrita é best-effort: erros são logados + capturados pelo Sentry mas nunca quebram o pipeline de billing.

GET /api/scheduled-charges/:id/attempts

Endpoint paginado que retorna todas as tentativas de uma série, com filtro opcional por cycleNumber. Junta com o ciclo pai para incluir cycleNumber em cada linha — sem precisar de uma segunda chamada.

curl 'https://garu.com.br/api/scheduled-charges/sch_abc/attempts?cycleNumber=3' \
  -H "Authorization: Bearer $GARU_API_KEY"

SDK — garu.scheduledCharges.listAttempts

@garuhq/node@0.8.0 traz o método e os tipos:

const { data } = await garu.scheduledCharges.listAttempts('sch_abc', {
  cycleNumber: 3,
});
const declines = data.filter((a) => a.status === 'declined');
// → cada declines[i].failureCode é um GaruFailureCode estável

Tipos exportados: ScheduledChargeAttempt, ScheduledChargeAttemptList, ScheduledChargeAttemptSource, ScheduledChargeAttemptStatus, ListScheduledChargeAttemptsParams.

MCP — list_scheduled_charge_attempts

Agentes podem inspecionar histórico de cobrança como parte de fluxos de suporte. @garuhq/mcp@0.8.0 expõe a ferramenta com filtro por cycleNumber + paginação.

Quando usar

  • Cliente reclamou "minha cobrança não passou e não sei por quê" → consulta direta sem joins.
  • Dashboard interno mostrando histórico de tentativas por série.
  • Auditoria de SLA: quantas tentativas a Garu fez antes de marcar overdue.

Migração

Aditivo. Nada quebra. A tabela scheduled_charge_cycle_attempt já existia desde a v0.8.0; a v0.9.0 apenas começa a escrever nela e a expor via API. Séries existentes não terão linhas para tentativas anteriores à v0.9.0 (não há backfill de histórico).

Próximos passos

  • Painel "Histórico de cobrança" no dashboard mostrando o log por série.
  • SDK Flutter — wrapper listAttempts + tipos (paridade @garuhq/node).
  • Backfill opcional do log para séries em curso (sob demanda — entre em contato).