PayZuDocs

Webhooks

Em vez de ficar perguntando se a cobrança pagou, a PayZu avisa o seu sistema sozinha quando o status muda, o antifraude decide, entra um chargeback ou começa um novo ciclo de recorrência. Você configura o recebimento, valida a assinatura e trata as retentativas.

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.updateMudança de status do Antifraude
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