Modelo mental: três fronteiras que não podem se misturar
Chamando do browser: o query param key não é opcional
Se alguma chamada sair do navegador (fetch em Client Component, ou uma SPA), inclua a chave publishable como query param além do header:
Authorization não é um header safelisted do CORS: toda requisição com esse header dispara um preflight OPTIONS, e o preflight não carrega Authorization. Sem outro sinal, a API não consegue identificar a loja para checar a allowlist de origens, e responde 403 sem Access-Control-Allow-Origin — o que no console do navegador parece um erro de CORS por origem não cadastrada, mesmo quando a origem está correta. Chamadas server-to-server não têm header Origin, não disparam preflight e não precisam do query param.
Variáveis de ambiente
.env.local
Um cliente de API mínimo
src/lib/huskpay/client.ts — o import 'server-only' no topo faz o build quebrar se algum Client Component importar este arquivo por engano. É a garantia real contra vazamento de chave; organização de pastas sozinha não é suficiente.
src/lib/huskpay/client.ts
Padrões recomendados
Catálogo em Server Component
Busque produtos com
huskpay('/products', { next: { revalidate: 60 } }) diretamente no componente de servidor — sem expor a chave ao cliente.Checkout via Server Action
Receba o carrinho do cliente, gere um
Idempotency-Key (UUID) por tentativa de compra e chame /checkout/sessions no servidor antes de redirecionar.Pedidos com chave secreta
Só use
key: 'secret' em Server Actions ou Route Handlers — nunca em código que pode acabar em um Client Component.Entrega no webhook
Implemente a entrega do produto em uma Route Handler dedicada para o webhook, validando a assinatura antes de processar. Veja Webhooks.
