Webhooks
当支付事件发生时,QuantaPay 会向您配置的 webhook URL 发送 HTTP POST 请求。这使您的服务器能够实时响应支付事件。
当支付事件发生时,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"
}
}
交易字段
| 字段 | 类型 | 描述 |
|---|---|---|
id | string | QuantaPay 内部交易 ID。 |
biz_id | string | QuantaPay 订单编号(例如 QP-20260408-560BF)。 |
title | string | 交易标题。 |
from | string | 客户的钱包地址(发送方)。 |
to | string | 您的钱包地址(接收方)。 |
hash | string | 区块链交易哈希。 |
amount | string | 加密货币金额。 |
amount_fiat | string | 法定货币金额(8位小数,例如 "49.99000000")。 |
cryptocurrency | string | 加密货币代码(例如 btc、eth、usdt)。 |
currency | string | 法定货币代码(例如 usd、eur、hkd)。 |
external_reference | string | 您的订单号 — 创建 Checkout Session 时传入,用于匹配您的订单。 |
creation_time | string | 交易创建时间戳(UTC)。 |
status | string | 交易状态:C(已完成)。 |
checkout_id | string|null | 关联的 Checkout Session ID,或 null。 |
type | string | 交易类型: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 主动查询支付状态作为兜底。