Webhook secret do Stripe: onde encontrar e como verificar eventos
Encontre o webhook secret do Stripe (whsec_), verifique cada evento no corpo bruto, guarde-o com segurança no Supabase ou na hospedagem e rotacione se vazar.

Nesta página · 8 seções
- O que é o webhook secret do Stripe (signing secret)?
- Onde encontro o webhook secret no dashboard do Stripe?
- Por que os secrets do modo de teste e do modo de produção são diferentes?
- Como funciona a verificação de assinatura?
- Por que o secret nunca deve estar no código do frontend?
- Onde devo guardar o secret no Supabase ou na minha própria hospedagem?
- O que faço se o secret vazou?
- Quer um segundo par de olhos?
Principais pontos
- O webhook secret (whsec_…) é por endpoint e por modo. Ele não é sua chave de API.
- Verifique a assinatura no corpo bruto da requisição antes de confiar em qualquer evento.
- Os modos de teste e de produção têm secrets diferentes, então sua hospedagem precisa ter os dois configurados corretamente.
- Nunca coloque o secret no código do frontend nem numa variável que o navegador consiga ler.
- Se ele vazar, rotacione o secret no dashboard e faça um novo deploy.
- O dono do app é responsável pelas chaves e pela confirmação de pagamento; o Stripe protege a própria infraestrutura.
O webhook secret do Stripe é um secret de assinatura, uma string que começa com whsec_, que o Stripe usa para assinar cada evento que envia ao seu endpoint. Seu servidor usa a mesma string para conferir se o evento veio mesmo do Stripe e não foi alterado no caminho.
Se o seu app foi construído com uma ferramenta de IA, este é um dos primeiros lugares que eu verifico. A confirmação de pagamento depende dele, e essas ferramentas costumam errar nas chaves e nos fluxos de pagamento. A seguir: onde encontrar o secret, como a verificação funciona, onde guardá-lo e o que fazer se ele vazar.
O que é o webhook secret do Stripe (signing secret)?
Quando você cria um endpoint de webhook no Stripe, o Stripe gera um secret de assinatura para ele. Todo evento enviado a esse endpoint carrega um header Stripe-Signature, calculado com esse secret.
Qualquer pessoa pode mandar uma requisição POST para a sua URL pública. Sem verificação, alguém poderia enviar um evento falso de "pagamento aprovado" e seu app poderia marcar um pedido como pago. O secret é o que separa um evento real de um forjado.
Qual a diferença para a chave de API?
- Chave secreta de API (sk_…): permite que seu servidor chame o Stripe para criar cobranças, reembolsos e assim por diante.
- Chave publicável (pk_…): segura no navegador, usada em formulários de checkout.
- Secret de assinatura do webhook (whsec_…): permite que seu servidor verifique os eventos que chegam do Stripe.
Um erro comum em código gerado por IA é misturar essas chaves, por exemplo usando a chave de API onde deveria estar o secret de assinatura.
Onde encontro o webhook secret no dashboard do Stripe?
Os nomes exatos dos menus podem mudar, então considere isto como o caminho geral:
- Abra o dashboard do Stripe e vá em Developers, depois em Webhooks.
- Clique no endpoint que aponta para o seu app.
- Encontre a seção "Signing secret" e escolha revelá-lo.
- Copie o valor que começa com whsec_.
Se você ainda não tem um endpoint, crie um com sua URL pública e os eventos que você quer. O secret aparece assim que o endpoint existe.
E o secret da Stripe CLI?
Quando você encaminha eventos para o localhost com a Stripe CLI, a CLI imprime seu próprio valor whsec_. Esse serve só para testes locais e é diferente do secret do endpoint no dashboard. Não copie ele para produção.

Por que os secrets do modo de teste e do modo de produção são diferentes?
O Stripe mantém os dados de teste e de produção totalmente separados. Cada modo tem seus próprios endpoints, então cada um tem seu próprio secret de assinatura. Um evento do modo de produção assinado com o secret de produção vai falhar na verificação se seu servidor tiver o secret de teste, e vice-versa.
Sintomas típicos dessa divergência:
- Tudo funciona em teste, mas os pagamentos reais nunca são confirmados.
- Erros de verificação aparecem logo depois do lançamento.
- Os pedidos ficam "pendentes" mesmo com o Stripe mostrando que foram pagos.
Antes de ir para produção, confira se a hospedagem de produção tem o secret de produção e a chave de API de produção, e se a URL do endpoint de produção está registrada no modo de produção. É fácil deixar isso passar quando um construtor de apps com IA ligou tudo no modo de teste.
Como funciona a verificação de assinatura?
O header Stripe-Signature contém um timestamp e uma ou mais assinaturas. O Stripe calcula um HMAC do timestamp mais o corpo bruto exato usando o seu secret. Seu servidor repete o cálculo e compara os resultados.
Você não deveria escrever isso na mão. As bibliotecas oficiais do Stripe têm uma função (no Node, stripe.webhooks.constructEvent) que recebe o corpo bruto, o header e o secret, e lança um erro se algo estiver errado. Ela também rejeita timestamps antigos demais, o que ajuda contra eventos reenviados (replay).
A armadilha do corpo bruto
Um bug que encontro com frequência: o framework faz o parse do JSON primeiro e depois o código serializa de novo para a verificação. Até uma mudança de espaço em branco quebra a assinatura. Você precisa passar os bytes brutos, intocados. Em route handlers do Next.js, leia o texto da requisição; no Express, use um parser de corpo bruto só nessa rota.
O que fazer quando a verificação falha
- Retorne uma resposta 400 e não faça mais nada com o payload.
- Não desative a verificação para "fazer funcionar". Isso transforma seu endpoint numa porta aberta.
- Confira o secret, o modo e o corpo bruto, nessa ordem.
Um scanner de segurança genérico não vai detectar uma etapa de verificação ausente, porque isso é lógica de negócio. Quando reviso um app construído com IA, leio o handler do webhook manualmente exatamente por esse motivo.

