Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.chargefy.io/llms.txt

Use this file to discover all available pages before exploring further.

A Chargefy utiliza um sistema robusto de entrega de webhooks com retry automático, assinatura HMAC e monitoramento de entregas.

Mecanismo de Entrega

Webhooks são processados de forma assíncrona via fila Postgres (pgmq) disparada por pg_cron:
  1. Um evento ocorre na plataforma (ex: pagamento confirmado)
  2. O evento é enfileirado em pgmq (queue webhook_delivery)
  3. Uma Edge Function consome a fila e envia o POST para cada endpoint inscrito
  4. Se a entrega falhar, o retry automático é acionado
Evento → pgmq (Postgres) → Edge Function → POST https://seu-endpoint

                             Falhou? → Retry com backoff exponencial

Configurações de Entrega

ParâmetroValor
Timeout por tentativa20 segundos
Máximo de retries10 tentativas
BackoffExponencial
Retenção de payload30 dias
Após 30 dias, o payload do evento é removido automaticamente (archival), mas os metadados da entrega (status, data, HTTP code) são mantidos.

Retry Automático

Quando seu endpoint retorna um status diferente de 2xx ou não responde dentro de 20 segundos, a Chargefy automaticamente reagenda a entrega com backoff exponencial.
TentativaDelay aproximado
Imediato
~1 minuto
~5 minutos
~30 minutos
~2 horas
~8 horas
7ª-10ªIntervalos crescentes
Após 10 tentativas falhadas, a entrega é marcada como falha permanente. Você pode solicitar reentrega manual via API ou dashboard.

Verificação de Assinatura HMAC

Todos os webhooks são assinados usando HMAC-SHA256 seguindo o padrão Standard Webhooks.

Headers de Assinatura

Cada requisição de webhook inclui os seguintes headers:
HeaderDescriçãoExemplo
webhook-idID único do eventoevt_abc123
webhook-timestampTimestamp Unix (segundos)1710244200
webhook-signatureAssinatura HMAC-SHA256v1,K7gNU3sdo+OL0wNh...
content-typeTipo do conteúdoapplication/json
user-agentAgente da requisiçãochargefy.com webhooks

Corpo JSON (payload)

O corpo é JSON com campos de primeiro nível nesta ordem:
CampoDescrição
typeTipo do evento (ex.: subscription.active)
idUUID do evento — o mesmo valor do header webhook-id e do registro em entregas
timestampISO 8601 do momento em que o evento foi criado
dataObjeto com os dados específicos do evento
Assim você pode persistir um único JSON (por exemplo em fila ou banco) já com o identificador para idempotência, alinhado ao header.
{
  "type": "subscription.active",
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2025-03-18T12:00:00.000Z",
  "data": { }
}
Eventos antigos (antes desta convenção) podem não incluir id no corpo; use sempre o header webhook-id para verificação de assinatura e idempotência.

Como verificar

O secret está no formato canônico whsec_<base64>, então a forma mais simples e segura é usar a biblioteca oficial svix — ela cuida da decodificação do secret, da verificação de timestamp, da comparação constant-time e do suporte a múltiplas assinaturas durante rotação. Existe lib oficial em Node, Python, Ruby, Go, PHP, Java, C# e Rust.
import { Webhook } from 'svix';
import express from 'express';

const app = express();
const wh = new Webhook(process.env.CHARGEFY_WEBHOOK_SECRET!); // whsec_...

// IMPORTANTE: use raw body — qualquer re-serialização quebra a assinatura
app.post('/webhooks/chargefy', express.raw({ type: 'application/json' }), (req, res) => {
  let evt: { type: string; id: string; timestamp: string; data: unknown };
  try {
    evt = wh.verify(req.body, {
      'webhook-id': req.headers['webhook-id'] as string,
      'webhook-timestamp': req.headers['webhook-timestamp'] as string,
      'webhook-signature': req.headers['webhook-signature'] as string,
    }) as typeof evt;
  } catch {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  switch (evt.type) {
    case 'subscription.active':
      handleSubscriptionActive(evt.data);
      break;
    case 'subscription.canceled':
      handleSubscriptionCanceled(evt.data);
      break;
  }

  res.status(200).json({ received: true });
});
Sempre use express.raw() (ou req.text(), request.get_data() etc.) para receber o body como Buffer/string bruta. Se o body for parseado como JSON antes da verificação, a assinatura não vai corresponder porque a serialização pode diferir.
Se preferir verificar manualmente sem a lib svix: decodifique o base64 do secret depois do prefixo whsec_, calcule HMAC-SHA-256(${webhook-id}.${webhook-timestamp}.${raw_body}) com esses bytes como chave, codifique em base64 e compare (constant-time) contra cada assinatura no header (separadas por espaço, prefixo v1,). Aceite apenas timestamps dentro de uma janela de ±5 minutos.

Múltiplas Assinaturas

O header webhook-signature pode conter múltiplas assinaturas separadas por espaço. Isso permite rotação de secrets sem downtime:
webhook-signature: v1,assinatura_antiga v1,assinatura_nova
Sua aplicação deve verificar se qualquer uma das assinaturas é válida.

Boas Práticas

Processe webhooks de forma assíncrona. Responda com 200 imediatamente e processe o evento em background. Se seu handler demorar mais de 20 segundos, a Chargefy considerará a entrega como falhada.
Use o webhook-id (ou o campo id no JSON do corpo, quando presente — mesmo valor) como chave de idempotência. O mesmo evento pode ser entregue mais de uma vez (retry). Garanta que processar o mesmo evento duas vezes não cause efeitos duplicados.
// Exemplo: verificar se já processou
const processed = await redis.get(`webhook:${webhookId}`);
if (processed) {
  return res.status(200).json({ received: true, duplicate: true });
}
await redis.set(`webhook:${webhookId}`, '1', 'EX', 86400);
Nunca processe um webhook sem verificar a assinatura HMAC. Isso protege contra requisições forjadas.
Acompanhe o status das entregas em ConfiguraçõesWebhooksEntregas. Verifique regularmente se há falhas persistentes.
URLs de webhook devem usar HTTPS. A Chargefy não aceita endpoints HTTP.

Desenvolvimento Local

Para testar webhooks em desenvolvimento local, use um tunnel:
# Instalar ngrok
npm install -g ngrok

# Expor porta local
ngrok http 3000

# Use a URL gerada (https://abc123.ngrok.io) como URL do webhook

Troubleshooting

ProblemaCausa ProvávelSolução
Status 401Assinatura inválidaVerifique se está usando o secret correto e raw body
Status 500Erro no seu handlerVerifique logs da sua aplicação
TimeoutHandler muito lentoProcesse em background, responda com 200 imediatamente
Não recebe webhooksURL incorreta ou firewallVerifique URL (HTTPS), teste com curl
Webhook duplicadoRetry após falha anteriorImplemente idempotência com webhook-id

Próximos Passos

Eventos Disponíveis

Lista completa de 27 tipos de eventos

Gerenciar Endpoints

Criar, atualizar e deletar endpoints