Como aceitar pagamentos cripto em um site
Passo a passo: crie um pagamento no seu servidor, envie o cliente à página de pagamento, trate o webhook e teste. Com código Node.js funcional.

Para aceitar cripto em um site, você precisa de duas coisas no servidor: uma chamada que cria um pagamento para um pedido e um endpoint que recebe o resultado. Este guia percorre as duas com código funcional e depois trata de testes, casos atípicos e dos erros que custam dinheiro aos lojistas.
O que você precisa antes de começar
Você não precisa rodar um nó de blockchain nem guardar carteiras de clientes. Você precisa de:
- uma conta no gateway com um projeto, uma chave de API e, se quiser definir o método de pagamento, o ID desse método no seu projeto;
- um endpoint HTTPS público no seu servidor para o webhook, salvo no projeto como callback URL;
- um lugar no seu sistema de pedidos para guardar o estado do pagamento, associado pelo seu próprio número de pedido.
Decida uma coisa logo: se o cliente escolhe o ativo e a rede na página de pagamento ou se você os pré-seleciona. Deixar o cliente escolher é mais simples e serve à maioria das lojas. Se você passar um método de pagamento, a página abre com ele já selecionado.
Como todo o fluxo se encaixa
Este é o caminho de um pedido, do seu checkout até o status de pago:
- O cliente escolhe «Pagar com cripto» no checkout e o seu servidor cria um pagamento para esse pedido.
- O gateway devolve um link para a sua página de pagamento hospedada.
- Você redireciona o cliente para essa página. Ele escolhe um ativo e uma rede e envia o valor exato da sua própria carteira.
- O gateway observa a blockchain e, quando o pagamento é definitivo, chama o seu webhook.
- O seu webhook marca o pedido como pago e inicia o envio do pedido.
Repare no que nunca acontece do seu lado: você não gera endereços, não varre blocos nem compara valores. O seu código faz duas tarefas pequenas: criar o pagamento e reagir ao status final.
Passo 1: crie o pagamento no seu servidor
Chame o gateway a partir do seu backend, nunca do navegador, porque a requisição leva a sua chave de API secreta. Na Qvard é um único POST para https://api.qvard.com/api/v1/payment com a sua chave no cabeçalho x-api-key e um corpo JSON:
id: o seu próprio número de pedido, como string. Ele volta para você depois no webhook, então use algo que você consiga localizar.price: o valor em dólares americanos. O cliente paga o equivalente no ativo que escolher.paymentSystemId(opcional): o ID de um método de pagamento do seu projeto, para pré-selecionar ativo e rede.
Em caso de sucesso, a resposta é 201 com um id de pagamento e um redirectUrl. Enviar o mesmo id de novo para o mesmo método de pagamento devolve o pagamento existente em vez de criar um duplicado, então é seguro tentar de novo após um timeout.
Se a requisição estiver errada, você recebe 400 com o texto Error validation e uma mensagem por campo; um valor abaixo do mínimo do método escolhido é rejeitado da mesma forma. Uma chave ausente ou errada retorna 401.
// Called from your checkout handler when the customer chooses "Pay with crypto".
async function createQvardPayment(order) {
const res = await fetch('https://api.qvard.com/api/v1/payment', {
method: 'POST',
headers: {
'x-api-key': process.env.QVARD_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: String(order.id), // your order number, also used to match the webhook
price: order.totalUsd // amount in US dollars
})
});
if (!res.ok) throw new Error('Qvard ' + res.status + ' ' + (await res.text()));
const { redirectUrl } = await res.json();
return redirectUrl; // send the customer to this page
}Passo 2: envie o cliente à página de pagamento
Redirecione o cliente para o redirectUrl, que tem a forma https://my.qvard.com/pay/<id do pagamento>. Nessa página ele escolhe o ativo e a rede, vê o valor exato e o endereço e paga da sua carteira. A taxa de câmbio fica fixada por uma hora, então o valor não muda enquanto ele paga.
Duas coisas para planejar:
- A volta. As URLs de retorno após o pagamento ainda não estão disponíveis, então o cliente volta ao seu site por conta própria. Dê a ele uma página de pedido que mostre o status atual e que se atualize quando o seu webhook chegar.
- Não trate a volta como pagamento. Um cliente que voltou ao seu site não necessariamente pagou. Só o webhook diz que o dinheiro chegou.
Passo 3: receba o webhook
Quando um pagamento é concluído, a Qvard envia um POST para a callback URL do seu projeto com um JSON pequeno: o id que você enviou e o status do pagamento. A requisição leva a sua chave de API no cabeçalho x-api-key. É um cabeçalho de autenticação, não uma assinatura criptográfica, então compare-o com a sua chave e rejeite todo o resto.
O modo como a entrega funciona importa para o seu manipulador:
- A Qvard considera o webhook entregue apenas quando o seu endpoint responde exatamente HTTP 200. Qualquer outro status ou um timeout conta como falha.
- Uma entrega que falhou é reenviada: cinco tentativas no total, em cerca de seis minutos.
- Redirecionamentos não são seguidos, então uma resposta
3xxtambém é falha. - Você também pode reenviar a notificação de um pagamento pelo painel.
Por causa das novas tentativas, o seu manipulador precisa ser idempotente: ver o mesmo evento duas vezes não pode enviar o pedido duas vezes. O exemplo abaixo verifica a chave, encontra o pedido pelo seu id e o marca como pago apenas se ainda não estiver pago.
import crypto from 'node:crypto';
import express from 'express';
const app = express();
app.post('/webhooks/qvard', express.json(), async (req, res) => {
// Qvard sends your API key in the x-api-key header: compare it with yours.
const received = Buffer.from(String(req.get('x-api-key') || ''));
const expected = Buffer.from(process.env.QVARD_API_KEY);
const valid = received.length === expected.length && crypto.timingSafeEqual(received, expected);
if (!valid) return res.sendStatus(401);
const { id, status } = req.body; // id is the order number you sent
const order = await db.orders.findById(id);
if (!order) return res.sendStatus(200); // unknown order: nothing to retry
if (status === 'completed' && order.state !== 'paid') {
await markOrderPaid(order); // safe to run twice: the check above skips repeats
}
return res.sendStatus(200); // must be exactly 200, otherwise Qvard retries
});Trate os pagamentos fora do padrão
Um pagamento tem um de quatro status:
wait: criado, aguardando o cliente.partial: o cliente enviou menos do que o necessário.completed: o valor total chegou; é o que dispara o webhook.rejected: o pagamento foi rejeitado.
Decida com antecedência o que a sua loja faz em cada caso. Para partial, a escolha usual é manter o pedido aberto e pedir ao cliente que envie o restante, ou reembolsá-lo. Mantenha a lógica simples: marque um pedido como pago apenas com completed e mostre ao cliente uma mensagem clara nos demais estados.
Teste antes de ir ao ar
Um plano de testes curto que pega a maioria dos problemas:
- Abra uma fatura de demonstração para ver a página de pagamento como o seu cliente a verá.
- Crie um pagamento no seu servidor e confirme que recebe um
redirectUrl. - Pague um valor real pequeno e confirme que o webhook chega e que o pedido fica pago.
- Faça o seu endpoint retornar um erro uma vez, confirme que a Qvard tenta de novo e que o seu manipulador não processa o pedido duas vezes.
- Saque um valor pequeno do seu saldo para a sua carteira para saber que todo o caminho funciona.
A Qvard ainda não tem um sandbox separado; um sandbox está no roteiro, então use valores reais pequenos nos passos 3 a 5.
Lista de segurança
- Guarde a chave de API apenas no servidor. Nunca a coloque em código de front-end ou em um repositório público.
- Compare o cabeçalho
x-api-keyem cada webhook e rejeite divergências, usando uma comparação em tempo constante. - Sirva o endpoint por HTTPS e mantenha-o rápido; faça o trabalho lento depois de responder.
- Associe pagamentos a pedidos pelo seu próprio
ide ignore eventos de pedidos que você não conhece. - Registre cada webhook com o seu status, para poder resolver disputas depois.
Mais sobre o tema em como proteger webhooks de pagamentos cripto.
E o WooCommerce, a Shopify ou outra plataforma?
Valem as mesmas duas chamadas: o código da sua plataforma cria o pagamento e recebe o webhook. A Qvard ainda não tem um plugin pronto, então em uma plataforma como o WooCommerce a integração é um pequeno trecho de código próprio ou uma hora de um desenvolvedor, não uma instalação com um clique. Veja integrações para saber em que pé está, e a página para desenvolvedores para a referência da API. Se você quer primeiro o panorama geral, comece por o que é um gateway de pagamento cripto.
FAQ
Preciso saber desenvolver para blockchain?
Não. Você faz uma requisição HTTP para criar um pagamento e trata um webhook. Endereços, monitoramento da rede e confirmações ficam por conta do gateway.
Posso aceitar cripto sem escrever código?
Ainda não com a Qvard. Links de pagamento e outras formas sem código de receber estão em desenvolvimento; hoje a integração é pela API, que exige um desenvolvedor.
E se o meu servidor estiver fora do ar quando o pagamento for concluído?
A notificação é reenviada cinco vezes em cerca de seis minutos. Se o seu servidor ficou fora do ar por mais tempo, reenvie a notificação desse pagamento pelo painel e concilie a partir daí.
Em qual moeda defino o preço?
Em dólares americanos. O cliente paga o equivalente no ativo e na rede que escolher na página de pagamento, a uma taxa fixada por uma hora.
