Webhooks
QuantaPay envía solicitudes HTTP POST a la URL de webhook configurada cuando ocurren eventos de pago. Esto permite que tu servidor reaccione a los pagos en tiempo real.
QuantaPay envía solicitudes HTTP POST a la URL de webhook configurada cuando ocurren eventos de pago. Esto permite que tu servidor reaccione a los pagos en tiempo real.
Configuración
Configura tu webhook en Ajustes → Pagos → Webhook:
- Webhook URL: Tu endpoint HTTPS (p. ej., https://tusitio.com/webhook/quantapay)
- Webhook Secret Key: Clave generada automáticamente para verificar el payload
Carga útil del webhook
Cuando se completa un pago, QuantaPay envía una solicitud POST con Content-Type: application/json:
{
"key": "your-webhook-secret-key",
"transaction": {
"id": "197",
"biz_id": "QP-20260408-560BF",
"title": "",
"description": "",
"from": "0xCustomerWalletAddress",
"to": "0xYourWalletAddress",
"hash": "0xBlockchainTransactionHash",
"amount": "0.025",
"amount_fiat": "49.99000000",
"cryptocurrency": "eth",
"currency": "usd",
"external_reference": "your-order-id-123",
"creation_time": "2026-03-08 10:00:00",
"status": "C",
"webhook": "1",
"vat": "0",
"billing": "",
"checkout_id": null,
"type": "2"
}
}
Campos de la transacción
| Campo | Tipo | Descripción |
|---|---|---|
id | string | ID interno de transacción en QuantaPay. |
biz_id | string | Referencia de pedido de QuantaPay (p. ej., QP-20260408-560BF). |
title | string | Título de la transacción. |
from | string | Dirección de cartera del cliente (remitente). |
to | string | Tu dirección de cartera (receptor). |
hash | string | Hash de la transacción en blockchain. |
amount | string | Monto en criptomoneda. |
amount_fiat | string | Monto en moneda fiat (8 decimales, p. ej., "49.99000000"). |
cryptocurrency | string | Código de criptomoneda (p. ej., btc, eth, usdt). |
currency | string | Código de moneda fiat (p. ej., usd, eur, hkd). |
external_reference | string | Tu ID de pedido — proporcionado al crear la Checkout Session. Úsalo para localizar tu pedido. |
creation_time | string | Marca de tiempo de creación de la transacción (UTC). |
status | string | Estado de la transacción: C (completada). |
checkout_id | string|null | ID de la Checkout Session asociada, o null. |
type | string | Tipo de transacción: 1 = pago directo, 2 = Checkout Session. |
Asociar pedidos con external_reference
Esta es la forma recomendada de vincular un pago de QuantaPay con tu propio pedido.
Al crear una Checkout Session, pasa el ID de tu pedido en el campo external_reference:
{
"order_id": "your-order-id-123",
"external_reference": "your-order-id-123",
"price": 49.99,
"currency": "usd"
}
QuantaPay almacena este valor y lo devuelve sin cambios en el payload del webhook bajo transaction.external_reference. Úsalo para encontrar y completar el pedido correcto.
Verificación de firma
El campo key del payload contiene tu Webhook Secret Key. Si no está configurado, el sistema usa la clave de cifrado de tu cuenta. Configura siempre una Webhook Secret Key dedicada.
Ejemplo PHP
$payload = json_decode(file_get_contents('php://input'), true);
$webhook_secret = 'your-webhook-secret-key'; // From Settings
// 1. Verify webhook authenticity
if ($payload['key'] !== $webhook_secret) {
http_response_code(401);
die('Invalid webhook signature');
}
$transaction = $payload['transaction'];
// 2. Check payment is complete
if ($transaction['status'] !== 'C') {
http_response_code(200);
die('Not completed');
}
// 3. Match your order using external_reference
$your_order_id = $transaction['external_reference'];
$order = find_order($your_order_id);
if (!$order) {
http_response_code(200);
die('Order not found');
}
// 4. Verify amount using float comparison (amount_fiat has 8 decimal places)
$paid_amount = floatval($transaction['amount_fiat']);
$expected_amount = floatval($order->total);
if (abs($paid_amount - $expected_amount) > 0.01 || $transaction['currency'] !== $order->currency) {
http_response_code(200);
die('Amount mismatch');
}
// 5. Idempotency check — use biz_id as dedup key
if (order_already_fulfilled($transaction['biz_id'])) {
http_response_code(200);
die('Already processed');
}
// 6. Mark order as paid
fulfill_order($your_order_id);
http_response_code(200);
echo 'OK';
Ejemplo Node.js
const express = require('express');
const app = express();
app.use(express.json());
const WEBHOOK_SECRET = 'your-webhook-secret-key';
app.post('/webhook/quantapay', (req, res) => {
const { key, transaction } = req.body;
// 1. Verify webhook authenticity
if (key !== WEBHOOK_SECRET) {
return res.status(401).send('Invalid signature');
}
// 2. Only process completed payments
if (transaction.status !== 'C') {
return res.status(200).send('OK');
}
// 3. Match your order using external_reference
const yourOrderId = transaction.external_reference;
const bizId = transaction.biz_id; // use as dedup key
console.log(`Payment completed: ${transaction.amount_fiat} ${transaction.currency}`);
console.log(`Your order: ${yourOrderId}, QuantaPay ref: ${bizId}`);
console.log(`TX Hash: ${transaction.hash}`);
// Fulfill order logic here (use biz_id for idempotency)
res.status(200).send('OK');
});
app.listen(3000);
Prueba con cURL
curl -X POST https://yoursite.com/webhook/quantapay \
-H "Content-Type: application/json" \
-d '{
"key": "your-webhook-secret-key",
"transaction": {
"id": "197",
"biz_id": "QP-20260408-560BF",
"status": "C",
"amount": "0.025",
"amount_fiat": "49.99000000",
"cryptocurrency": "eth",
"currency": "usd",
"external_reference": "your-order-id-123",
"hash": "0xabc123..."
}
}'
Tipos de eventos
| Estado | Evento | Descripción |
|---|---|---|
C | Pago completado | El pago ha sido confirmado en la blockchain con suficientes confirmaciones. |
Nota: Los webhooks solo se envían para transacciones completadas (estado C). Las transacciones expiradas o pendientes no los activan.
Mejores prácticas
1. Responder rápidamente
Devuelve HTTP 200 tan pronto como recibas el webhook. Procesa tareas pesadas de forma asíncrona.
2. Procesamiento idempotente
Tu manejador debe ser idempotente — procesar el mismo webhook dos veces no debe causar duplicados. Usa biz_id como clave de deduplicación.
if (order_already_fulfilled($transaction['biz_id'])) {
http_response_code(200);
die('Already processed');
}
3. Verificar la clave
Verifica siempre que el campo key coincida con tu Webhook Secret Key antes de procesar.
4. Usar HTTPS
Usa siempre un endpoint HTTPS para tu URL de webhook.
5. Registrar todo
Registra todos los webhooks entrantes para depuración y auditoría.
error_log('QuantaPay Webhook: ' . json_encode($payload));
6. Gestionar fallos correctamente
QuantaPay no reintenta automáticamente los webhooks fallidos. Usa la API get-checkout-session o get-transactions como alternativa para verificar el estado del pago si tu servidor estuvo temporalmente inaccesible.