深入探索 Stripe 订阅计划:如何巧妙使用 Subscription Schedule

在使用 Stripe API 更新订阅(Subscription)时,虽然 API 本身并不复杂,但某些情况下,由于账单日期或产品订阅类型的原因,我们无法立即更新订阅。这时,Stripe 的 Subscription Schedule 功能就显得尤为重要。它允许我们在未来某个时间点创建或更新订阅,从而满足复杂的业务需求。

Subscription Schedule 的强大功能

Subscription Schedule 是一个功能强大的工具,但初次使用时可能会让人感到困惑。尽管 Stripe 提供了详尽的 API 文档Use Cases 来解释其用法,但由于 API 的不断更新,某些字段可能会被弃用,或者新的对象被引入,导致文档与实际操作存在差异。例如,Price 已经取代了 Plan,而在 Phase Items 中,coupon 字段也被弃用。

在本文中,我将分享一个实际案例:如何在未来某个时间点安排更新单个 Subscription Item 的数量(quantity),同时保留现有的优惠券(coupon)。我还将讨论在这一过程中遇到的挑战及其解决方案。

场景设定

假设我们有一个 Subscription,其中包含一个 Subscription Item——SaaS Member Fee,单价为 $10,数量为 5,并且附带一个三个月的免费优惠券。我们的目标是在 9 月 1 日将数量更新为 10,从而实现以下效果:

Subscription Schedule

如果你只想快速查看解决方案,可以直接跳到 解决方案 部分。

Stripe 内部的实现方式

尽管有详细的 API 文档和用例,初次对接 Subscription Schedule API 时,仍然会感到困惑。例如,我们知道需要创建一个 Subscription Schedule,但具体如何传递参数仍不清楚。

对于创建 Schedule 的 API POST /v1/subscription_schedules,文档中提供的示例如下:

ruby
Stripe::SubscriptionSchedule.create({
customer: ‘cus_NcI8FsMbh0OeFs’,
start_date: 1680716828,
end_behavior: ‘release’,
phases: [
{
items: [
{
price: ‘price_1Mr3YcLkdIwHu7ixYCFhXHNb’,
quantity: 1,
},
],
iterations: 12,
},
],
})

然而,这个示例并不适用于我的场景,因为我们需要修改一个已经存在的 Subscription。文档中提到,可以使用 from_subscription 参数,它接受一个现有 Subscription 的 ID。文档还指出,如果使用了这个参数,其他参数(如 phase 值)就不能再设置,建议通过两次 API 调用来实现。

通过观察 Stripe 自身的操作,我们可以发现,Stripe 也是通过两次 API 调用来实现这一功能的:

第一次 API 请求

ruby
POST /v1/subscription_schedules

Request Body:
{
from_subscription: “sub_1PeCNNLiQSlGYPd5Wpmvb1n6”
}

Response:
{
id: “sub_sched_1PeH6LLiQSlGYPd599aOVdoW”,
# … 其他字段省略
}

第二次 API 请求

ruby
POST /v1/subscription_schedules/sub_sched_1PeH6LLiQSlGYPd599aOVdoW

Request Body:
{
proration_behavior: “none”,
phases: [
{
start_date: 1721378477,
end_date: “now”,
iterations: “”,
discounts: [
{
coupon: “EDHtWtdk”
}
],
default_tax_rates: “”,
automatic_tax: {
enabled: false
},
items: [
{
price: “price_1PeCLRLiQSlGYPd50cFz00Na”,
quantity: 5
}
],
collection_method: “charge_automatically”
},
{
start_date: “now”,
end_date: 1725217200,
discounts: [
{
coupon: “EDHtWtdk”
}
],
default_tax_rates: “”,
items: [
{
quantity: 5,
tax_rates: “”,
price: “price_1PeCLRLiQSlGYPd50cFz00Na”
}
]
},
{
start_date: 1725217200,
discounts: [
{
coupon: “EDHtWtdk”
}
],
default_tax_rates: “”,
items: [
{
quantity: 10,
tax_rates: “”,
price: “price_1PeCLRLiQSlGYPd50cFz00Na”
}
],
proration_behavior: “none”,
collection_method: “charge_automatically”,
invoice_settings: {
description: “Thank you for your business!”
}
}
],
end_behavior: “release”
}

关键概念解析

什么是 Phase?

Phase 是 Subscription Schedule 中的一段特定时间,在这段时间内会应用所指定的 Price、Charge、Tax 等参数。一个 Phase 的关键参数包括:

  1. start_dateend_date:标识每个 Phase 的开始和结束时间。
  2. pricequantity:为订阅项目指定不同的价格和数量。
  3. discounts:在该 Phase 中使用的特定折扣和优惠券。
  4. tax_ratesautomatic_tax:税率相关参数。

从上述请求中可以看到,Stripe 设定了 3 个 Phase:

  1. Phase#0:立即终止当前订阅。
  2. Phase#1:从当前时间到预期时间,保持原有订阅参数。
  3. Phase#2:在预期时间更新数量为 10。

实际上,使用两个 Phase 也是可行的。

什么是 Proration 和 Proration Behavior?

Proration 是指在订阅计划发生变化时,按照比例调整账单金额。Stripe 提供了三种 proration_behavior 选项:

  • create_prorations:默认值,按比例调整账单。
  • none:不进行按比例调整。
  • always_invoice:立即创建按比例调整的账单。

例如,如果客户在账单周期中间升级了订阅,选择 create_prorations 会按比例计算剩余天数的费用,而选择 none 则不会进行任何调整。

什么是 End Behavior?

end_behavior 定义了 Schedule 结束时 Subscription 的行为,其值可以是 releasecancel

  • release:默认值,Subscription 保持当前状态继续。
  • cancel:Subscription 直接结束。

需要注意的是,Stripe 只允许一个 Subscription 最多有一个激活状态的 Schedule。如果遇到错误 “You cannot migrate a subscription that is already attached to a schedule”,说明当前 Subscription 已经有一个 Schedule,此时需要先 release 或 cancel 它。

解决方案

假设我们有一个 subscription,希望在 time 时修改其 item 的 quantity。我们只需要使用两个 Phase:

ruby
schedule = Stripe::SubscriptionSchedule.create from_subscription: subscription.id

phase0 = schedule.phases[0]
phase0_item = phase0.items[0]

original_quantity = phase0_item.quantity
price_id = phase0_item.price
discounts = phase0.discounts.collect(&:to_hash)

phases = [
# Phase 0 – 修改现有订阅阶段,结束日期为 time
{
start_date: phase0.start_date,
end_date: time.to_i,
proration_behavior: ‘none’,
discounts: discounts,
items: [
{
price: price_id,
quantity: original_quantity,
}
]
},
# Phase 1 – 设置一个新的订阅阶段,开始日期为 time,数量为 quantity
{
start_date: time.to_i,
end_date: time.to_i + 60,
proration_behavior: ‘none’,
discounts: discounts,
items: [
{
price: price_id,
quantity: quantity,
}
]
}
]

Stripe::SubscriptionSchedule.update schedule.id, phases: phases, proration_behavior: ‘none’

结语

Stripe 的 Subscription Schedule 功能为订阅管理提供了极大的灵活性。尽管初次使用时可能会遇到一些挑战,但通过深入理解其机制和多次实践,我们可以充分发挥其潜力。希望本文能帮助你更好地理解和使用 Subscription Schedule。

👉 野卡 | 一分钟注册,轻松订阅海外线上服务

上一篇 2天前
下一篇 2天前

热门推荐