Cómo aceptar pagos cripto en un sitio web
Paso a paso: crea un pago desde tu servidor, envía al cliente a la página de pago, gestiona el webhook y pruébalo. Con código Node.js funcional.

Para aceptar cripto en un sitio web necesitas dos cosas en tu servidor: una llamada que cree un pago para un pedido y un endpoint que reciba el resultado. Esta guía recorre ambas con código funcional y después trata las pruebas, los casos límite y los errores que cuestan dinero a los comerciantes.
Qué necesitas antes de empezar
No necesitas ejecutar un nodo de blockchain ni custodiar billeteras de clientes. Necesitas:
- una cuenta en la pasarela con un proyecto, una clave API y, si quieres fijar tú el método de pago, el ID de ese método de tu proyecto;
- un endpoint HTTPS público en tu servidor para el webhook, guardado en el proyecto como callback URL;
- un lugar en tu sistema de pedidos donde guardar el estado del pago, asociado por tu propio número de pedido.
Decide pronto una cosa: si el cliente elige el activo y la red en la página de pago o si los preseleccionas tú. Dejar que elija el cliente es más sencillo y sirve para la mayoría de tiendas. Si pasas un método de pago, la página se abre con él ya seleccionado.
Cómo encaja todo el flujo
Este es el recorrido de un pedido, desde tu checkout hasta el estado de pagado:
- El cliente elige «Pagar con cripto» en el checkout y tu servidor crea un pago para ese pedido.
- La pasarela devuelve un enlace a su página de pago alojada.
- Rediriges al cliente a esa página. Elige un activo y una red y envía el importe exacto desde su propia billetera.
- La pasarela vigila la blockchain y, cuando el pago es definitivo, llama a tu webhook.
- Tu webhook marca el pedido como pagado e inicia el despacho del pedido.
Fíjate en lo que nunca ocurre por tu parte: no generas direcciones, no escaneas bloques ni comparas importes. Tu código hace dos tareas pequeñas: crear el pago y reaccionar al estado final.
Paso 1: crea el pago desde tu servidor
Llama a la pasarela desde tu backend, nunca desde el navegador, porque la solicitud lleva tu clave API secreta. Con Qvard es un único POST a https://api.qvard.com/api/v1/payment con tu clave en la cabecera x-api-key y un cuerpo JSON:
id: tu propio número de pedido, como cadena. Se te devuelve después en el webhook, así que usa algo que puedas buscar.price: el importe en dólares estadounidenses. El cliente paga el equivalente en el activo que elija.paymentSystemId(opcional): el ID de un método de pago de tu proyecto, para preseleccionar activo y red.
Si todo va bien, la respuesta es 201 con un id de pago y un redirectUrl. Enviar de nuevo el mismo id con el mismo método de pago devuelve el pago existente en lugar de crear un duplicado, así que reintentar tras un tiempo de espera agotado es seguro.
Si la solicitud es incorrecta recibes 400 con el texto Error validation y un mensaje por campo; un importe inferior al mínimo del método elegido se rechaza igual. Una clave ausente o errónea devuelve 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
}Paso 2: envía al cliente a la página de pago
Redirige al cliente a redirectUrl, que tiene la forma https://my.qvard.com/pay/<id del pago>. En esa página elige el activo y la red, ve el importe exacto y la dirección, y paga desde su billetera. El tipo de cambio queda fijado durante una hora, así que el importe no cambia mientras paga.
Hay dos cosas que prever:
- El regreso. Las URL de retorno tras el pago aún no están disponibles, así que el cliente vuelve a tu sitio por su cuenta. Dale una página de pedido que muestre el estado actual y que se actualice cuando llegue tu webhook.
- No tomes el regreso por pago. Un cliente que vuelve a tu sitio no necesariamente ha pagado. Solo el webhook te dice que el dinero ha llegado.
Paso 3: recibe el webhook
Cuando un pago se completa, Qvard envía un POST a la callback URL de tu proyecto con un JSON pequeño: el id que enviaste y el status del pago. La solicitud lleva tu clave API en la cabecera x-api-key. Es una cabecera de autenticación, no una firma criptográfica, así que compárala con tu clave y rechaza todo lo demás.
Importa cómo funciona la entrega para tu gestor:
- Qvard considera entregado el webhook solo cuando tu endpoint responde exactamente HTTP 200. Cualquier otro estado o un tiempo de espera agotado cuenta como fallo.
- Una entrega fallida se reintenta: cinco intentos en total, durante unos seis minutos.
- No se siguen las redirecciones, así que una respuesta
3xxtambién es un fallo. - También puedes reenviar la notificación de un pago desde el panel.
Por los reintentos, tu gestor debe ser idempotente: ver el mismo evento dos veces no debe enviar el pedido dos veces. El ejemplo siguiente comprueba la clave, busca el pedido por tu id y lo marca como pagado solo si aún no lo está.
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
});Gestiona los pagos atípicos
Un pago tiene uno de cuatro estados:
wait: creado, esperando al cliente.partial: el cliente envió menos de lo necesario.completed: llegó el importe completo; es el que dispara el webhook.rejected: el pago fue rechazado.
Decide de antemano qué hace tu tienda con cada uno. Para partial, lo habitual es mantener el pedido abierto y pedir al cliente que envíe el resto, o reembolsarle. Mantén la lógica simple: marca un pedido como pagado solo con completed y muestra al cliente un mensaje claro en los demás estados.
Prueba antes de salir a producción
Un plan de pruebas corto que detecta la mayoría de los problemas:
- Abre una factura de demostración para ver la página de pago como la verá tu cliente.
- Crea un pago desde tu servidor y comprueba que recibes un
redirectUrl. - Paga un importe real pequeño y confirma que llega el webhook y que el pedido pasa a pagado.
- Haz que tu endpoint devuelva un error una vez, confirma que Qvard reintenta y que tu gestor no procesa el pedido dos veces.
- Retira una cantidad pequeña de tu saldo a tu billetera para saber que todo el camino funciona.
Qvard todavía no tiene un sandbox independiente; un sandbox está en la hoja de ruta, así que usa importes reales pequeños en los pasos 3 a 5.
Lista de seguridad
- Guarda la clave API solo en el servidor. Nunca la pongas en código del front-end ni en un repositorio público.
- Compara la cabecera
x-api-keyen cada webhook y rechaza las que no coincidan, con una comparación en tiempo constante. - Sirve el endpoint por HTTPS y mantenlo rápido; haz el trabajo lento después de responder.
- Asocia los pagos con los pedidos por tu propio
ide ignora los eventos de pedidos que no conozcas. - Registra cada webhook con su estado para poder resolver disputas más adelante.
Más sobre este tema en cómo proteger los webhooks de pagos cripto.
¿Y WooCommerce, Shopify u otra plataforma?
Se aplican las mismas dos llamadas: el código de tu plataforma crea el pago y recibe el webhook. Qvard aún no tiene un plugin listo, así que en una plataforma como WooCommerce la integración es un pequeño fragmento de código propio o una hora de un desarrollador, no un paso de instalar y hacer clic. Consulta integraciones para ver en qué punto está, y la página para desarrolladores para la referencia de la API. Si primero quieres la visión general, empieza por qué es una pasarela de pagos cripto.
FAQ
¿Necesito saber desarrollar para blockchain?
No. Haces una solicitud HTTP para crear un pago y gestionas un webhook. Las direcciones, la vigilancia de la red y las confirmaciones las gestiona la pasarela.
¿Puedo aceptar cripto sin escribir código?
Todavía no con Qvard. Los enlaces de pago y otras formas sin código de cobrar están en desarrollo; hoy la integración es mediante la API, para la que hace falta un desarrollador.
¿Qué pasa si mi servidor está caído cuando el pago se completa?
La notificación se reintenta cinco veces durante unos seis minutos. Si tu servidor estuvo caído más tiempo, reenvía la notificación de ese pago desde el panel y concilia desde ahí.
¿En qué moneda fijo el precio?
En dólares estadounidenses. El cliente paga el equivalente en el activo y la red que elija en la página de pago, a un tipo fijado durante una hora.
