Todos os artigos
Shopifywebhook debugginglocal testinge-commerce

Teste webhooks do Shopify localmente sem surpresas

A maioria dos bugs de webhook do Shopify no desenvolvimento local se resume a uma linha: o HMAC é codificado em base64, não em hex. Se você chama .digest('hex') e compara com X-Shopify-Hmac-Sha256, a verificação nunca vai passar — mesmo com o segredo compartilhado correto. Já vimos engenheiros sênior perderem uma tarde por causa desse um caractere.

A armadilha do base64

O Stripe usa hex. O GitHub usa hex (com prefixo sha256=). O Shopify usa base64. Três provedores, três codificações. Sua primeira versão do verificador quase certamente vai copiar um snippet de outro projeto e falhar em silêncio.

Eis como a verificação deveria parecer:

const hmac = crypto
  .createHmac('sha256', SHOPIFY_API_SECRET)
  .update(rawBody)         // raw body, not JSON-parsed
  .digest('base64');       // base64, not hex

const valid = crypto.timingSafeEqual(
  Buffer.from(hmac),
  Buffer.from(req.headers['x-shopify-hmac-sha256']),
);

Duas coisas importam além da codificação. O corpo tem que ser os bytes crus, sem parse. E a comparação tem que ser de tempo constante — a igualdade de strings vaza informação byte a byte.

Por que um túnel facilita isso

Você também pode usar o comando do Shopify shopify app dev, que sobe um túnel interno. Funciona. Mas também embrulha seu servidor de desenvolvimento no próprio processo, engole logs de um jeito específico e não te dá botão de replay. Para desenvolvimento de app além do "hello world", uma URL pública estável mais um túnel localhost com captura de requisições economiza mais tempo do que a CLI economiza na configuração inicial.

npx portpreview 3000

Você cola a URL HTTPS na configuração de webhooks do seu app no painel de Partners, dispara um evento de uma loja de teste, e a requisição aterrissa no seu handler local com todos os cabeçalhos intactos.

Lojas de teste: a parte que a documentação do Shopify passa por cima

Duas coisas para saber antes de ligar qualquer coisa:

  • Lojas de desenvolvimento disparam todos os eventos de webhook. Criação de pedido, fulfillment, estoque — tudo. Você não precisa falsear nada; só instale seu app numa loja dev e clique por aí.
  • O segredo do webhook difere por app e por canal de entrega. Se você também assina o EventBridge ou Pub/Sub, o comportamento do HMAC é diferente. Aqui falamos de entrega de webhook HTTPS pura.

Configuração passo a passo

  1. Inicie seu app localmente na porta que você usa (3000 é comum).
  2. Rode npx portpreview 3000 e copie a URL HTTPS que ele imprime.
  3. No painel de Partners do Shopify, abra seu app e vá em Configuration → Webhooks.
  4. Defina o endpoint do webhook como https://your-tunnel.portpreview.dev/api/webhooks/shopify (ou o path que seu app usa).
  5. Instale o app numa loja dev, depois dispare um evento — crie um pedido rascunho, faça fulfillment de um item, mude o estoque.
  6. Veja a requisição aterrissar. Inspecione cabeçalhos e corpo. Faça replay da requisição capturada após cada correção do handler.

Os erros que continuamos vendo

Corpo parseado antes da verificação

Se você colocar app.use(express.json()) antes da sua rota de webhook, os bytes crus já se foram quando você tenta verificar. Monte um parser de corpo cru só no path do webhook, ou puxe o corpo manualmente do stream da requisição antes de qualquer parsing JSON.

Segredos trocados

O segredo do app de Partners não é o mesmo que o token da Storefront API. O HMAC do webhook usa o segredo do app. Se você está olhando para shp_xxx no seu arquivo env, pegou o errado.

Eventos de teste que parecem idênticos

O botão "Send test notification" do Shopify entrega um evento sintético com corpo de placeholder. A assinatura desse evento de teste é real, mas o payload é fixo. Para testes realistas de formato de payload, dispare eventos da própria loja dev.

Replay não é negociável para dev de app

O Shopify tenta novamente webhooks falhos por até 48 horas com backoff exponencial. É generoso, mas quando você itera, não quer esperar a próxima tentativa. Capture a primeira entrega no histórico de requisições do seu túnel e faça replay sob demanda contra seu handler local.

Quando o local deixa de bastar

Fluxos de registro de loja, webhooks de GDPR e mudanças de cobrança de assinatura são mais fáceis de validar contra um ambiente implantado porque interagem com o próprio estado de conta do Shopify. Para todo o resto — parsing de payload, verificação de assinatura, lógica de negócio — local é mais rápido.

Para a matemática de assinatura subjacente em todos os provedores, leia nosso guia de verificação de assinatura de webhooks. Entre na lista de espera do PortPreview se quiser um túnel com replay embutido.

Perguntas frequentes

Como testo webhooks do Shopify no localhost?
Inicie um túnel como o PortPreview, obtenha uma URL HTTPS pública e registre-a como endpoint de webhook no painel de Partners do Shopify. Instale seu app numa loja de desenvolvimento, dispare eventos, e as entregas chegam ao seu handler local.
Por que minha verificação HMAC do Shopify sempre falha?
Na maioria das vezes é a codificação. O Shopify assina com HMAC SHA-256 e codifica o resultado em base64. Se seu código usa .digest('hex') em vez de .digest('base64'), toda comparação falha, mesmo com o segredo correto.
Preciso do shopify app dev ou posso usar qualquer túnel?
Qualquer túnel funciona. O shopify app dev inclui o próprio túnel por conveniência, mas um túnel de propósito geral com captura e replay de requisições é mais útil para o desenvolvimento sustentado.