api

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.

Updated: 3/9/2026

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

FieldTypeDescription
idstringTransaction ID in QuantaPay.
titlestringTransaction title/description.
fromstringCustomer's wallet address (sender).
tostringYour wallet address (receiver).
hashstringBlockchain transaction hash.
amountstringAmount in cryptocurrency.
amount_fiatstringAmount in fiat currency.
cryptocurrencystringCryptocurrency code (e.g., btc, eth, usdt).
currencystringFiat currency code (e.g., usd, eur).
external_referencestringExternal reference (e.g., WordPress order data).
creation_timestringTransaction creation timestamp.
statusstringTransaction status: C (completed).
checkout_idstringAssociated 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

StatusEventDescription
CPayment CompletedThe 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.