订阅 Webhook
本页适用于 Halo 商城版 2.27.0 及以上版本。Webhook 的创建与投递记录查看见商城 / Webhook,订阅业务语义见订阅生命周期。
投递格式
Halo 以 POST 请求把事件投递到接入方配置的回调 URL,请求体是统一信封:
请求头:
验证签名
使用创建 Webhook 时填写的密钥,对未经解析的原始请求体验签,并使用常量时间比较:
X-Halo-Delivery-Timestamp 不参与签名,签名本身不提供防重放能力;如需防重放,请自行基于该请求头做时间窗校验。另外不要先解析再重新序列化 JSON,字节变化会导致验签失败。
返回状态与重试
Halo 等待响应的超时时间为 10 秒。请先完成验签与持久化,尽快返回 2xx,耗时处理放到后台任务。投递失败后可以在控制台的投递记录中查看请求与响应,并手动重新投递。
幂等与顺序
- 采用至少一次投递:自动重试与手动重投都可能产生重复请求。请以
X-Halo-Webhook-Id作为幂等键,处理过的 ID 直接返回2xx。 - 同一订单(或同一订阅)的事件通常按发布顺序投递,但重试不受顺序约束,可能出现交错。请以订阅的当前快照收敛本地状态,不要假设事件严格有序。
- 同一逻辑事件的多次投递共享同一个
X-Halo-Webhook-Id;手动重新投递时X-Halo-Delivery-Attempt会重置为 1。
订阅事件参考
SUBSCRIPTION_RENEWAL_FAILED 与 SUBSCRIPTION_CHANGE_FAILED 会出现在控制台的事件列表中,但当前版本不会产生投递:支付失败不会改变订阅状态,PAST_DUE 仅由到期时间驱动。
试用中的订阅调用续费入口时生成的是转正单,不会触发 SUBSCRIPTION_RENEWAL_ORDER_CREATED;转正支付完成后会触发 SUBSCRIPTION_TRIAL_CONVERTED。
载荷字段
所有订阅事件的 data.subscription 结构一致,是事件发生之后的订阅快照,不包含订单号与变更前后明细:
需要订单号、差价明细或变更前后的周期时,请用 Console 查询接口补全:续费单、转正单与变更单本身也是订单,会同时触发订单域事件。
关联的订单事件
订阅的续费单、转正单与变更单都是普通订单,因此还会触发订单域 Webhook:
ORDER_CREATED:订单创建。ORDER_PAID:订单支付完成。应付金额为 0 的订单在创建时即视为已支付,会同时触发ORDER_CREATED与ORDER_PAID。FULFILLMENT_REQUESTED:需要接入方交付订阅行时的交付请求,见履约回调。
订单载荷中的 data.order.items[].subscriptionMetadata 可以区分订单来源:
处理建议
- 验签 → 用
X-Halo-Webhook-Id去重 → 用data.subscription覆盖本地快照 → 按新的status与entitlements调整限额。 - 开通与关闭权益必须幂等,重复投递不得叠加额度。
- 以 Halo 下发的快照为准,不要在本地自行推算周期,详见权益如何判定。
- 控制台的发送测试事件会投递
WEBHOOK_TEST,载荷是订单结构且字段与真实事件不完全一致(例如收货地址使用的是测试字段),请勿写入业务数据。