Pitch: como confirmamos pagamentos em cripto em módulos de WooCommerce e WHMCS sem perder nenhum webhook
Sou da equipe da SnapEX Pay, um gateway de pagamento cripto para lojas virtuais, empresas de hospedagem e bots de VPN. Neste pitch quero mostrar a parte técnica que deu mais trabalho: como garantir que uma fatura paga vire um pedido pago — uma única vez — mesmo quando o webhook se perde, chega duplicado ou fora de ordem.

O fluxo
- A loja cria uma fatura pela API (valor em USD, de US$ 0,50 a US$ 100.000; por padrão a fatura vale 30 minutos).
- O cliente escolhe a moeda na página de pagamento: USDT nas redes TRC20, ERC20 e BEP20 ou uma de mais de 50 moedas.
- Quando o valor total chega, a fatura passa para
paid, o dinheiro entra no saldo do comerciante e sai um webhookinvoice.paid.
Parece simples, mas cada passo tem um caso de borda.
Webhook assinado
Toda requisição traz Webhook-Id, Webhook-Timestamp e Webhook-Signature. A assinatura é v1= + HMAC-SHA256 em hex de {timestamp}.{corpo bruto}, com a chave secreta whsec_… que o painel mostra uma única vez.
$body = file_get_contents('php://input'); // corpo bruto, antes do json_decode
$ts = $_SERVER['HTTP_WEBHOOK_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_WEBHOOK_SIGNATURE'] ?? '';
$expected = 'v1=' . hash_hmac('sha256', $ts . '.' . $body, getenv('SNAPEX_WEBHOOK_SECRET'));
if (!hash_equals($expected, $sig) || abs(time() - (int) $ts) > 300) {
http_response_code(400);
exit;
}
Três detalhes que costumam quebrar a verificação: usar o JSON já decodificado (serializado de novo, ele não bate), comparar com == em vez de comparação em tempo constante e não rejeitar timestamps antigos (abre espaço para replay).
Novas tentativas e duplicatas
- Sucesso é qualquer 2xx. Até 5 tentativas por evento: a primeira e depois novas após ~10 s, 1 min, 5 min e 15 min.
- A entrega é at-least-once: o mesmo evento pode chegar duas vezes. O handler guarda os
Webhook-Idjá processados. - Os eventos podem chegar fora de ordem, então o handler compara o status do evento com o que já tem, em vez de confiar na sequência.
- O painel guarda as entregas recentes e tem o botão “Reenviar”, que manda o evento de novo com o mesmo
Webhook-Id.
Por que não confiar só no webhook
Nos módulos prontos (WooCommerce, WHMCS, BILLmanager) o pedido é marcado como pago por três caminhos independentes: o webhook assinado, o retorno do cliente da página de pagamento e uma verificação periódica das faturas recentes não pagas. Nos três casos o módulo consulta a fatura de novo na API (GET /invoices/{id}) antes de creditar. Resultado: um webhook perdido nunca deixa um pedido pago como não pago, e um pedido nunca é concluído duas vezes.
Pagamento a menor e pagamento atrasado
Cripto não tem estorno, então os casos “estranhos” precisam de regra clara:
- chegou menos que o valor →
invoice.underpaid, e o mesmo endereço aguarda o restante; - o prazo para completar acabou →
invoice.partially_paid, a parte recebida é creditada e o módulo deixa o pedido para revisão; - o cliente pagou depois que a fatura expirou → o pagamento atrasado também é creditado;
- chegou uma segunda transferência numa fatura já paga →
invoice.extra_payment, creditado à parte.
Testar sem dinheiro real
Não há chaves separadas de teste: a fatura recebe "environment": "test", passa pelos mesmos status e envia os mesmos webhooks (com data.environment = "test"), mas se confirma sozinha e não mexe no saldo. Nos módulos isso é o “modo de teste”: a forma de pagamento de teste aparece só para administradores ou para um cliente de teste.
Números
Taxa de 1% por pagamento, sem mensalidade. São 13 módulos prontos — entre eles WHMCS, Blesta, BILLmanager, WooCommerce, CS-Cart, FOSSBilling, Paymenter e Remnawave — e uma API REST para o resto.
Guia completo de webhooks (com versão em Node.js): https://pay.snapex.pro/guides/webhooks-pagamentos-cripto-assinatura-tentativas?lang=pt&utm_source=tabnews&utm_medium=post&utm_campaign=pitch-webhooks
Site: https://pay.snapex.pro/?lang=pt&utm_source=tabnews&utm_medium=post&utm_campaign=pitch-webhooks
Fico à disposição para responder dúvidas sobre a implementação nos comentários.