1

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.

Webhooks da SnapEX Pay

O fluxo

  1. 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).
  2. O cliente escolhe a moeda na página de pagamento: USDT nas redes TRC20, ERC20 e BEP20 ou uma de mais de 50 moedas.
  3. Quando o valor total chega, a fatura passa para paid, o dinheiro entra no saldo do comerciante e sai um webhook invoice.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-Id já 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.

Carregando publicação patrocinada...