Webhook 介绍

Webhook 是 GoGoPay 向您的应用程序发送实时事件通知的机制。 通过 Webhook,您可以及时了解支付状态变化,确保业务流程的准确性。

什么是 Webhook?

Webhook 是一种 HTTP 回调机制,当特定事件发生时,GoGoPay 会向您预先配置的 URL 发送 POST 请求。这种方式比轮询 API 更高效,能够实时获取事件通知。

为什么使用 Webhook?
  • 实时性:事件发生时立即通知,无需轮询
  • 可靠性:自动重试机制确保消息送达
  • 高效性:减少不必要的 API 调用
  • 安全性:签名验证确保数据完整性

Webhook 工作流程

  1. 事件触发:支付状态发生变化(如支付成功、失败等)
  2. 生成通知:GoGoPay 系统生成相应的事件数据
  3. 发送请求:向您配置的 Webhook URL 发送 POST 请求
  4. 验证签名:您的服务器验证请求签名
  5. 处理事件:根据事件类型执行相应的业务逻辑
  6. 返回响应:返回 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 会在请求头中包含签名信息。您需要验证签名以确认请求来源的合法性。

验证步骤

  1. 从请求头 GoGoPay-Signature 中提取时间戳和签名
  2. 使用 Webhook 密钥、时间戳和请求体计算 HMAC-SHA256 签名
  3. 比较计算出的签名与请求中的签名是否一致
  4. 检查时间戳是否在合理范围内(防止重放攻击)

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 机制后,您可以: