Pular para o conteúdo principal

Webhook

Os webhooks permitem que seu sistema receba notificações automáticas em tempo real do PayerScan. O sistema envia requisições POST para a callback_url fornecida pelo Merchant ao criar uma fatura.

Tipos de Eventos

O PayerScan possui 2 eventos principais retornados no campo status:

  1. completed — Pagamento bem-sucedido: Uma transação correspondente foi confirmada com a fatura (blockchain ou Binance Pay).
  2. expired — Fatura expirada: A fatura excedeu o tempo de espera para pagamento.

Os comerciantes precisam expor uma URL pública (HTTPS recomendado), receber requisições POST, processar o payload e retornar HTTP 2xx dentro do timeout especificado (ex.: 10 segundos).


Estrutura do Payload

1. Webhook em pagamento bem-sucedido (status: completed)

Quando uma transação correspondente é encontrada (endereço correto, valor correto, dentro da janela de tempo), o sistema atualiza a fatura para completed e envia um POST para callback_url com este payload:

Payload (JSON body)

{
"merchant_id": "MERCHANT_001",
"api_key": "YOUR_API_KEY",
"request_id": "order-1234",
"trans_id": "TID-ABC123DEF4567890",
"status": "completed",
"amount": "100",
"token_symbol": "USDT",
"token_price": "1",
"token_amount": "100.0026",
"network_symbol": "BSC",
"from_address": "0x8894e0a0c962cb723c1976a4421c95949be2d4e3",
"to_address": "0xc221460115e2CfCa5bF089A7e647b11cb9631efE",
"transaction_hash": "0xd346b32b83b35376d42a2464598fbf565fffb39e6569200f034a5e8342c532d7"
}
CampoDescrição
merchant_idCódigo do comerciante da loja.
api_keyAPI Key (para sua verificação cruzada, se necessário).
request_idSeu ID de pedido (para comparar com sua fatura interna).
trans_idCódigo da fatura. Use para atualizar o pedido correspondente.
statusSempre "completed".
amountValor em USD (string).
token_symbolMoeda/Token (ex.: USDT).
token_priceTaxa de câmbio no momento da transação (string). Pode usar amount como fallback se indisponível.
token_amountQuantidade real de crypto transferida (string). Pode ser null em casos raros.
network_symbolRede (ex.: BSC, TRC20, ETH, SOL, BINANCE_ID, OKX_UID, BYBIT_UID, ...).
from_addressEndereço da carteira do remetente.
to_addressEndereço da carteira de recebimento (da loja).
transaction_hashHash da transação na blockchain.

Requisitos do Merchant

  1. Receba o POST no endpoint correspondente ao callback_url.
  2. Analise o JSON body.
  3. Verifique (opcional, mas recomendado): compare merchant_id ou trans_id com seus dados internos.
  4. Atualize o pedido: marque o pedido correspondente a trans_id ou request_id como pago; salve transaction_hash se necessário.
  5. Retorne HTTP 2xx (ex.: 200 OK) dentro do timeout (ex.: 10 segundos). O corpo da resposta não é obrigatório; o sistema precisa apenas de um status 2xx para considerar o webhook bem-sucedido.

Se você retornar 4xx/5xx ou timeout, o sistema marca o callback como failed e possui um mecanismo de retentativa (o número de tentativas e intervalos dependem da configuração do backend).


2. Webhook na expiração da fatura (status: expired)

Quando uma fatura muda para o estado expired (ultrapassando expires_at), o sistema envia um POST para callback_url com um payload mais simples:

Payload (JSON body)

{
"merchant_id": "MERCHANT_001",
"request_id": "order-1234",
"trans_id": "TID-ABC123DEF4567890",
"status": "expired",
"amount": "100"
}
CampoDescrição
merchant_idCódigo do comerciante da loja.
request_idSeu ID de pedido (se fornecido ao criar a fatura).
trans_idCódigo da fatura. Use para atualizar o pedido correspondente.
statusSempre "expired".
amountValor em USD (string).

Você pode usar esta informação para atualizar o pedido como "expirado", cancelar reservas, etc. Você ainda deve retornar HTTP 2xx para que o sistema não trate como erro.


3. Exemplo de Handler de Webhook (Node.js)

app.post('/webhook/payment', express.json(), async (req, res) => {
const { merchant_id, api_key, trans_id, request_id, status, amount, transaction_hash } = req.body;

// Verificar merchant_id (completed e expired)
if (merchant_id !== process.env.PAYERSCAN_MERCHANT_ID) {
return res.status(401).send('Unauthorized');
}

// Verificar api_key (apenas completed — expired não inclui api_key)
if (status === 'completed' && api_key !== process.env.PAYERSCAN_API_KEY) {
return res.status(401).send('Unauthorized');
}

// Encontrar pedido por request_id ou trans_id
const order = await orderService.findByRequestId(request_id) || await orderService.findByTransId(trans_id);
if (!order) {
return res.status(404).send('Order not found');
}

if (status === 'completed') {
await orderService.markPaid(order, { transaction_hash, amount });
} else if (status === 'expired') {
await orderService.markExpired(order);
}

res.status(200).send('OK');
});

Verificação de Assinatura

Para garantir que os webhooks realmente vêm do PayerScan, você deve verificar a assinatura ou fazer a verificação cruzada dos dados.

  • HTTPS: Sempre use HTTPS para callback_url para evitar interceptação.
  • Verificação da API Key (apenas completed): O webhook completed inclui sua api_key no payload. Compare-a com sua API key armazenada para verificar a autenticidade. Nota: o webhook expired não inclui api_key.

Idempotência

Um trans_id pode receber apenas um evento completed, mas as retentativas podem reenviá-lo. Garanta que sua lógica de processamento seja idempotente (atualizar múltiplas vezes é seguro, sem crédito duplicado).

Retentativa em caso de falha do Webhook

Quando o servidor do Merchant retorna um erro (4xx/5xx) ou timeout, o sistema retenta o webhook seguindo este cronograma:

TentativaMomento
Tentativa 1Imediatamente (quando a fatura é concluída)
Tentativa 2Após 10 segundos
Tentativa 3Após 30 segundos
Tentativa 4Após 30 segundos
Tentativa 5Após 60 segundos

Máximo de 5 tentativas. Após 5 falhas, callback_status muda para failed e nenhuma tentativa adicional é feita.

Os comerciantes devem garantir que seu endpoint seja estável e retorne 2xx rapidamente (< 10 segundos) para evitar retentativas desnecessárias.