Integração de API de pagamento cripto: cobranças, assinaturas e webhooks

Um guia prático para desenvolvedores: uma requisição assinada para criar a cobrança, um endpoint de webhook para receber o status e os detalhes de segurança que tornam tudo confiável.

Atualizado: 6 min de leitura
Integração de API de pagamento cripto: requisição POST assinada criando uma cobrança e resposta de webhook

Uma integração de API de pagamento cripto precisa de duas peças: uma requisição do lado do servidor que cria uma cobrança (invoice) para cada pedido e devolve uma URL de checkout, e um endpoint de webhook que recebe atualizações de status assinadas quando o pagamento é detectado e confirmado na blockchain. Com o mistKET, as duas são HTTPS e JSON simples, assinados com HMAC-SHA256 — sem precisar de SDK.

Este guia percorre o fluxo completo com código e depois cobre os detalhes de segurança e confiabilidade que separam uma demo de um sistema em produção.

A arquitetura de uma API de pagamento cripto em uma imagem

Toda integração de API de pagamento cripto segue o mesmo ciclo de cinco etapas:

  1. Pedido criado — seu backend chama POST /api/v1/invoices com valor, moeda, ID do pedido e URL de callback.
  2. Redirecionamento — você envia o cliente para a checkout_url retornada.
  3. Pagamento — o cliente escolhe uma rede e paga na página hospedada; a cotação fica travada durante a validade da cobrança.
  4. Webhook — o mistKET faz um POST de invoice.paid, invoice.confirmed ou de um evento de exceção para a sua URL de callback.
  5. Entrega — você verifica a assinatura, marca o pedido como pago e entrega.

Sua API key, sua API secret e sua webhook secret são emitidas no painel do lojista depois que sua conta é aprovada. A referência completa fica no painel, na seção API.

Etapa 1: assine e envie a requisição da cobrança

Toda requisição à API leva três cabeçalhos. A assinatura é o HMAC-SHA256 em hexadecimal da string {timestamp}.{raw body} usando sua API secret:

POST /api/v1/invoices
X-Api-Key: mp_live_…
X-Api-Timestamp: 1760000000
X-Api-Signature: hex(hmac_sha256("{ts}.{body}", secret))

{
  "amount": "184.50",
  "currency": "USD",
  "order_id": "2041",
  "callback_url": "https://yourstore.com/webhooks/mistket"
}

Uma versão mínima em PHP fica assim:

$body = json_encode([
  'amount' => '184.50', 'currency' => 'USD',
  'order_id' => '2041',
  'callback_url' => 'https://yourstore.com/webhooks/mistket',
]);
$ts  = (string) time();
$sig = hash_hmac('sha256', $ts . '.' . $body, $apiSecret);
// envie $body com X-Api-Key, X-Api-Timestamp, X-Api-Signature

Assine exatamente os bytes que você envia. Recodificar o JSON depois de assinar (espaçamento ou ordem de chaves diferentes) é a causa número um de erros de assinatura.

Etapa 2: redirecione para o checkout hospedado

Uma chamada bem-sucedida retorna 201 Created com o UUID da cobrança, seu status (new) e uma checkout_url. Salve o UUID junto com o pedido e redirecione o cliente. A página hospedada mostra o valor exato, um QR code, uma contagem regressiva e o status ao vivo, e funciona no celular. O cliente escolhe a rede ali — USDT na TRON, BNB Smart Chain, Ethereum ou Arbitrum, além de TRX, BNB e ETH — a menos que você fixe a rede na chamada da API.

Além de criar cobranças, a API permite listar, consultar e cancelar cobranças, o que é útil quando um carrinho muda ou um pedido é abandonado.

Etapa 3: receba e verifique os webhooks de pagamento cripto

Quando o status muda, o mistKET faz um POST com JSON para a sua URL de callback com estes cabeçalhos:

CabeçalhoFunção
X-Mistket-EventNome do evento, ex.: invoice.confirmed
X-Mistket-TimestampHorário Unix usado na assinatura
X-Mistket-Signaturesha256= + HMAC-SHA256 de {timestamp}.{raw body} com sua webhook secret
X-Mistket-DeliveryID da entrega, útil para logs

Verificação em PHP:

$raw = file_get_contents('php://input');
$ts  = $_SERVER['HTTP_X_MISTKET_TIMESTAMP'] ?? '';
$got = $_SERVER['HTTP_X_MISTKET_SIGNATURE'] ?? '';
$exp = 'sha256=' . hash_hmac('sha256', $ts . '.' . $raw, $webhookSecret);
if (!hash_equals($exp, $got) || abs(time() - (int) $ts) > 300) {
  http_response_code(401); exit;
}
$event = json_decode($raw, true);

Use uma comparação em tempo constante (hash_equals no PHP, hmac.compare_digest no Python, crypto.timingSafeEqual no Node.js) e rejeite timestamps antigos para bloquear ataques de repetição. Para entender o fundamento, veja a RFC 2104 (HMAC).

Ponto-chave: a fonte da verdade é o webhook — não o navegador do cliente voltando para o seu site. Nunca marque um pedido como pago sem uma assinatura válida.

Exemplo de payload de webhook

Um pagamento confirmado chega como um documento JSON compacto. Os campos que você mais vai usar são o evento, o ID do pedido, o status e os valores esperado e recebido:

POST https://yourstore.com/webhooks/mistket
X-Mistket-Event: invoice.confirmed
X-Mistket-Signature: sha256=…

{ "event": "invoice.confirmed", "data": {
  "order_id": "2041", "status": "confirmed",
  "pay_amount": "184.5", "paid_amount": "184.5",
  "pay_currency": "USDT", "network": "TRC20" } }

