Skip to main content
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 Redis (BullMQ):
  1. Um evento ocorre na plataforma (ex: pagamento confirmado)
  2. O evento é adicionado à fila de webhooks
  3. Um worker processa a fila e envia o POST para cada endpoint inscrito
  4. Se a entrega falhar, o retry automático é acionado
Evento → Fila (Redis/BullMQ) → Worker → 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

A assinatura é calculada sobre a string: {webhook_id}.{timestamp}.{payload_json}
import crypto from 'crypto';
import express from 'express';

const app = express();

// IMPORTANTE: Use raw body para verificação
app.post('/webhooks/chargefy', express.raw({ type: 'application/json' }), (req, res) => {
  const webhookId = req.headers['webhook-id'] as string;
  const timestamp = req.headers['webhook-timestamp'] as string;
  const signature = req.headers['webhook-signature'] as string;

  const secret = process.env.CHARGEFY_WEBHOOK_SECRET!;

  // Verificar timestamp (tolerância de 5 minutos)
  const now = Math.floor(Date.now() / 1000);
  const webhookTimestamp = parseInt(timestamp, 10);
  if (Math.abs(now - webhookTimestamp) > 300) {
    return res.status(401).json({ error: 'Timestamp expired' });
  }

  // Calcular assinatura esperada
  const payload = req.body.toString();
  const signedContent = `${webhookId}.${timestamp}.${payload}`;

  // O secret pode ter prefixo "whsec_" que deve ser removido
  const secretBytes = Buffer.from(
    secret.startsWith('whsec_') ? secret.slice(6) : secret,
    'base64'
  );

  const expectedSignature = crypto
    .createHmac('sha256', secretBytes)
    .update(signedContent)
    .digest('base64');

  // Comparação segura contra timing attacks
  const signatures = signature.split(' ');
  const isValid = signatures.some(sig => {
    const sigValue = sig.startsWith('v1,') ? sig.slice(3) : sig;
    return crypto.timingSafeEqual(
      Buffer.from(expectedSignature),
      Buffer.from(sigValue)
    );
  });

  if (!isValid) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Assinatura válida — processar evento
  const event = JSON.parse(payload);
  console.log(`Evento recebido: ${event.type}`);

  switch (event.type) {
    case 'subscription.active':
      // Liberar acesso, enviar email, etc.
      handleSubscriptionActive(event.data);
      break;
    case 'subscription.canceled':
      // Revogar acessos
      handleSubscriptionCanceled(event.data);
      break;
    // ... outros eventos
  }

  // Responder com 200 rapidamente
  res.status(200).json({ received: true });
});
Sempre use express.raw() ou equivalente 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.

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