PAYATHON 2026

如何使用 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单价
产品 Aproduct_a€10
产品 Bproduct_b€6
产品 Cproduct_c€8
产品 Dproduct_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,可以采用以下方式:

  1. 使用 €0 subscription 保存客户的月度服务关系和续订日期;
  2. 在自己的系统中保存客户组合包;
  3. 每次确认配送后,为本次商品创建 one-time charge;
  4. 将这些 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 更适合这个业务模型。

备注:内容仅供参考。