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.

Illustration of a webhook envelope with an orange key wax seal being checked at a gate, representing Stripe webhook secret verification
Nesta página · 8 seções
  1. O que é o webhook secret do Stripe (signing secret)?
  2. Onde encontro o webhook secret no dashboard do Stripe?
  3. Por que os secrets do modo de teste e do modo de produção são diferentes?
  4. Como funciona a verificação de assinatura?
  5. Por que o secret nunca deve estar no código do frontend?
  6. Onde devo guardar o secret no Supabase ou na minha própria hospedagem?
  7. O que faço se o secret vazou?
  8. 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:

  1. Abra o dashboard do Stripe e vá em Developers, depois em Webhooks.
  2. Clique no endpoint que aponta para o seu app.
  3. Encontre a seção "Signing secret" e escolha revelá-lo.
  4. 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.

Ilustração de um dashboard de pagamentos com um campo de secret de assinatura mascarado ao lado de um cadeado
O secret de assinatura fica nas configurações do endpoint e só é revelado quando você pede.

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.

Envelope com dois selos idênticos comparados sob uma lupa, representando a verificação de assinatura
A verificação é uma comparação: seu servidor recalcula a assinatura e confere se ela bate.

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:

  1. 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.
  2. Atualize a variável de ambiente na sua hospedagem e faça um novo deploy.
  3. Remova o secret do código e, se ele estava num repositório Git, considere-o exposto para sempre, porque o histórico guarda.
  4. Revise os eventos e pedidos recentes em busca de algo suspeito, como pedidos pagos sem um pagamento correspondente no Stripe.
  5. 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.

stripe webhook secret

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