Integración de API de pagos cripto: facturas, firmas y webhooks

Una guía práctica para desarrolladores: una petición firmada para crear la factura, un endpoint de webhook para recibir el estado y los detalles de seguridad que lo hacen fiable.

Actualizado: 6 min de lectura
Integración de API de pagos cripto: petición POST firmada que crea una factura y respuesta de webhook

Una integración de API de pagos cripto necesita dos piezas: una petición desde el servidor que crea una factura para cada pedido y devuelve una URL de checkout, y un endpoint de webhook que recibe actualizaciones de estado firmadas cuando el pago se detecta y se confirma en la blockchain. Con mistKET ambas son HTTPS y JSON simples, firmados con HMAC-SHA256: no necesitas ningún SDK.

Esta guía recorre el flujo completo con código y después repasa los detalles de seguridad y fiabilidad que separan una demo de un sistema en producción.

La arquitectura de una API de pagos cripto en una imagen

Toda integración de API de pagos cripto sigue el mismo ciclo de cinco pasos:

  1. Pedido creado: tu backend llama a POST /api/v1/invoices con importe, moneda, ID de pedido y URL de callback.
  2. Redirección: envías al cliente a la checkout_url devuelta.
  3. Pago: el cliente elige una red y paga en la página alojada; el tipo de cambio queda bloqueado durante la vida de la factura.
  4. Webhook: mistKET envía por POST invoice.paid, invoice.confirmed o un evento de excepción a tu URL de callback.
  5. Entrega: verificas la firma, marcas el pedido como pagado y entregas.

Tu API key, tu API secret y tu webhook secret se emiten en el panel del comercio una vez aprobada tu cuenta. La referencia completa está en el panel, en la sección API.

Paso 1: firma y envía la petición de factura

Cada petición a la API lleva tres cabeceras. La firma es el HMAC-SHA256 en hexadecimal de la cadena {timestamp}.{raw body} usando tu 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"
}

Una versión mínima en PHP tiene este aspecto:

$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);
// envía $body con X-Api-Key, X-Api-Timestamp, X-Api-Signature

Firma exactamente los bytes que envías. Volver a codificar el JSON después de firmar (con otros espacios u otro orden de claves) es la causa número uno de los errores de firma.

Paso 2: redirige al checkout alojado

Una llamada correcta devuelve 201 Created con el UUID de la factura, su estado (new) y una checkout_url. Guarda el UUID junto a tu pedido y redirige al cliente. La página alojada muestra el importe exacto, un código QR, una cuenta atrás y el estado en vivo, y funciona en móvil. El cliente elige allí la red (USDT en TRON, BNB Smart Chain, Ethereum o Arbitrum, además de TRX, BNB y ETH), salvo que fijes la red en la llamada a la API.

Además de crear facturas, la API permite listarlas, consultarlas y cancelarlas, algo útil cuando cambia un carrito o se abandona un pedido.

Paso 3: recibe y verifica los webhooks de pago cripto

Cuando cambia el estado, mistKET envía un POST con JSON a tu URL de callback con estas cabeceras:

CabeceraFunción
X-Mistket-EventNombre del evento, p. ej. invoice.confirmed
X-Mistket-TimestampHora Unix usada en la firma
X-Mistket-Signaturesha256= + HMAC-SHA256 de {timestamp}.{raw body} con tu webhook secret
X-Mistket-DeliveryID de entrega, práctico para los logs

Verificación en 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);

Usa una comparación de tiempo constante (hash_equals en PHP, hmac.compare_digest en Python, crypto.timingSafeEqual en Node.js) y rechaza los timestamps antiguos para bloquear ataques de repetición. Para conocer el fundamento, consulta la RFC 2104 (HMAC).

Idea clave: la fuente de verdad es el webhook, no el navegador del cliente que vuelve a tu web. Nunca marques un pedido como pagado sin una firma válida.

Ejemplo de payload de webhook

Un pago confirmado llega como un documento JSON compacto. Los campos que más usarás son el evento, tu ID de pedido, el estado y los importes esperado y recibido:

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" } }

Guarda pay_currency y network con el pedido. Son útiles para la contabilidad y para futuras conversaciones de soporte.

Verificación en Node.js y Python

La misma comprobación en otros dos lenguajes populares:

// Node.js (Express con express.raw para esta ruta)
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)

En ambos casos, asegúrate de leer el cuerpo bruto de la petición. Muchos frameworks parsean el JSON automáticamente; una vez parseado y serializado de nuevo, los bytes ya no coinciden con la firma.

Pruebas en local