Salve pay_currency e network junto com o pedido. Eles ajudam na contabilidade e em conversas com o suporte mais tarde.

Verificação em Node.js e Python

A mesma checagem em outras duas linguagens populares:

// Node.js (Express com express.raw nesta rota)
const exp = 'sha256=' + crypto.createHmac('sha256', secret)
  .update(ts + '.' + rawBody).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(exp), Buffer.from(sig));

# Python
exp = 'sha256=' + hmac.new(secret.encode(), f'{ts}.{raw}'.encode(),
                           hashlib.sha256).hexdigest()
ok = hmac.compare_digest(exp, sig)

Nos dois casos, garanta que você está lendo o corpo bruto da requisição. Muitos frameworks fazem o parse do JSON automaticamente; depois de parseado e serializado de novo, os bytes não batem mais com a assinatura.

Testando localmente

Webhooks precisam de uma URL HTTPS pública, então um notebook local não consegue recebê-los diretamente. Há duas opções práticas: publicar cedo o handler em um servidor de homologação ou expor temporariamente sua porta local com uma ferramenta de túnel. Crie cobranças pequenas apontando para o callback de homologação e confira os logs de entrega no painel do lojista para ver exatamente o que foi enviado e o que seu servidor respondeu.

Tente disparar cada tipo de evento pelo menos uma vez durante os testes: paid, confirmed, underpaid e expired. Quando cada ramo do seu handler tiver rodado com um webhook real, sobra muito pouco para te surpreender em produção, e você saberá exatamente como ficam os logs de entrega quando algo der errado.

Etapa 4: trate cada status

O payload do webhook inclui o ID do pedido, o status, o valor esperado (pay_amount), o valor recebido (paid_amount), a moeda e a rede. Associe cada evento a uma ação:

EventoSignificadoAção típica
invoice.paidTransferência detectada, confirmações pendentesMostrar "pagamento recebido, confirmando"
invoice.confirmedConfirmações necessárias atingidasMarcar como pago e entregar
invoice.underpaidAbaixo da sua tolerânciaAguardar complemento ou falar com o cliente
invoice.overpaidMais do que o esperadoEntregar; decidir o que fazer com a diferença
invoice.expiredNenhum pagamento válido no prazoLiberar estoque, manter o pedido não pago

Tolerância de pagamento a menor, pagamentos parciais que somam o total e pagamentos atrasados dentro de um período de carência são tratados pelo mistKET; seu código só reage ao resultado final.

Confiabilidade: reenvios e idempotência

Se o seu endpoint estiver fora do ar ou retornar erro, os webhooks são reenviados com back-off por horas. Ou seja, às vezes você vai receber o mesmo evento duas vezes. Deixe seu handler idempotente pelo ID do pedido:

  • Responda rápido com um 2xx e processe o trabalho pesado em uma fila.
  • Antes de entregar, verifique se o pedido já está marcado como pago.
  • Registre o ID da entrega e o evento para rastrear problemas; o painel também mostra os logs de entrega dos webhooks.

Checklist de segurança

Uma integração de API de pagamento cripto lida com dinheiro, então trate-a como qualquer outro código de pagamento:

  • Mantenha a API secret e a webhook secret só no servidor — nunca no front-end nem em apps móveis.
  • Restrinja o acesso à API com a lista de IPs permitidos do painel.
  • Rejeite requisições fora de uma janela curta de timestamp.
  • Configure a expiração das cobranças de acordo com sua lógica de reserva de estoque.
  • Trate valores como strings ou decimais, nunca como floats.
Dica: antes de ir para produção, pague você mesmo uma cobrança pequena, envie de propósito um valor a menor para ver o fluxo de pagamento insuficiente e deixe uma cobrança expirar. Você terá testado todos os ramos em menos de uma hora.

Próximos passos

Essa é a integração completa de uma API de pagamento cripto: uma requisição assinada, um webhook verificado e um mapa de status. Para a visão não técnica do mesmo fluxo, leia como aceitar pagamentos em cripto no seu site. Está criando um bot em vez de um site? Veja como aceitar pagamentos cripto em um bot do Telegram. Peça suas chaves de API no Telegram @mistnetwork, confira o exemplo na seção para desenvolvedores e gerencie as chaves no login do lojista.

Perguntas frequentes

Preciso de um SDK para integrar uma API de pagamento cripto?
Com o mistKET, não. Ele usa HTTPS e JSON simples; qualquer linguagem que faça uma requisição HTTP e calcule um HMAC-SHA256 consegue integrar.
Como as requisições à API do mistKET são autenticadas?
Cada requisição envia X-Api-Key, X-Api-Timestamp e X-Api-Signature, sendo a assinatura o HMAC-SHA256 em hexadecimal de '{timestamp}.{raw body}' com sua API secret.
Como verifico um webhook de pagamento cripto?
Recalcule o HMAC-SHA256 de '{timestamp}.{raw body}' com sua webhook secret, compare com X-Mistket-Signature em tempo constante e rejeite timestamps antigos.
E se meu servidor perder um webhook?
Os webhooks são reenviados com back-off por horas, e você também pode consultar o status da cobrança pela API. Deixe seu handler idempotente pelo ID do pedido.
Posso forçar uma rede específica pela API?
Sim. Você pode fixar a rede na chamada da API; caso contrário, o cliente escolhe na página de pagamento hospedada.
Quais eventos devem liberar a entrega do pedido?
Entregue em invoice.confirmed. Use invoice.paid só para mostrar o andamento e trate underpaid, overpaid e expired conforme sua política.

Artigos relacionados