PayZuDocs

Webhooks

Notificações enviadas ao seu sistema quando o status da cobrança muda.

Em vez do seu sistema ficar perguntando "já pagou?", a PayZu chama você quando algo acontece: mudança de status da cobrança, atualização do antifraude, chargeback ou um novo ciclo de recorrência.

Como configurar

Informe a postbackUrl na criação da cobrança (POST /charges). Sempre que houver um evento, a PayZu envia uma requisição POST em JSON para essa URL.

Eventos

Tipos de eventoDescrição
charge.updateMudança no status de pagamento
antifraud.updateConclusão da análise antifraude, refletida em status e reasonCode da cobrança
chargebackNotificação de chargeback
recurrence.cycleNovo ciclo de recorrência cobrado

Estrutura do payload

ParâmetrosDescriçãoTipo
eventEvento que chamou o webhookVer a tabela de eventos
dataDados atualizados da cobrançaMesmo valor retornado por Consultar cobrança
{
  "event": "charge.update",
  "data": {}
}

O objeto data tem exatamente o mesmo formato da resposta de Consultar cobrança.

Cabeçalhos da requisição

Cada POST chega com os seguintes cabeçalhos:

CabeçalhoDescrição
Content-TypeSempre application/json
X-Webhook-SignatureAssinatura HMAC SHA-256 do payload, em hexadecimal (64 caracteres)
X-Webhook-TimestampInstante do envio, em milissegundos desde a época Unix
X-Webhook-NonceIdentificador único da requisição (32 caracteres hexadecimais)

Nomes de cabeçalho HTTP não diferenciam maiúsculas de minúsculas: dependendo do framework, eles chegam normalizados como x-webhook-signature, x-webhook-timestamp e x-webhook-nonce.

Retentativas

O primeiro envio acontece assim que o evento ocorre. A entrega só é considerada bem-sucedida se a sua URL responder com um status HTTP 2xx em até 5 segundos: qualquer outro status, ou uma resposta mais lenta que isso, conta como falha.

Depois de uma falha, o webhook faz até 5 retentativas. A cada falha, o tempo até a próxima tentativa aumenta: as retentativas são feitas, respectivamente, depois de 1 minuto, 10 minutos, 1 hora, 6 horas e 24 horas. Depois disso, as tentativas param.

Responda o webhook rapidamente (um 200 simples basta) e processe o payload de forma assíncrona, para não estourar o limite de 5 segundos. Como um timeout pode gerar reenvio de um evento que você já processou, o consumo precisa ser idempotente: use o id da cobrança combinado com a transição de status como chave de deduplicação. Não use o X-Webhook-Nonce para isso, ele identifica a requisição HTTP e muda a cada reenvio.

Verificação HMAC

Cada webhook é assinado com o seu webhook secret, fornecido pela PayZu junto com as suas credenciais de API. Sua API deve validar a assinatura antes de processar o payload:

Extraia os cabeçalhos x-webhook-timestamp, x-webhook-nonce e x-webhook-signature.

Concatene os valores do timestamp, do nonce e do payload, separados por ., formando a string base de verificação: timestamp.nonce.payload.

Gere uma assinatura HMAC com o algoritmo SHA-256 a partir dessa string, usando o seu webhook secret.

Compare a assinatura gerada com o valor do cabeçalho x-webhook-signature. Se não coincidirem, rejeite o webhook.

Exemplo em Node.js, usando crypto.timingSafeEqual para comparar as assinaturas em tempo constante:

const crypto = require("node:crypto");

function verifyWebhookSignature(request, webhookSecret) {
  const timestamp = request.headers["x-webhook-timestamp"];
  const nonce = request.headers["x-webhook-nonce"];
  const signature = request.headers["x-webhook-signature"];

  if (typeof signature !== "string" || !/^[0-9a-f]{64}$/i.test(signature)) {
    return false;
  }

  const baseString = `${timestamp}.${nonce}.${request.rawBody}`;
  const expectedSignature = crypto
    .createHmac("sha256", webhookSecret)
    .update(baseString)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature, "hex"),
    Buffer.from(signature, "hex"),
  );
}

Calcule o HMAC sobre o corpo bruto da requisição (raw body), exatamente como recebido, antes de qualquer parse de JSON.

Verificação do nonce (opcional)

O valor do cabeçalho x-webhook-nonce atua como identificador único e temporário de cada requisição. Após extraí-lo, verifique se esse nonce já foi registrado antes:

  • Se o valor já tiver sido utilizado, rejeite a requisição para mitigar ataques de repetição (replay attacks).
  • Se o nonce for novo, armazene-o como utilizado, garantindo que não possa ser reaproveitado em chamadas futuras.

Verificação do timestamp (opcional)

O valor do cabeçalho x-webhook-timestamp é o instante do envio em milissegundos desde a época Unix. Compare-o com o horário atual: se a diferença for superior a 5 minutos, rejeite a requisição. Essa validação descarta webhooks expirados, evitando o processamento de mensagens antigas ou potencialmente maliciosas.

Nesta página