Webhook do Stripe: como funciona em um app construído com IA

Como funciona um webhook do Stripe em um app feito com Lovable, Bolt ou Base44: onde fica, assinatura, novas tentativas e proteção contra pedidos duplicados.

Illustration of a Stripe webhook event passing a signature check, with retried duplicates deflected from the server
Nesta página · 8 seções
  1. O que é um webhook do Stripe e por que meu app precisa de um?
  2. Por que a página de sucesso não é prova de pagamento?
  3. Onde o endpoint do webhook deve ficar em um app construído com IA?
  4. Como configuro a URL do endpoint de webhook do Stripe?
  5. O que acontece se o webhook falhar ou chegar duas vezes?
  6. Como evito pedidos duplicados?
  7. Um scanner de segurança consegue pegar bugs de webhook?
  8. Quer uma segunda opinião sobre o seu fluxo de pagamento?

Principais pontos

  • Quem deve decidir se um pedido está pago é o webhook, não a página de sucesso.
  • O endpoint precisa rodar em um servidor que você controla, nunca em código do navegador.
  • Sempre verifique a assinatura e trate cada evento como possivelmente atrasado, perdido ou duplicado.
  • Guarde o ID do evento do Stripe e o ID da checkout session para que um pedido seja atendido uma única vez.
  • Scanners de segurança não entendem essa lógica; você precisa testar o próprio fluxo de pagamento.

Um webhook do Stripe é uma requisição HTTP que o Stripe envia para o seu servidor quando algo acontece com um pagamento: uma cobrança é aprovada, uma assinatura é renovada, um reembolso é emitido. É a única forma confiável de o seu backend saber o resultado real de um pagamento, porque a informação vem direto do Stripe, e não do navegador do cliente.

Em apps construídos com ferramentas de IA como Lovable, Bolt ou Base44, o webhook, nos apps que já vi, às vezes não existe, está no lugar errado ou não é verificado. Sou um engenheiro sênior brasileiro que já trabalhou com fluxos de checkout, inclusive no time de carrinho e checkout da Total Wine & More, e esse é um dos primeiros lugares para onde eu olho.

O que é um webhook do Stripe e por que meu app precisa de um?

O Stripe processa os pagamentos nos próprios servidores. O seu app só fica sabendo o que aconteceu se o Stripe avisar. Um webhook é uma URL no seu backend que o Stripe chama com um evento em JSON descrevendo o que aconteceu, como checkout.session.completed ou invoice.paid.

O seu handler lê o evento, confirma que ele é legítimo e então atualiza o banco de dados: marca o pedido como pago, libera o acesso, envia o recibo. Sem isso, o seu app está chutando.

Os pagamentos também mudam depois do primeiro momento. Cartões sofrem contestação, assinaturas falham na renovação, reembolsos são feitos pelo dashboard do Stripe. Só os webhooks mantêm os seus registros alinhados com essas mudanças posteriores.

Por que a página de sucesso não é prova de pagamento?

Depois do checkout, o Stripe redireciona o cliente para uma URL de sucesso no seu site. Alguns apps gerados por IA tratam a chegada a essa página como prova de pagamento e liberam o produto ali. Isso é arriscado por três motivos:

  • Qualquer pessoa pode digitar a URL de sucesso no navegador sem pagar.
  • O cliente pode fechar a aba antes do redirecionamento, e aí um pedido pago nunca é registrado.
  • Alguns meios de pagamento confirmam depois, então o redirecionamento chega antes do dinheiro.

A página de sucesso serve para a experiência do usuário: mostrar uma mensagem de agradecimento. A decisão de que um pedido está pago deve vir de um evento de webhook verificado, ou do seu servidor consultando diretamente no Stripe o status da sessão.

Na minha experiência, o que o construtor gera pode parecer certo em uma demo. O teste passa porque quem pagou foi você. O bug só aparece quando alguém se comporta de outro jeito.

Ilustração em estilo de diagrama de um caminho de redirecionamento do navegador e de um caminho separado de confirmação no servidor para um webhook do Stripe
O redirecionamento do navegador e a confirmação no servidor são dois caminhos diferentes; só um é confiável.

