使用试用和促销代码管理条纹订阅

问题描述 投票:0回答:1

我正在使用 Next.js、Supabase 和 Stripe 构建基于订阅的 SaaS 产品。

到目前为止,它正在发挥作用,但我正在努力寻找正确的付款工作流程。

我提供两种类型的订阅:每月 5 欧元,每年 50 欧元。这被建模为一个

Product
和两个
Price

这两个计划都可以完全访问所有应用程序功能,唯一的区别是一年后节省了多少钱。

我想为这两个计划提供 30 天的试用期。 30 天试用期结束后,订阅将

active
变成所选的两个计划之一。

我使用 webhooks 更新 Supabase 中的数据库,其中存储了

products
prices
customers
subscriptions
users
,遵循官方模板。

我目前有两个促销代码,可以随时应用于任一计划(试用期或有效订阅期间):

  1. 永远免费:对于亲密的人,我想赠送该应用程序
  2. 免费 X 个月:适用于会使用该应用程序并提供反馈的潜在测试人员

当前的工作流程是这样的:

  1. /register
    页面上,用户选择其中一个计划。我将
    priceId
    存储在网址中,然后显示注册表单(只需电子邮件和密码)。
  2. 当用户注册时,我使用 stripe API 静默创建客户和订阅,然后触发将这些事件存储在我的数据库中的事件。订阅是使用选定的价格计划和 30 天的试用期创建的。不需要付款详细信息,因为我在幕后创建了所有内容。
  3. /account/billing
    页面的任何位置,用户都可以应用现有促销代码之一。他们还可以访问客户门户来添加付款详细信息。

根据我对 stripe API 的了解,我似乎找不到正确的方法来处理事情。

  1. 如果用户应用“永久免费”促销代码,我会立即结束试用并使用兑换的优惠券开始“年度”订阅。这有效。
  2. 如果用户申请“免费X个月”,事情就会变得更加复杂。理想情况下,我希望将下一次(或第一次,如果
    trialing
    )付款推迟到促销代码所规定的“几个月”。我没有找到方法来做到这一点,因为我无法将
    current_period_end
    billing_cycle
    修改为任意值。

我对这种情况最接近的是使用“订阅时间表”:我分两个阶段创建一个时间表:

  1. 第一阶段:使用优惠券免费 X 个月,从
    trial_ends
    (如果
    trialing
    )或
    current_period_end
    (如果
    active
  2. 开始
  3. 第二阶段:X个月过去后,回退到所选价格计划并相应收费。

这种方法似乎有效,但如果用户在第一阶段选择更改计划,事情就会变得混乱。有一个解决方法(拦截

subscription.update
事件并相应地更新数据),但我认为这会让事情变得过于复杂。 另一种方法是更改/延长试用期,但我更希望用户
trialing
与使用优惠券
active
订阅的用户之间有明确的界限。

  1. 我应该改变当前的工作流程吗?
  2. 有更好的实现方法吗?
  3. 使用优惠券的情况下还有什么办法可以将“下次付款”推X个月吗?
export const addPromoCode = async (code: string) => {
  const annualPriceId = PRICES.ANNUAL;
  const monthlyPriceId = PRICES.MONTHLY;

  let coupon = undefined;

  try {
    coupon = await getPromoDetails(code);
  } catch (error: any) {
    return { status: FORM_STATUS.ERROR, message: error.message };
  }

  const {
    percent_off,
    id: couponId,
    duration,
    duration_in_months,
  } = coupon || {};

  const isFreeForever = percent_off === 100 && duration === "forever";

  const { id, trial_end, current_period_end, customer } =
    await createOrRetrieveSubscription(
      isFreeForever ? annualPriceId : monthlyPriceId
    );

  if (isFreeForever) {
    await stripe.subscriptions.update(id, {
      discounts: [{ coupon: couponId }],
      trial_end: "now",
    });
  } else if (duration_in_months) {
    await stripe.subscriptionSchedules.create({
      customer: typeof customer === "string" ? customer : customer.id,
      start_date: trial_end || current_period_end,
      end_behavior: "release",
      phases: [
        {
          items: [{ price: monthlyPriceId, quantity: 1 }],
          iterations: duration_in_months,
          billing_cycle_anchor: "phase_start",
          proration_behavior: "none",
          discounts: [{ coupon: couponId }],
        },
        {
          items: [{ price: monthlyPriceId, quantity: 1 }],
        },
      ],
    });
  }

  revalidatePath(LINKS.BILLING);

  return {
    status: FORM_STATUS.SUCCESS,
    message: "Promo code applied successfully!",
  };
};
stripe-payments supabase nextjs14
1个回答
0
投票

不幸的是,没有更优雅的解决方案。您使用订阅计划 API 构建的工作流程是处理此用例的最优雅的方式。

© www.soinside.com 2019 - 2024. All rights reserved.