跳到主要内容

Webhook 集成

Webhook 使您能够在支付事件发生时实时收到后端通知。HashNut 会向您配置的 webhook URL 发送 HTTP POST 请求。

概述

Webhook 配置

在创建支付订单时配置您的 webhook URL:

{
"callBackUrl": "https://your-site.com/api/payment/webhook"
}

要求

  • ✅ 必须使用 HTTPS(不支持 HTTP)
  • ✅ 成功处理时必须返回 200 OK 且响应体为 "success"
  • ✅ 应在 30 秒内响应

Webhook 事件

支付状态更新

当订单状态发生变化时,HashNut 会发送 webhook:

事件触发条件状态值
支付已发起客户开始支付1
支付确认中交易已确认3
支付成功支付已验证4
支付失败交易失败-1
订单已过期订单已过期-2
订单已取消订单已取消-3

Webhook 请求

请求头

Content-Type: application/json

请求体

Webhook 请求体以 JSON 字符串形式发送:

{
"payOrderId": "01KBZ292SK2GKFK97916F5EC3B",
"merchantOrderId": "e30ff306-5552-497d-9083-fd6e943dfd73",
"accessSign": "D3DE7E4002057C0EAED1BE2268DA53CC9058DCFC9DCAF50D999AF270A7B033C5",
"state": 4
}

Webhook 载荷字段

字段类型描述
payOrderIdstringHashNut 平台订单 ID
merchantOrderIdstring商户订单 ID
accessSignstring用于查询订单的访问签名
stateinteger订单状态(参见订单状态

Webhook 处理

注意:Webhook 签名验证不需要。您可以直接处理 webhook,无需签名验证。这在开发和生产环境中都是可接受的。

Webhook 响应

您的 webhook 端点必须返回:

成功

  • 状态码:200 OK
  • 响应体:"success"(纯文本)
  • Content-Type:text/htmltext/plain

失败

  • 状态码:400500
  • 响应体:"failed"(纯文本)

响应示例

// Express.js
res.status(200).send('success');

// 或错误情况
res.status(400).send('failed');

Webhook 处理

推荐流程

处理步骤

  1. 解析 JSON:解析请求体
  2. 查询订单状态:使用 queryPayOrderWithAccessSign 获取最新订单详情
  3. 验证订单:确保订单详情与您的记录匹配
  4. 更新数据库:在您的系统中更新订单状态
  5. 处理业务逻辑:履行订单、发送通知等
  6. 返回成功:返回 200 OK"success"

实现示例

app.post('/webhook', express.json(), async (req, res) => {
try {
// 1. 解析 JSON
const data = req.body;
const { payOrderId, merchantOrderId, accessSign, state } = data;

// 2. 查询订单状态(获取最新详情)
const orderResponse = await fetch(
'https://testnet.hashnut.io/api/v3.0.0/pay/queryPayOrderWithAccessSign',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ payOrderId, merchantOrderId, accessSign })
}
);

const orderResult = await orderResponse.json();
if (orderResult.code !== 0) {
console.error('Failed to query order:', orderResult.msg);
return res.status(400).send('failed');
}

const order = orderResult.data;

// 3. 根据状态更新数据库
if (order.state === 4) {
// 支付成功
await updateOrderStatus(merchantOrderId, 'paid', order.payTxId);
await fulfillOrder(merchantOrderId);
} else if (order.state < 0) {
// 支付失败、过期或已取消
await updateOrderStatus(merchantOrderId, 'failed');
}

// 4. 返回成功
res.send('success');
} catch (error) {
console.error('Webhook processing error:', error);
res.status(500).send('failed');
}
});

Webhook 重试逻辑

HashNut 会自动重试失败的 webhook 交付:

  • 重试次数:3 次重试
  • 重试间隔:1 秒、5 秒、30 秒(指数退避)
  • 超时时间:每次尝试 30 秒
  • 成功标准:必须收到 200 OK 且响应体为 "success"

重试序列

幂等性

重要:Webhook 可能会被多次交付。实现幂等性:

  1. 检查是否已处理:使用 payOrderId 检查 webhook 是否已处理
  2. 幂等操作:确保您的处理逻辑是幂等的
  3. 日志记录:记录所有 webhook 交付以便调试
// 幂等性检查
const existingOrder = await getOrderByPayOrderId(payOrderId);
if (existingOrder && existingOrder.status === 'paid') {
// 已处理
return res.send('success');
}

安全最佳实践

  1. 使用 HTTPS:Webhook 端点必须使用 HTTPS
  2. 验证数据:始终查询订单状态以获取最新详情
  3. 优雅处理错误:返回适当的错误代码
  4. 监控 Webhook:记录所有 webhook 交付以便监控
  5. 速率限制:在 webhook 端点上实现速率限制
  6. IP 白名单:考虑 IP 白名单(如果 HashNut 提供 IP 范围)

测试 Webhook

本地测试

使用 ngroklocaltunnel 等工具暴露您的本地服务器:

# 使用 ngrok
ngrok http 3000

# 使用提供的 HTTPS URL 作为您的 webhook URL
# https://abc123.ngrok.io/api/webhook

Webhook 测试清单

  • Webhook 端点可通过 HTTPS 访问
  • 对于有效 webhook 返回 200 OK"success"
  • 正确处理所有订单状态
  • 实现幂等性
  • 记录 webhook 交付
  • 优雅处理错误

故障排除

未收到 Webhook

  1. 检查 URL:验证 webhook URL 是否正确且可访问
  2. 检查 HTTPS:确保端点使用 HTTPS
  3. 检查防火墙:验证防火墙是否允许 HashNut IP
  4. 检查日志:查看 HashNut 仪表板中的交付日志

Webhook 重试

如果 webhook 正在重试:

  1. 检查响应:确保返回 200 OK"success"
  2. 检查超时:确保处理在 30 秒内完成
  3. 检查错误:查看错误日志以了解处理失败原因

下一步


准备设置 webhook? 查看快速开始 →