Onde o endpoint do webhook deve ficar em um app construído com IA?

Ele precisa ficar em um servidor, porque usa a sua chave secreta do Stripe e o segredo de assinatura do webhook. Nenhum dos dois pode chegar ao navegador. Onde fica esse servidor depende da sua stack:

  • Supabase: uma Edge Function é o lugar mais comum. Apps construídos com Lovable costumam usar Supabase, então essa é uma escolha frequente.
  • Next.js: um route handler no seu app, publicado na sua própria hospedagem.
  • Um backend separado: por exemplo NestJS ou Go, se você tirou o código do construtor.

Dois detalhes costumam pegar as pessoas. Primeiro, o endpoint é chamado pelo Stripe, não por um usuário logado, então ele não vai trazer o seu token de autenticação normal. Você o protege com a verificação de assinatura. Segundo, a verificação de assinatura precisa do corpo bruto da requisição. Se um framework faz o parse do JSON antes, a verificação falha, e o código gerado às vezes “corrige” isso removendo a verificação por completo. Não aceite essa correção.

As regras de dados também importam. O handler normalmente grava com uma chave privilegiada, então ele deve ser o único lugar que faz isso, e essa chave precisa ficar nas variáveis de ambiente do servidor. Essa responsabilidade é do dono do app, não da plataforma.

Como configuro a URL do endpoint de webhook do Stripe?

A configuração é curta depois que o endpoint existe:

  1. Publique o handler e anote a URL HTTPS pública dele.
  2. No dashboard do Stripe, abra a seção de desenvolvedores, depois webhooks, e adicione um endpoint com essa URL.
  3. Selecione só os eventos que o seu app trata. Comece pequeno, por exemplo com checkout sessions concluídas, e adicione outros de forma deliberada.
  4. Copie o segredo de assinatura que o Stripe mostra para esse endpoint e salve como variável de ambiente no servidor.
  5. No handler, verifique a assinatura com a biblioteca do Stripe antes de ler qualquer outra coisa.
  6. Retorne uma resposta 2xx rapidamente assim que o evento estiver registrado com segurança.

O modo de teste e o modo live têm endpoints e segredos de assinatura separados. Um bug comum no lançamento é publicar com o segredo de teste ainda configurado, e aí todo evento real é rejeitado. Confira isso antes de entrar em produção.

Faça o trabalho lento, como enviar emails, depois de responder ou em uma fila. Se o seu handler demorar demais, o Stripe considera uma falha e tenta de novo.

O que acontece se o webhook falhar ou chegar duas vezes?

Segundo a documentação, o Stripe tenta reenviar entregas que falharam durante um período, com intervalos cada vez maiores. Então uma indisponibilidade temporária do seu lado normalmente é recuperável, se o seu endpoint foi construído para lidar com isso. Consulte a documentação atual do Stripe para saber a janela exata de novas tentativas.

As novas tentativas também significam que o mesmo evento pode chegar mais de uma vez. Os eventos também podem chegar fora de ordem: uma atualização pode aparecer antes do evento de criação que você esperava.

Planeje para estes casos:

  • Endpoint fora do ar: o Stripe tenta de novo, e você pode reenviar os eventos pelo dashboard depois de corrigir o bug.
  • Handler lança um erro: retorne um status diferente de 2xx para que o Stripe tente de novo, mas garanta que nenhum trabalho fique pela metade.
  • Entrega duplicada: detecte e não faça nada na segunda vez.
  • Eventos fora de ordem: confie no estado atual do objeto no Stripe, não na sequência de chegada.

Crie também o hábito da conciliação. Um job agendado ou uma checagem manual que compare os pagamentos recentes no Stripe com os seus pedidos vai pegar eventos que se perderam de vez.

Ilustração de uma mensagem duplicada entregue duas vezes e processada só uma vez, representando o tratamento idempotente de webhooks
Uma entrega duplicada deve ser reconhecida e deixada de lado, não processada de novo.

