如何使用 Recurly 实现 Products as a Service 订阅
结论
建议采用以下建模方式:
- 为整个服务创建一个价格为
0的月度 plan; - 将产品 A、B、C、D 分别定义为 add-on。在支持 Item Catalog 的 Recurly 配置中,也可以定义为 item-backed add-on;
- 每位客户只保留一个 subscription;
- 用 subscription add-on 的
quantity表示每月配送数量; - 客户调整组合包时,创建 subscription change,并让变更从下一计费周期开始生效;
- 如果必须等配送完成后再收费,可以使用 usage-based add-on,也可以由业务系统在配送时创建一次性 charge/invoice。
不必为每种组合包单独创建 plan 或 subscription。Add-on 定义产品目录,subscription 上的 add-on 列表及数量则表示客户当期选择的实际组合。
推荐的数据模型
假设产品单价如下:
| 产品 | Add-on code | 单价 |
|---|---|---|
| 产品 A | product_a | €10 |
| 产品 B | product_b | €6 |
| 产品 C | product_c | €8 |
| 产品 D | product_d | €12 |
先创建一个月度 plan:
Plan: products_as_a_service
Billing period: 1 month
Base price: €0.00
客户最初选择 1 个 A 和 2 个 B 时,只需创建一个 subscription:
{
"plan_code": "products_as_a_service",
"currency": "EUR",
"add_ons": [
{
"code": "product_a",
"quantity": 1
},
{
"code": "product_b",
"quantity": 2
}
]
}
该周期的商品金额是:
1 × €10 + 2 × €6 = €22
这里的 JSON 只用于说明数据结构。Recurly 的不同 API 版本可能使用 add_ons、subscription_add_ons、add_on_code 或 item code 等不同字段名,实际应以当前使用的 Recurly API 和客户端 SDK 为准。
每月修改组合包
如果客户删除产品 A,保留 2 个产品 B,再添加 1 个产品 C,可以提交一次 subscription change:
{
"timeframe": "renewal",
"add_ons": [
{
"code": "product_b",
"quantity": 2
},
{
"code": "product_c",
"quantity": 1
}
]
}
设置 timeframe: "renewal" 后,原组合会继续覆盖当前周期,新组合从下一周期开始生效。这样通常还能避免立即变更引起按比例补收或退款。
变更后,下一个周期的金额是:
2 × €6 + 1 × €8 = €20
实施前需要确认当前 API 更新 add-on 时采用哪种语义:
- 全量替换:请求中必须包含变更后需要保留的全部 add-on;
- 增量修改:新增、更新和删除需要分别提交。
如果接口采用全量替换,只发送产品 C 可能会导致产品 B 被意外移除。
“不预先收费”需要单独处理
Plan 的价格为 0,不代表 add-on 一定会在周期结束时收费。固定数量 add-on 通常会随订阅账期一起开票,具体时间由站点配置、plan 配置和收费模式决定。
因此,首先要明确这里的”不预先收费”具体指什么。
方案一:账期开始收费,但不收注册费
如果”不预先收费”只是指:
- 不收 setup fee;
- 不要求预存余额;
- 创建订阅时不额外收费;
那么可以使用 €0 plan 加固定数量 add-on。为了避免创建 subscription 时立即产生首期商品费用,可以设置 trial,让第一次正常计费发生在首个配送周期对应的日期。
Trial 只能推迟首次计费,并不等于以后都会采用后付费。
方案二:配送完成后按实际数量收费
如果要求先配送、后收费,可以把各产品配置为 usage-based add-on,等配送确认后再记录实际数量。例如,本月配送 1 个 A 和 2 个 B:
[
{
"add_on_code": "product_a",
"amount": 1,
"recorded_at": "2026-08-29T10:00:00Z"
},
{
"add_on_code": "product_b",
"amount": 2,
"recorded_at": "2026-08-29T10:00:00Z"
}
]
Usage-based billing 不要求固定月费大于零。基础 plan 可以为 0,各产品仍按以下方式计费:
产品费用 = 单价 × 本周期实际配送数量
这种计费方式仍然是线性的,只是计量单位由”分钟”换成了”件”。
需要注意的是,usage 通常表示已经发生的消费或配送,不适合直接表示客户下个月想要的组合。业务系统仍需保存”下一次配送配置”,并在实际发货后向 Recurly 提交 usage。
方案三:订阅负责周期,商品通过一次性 charge 开票
如果 Recurly 当前配置不便将实体商品建模为 usage add-on,可以采用以下方式:
- 使用
€0subscription 保存客户的月度服务关系和续订日期; - 在自己的系统中保存客户组合包;
- 每次确认配送后,为本次商品创建 one-time charge;
- 将这些 charge 汇总到 invoice 后收款。
这种方式更方便处理换货、缺货、临时加购和实际配送数量调整,但商品组合、履约状态和幂等控制需要由业务系统负责。
不建议为组合包动态创建 plan 或 subscription
不要把”1 个 A + 2 个 B”定义成一个新 plan。随着组合数量增加,系统会很快积累大量相似但不同的 plan 或 subscription,历史数据和报表也会越来越难管理。
更合适的数据关系是:
一个客户
└── 一个 subscription
├── 产品 A × 1
└── 产品 B × 2
下个月只需修改同一个 subscription 的 add-on,或更新业务系统里的配送配置,无需创建新的 subscription。
实施时的注意事项
- 产品定义和客户选择需要分开保存。产品价格放在目录或 add-on 中,客户数量放在 subscription add-on 或配送记录中。
- 下一期变更尽量设置为 renewal 生效,以免意外触发 proration。
- 如果产品价格会调整,需要确认 Recurly 如何处理已有 subscription 的 add-on 单价,不能假设修改目录价格后会自动更新所有存量订阅。
- 如果客户一个月内多次修改组合,业务系统应保存最终版本,并明确修改截止时间。
- 提交 usage 或 charge 时,应以配送单号作为幂等依据,防止重试导致重复计费。
- Recurly 可以处理订阅账期、发票和收款,但通常不管理库存、拣货、配送状态和组合包版本,这些信息仍需保存在业务系统中。
- 取消 subscription 和修改组合包是两种不同操作。客户清空组合包时,需要明确其含义是暂停配送、以零商品续订,还是取消整个 subscription。
如果每月商品数量在计费前已经确定,优先使用 €0 plan + fixed add-ons + quantity + renewal change。如果金额必须根据实际配送结果计算,则使用 €0 plan + usage-based add-ons。两种方式都比为每个组合单独创建 subscription 更适合这个业务模型。
备注:内容仅供参考。
