Webhooks
QuantaPay sends HTTP POST requests to your configured webhook URL when payment events occur. This allows your server to react to payments in real time.
QuantaPay sends HTTP POST requests to your configured webhook URL when payment events occur. This allows your server to react to payments in real time.
Setup
Configure your webhook in Settings → Payments → Webhook:
- Webhook URL: Your HTTPS endpoint (e.g.,
https://yoursite.com/webhook/quantapay) - Webhook Secret Key: Auto-generated key for payload verification
Webhook Payload
When a payment is completed, QuantaPay sends a POST request with Content-Type: application/json:
{
"key": "your-webhook-secret-key",
"transaction": {
"id": "123",
"title": "Payment #ORD-12345",
"description": "",
"from": "0xCustomerWalletAddress",
"to": "0xYourWalletAddress",
"hash": "0xBlockchainTransactionHash",
"amount": "0.025",
"amount_fiat": "49.99",
"cryptocurrency": "eth",
"currency": "usd",
"external_reference": "",
"creation_time": "2026-03-08 10:00:00",
"status": "C",
"webhook": "1",
"vat": "",
"billing": "",
"checkout_id": "cs-cs_a1b2c3d4e5f6a1b2c3d4"
}
}
Transaction Fields
| Field | Type | Description |
|---|---|---|
id | string | Transaction ID in QuantaPay. |
title | string | Transaction title/description. |
from | string | Customer's wallet address (sender). |
to | string | Your wallet address (receiver). |
hash | string | Blockchain transaction hash. |
amount | string | Amount in cryptocurrency. |
amount_fiat | string | Amount in fiat currency. |
cryptocurrency | string | Cryptocurrency code (e.g., btc, eth, usdt). |
currency | string | Fiat currency code (e.g., usd, eur). |
external_reference | string | External reference (e.g., WordPress order data). |
creation_time | string | Transaction creation timestamp. |
status | string | Transaction status: C (completed). |
checkout_id | string | Associated checkout session ID (format: cs-cs_xxxxx). |
Signature Verification
The key field in the payload contains your Webhook Secret Key. If no Webhook Secret Key is configured, the system falls back to using your account's encryption key instead. Verify this value matches your stored secret to confirm the webhook is genuine.
PHP Example
$payload = json_decode(file_get_contents('php://input'), true);
$webhook_secret = 'your-webhook-secret-key'; // From Settings
if ($payload['key'] !== $webhook_secret) {
http_response_code(401);
die('Invalid webhook signature');
}
// Process the payment
$transaction = $payload['transaction'];
$order_id = $transaction['title'];
$amount = $transaction['amount_fiat'];
$status = $transaction['status'];
if ($status === 'C') {
// Payment completed — fulfill the order
fulfill_order($order_id, $amount);
}
http_response_code(200);
echo 'OK';
Node.js Example
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;
// Verify webhook authenticity
if (key !== WEBHOOK_SECRET) {
return res.status(401).send('Invalid signature');
}
// Process completed payment
if (transaction.status === 'C') {
console.log(`Payment received: ${transaction.amount_fiat} ${transaction.currency}`);
console.log(`Crypto: ${transaction.amount} ${transaction.cryptocurrency}`);
console.log(`TX Hash: ${transaction.hash}`);
// Fulfill order logic here
}
res.status(200).send('OK');
});
app.listen(3000);
cURL Test
# Simulate a webhook (for testing)
curl -X POST https://yoursite.com/webhook/quantapay \
-H "Content-Type: application/json" \
-d '{
"key": "your-webhook-secret-key",
"transaction": {
"id": "123",
"status": "C",
"amount": "0.025",
"amount_fiat": "49.99",
"cryptocurrency": "eth",
"currency": "usd",
"hash": "0xabc123..."
}
}'
Event Types
| Status | Event | Description |
|---|---|---|
C | Payment Completed | The payment has been confirmed on the blockchain with sufficient confirmations. |
Note: Webhooks are only sent for completed transactions (status
C). Expired and pending transactions do not trigger webhooks.
Best Practices
1. Respond Quickly
Return an HTTP 200 response as soon as you receive the webhook. Do heavy processing asynchronously.
2. Idempotent Processing
Your webhook handler should be idempotent — processing the same webhook twice should not cause duplicate fulfillment. Use the transaction id as a dedup key.
// Check if already processed
if (order_already_fulfilled($transaction['id'])) {
http_response_code(200);
die('Already processed');
}
3. Verify the Key
Always verify the key field matches your Webhook Secret Key before processing.
4. Use HTTPS
Always use an HTTPS endpoint for your webhook URL. HTTP endpoints may leak sensitive transaction data.
5. Log Everything
Log all incoming webhooks for debugging and audit purposes.
error_log('QuantaPay Webhook: ' . json_encode($payload));
6. Handle Failures Gracefully
If your server is temporarily down, the webhook delivery may be missed. Use the get-checkout-session or get-transactions API as a fallback to check payment status.