Como evito pedidos duplicados?

A técnica se chama idempotência: processar a mesma entrada duas vezes tem o mesmo resultado que processá-la uma vez. Na prática:

  1. Crie uma tabela de IDs de eventos do Stripe já processados, com uma restrição de unicidade.
  2. Quando um evento chegar, tente inserir o ID dele. Se a inserção falhar porque ele já existe, retorne 200 e pare.
  3. Guarde também no pedido o ID da checkout session ou do payment intent, com a sua própria restrição de unicidade, para que dois eventos diferentes sobre o mesmo pagamento não criem dois pedidos.
  4. Faça a inserção e a atualização do pedido em uma única transação no banco de dados.

A restrição de unicidade importa mais do que uma checagem “se existe” no código. Duas entregas podem chegar quase no mesmo instante, ambas passam pela checagem e ambas criam um pedido. O banco de dados é o único lugar que consegue dizer não com confiabilidade.

Pedidos duplicados também vêm do lado do navegador: um cliente que recarrega a página de sucesso, ou um app que cria um pedido antes do pagamento e outro depois. Mantenha um único registro de pedido, criado uma vez, e deixe o webhook passá-lo para pago.

Um scanner de segurança consegue pegar bugs de webhook?

Na maioria das vezes, não. Um scanner genérico encontra uma chave exposta ou uma porta aberta. Ele não sabe que a sua página de sucesso libera conteúdo, que o seu handler pula a verificação de assinatura por conveniência ou que dois eventos criam dois pedidos. Esses são problemas de lógica de negócio e estão no desenho do seu fluxo de pagamento.

Pedir ao construtor com IA para revisar o próprio trabalho tem um limite parecido. Na minha experiência, regras de dados, chaves e fluxos de pagamento são exatamente o que essas ferramentas costumam errar, e uma mensagem confiante de “corrigido” não é evidência.

Antes de chegarem usuários reais, quatro checagens cobrem a maior parte do risco: regras de acesso a dados, chaves expostas, confirmação de pagamento e backups. O webhook é a checagem de confirmação de pagamento. Para testá-lo, faça uma compra real no modo de teste e depois tente quebrar o fluxo: reenvie o evento, mande um com assinatura inválida, derrube o endpoint e reenvie, e acesse a URL de sucesso sem pagar. Cada um desses casos deve terminar de forma segura.

Quer uma segunda opinião sobre o seu fluxo de pagamento?

Se o seu app construído com IA recebe pagamentos e você não tem certeza de que o webhook está certo, mande um email para me@filipeeduardo.dev com o setup, e eu digo o que eu checaria primeiro.

Perguntas frequentes

Preciso de um webhook se uso Stripe Checkout ou Payment Links?

Sim, se o seu app precisa reagir aos pagamentos, por exemplo liberando acesso ou registrando pedidos. A página hospedada recebe o pagamento, mas só um webhook avisa o seu backend de forma confiável.

Posso colocar o handler do webhook no código do frontend?

Não. Ele precisa de chaves secretas e tem que ser acessível pelos servidores do Stripe. Use uma função de servidor ou uma rota de backend.

Devo retornar um erro se receber um evento que não trato?

Em geral, retorne 200 para eventos válidos que você não trata, para que o Stripe não fique tentando de novo. Melhor ainda, assine só os eventos que você usa.

O webhook basta ou devo também consultar a API do Stripe?

O evento verificado normalmente basta. Para ações de alto valor, buscar o objeto no Stripe antes de atender o pedido adiciona uma checagem de segurança extra e barata.

Isto é aconselhamento jurídico ou tributário?

Não. Trata apenas de prática técnica. Para compliance de pagamentos, reembolsos ou questões tributárias, converse com um advogado ou contador.

stripe webhook

Filipe Eduardo

Engenheiro de Software Sênior. Sete anos construindo produtos web e mobile de ponta a ponta e liderando os times que os entregam.

Acompanhe

Quer conversar
sobre isso?

Trabalho com problemas assim todos os dias. Me conte o seu.

Fale comigo