Free tools Windows power users keep installed
One-click scans. No signup required.
Um aluno recebeu o dobro dos créditos esperados após comprar um pacote no surfaai: duas linhas completas foram criadas em user_credits, com segundos de diferença. Segundo o relato de César Eduardo Sturmer, o F5 não processou o pagamento outra vez; ele repetiu a chamada que criava os créditos. A correção foi tirar essa responsabilidade da página de confirmação e fazer o crédito nascer no servidor, a partir do evento de pagamento da Stripe.
Como um F5 virou crédito duplicado
No fluxo descrito por Sturmer, o frontend criava um PaymentIntent, o aluno pagava, a Stripe devolvia a confirmação ao navegador e, então, o frontend chamava uma rota para criar os créditos. Como a página de confirmação disparava essa chamada ao montar, recarregá-la podia repetir a operação.
As an Amazon Associate I earn from qualifying purchases.
O relato diz que a rota verificava corretamente o pagamento e o valor. O problema não era uma validação incorreta em uma execução: a mesma operação podia ser invocada novamente, sem uma regra que impedisse criar créditos pela segunda vez. A descrição do caso — dois registros completos, separados por segundos — é anedótica e se refere a esse incidente, não a uma taxa típica de falha.
O que mudou: a confirmação do navegador deixou de criar créditos
Sturmer substituiu o fluxo acionado pela tela por um caminho de criação no servidor. O crédito passou a ser criado quando o webhook recebe payment_intent.succeeded; o frontend deixou de criar créditos e passou a consultar o estado. A Stripe define esse evento como o sinal de que um PaymentIntent concluiu o pagamento com sucesso (documentação de eventos da Stripe).
#1 Best Overall
| Aspecto | Tela de confirmação | Webhook de pagamento |
|---|---|---|
| Quem inicia a criação | O frontend, ao montar a página de confirmação. | O handler no servidor, ao receber o evento Stripe configurado. |
| Se a página for recarregada | A chamada pode ser repetida e, sem proteção, criar créditos outra vez. | Recarregar a página não é a origem da criação. |
| Se o cliente não voltar ao site | O fluxo depende de uma confirmação no navegador. | O servidor pode tratar o evento sem depender do retorno do cliente. |
Isso também atende ao cenário de pagamento posterior que o autor destaca para PIX e boleto: o comprador pode fechar a aba e pagar mais tarde pelo aplicativo do banco, sem retornar ao site. No exemplo narrativo do postmortem, o pagamento ocorre quarenta minutos depois; esse intervalo ilustra o caso e não é uma métrica. Uma confirmação que só existe no navegador não pode ser a origem confiável para liberar esses créditos.
A documentação da Stripe recomenda um PaymentIntent por pedido ou sessão do cliente e informa que um PaymentIntent cria no máximo uma cobrança bem-sucedida (PaymentIntents). Isso contextualiza o papel do objeto de pagamento, mas não explica o defeito específico do surfaai: no relato, a duplicação estava na criação dos créditos pela aplicação.
Rank #2
Webhook não significa execução exatamente uma vez
Mover a criação para um webhook corrige a dependência da página, mas não basta para impedir repetições. Sturmer relata que as entregas de webhook são at-least-once: o mesmo evento pode ser entregue novamente. Por isso, o handler usa o identificador estável do evento para reconhecer reentregas.
Primeira barreira: registrar e reconhecer o evento
Na entrada do handler, o código consulta stripe_webhook_events pelo stripe_event_id. Se o evento já consta como processado, o handler retorna sucesso em vez de criar os créditos outra vez. Essa verificação evita repetir o trabalho em uma reentrega conhecida.
Rank #3
- TOOL SUSPENSION & FALL SUPPORT: No power source necessary. Holds and stabilizes tools. Ergonomically beneficial/efficient by alleviating tool's weight and worker fatigue while completing repetitive tasks.
- LIGHT WEIGHT: Zero gravity balancer makes tools feel virtually weightless and maximizes efficiency and accuracy while operating. Adjustable tension and cable length.
- WORKPLACE SAFETY: Prevents tool from falling in the event of spring/tension failure. Keeps the work environment safe and the workplace clear of tools, fixtures, etc.
- MATERIALS: Made in Korea, Aluminum casing and drum, steel cable
- LOAD CAPACITY: 5.5 - 11 lbs; STROKE: 51 inches; WEIGHT: 3.31 lbs
Segunda barreira: unicidade persistente no banco
A consulta prévia, sozinha, não resolve duas entregas concorrentes. Ambas podem consultar antes de qualquer uma gravar e, portanto, ambas concluir que ainda não há crédito. O autor adicionou uma restrição de unicidade no banco para impedir a duplicação; se uma tentativa concorrente recebe a violação, o código trata o crédito já criado como resultado concluído.
| Proteção | O que impede | Limite |
|---|---|---|
Consulta de stripe_event_id em stripe_webhook_events |
Reprocessar um evento que já foi registrado como processado. | Não fecha, sozinha, a janela entre a consulta e a gravação quando duas entregas correm ao mesmo tempo. |
| Restrição de unicidade no banco | Persistir uma segunda criação duplicada mesmo diante de tentativas concorrentes. | Precisa estar alinhada à regra que define quando um crédito é único; o postmortem não especifica a definição da chave de unicidade. |
A Stripe também documenta chaves de idempotência para repetir com segurança determinadas requisições de criação ou atualização à API; requisições subsequentes com a mesma chave retornam o resultado registrado, dentro das regras de parâmetros e retenção descritas pela plataforma (documentação de idempotência da Stripe). Essa funcionalidade não substitui as duas barreiras descritas para o banco da aplicação: a chave de uma requisição à API Stripe e a deduplicação do evento mais a unicidade dos créditos protegem operações diferentes.
Rank #4
Por que a causa não foi uma condição de corrida
A hipótese inicial do autor foi uma condição de corrida entre chamadas simultâneas, o que o levou a considerar um lock. O diagnóstico posterior foi outro: a página podia repetir a mesma ação em momentos distintos, e nenhuma regra dizia que a criação só podia valer uma vez. A concorrência ainda precisava ser tratada na solução do webhook, porque duas entregas simultâneas podem atravessar a verificação de existência antes da inserção. Por isso, o lock cogitado como explicação original não eliminava a necessidade da restrição no banco.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
O que perguntar antes de automatizar uma operação financeira
- Qual é a origem autorizada do efeito? No caso relatado, o cliente podia solicitar a criação de créditos; depois da mudança, o servidor passou a reagir ao evento de pagamento.
- O que acontece se a chamada rodar duas vezes? Uma validação correta em uma execução não garante que uma repetição seja inofensiva.
- O sistema funciona se o cliente não voltar ao site? Essa pergunta importa quando o pagamento pode acontecer depois que o navegador foi fechado.
- Qual proteção continua valendo sob concorrência? A consulta do evento ajuda com reentregas conhecidas; a restrição de unicidade protege a gravação quando verificações e inserções se sobrepõem.
“A pergunta útil não era "esse código está certo?", era "o que acontece se isso rodar duas vezes?".”
O relato completo foi publicado na DEV Community em 1º de outubro de 2026, com o original no site do autor na mesma data (postmortem de César Eduardo Sturmer na DEV Community). Os detalhes do incidente, das tabelas e do código são o relato do responsável pelo caso; não há validação independente apresentada para eles.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




