api

Webhooks

当支付事件发生时,QuantaPay 会向您配置的 webhook URL 发送 HTTP POST 请求。这使您的服务器能够实时响应支付事件。

更新于: 2026/4/8

当支付事件发生时,QuantaPay 会向您配置的 webhook URL 发送 HTTP POST 请求。这使您的服务器能够实时响应支付事件。

设置

在 Settings → Payments → Webhook 中配置您的 webhook:

  • Webhook URL:您的 HTTPS 端点(例如 https://yoursite.com/webhook/quantapay)
  • Webhook Secret Key:自动生成的密钥,用于载荷验证

Webhook 载荷

当支付完成时,QuantaPay 会发送一个 Content-Type: application/json 的 POST 请求:

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

交易字段

字段类型描述
idstringQuantaPay 内部交易 ID。
biz_idstringQuantaPay 订单编号(例如 QP-20260408-560BF)。
titlestring交易标题。
fromstring客户的钱包地址(发送方)。
tostring您的钱包地址(接收方)。
hashstring区块链交易哈希。
amountstring加密货币金额。
amount_fiatstring法定货币金额(8位小数,例如 "49.99000000")。
cryptocurrencystring加密货币代码(例如 btc、eth、usdt)。
currencystring法定货币代码(例如 usd、eur、hkd)。
external_referencestring您的订单号 — 创建 Checkout Session 时传入,用于匹配您的订单。
creation_timestring交易创建时间戳(UTC)。
statusstring交易状态:C(已完成)。
checkout_idstring|null关联的 Checkout Session ID,或 null。
typestring交易类型:1 = 直接支付,2 = Checkout Session。

用 external_reference 匹配订单

这是将 QuantaPay 支付与您自己订单关联的推荐方式。

创建 Checkout Session 时,在 external_reference 字段传入您的订单号:

{
  "order_id": "your-order-id-123",
  "external_reference": "your-order-id-123",
  "price": 49.99,
  "currency": "usd"
}

QuantaPay 会保存此值并在 webhook 载荷的 transaction.external_reference 中原样返回。用它找到并完成对应订单。

签名验证

载荷中的 key 字段包含您的 Webhook Secret Key。如果未配置 Webhook Secret Key,系统会回退使用您账户的加密密钥。请务必配置专用的 Webhook Secret Key。

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';

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);

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

事件类型

状态事件描述
C支付完成付款已在区块链上获得足够的确认。

注意:Webhook 仅在交易完成(状态 C)时发送。过期和待处理的交易不会触发 webhook。

最佳实践

1. 快速响应

收到 webhook 后立即返回 HTTP 200 响应。将耗时处理放在异步流程中执行。

2. 幂等处理

您的 webhook 处理程序应该是幂等的 — 重复处理同一个 webhook 不应导致重复发货。使用 biz_id 作为去重键。

if (order_already_fulfilled($transaction['biz_id'])) {
    http_response_code(200);
    die('Already processed');
}

3. 验证密钥

在处理之前始终验证 key 字段是否与您的 Webhook Secret Key 匹配。

4. 使用 HTTPS

始终为 webhook URL 使用 HTTPS 端点。

5. 记录所有日志

记录所有传入的 webhook,用于调试和审计。

error_log('QuantaPay Webhook: ' . json_encode($payload));

6. 优雅处理故障

QuantaPay 不会自动重试失败的 webhook。如果您的服务器曾经不可用,请使用 get-checkout-session 或 get-transactions API 主动查询支付状态作为兜底。