订阅生命周期
本页适用于 Halo 商城版 2.27.0 及以上版本。传输与鉴权约定见商城订阅对接,事件推送见订阅 Webhook。
数据模型
订阅由四层对象组成,由运营在控制台 商店 → 商品 中配置(操作说明见商城 / 订阅管理):
产品线策略决定同一客户能否并存多条订阅、试用是否只发一次、到期前提前几天提醒:
计划字段中与周期相关的取值:
接入方需要长期依赖的关联键:
customerId:商城客户 ID,用于与接入方账号对齐(控制台可通过顾客接口换取userId、邮箱)。productId:产品线 ID。planId:档次内的具体套餐,请以此判断档位,不要用价格或名称。variantId:计划对应的商品规格 ID,订单行使用它标识订阅行。
状态机
仅当产品线策略 singleSubscription=true 时,重新购买会复用 EXPIRED / CANCELLED 的历史订阅行;为 false 时每次购买都会新建订阅。此外,如果客户重新购买时仍满足试用资格(trialOncePerCustomer=false),订阅会回到 TRIALING 而非 ACTIVE。
TRIALING、ACTIVE、PAST_DUE 统称为生效订阅。只有生效订阅会阻止同一客户重复购买(singleSubscription=true 时),权益查询也只返回这些订阅。
周期与时间字段
周期长度按 UTC 计算:MONTHLY 使用 plusMonths(periodCount),YEARLY 使用 plusYears(periodCount)。
一条订阅通常经历以下阶段:
- 购买(PURCHASE):支付成功后开通订阅,写入首个周期与权益快照。命中试用时为
TRIALING,否则为ACTIVE;买断订阅的周期字段为null。 - 试用转正(TRIAL_CONVERT):客户在客户中心续费(试用期使用同一入口)并支付后转为
ACTIVE,首期从trialEndAt起算。 - 续费(RENEWAL):客户手动续费并支付后,覆盖终点顺延一个周期,状态保持或恢复为
ACTIVE。 - 到期:连续计费进入
PAST_DUE并开始宽限期;预付一期直接EXPIRED;已勾选到期取消则转为CANCELLED。 - 计划变更(PLAN_CHANGE):支付完成(或免费变更)后立即生效,并从生效时刻重新开一期。
- 取消(CANCEL):客户勾选到期取消后
cancelAtPeriodEnd=true,当期继续有效,到期转为CANCELLED。
续费与到期
Halo 不保存支付凭据,也不会自动扣款,任何续费都必须由客户主动发起:
- 到期前
renewalLeadDays天,Halo 发送一次SUBSCRIPTION_RENEWAL_REMINDER并给客户发提醒邮件;同一个到期时点只发一次,已勾选到期取消的订阅不发提醒。只有连续计费(billingMode=SUBSCRIPTION)的计划会发送提醒,预付一期到期直接过期、不提醒,买断没有到期。 - 客户在客户中心点击续费,生成一张续费订单(连续计费)或转正订单(试用中),支付成功后周期与状态更新。
- 续费、转正、变更订单与普通订单一致,24 小时未支付会自动过期;价格按下单时的现价计算,订阅上不保存价格快照。
- 试用到期后不会自动生成转正订单;超过
trialEndAt加宽限期(gracePeriodDays,最少 1 天)仍未支付即EXPIRED。 - 到期转移顺序:
cancelAtPeriodEnd=true优先转为CANCELLED;否则连续计费进入PAST_DUE,预付一期直接EXPIRED。
如需为客户钱包自动扣款,需要在客户登录态下发起续费(客户中心接口)取得订单,再由运营侧令牌调用 POST /orders/{id}/mark-as-paid 完成支付。Halo 当前没有面向接入方的「代客户创建续费单」接口。
计划变更
- 只支持升级与平移,且两端必须是相同的
billingPeriod;降级与跨周期变更不被支持。 - 每个方向都必须由运营显式配置一条启用中的变更规则(
fromPlanId → toPlanId)。没有规则的方向一律拒绝,即使是升级。 - 费用模式由规则决定:
FREE(免费,立即生效且不产生订单)、FIXED_FEE(按固定单价 × 数量)、PRORATED(新一期全价减去当前计划未使用的剩余价值,基数为计划标价)。 - 变更会重置周期:生效后
currentPeriodStartAt为生效时刻,currentPeriodEndAt与paidThroughAt为生效时刻加目标计划的周期。接入方必须使用事件或查询返回的新的paidThroughAt更新到期时间,不要沿用旧到期日,也不要按剩余天数顺延。 - 变更不修改数量,数量沿用订阅当前值。
- 订阅已过期或已取消、仍在试用期、存在待支付变更单时,变更会被拒绝。
取消
- 客户在客户中心取消(默认到期取消):写入
cancelAtPeriodEnd=true,当期权益继续有效,到期时转为CANCELLED。 - 立即取消仅在该订阅已进入
PAST_DUE时允许客户自助发起;运营可以在控制台对未终止的订阅立即取消。 - 试用中的订阅勾选到期取消后,到期会按试用过期处理并进入
EXPIRED。
权益如何判定
entitlements 是运营与接入方约定的契约,Halo 只负责快照与下发,不解释也不强制执行。已付费覆盖终点不等于宽限期截止时间,不能仅用 paidThroughAt 或 currentPeriodEndAt 决定是否停用权益:
宽限期内的访问
PAST_DUE 仍是生效订阅,权益查询会继续返回其契约。接入方可以保留访问或按业务规则限流;不要仅因为 paidThroughAt 已过就把它当作 EXPIRED。
2.27 调度器按 (paidThroughAt ?? currentPeriodEndAt) + gracePeriodDays × 24 小时 计算续费宽限截止时间,达到截止时间后转为 EXPIRED。gracePeriodDays 为空时按 0 计算;它来自订阅当前计划,并非订阅快照字段。需要计算截止时间时,先通过 planId 查询计划,不能假设 Webhook 或订阅详情已经包含宽限天数。
例如已付费覆盖到 10 月 16 日 00:00 UTC、计划宽限期为 3 天,保留宽限期访问的截止时间是 10 月 19 日 00:00 UTC。试用转正宽限的规则不同,为 trialEndAt + max(gracePeriodDays, 1) × 24 小时;在此期间状态仍为 TRIALING。
状态转换由定时任务执行,Webhook 也可能延迟或乱序。接入方应查询当前订阅及计划核对状态和截止时间,不要仅等待过期通知,也不要使用旧快照延长已取消订阅的权益。
需要注意:
TRIALING期间currentPeriodStartAt/currentPeriodEndAt描述的是转正后的第一个周期,不能当作当前有效周期,直接使用会把到期时间显示成「试用结束加一个周期」。- 权益快照在购买、变更应用、续费时重算。运营修改档次的
entitlements不会立即推送给已有订阅,要等下一次续费或变更。 - 使用查询确认的当前快照整体更新本地权益,不要在本地累加周期或额度;Webhook 可能乱序,不能直接用迟到的事件快照覆盖当前状态,处理流程见幂等与顺序。
查询与对账接口
Console API(前缀 https://{host}/apis/console.api.ecommerce.halo.run/v1alpha1,需要管理员令牌):
客户中心 API(前缀 https://{host}/apis/uc.api.ecommerce.halo.run/v1alpha1,需要客户登录态)由 Halo 页面使用,接入方通常不需要直接调用;如需代客户发起,必须在客户登录态下调用:
/uc 段
客户中心接口的路径是 /apis/uc.api.ecommerce.halo.run/v1alpha1/subscriptions,不要再加一层 /uc。写成 /v1alpha1/uc/subscriptions 会因权限校验把首段路径当作资源名而返回 403。
以上仅为常用接口。字段与状态码的完整定义以运行实例的 API 文档为准,也可以查阅 Halo 商城版 2.27 RESTful API 中的 Console 和 User-center 分组,对应 console.api.ecommerce.halo.run 与 uc.api.ecommerce.halo.run。