惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

腾讯CDC
Microsoft Azure Blog
Microsoft Azure Blog
B
Blog
S
SegmentFault 最新的问题
WordPress大学
WordPress大学
P
Proofpoint News Feed
Hugging Face - Blog
Hugging Face - Blog
MyScale Blog
MyScale Blog
A
About on SuperTechFans
雷峰网
雷峰网
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
The Blog of Author Tim Ferriss
MongoDB | Blog
MongoDB | Blog
博客园 - 【当耐特】
The Cloudflare Blog
F
Fortinet All Blogs
小众软件
小众软件
博客园 - 三生石上(FineUI控件)
宝玉的分享
宝玉的分享
罗磊的独立博客
量子位
有赞技术团队
有赞技术团队
V
V2EX
Engineering at Meta
Engineering at Meta

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Cobrança recorrente em produção: o que ninguém te conta a...
Felipe Ricarte · 2026-06-18 · via DEV Community

Implementar cobrança recorrente parece um problema resolvido. Você integra um gateway, agenda um job pra rodar todo dia e cobra quem vence hoje. Em uma tarde está "funcionando".

Aí o sistema vai pra produção, você escala pra duas instâncias, e numa madrugada o mesmo cliente é cobrado duas vezes. Ou pior: o pagamento confirma, mas o módulo que libera o acesso nunca fica sabendo — e o cliente paga sem receber.

A cobrança em si é a parte fácil. O que separa um protótipo de um billing de produção são quatro detalhes que quase ninguém trata no começo.

1. Concorrência: o job que roda duas vezes

O cenário clássico: um @Scheduled que busca assinaturas vencidas e cobra cada uma. Com uma instância, funciona. Com duas (deploy sem downtime, autoscaling), as duas leem a mesma assinatura no mesmo segundo e cobram em dobro.

A solução não é "garantir uma instância" — é tornar a leitura segura sob concorrência. No PostgreSQL:

SELECT * FROM assinatura
WHERE proxima_cobranca <= now() AND status = 'ATIVA'
FOR UPDATE SKIP LOCKED
LIMIT 50;


{data-source-line="212"}

FOR UPDATE SKIP LOCKED faz cada instância travar e processar um lote disjunto: ninguém espera, ninguém duplica. Escala horizontalmente sem coordenação externa.

2. Retry determinístico: falhar não pode ser improviso

Cartão recusado acontece o tempo todo. A pergunta é: quantas vezes tentar, com qual intervalo, e quando desistir? Se isso for "ad hoc", você terá clientes suspensos cedo demais e outros cobrados pra sempre.

Trate a falha como uma máquina de estados explícita:

  • 1ª falha → reagenda +15 min
  • 2ª falha → +60 min
  • 3ª falha → suspende a assinatura e desliga a renovação

Cada tentativa fica registrada e auditável. O comportamento é previsível — para você e para o cliente.

3. Outbox: o evento que não pode se perder

O erro mais caro: cobrar com sucesso e, na sequência, publicar o evento PagamentoConfirmado num broker. Se a aplicação morre entre as duas operações, o pagamento aconteceu mas o resto do sistema nunca soube. Dinheiro entrou, acesso não foi liberado.

O padrão Outbox resolve: grave o evento na mesma transação da cobrança, numa tabela outbox. Um publicador lê essa tabela e entrega ao broker com retry/backoff, marcando como DEAD ao estourar o limite.

BEGIN;
  UPDATE assinatura SET status = 'ATIVA', proxima_cobranca = ... WHERE id = ?;
  INSERT INTO outbox (tipo, payload, status) VALUES ('PagamentoConfirmado', ?, 'PENDENTE');
COMMIT;


{data-source-line="239"}

Ou a cobrança e o evento são persistidos, ou nenhum dos dois. Sem estado inconsistente.

4. Idempotência no consumidor

O outbox garante entrega pelo menos uma vez — então o mesmo evento pode chegar duas vezes. O consumidor precisa ser idempotente. A forma mais simples e robusta é uma constraint única na chave do evento:

ALTER TABLE evento_processado ADD CONSTRAINT uq_evento UNIQUE (evento_id);


{data-source-line="249"}

Processou de novo? A inserção viola a constraint e você ignora com segurança. Sem efeito colateral duplicado.

O encanamento toma mais tempo que o produto

Nada disso é "difícil" isoladamente. O problema é que são semanas de encanamento — concorrência, retry, outbox, idempotência, métricas, testes com containers — antes de escrever uma linha da regra de negócio que importa pro seu SaaS. E é justamente a parte onde um bug custa cliente e reputação.


Atalho

Empacotei exatamente essa camada num starter kit em Java 21 + Spring Boot 3: renovação automática, retry determinístico, outbox com idempotência, suspensão por inadimplência, métricas Prometheus e testes com Testcontainers — tudo documentado e rodando via Docker Compose.

É o AssinaFlow. Se você vai construir cobrança recorrente, ele economiza as semanas chatas e te dá uma base que já nasce pronta pra produção.

👉 Conheça o AssinaFlow

Dúvidas sobre alguma das decisões acima? Comenta aí — respondo todas.