Webhooks 集成
Webhook 用于在 GStable 侧发生重要变更时主动通知你的服务端(例如支付会话状态变化、结算完成等)。你只需提供稳定的 HTTPS 地址、订阅所需的事件类型,并在校验签名后处理 JSON 载荷,无需高频轮询业务接口。
本文是落地集成指南,分为两部分:
- 管理侧 — 使用
gstable-js的client.webhook维护端点配置。 - 投递侧 — 实现 HTTP 处理逻辑:校验 → 快速返回 2xx → 异步执行业务。
载荷结构、请求头与签名字符串构造等细节以 API 文档为准,请务必阅读 校验与处理。下文侧重要做什么与推荐顺序,不展开内部实现细节。
前置条件
- Node.js 18+ 与
gstable-js(包与模块格式)。 - 具备在商户侧管理 Webhook 的 API 密钥。
- 可对外提供 HTTPS 公网地址(生产环境需受信任证书),能接收 POST,并在校验路径下读取原始请求体(未经框架提前改写)。
- 已确认要订阅的 事件类型,
subscribedEvents中的字符串需与列表完全一致。
推荐集成顺序
- 设计 — 列出业务要响应的结果(如订单标记已支付、释放库存),映射到 事件列表 中的 eventType。
- 先实现接收端(建议预发环境) — 完成路由:校验签名与时间戳、解析信封、尽快返回 2xx、将任务放入队列。
- 注册端点 — 调用
client.webhook.create,填写 HTTPS URL、可读名称与subscribedEvents。 - 保管签名密钥 — 响应中的
key用于校验入站请求;写入密钥管理服务或仅限校验服务读取的环境变量,勿入库日志、勿提交到代码仓库。 - 上线 — 生产环境指向同一套处理逻辑;按计划或应急使用
refreshKey;维护窗口可disable端点暂停投递。 - 运维 — 关注投递失败与死信;以
eventId做去重(至少一次投递语义)。
第一部分 — 使用 gstable-js 管理端点
通过 client.webhook 维护 GStable 存量的回调配置:URL、订阅事件、描述、启停、轮换密钥等。
| 目标 | 调用 |
|---|---|
| 注册 URL 与事件 | client.webhook.create(body) |
| 全量更新名称、URL、事件等 | client.webhook.update(body) |
| 列举与审计 | client.webhook.list() |
| 查看单个端点 | client.webhook.detail(webhookId) |
| 暂停投递 | client.webhook.disable(webhookId) |
| 恢复投递 | client.webhook.enable(webhookId) |
| 轮换签名密钥 | client.webhook.refreshKey(webhookId) — 旧密钥立即失效 |
| 删除配置 | client.webhook.remove(webhookId) |
create 用于新增;update 一般为整包替换(需带齐必填字段)。字段说明见 Webhooks(SDK API)。
示例 — 注册生产端点
import { GStableClient } from 'gstable-js';
const client = new GStableClient({ apiKey: process.env.GSTABLE_API_KEY });
const hook = await client.webhook.create({
webhookName: 'Production ledger',
webhookUrl: 'https://api.example.com/v1/webhooks/gstable',
subscribedEvents: ['payment.completed', 'payment.failed'],
webhookDescription: '订单与财务消费方',
});
// 持久化 hook.webhookId,供后续 update / disable 使用。
// hook.key 仅存于密钥系统 — 用于校验入站 Webhook。
事件名必须完全一致
subscribedEvents 中的每一项须与 事件列表 一致;拼写错误或不受支持的名字无法按预期触发。
密钥轮换建议
refreshKey后旧密钥即刻作废,仍可能在途的请求使用旧密钥极短时间。- 可考虑轮换窗口内短时双密钥、先
disable再轮换、或在轮换前排空队列。 - 轮换完成后,务必更新校验服务使用的密钥(环境变量/密钥库)。
第二部分 — 接收并处理回调
SDK 不会替你校验入站 Webhook。你的服务需要:
1. 与注册信息一致的 URL
- 生产环境 HTTPS、证书受信。
- 响应 POST,正文为 JSON(统一信封字段如
eventId、eventType、payload等见 校验与处理)。
2. 用「原始 body」做签名
签名字符串必须与 GStable 实际发送的字节序列一致。若框架在校验前已解析 JSON,可能导致签名校验失败。请按文档使用 x-gstable-timestamp 与 x-gstable-signature。
3. 先校验,再执行业务
严格按 校验与处理 构造待签名字符串、计算 HMAC、常量时间比对,并按需校验时间戳重放窗口。
仅在确认无效请求时返回 4xx,并理解其与重试策略的关系(见下节)。
4. 快速应答
通过校验与基础校验后,应尽快返回 200–299。重数据库、调用第三方等耗时操作应异步化(队列/后台任务),避免 GStable 因超时而重试堆积。
5. 按「至少一次」设计幂等
同一逻辑事件可能投递多次。请使用 eventId(或其它稳定业务幂等键)在副作用前去重。
6. 以载荷为准
结合 eventType、businessType 与 payload 内文档化字段解析业务数据(例如会话类事件中的 sessionData 等)。
重试与错误响应
- 当你的服务不可用或返回
5xx时,平台通常会按策略退避重试(具体以 Webhook 产品文档为准)。 - 校验失败返回
4xx通常合理;若用5xx表示临时故障,应确保处理逻辑可安全重试。
安全清单
- TLS:生产勿用自签名证书。
- 密钥:签名密钥仅存放在密钥系统/校验服务环境;人员变动时轮换。
- 日志:_payload / 头信息做脱敏;绝不打印签名密钥。
- 重放:若规范要求时间窗,应拒绝过期时间戳(详见校验文档)。
排查思路
| 现象 | 建议检查 |
|---|---|
| 签名始终失败 | 是否使用原始 body、密钥是否一致、字符串拼接与编码是否与文档一致。 |
| 重复扣款/重复更新 | 是否缺少对 eventId 或业务键的幂等处理。 |
| 超时、重试风暴 | 是否在 HTTP 线程内做重活 — 应异步化。 |
| 收不到事件 | URL 错误、证书问题、长期非 2xx 等。 |
延伸阅读
- 校验与处理 — 信封字段、请求头、签名步骤。
- 事件列表 — 仅订阅受支持的
eventType。 - Webhooks(SDK API) — 管理接口与 TypeScript 类型。
- 错误码 —
client.webhook.*管理接口返回的错误。