Por que o secret nunca deve estar no código do frontend?
Tudo que vai para o navegador é público. Se o secret estiver no código do cliente, numa variável de ambiente pública ou num arquivo commitado num repositório público, qualquer pessoa pode lê-lo e assinar eventos falsos que passam pela sua verificação.
Em apps construídos com IA, fique de olho em:
- Variáveis com prefixos como NEXT_PUBLIC_ ou VITE_, que são incluídas no código do navegador.
- Secrets colados num prompt, que depois acabam nos arquivos de código-fonte.
- Um arquivo .env commitado no GitHub.
O handler do webhook precisa rodar num servidor: uma rota de API, uma função serverless ou um serviço de backend. Chaves no navegador são responsabilidade do dono do app, não da plataforma.
Onde devo guardar o secret no Supabase ou na minha própria hospedagem?
No Supabase
Se o handler é uma Edge Function, guarde o secret com o recurso de secrets do Supabase (o comando de CLI supabase secrets set ou o equivalente no dashboard) e leia-o do ambiente da função. Nunca grave o secret numa tabela que o cliente consiga consultar. Confirme também que o endpoint do webhook não exige um token de login de usuário, já que o Stripe não vai enviar um; a assinatura é a autenticação dele.
Na sua própria hospedagem
Use as variáveis de ambiente ou o gerenciador de secrets da hospedagem (Vercel, Fly.io, AWS e similares têm um). Defina valores separados para preview, teste e produção. Mantenha o arquivo .env fora do controle de versão com o .gitignore.
Um checklist simples
- Um secret por ambiente e por modo.
- Guardado só no servidor.
- Fora de logs, mensagens de erro e capturas de tela.
- Conhecido pelo menor número de pessoas possível.
O que faço se o secret vazou?
Aja rápido, mas com calma:
- No dashboard, abra o endpoint e rotacione o secret de assinatura. O Stripe permite manter o antigo válido por um tempo limitado, para você fazer a troca sem perder eventos.
- Atualize a variável de ambiente na sua hospedagem e faça um novo deploy.
- Remova o secret do código e, se ele estava num repositório Git, considere-o exposto para sempre, porque o histórico guarda.
- Revise os eventos e pedidos recentes em busca de algo suspeito, como pedidos pagos sem um pagamento correspondente no Stripe.
- Se sua chave de API também foi exposta, rotacione ela também.
Faça a entrega dos pedidos conferir o estado do pagamento no Stripe onde isso importa, e torne os handlers idempotentes para que um evento duplicado não crie um pedido duplicado. Isso não é garantia contra abuso, mas limita o estrago.
Quer um segundo par de olhos?
Se quiser que alguém olhe como seu app trata os eventos do Stripe, mande um email para me@filipeeduardo.dev com o link ou o código do handler, e eu te digo o que verificaria primeiro.
Perguntas frequentes
O webhook secret é a mesma coisa que minha chave de API do Stripe?
Não. A chave de API permite que você chame o Stripe. O webhook secret só verifica os eventos que o Stripe envia para você.
Posso ter mais de um webhook secret?
Sim. Cada endpoint tem o seu, e os modos de teste e de produção são separados, então um projeto costuma ter vários.
Posso pular a verificação durante os testes?
É melhor não. Use a Stripe CLI para encaminhar eventos de teste reais e assinados, assim o caminho de código que você testa é o mesmo que vai para produção.
A plataforma protege meu secret por mim?
A plataforma protege a infraestrutura dela. Onde você coloca o secret e quem consegue lê-lo é responsabilidade sua, como dono do app.
Com que frequência devo rotacioná-lo?
Não existe regra fixa. Rotacione quando alguém sair do time, quando suspeitar de exposição ou como parte da sua própria rotina de segurança.

