Webhooks
Documentação da API · Zypher Global
Enviaremos um POST para o seu webhook_url sempre que uma transação mudar de status. Aqui está TUDO sobre webhooks, cobrança dinâmica, chave/CLABE fixa (valor aberto), cash-out e disputas (MED), nas moedas habilitadas.
Segurança: valide a assinatura (HMAC)
Todo POST que enviamos é assinado com HMAC-SHA256 usando o seu client_secret (o mesmo da API). Valide a assinatura ANTES de confiar no evento: assim ninguém consegue forjar um "pagamento confirmado" no seu endpoint, mesmo sabendo a sua webhook_url.
X-Webhook-Signature— HMAC-SHA256 do CORPO cru (compatível com integrações antigas).X-Webhook-Timestamp— horário do envio (Unix, em segundos).X-Webhook-Signature-V2— HMAC-SHA256 de"{timestamp}.{corpo}"— recomendado: autentica E impede replay (reenvio de um webhook capturado).
X-Webhook-Signature-V2 e rejeite timestamp com mais de 5 minutos. Assine sempre sobre o CORPO CRU recebido, nunca sobre o JSON re-serializado. Contas sem client_secret não recebem assinatura.$raw = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE_V2'] ?? '';
$secret = 'SEU_CLIENT_SECRET';
if (abs(time() - (int)$ts) > 300) { http_response_code(400); exit; } // anti-replay (5 min)
$calc = hash_hmac('sha256', $ts . '.' . $raw, $secret);
if (!hash_equals($calc, $sig)) { http_response_code(401); exit; } // forjado -> rejeita
// ok: webhook autentico -> processe $raw
const crypto = require('crypto');
// use o CORPO CRU (ex.: express.raw())
const raw = req.body; // Buffer/string cru, NAO o JSON ja parseado
const ts = req.headers['x-webhook-timestamp'] || '';
const sig = req.headers['x-webhook-signature-v2'] || '';
const secret = 'SEU_CLIENT_SECRET';
if (Math.abs(Date.now()/1000 - Number(ts)) > 300) return res.sendStatus(400);
const calc = crypto.createHmac('sha256', secret).update(ts + '.' + raw).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(calc), Buffer.from(sig))) return res.sendStatus(401);
// ok: webhook autentico
Campos comuns
type—cashin|cashout.status—confirmed·completed·med·failed.currency— moeda da transação (BRL·MXN·USDT).transaction_id,e2e— identificadores; reconcilie peloe2e(único por pagamento).external_id— o seu identificador, quando você o enviou na criação.origin— presente e igual a"static"quando o pagamento caiu numa CHAVE/CLABE FIXA (valor aberto). Ausente = cobrança dinâmica (por valor).
provider vem sempre com o nome da plataforma. Sempre trate o resultado por status, não pela mensagem.Cash In (confirmado)
BRL · PIX
{ "type":"cashin", "status":"confirmed", "amount":123.45, "fee":1.23, "currency":"BRL", "request_number":"pr_...", "transaction_id":"pr_...", "e2e":"E...", "provider":"Zypher Global", "updated_at":"2025-11-24 12:00:00" }
MXN · SPEI
{ "type":"cashin", "status":"confirmed", "amount":500.00, "fee":5.00, "currency":"MXN", "request_number":"pr_...", "transaction_id":"pr_...", "e2e":"E...", "provider":"Zypher Global", "updated_at":"2025-11-24 12:00:00" }
USDT
{ "type":"cashin", "status":"confirmed", "amount":19.23, "fee":0.58, "currency":"USDT", "transaction_id":"CRYPTO-...", "provider":"Zypher Global", "updated_at":"2025-11-24 12:00:00" }
No BRL→USDT o webhook dispara assim que o PIX confirma, o amount é o USDT ESTIMADO (o valor real é creditado ao final da conversão).
Chave fixa / valor aberto (estática)
Pagamentos numa CHAVE FIXA (o QR PIX fixo em BRL ou a CLABE fixa em MXN) chegam com "origin":"static" e o seu external_id de volta. Como a mesma chave recebe vários pagamentos, cada evento é individual, reconcilie pelo e2e.
{ "type":"cashin", "origin":"static", "status":"confirmed", "amount":100.00, "currency":"MXN", "external_id":"PEDIDO-123", "transaction_id":"PEDIDO-123", "e2e":"E...", "provider":"Zypher Global" }
Mesmo formato para BRL (QR PIX fixo) e MXN (CLABE fixa), só muda o currency.
Cash Out (concluído)
{ "type":"cashout", "status":"completed", "amount":250.00, "fee":2.50, "currency":"BRL", "transaction_id":"pr_...", "external_id":"SEU-ID-123", "e2e":"E...", "beneficiary":{ "name":"JOAO DA SILVA", "document":"***.456.789-**", "bank":"...", "pix_key":"...", "pix_type":"CPF" }, "provider":"Zypher Global", "updated_at":"2025-11-24 12:00:00" }
{ "type":"cashout", "status":"completed", "amount":300.00, "fee":3.00, "currency":"MXN", "transaction_id":"pr_...", "e2e":"E...", "provider":"Zypher Global", "updated_at":"2025-11-24 12:00:00" }
{ "type":"cashout", "status":"completed", "usdt":20.00, "brl":100.00, "fee":0.50, "currency":"USDT", "transaction_id":"pr_...", "e2e":"E...", "provider":"Zypher Global", "updated_at":"2025-11-24 12:00:00" }
Cash Out (falhou / cancelado)
Se um saque já aceito (respondido como pending) NÃO for concluído, recusado pelo banco do recebedor, chave inválida, QR vencido ou sem liquidez a tempo, enviamos um POST com "status":"failed" e o fail_reason. O valor (amount + fee) é estornado ao seu saldo automaticamente; use o webhook para re-creditar ou reprocessar o seu usuário final.
{ "type":"cashout", "status":"failed", "amount":250.00, "fee":2.50, "transaction_id":"pr_...", "external_id":"SEU-ID-123", "e2e":"E...", "fail_reason":"Chave PIX inválida.", "updated_at":"2025-11-24 12:00:00" }
external_id (o mesmo que você enviou no cash-out) vem em TODOS os callbacks de saque, completed e failed, para casar a transação no seu lado. A recusa síncrona (dados inválidos no momento do POST) já volta no HTTP da própria chamada; este webhook failed cobre a falha assíncrona (depois do pending).MED / Disputa (BRL)
No cash-out, beneficiary traz quem recebeu: a chave PIX sempre; nome, documento e banco quando capturados na validação (DICT).
MED (disputa PIX): quando uma entrada já confirmada é contestada pelo pagador, enviamos um POST com "status":"med" no MESMO transaction_id da cobrança original. Use-o para travar ou estornar o pedido do seu lado, o valor líquido volta a ser debitado/retido no seu saldo.
{
"type": "cashin",
"status": "med",
"amount": 123.45,
"fee": 1.23,
"currency": "BRL",
"request_number": "pr_...",
"transaction_id": "pr_...",
"e2e": "E...",
"provider": "Zypher Global",
"updated_at": "2025-11-24 12:00:00",
"med": {
"payer_name": "João da Silva",
"payer_document": "***.456.789-**",
"payer_institution": "ISPB 12345678",
"infraction_type": "FRAUD",
"infraction_status": "OPEN",
"infraction_reason": "Pagador alega não reconhecer a transação",
"reference_id": "PROTOCOL-123",
"reported_by": "PAGADOR"
}
}
O objeto med traz os detalhes da infração (quando a adquirente os fornece); campos podem vir vazios/ausentes, trate como opcionais.
e2e (único por pagamento), a mesma chave/CLABE fixa reenvia o mesmo external_id em pagamentos diferentes. Se o webhook falhar, consulte o estado real em Consultar Transações.