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 evento | Descrição |
|---|---|
charge.update | Mudança no status de pagamento |
antifraud.update | Mudança de status do Antifraude |
chargeback | Notificação de chargeback |
recurrence.cycle | Novo ciclo de recorrência cobrado |
Estrutura do payload
| Parâmetros | Descrição | Tipo |
|---|---|---|
event | Evento que chamou o webhook | Ver a tabela de eventos |
data | Dados atualizados da cobrança | Mesmo 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çalho | Descrição |
|---|---|
Content-Type | Sempre application/json |
X-Webhook-Signature | Assinatura HMAC SHA-256 do payload, em hexadecimal (64 caracteres) |
X-Webhook-Timestamp | Instante do envio, em milissegundos desde a época Unix |
X-Webhook-Nonce | Identificador ú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.
Pagamentos Recorrentes
Monte uma assinatura: a primeira cobrança sai na hora e, a partir daí, a PayZu cobra o cartão do cliente sozinha a cada ciclo, mensal ou anual.
Status e motivos da transação
Todos os estados por que uma cobrança passa e o motivo por trás de cada resultado, com o lembrete de programar pelos códigos estáveis, e não pelo texto que pode mudar.