Webhook 介绍
Webhook 是 GoGoPay 向您的应用程序发送实时事件通知的机制。 通过 Webhook,您可以及时了解支付状态变化,确保业务流程的准确性。
什么是 Webhook?
Webhook 是一种 HTTP 回调机制,当特定事件发生时,GoGoPay 会向您预先配置的 URL 发送 POST 请求。这种方式比轮询 API 更高效,能够实时获取事件通知。
为什么使用 Webhook?
- 实时性:事件发生时立即通知,无需轮询
- 可靠性:自动重试机制确保消息送达
- 高效性:减少不必要的 API 调用
- 安全性:签名验证确保数据完整性
Webhook 工作流程
- 事件触发:支付状态发生变化(如支付成功、失败等)
- 生成通知:GoGoPay 系统生成相应的事件数据
- 发送请求:向您配置的 Webhook URL 发送 POST 请求
- 验证签名:您的服务器验证请求签名
- 处理事件:根据事件类型执行相应的业务逻辑
- 返回响应:返回 HTTP 200 状态码确认接收
支持的事件类型
GoGoPay 支持以下 Webhook 事件:
| 事件类型 | 描述 | 触发时机 |
|---|---|---|
payment.succeeded |
支付成功 | 用户完成支付且资金到账 |
payment.failed |
支付失败 | 支付过程中发生错误 |
payment.canceled |
支付取消 | 用户主动取消支付 |
payment.refunded |
退款完成 | 退款处理完成 |
session.expired |
会话过期 | 支付会话超时未完成 |
Webhook 数据格式
GoGoPay 发送的 Webhook 请求包含以下信息:
请求头
Content-Type: application/json
GoGoPay-Signature: t=1234567890,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
User-Agent: GoGoPay-Webhook/1.0
请求体示例
{
"id": "evt_1234567890",
"type": "payment.succeeded",
"created": 1234567890,
"data": {
"object": {
"id": "pi_1234567890",
"amount": 1000,
"currency": "CNY",
"status": "succeeded",
"customer_id": "cus_1234567890",
"metadata": {
"order_id": "order_123"
}
}
}
}
签名验证
为确保 Webhook 请求的安全性,GoGoPay 会在请求头中包含签名信息。您需要验证签名以确认请求来源的合法性。
验证步骤
- 从请求头
GoGoPay-Signature中提取时间戳和签名 - 使用 Webhook 密钥、时间戳和请求体计算 HMAC-SHA256 签名
- 比较计算出的签名与请求中的签名是否一致
- 检查时间戳是否在合理范围内(防止重放攻击)
Node.js 验证示例
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const elements = signature.split(',');
const timestamp = elements[0].split('=')[1];
const v1 = elements[1].split('=')[1];
// 检查时间戳(5分钟内有效)
const now = Math.floor(Date.now() / 1000);
if (now - timestamp > 300) {
throw new Error('Timestamp too old');
}
// 计算签名
const signedPayload = timestamp + '.' + payload;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload, 'utf8')
.digest('hex');
// 比较签名
return crypto.timingSafeEqual(
Buffer.from(v1, 'hex'),
Buffer.from(expectedSignature, 'hex')
);
}
Python 验证示例
import hmac
import hashlib
import time
def verify_webhook_signature(payload, signature, secret):
elements = signature.split(',')
timestamp = int(elements[0].split('=')[1])
v1 = elements[1].split('=')[1]
# 检查时间戳(5分钟内有效)
if time.time() - timestamp > 300:
raise ValueError('Timestamp too old')
# 计算签名
signed_payload = f"{timestamp}.{payload}"
expected_signature = hmac.new(
secret.encode('utf-8'),
signed_payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
# 比较签名
return hmac.compare_digest(v1, expected_signature)
处理 Webhook 事件
收到 Webhook 请求后,您的应用程序应该:
1. 验证签名
首先验证请求签名,确保请求来自 GoGoPay。
2. 幂等性处理
同一个事件可能会被发送多次,您需要确保重复处理不会产生副作用:
// 使用事件 ID 进行幂等性检查
if (processedEvents.has(event.id)) {
return res.status(200).send('OK');
}
// 处理事件
processEvent(event);
// 记录已处理的事件
processedEvents.add(event.id);
3. 快速响应
Webhook 处理应该尽快返回 HTTP 200 状态码。复杂的业务逻辑应该异步处理:
app.post('/webhook', (req, res) => {
// 验证签名
if (!verifySignature(req.body, req.headers['gogopay-signature'])) {
return res.status(400).send('Invalid signature');
}
// 立即响应
res.status(200).send('OK');
// 异步处理事件
processEventAsync(req.body);
});
重试机制
如果您的服务器没有返回 HTTP 200 状态码,GoGoPay 会自动重试发送 Webhook:
- 重试次数:最多重试 5 次
- 重试间隔:指数退避算法(1分钟、5分钟、25分钟、2小时、10小时)
- 超时时间:每次请求超时时间为 30 秒
最佳实践
- 使用 HTTPS:确保 Webhook URL 使用 HTTPS 协议
- 验证签名:始终验证 Webhook 签名
- 幂等性:确保重复处理同一事件不会产生副作用
- 快速响应:尽快返回 HTTP 200,复杂逻辑异步处理
- 日志记录:记录所有 Webhook 事件用于调试
- 监控告警:监控 Webhook 处理失败情况
故障排查
如果 Webhook 没有正常工作,请检查以下项目:
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 未收到 Webhook | URL 配置错误或服务器不可达 | 检查 URL 配置和网络连通性 |
| 签名验证失败 | 密钥错误或算法实现有误 | 检查 Webhook 密钥和验证逻辑 |
| 重复接收事件 | 服务器响应非 200 状态码 | 确保正确返回 HTTP 200 |
| 处理超时 | 业务逻辑处理时间过长 | 优化处理逻辑或异步处理 |
下一步
了解了 Webhook 机制后,您可以:
- 注册 Webhook - 在控制台配置 Webhook 端点
- 测试支付 - 使用测试卡号验证 Webhook 功能
- 集成支付页面 - 完整的支付流程集成