Los webhooks necesitan una URL HTTPS pública, así que un portátil local no puede recibirlos directamente. Hay dos opciones prácticas: desplegar pronto el handler en un servidor de staging o exponer temporalmente tu puerto local con una herramienta de túnel. Crea facturas pequeñas que apunten al callback de staging y revisa los logs de entrega en el panel del comercio para ver exactamente qué se envió y qué respondió tu servidor.

Intenta provocar cada tipo de evento al menos una vez durante las pruebas: paid, confirmed, underpaid y expired. Cuando cada rama de tu handler se haya ejecutado con un webhook real, quedará muy poco que pueda sorprenderte en producción, y sabrás exactamente cómo se ven los logs de entrega cuando algo falle.

Paso 4: gestiona cada estado

El payload de un webhook incluye el ID de pedido, el estado, el importe esperado (pay_amount), el importe recibido (paid_amount), la moneda y la red. Asocia cada evento a una acción:

EventoSignificadoAcción habitual
invoice.paidTransferencia detectada, confirmaciones pendientesMostrar "pago recibido, confirmando"
invoice.confirmedConfirmaciones necesarias alcanzadasMarcar como pagado y entregar
invoice.underpaidPor debajo de tu toleranciaEsperar el complemento o contactar al cliente
invoice.overpaidMás de lo esperadoEntregar; decidir qué hacer con la diferencia
invoice.expiredNingún pago válido a tiempoLiberar stock, dejar el pedido sin pagar

La tolerancia de pago insuficiente, los pagos parciales que suman el total y los pagos tardíos dentro de un periodo de gracia los gestiona mistKET; tu código solo reacciona al resultado final.

Fiabilidad: reintentos e idempotencia

Si tu endpoint está caído o devuelve un error, los webhooks se reintentan con back-off durante horas. Eso significa que a veces recibirás el mismo evento dos veces. Haz que tu handler sea idempotente por ID de pedido:

  • Responde rápido con un 2xx y procesa el trabajo pesado en una cola.
  • Antes de entregar, comprueba si el pedido ya está marcado como pagado.
  • Registra el ID de entrega y el evento para poder rastrear incidencias; el panel también muestra los logs de entrega de webhooks.

Lista de comprobación de seguridad

Una integración de API de pagos cripto mueve dinero, así que trátala como cualquier otro código de pagos:

  • Guarda el API secret y el webhook secret solo en el servidor, nunca en el código front-end ni en apps móviles.
  • Restringe el acceso a la API con la lista de IPs permitidas del panel.
  • Rechaza las peticiones fuera de una ventana corta de timestamp.
  • Configura la caducidad de las facturas según tu lógica de reserva de stock.
  • Trata los importes como cadenas o decimales, nunca como floats.
Consejo: antes de salir a producción, paga tú mismo una factura pequeña, envía a propósito un importe menor para ver el flujo de pago insuficiente y deja caducar una factura. Habrás probado todas las ramas en menos de una hora.

Próximos pasos

Esa es la integración completa de una API de pagos cripto: una petición firmada, un webhook verificado y un mapa de estados. Para ver el mismo flujo desde el lado no técnico, lee cómo aceptar pagos con criptomonedas en una web. ¿Estás creando un bot en lugar de una web? Consulta cómo aceptar pagos cripto en un bot de Telegram. Solicita tus claves de API en Telegram @mistnetwork, revisa el ejemplo en la sección para desarrolladores y gestiona tus claves en el acceso de comercios.

Preguntas frecuentes

¿Necesito un SDK para integrar una API de pagos cripto?
Con mistKET, no. Usa HTTPS y JSON simples; cualquier lenguaje capaz de hacer una petición HTTP y calcular un HMAC-SHA256 puede integrarse.
¿Cómo se autentican las peticiones a la API de mistKET?
Cada petición envía X-Api-Key, X-Api-Timestamp y X-Api-Signature, donde la firma es el HMAC-SHA256 en hexadecimal de '{timestamp}.{raw body}' con tu API secret.
¿Cómo verifico un webhook de pago cripto?
Recalcula el HMAC-SHA256 de '{timestamp}.{raw body}' con tu webhook secret, compáralo con X-Mistket-Signature en tiempo constante y rechaza los timestamps antiguos.
¿Qué pasa si mi servidor pierde un webhook?
Los webhooks se reintentan con back-off durante horas y además puedes consultar el estado de la factura por la API. Haz tu handler idempotente por ID de pedido.
¿Puedo forzar una red concreta desde la API?
Sí. Puedes fijar la red en la llamada a la API; si no, el cliente la elige en la página de pago alojada.
¿Qué eventos deben activar la entrega del pedido?
Entrega con invoice.confirmed. Usa invoice.paid solo para mostrar el progreso y gestiona underpaid, overpaid y expired según tu política.

Artículos relacionados