You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Stripe中实现自定义月末到期的月度订阅计费周期?

自定义月度订阅计费周期实现方案

核心思路

通过Stripe的billing_cycle_anchor(计费周期锚点)或subscription_schedule(订阅计划),结合Node.js日期计算逻辑,让首周期截止到当月最后一天,后续自动按自然月续费,全程不依赖试用期。

具体实现步骤

1. 编写当月最后一天时间戳计算函数

先实现工具函数,精准计算订阅当日对应的当月最后一天UTC秒级时间戳(Stripe API要求的时间格式):

function getMonthEndTimestamp(subscribeDate = new Date()) {
  const year = subscribeDate.getUTCFullYear();
  const month = subscribeDate.getUTCMonth();
  // 取下个月第一天0点,减1毫秒得到当月最后一天23:59:59.999 UTC
  const monthEnd = new Date(Date.UTC(year, month + 1, 1, 0, 0, 0, 0));
  monthEnd.setUTCMilliseconds(monthEnd.getUTCMilliseconds() - 1);
  return Math.floor(monthEnd.getTime() / 1000);
}

2. 基于支付链接的两种实现方案

方案A:Webhook拦截修改订阅

利用Stripe的checkout.session.completed事件,在用户完成支付后立即调整订阅的计费锚点:

const stripe = require('stripe')('你的Stripe密钥');

// Webhook处理路由
app.post('/stripe-webhook', express.raw({type: 'application/json'}), async (req, res) => {
  const sig = req.headers['stripe-signature'];
  const webhookSecret = '你的Webhook密钥';

  let event;
  try {
    event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  if (event.type === 'checkout.session.completed') {
    const session = event.data.object;
    // 仅处理订阅类型的结账会话
    if (session.mode === 'subscription' && session.subscription) {
      const subscriptionId = session.subscription;
      const monthEndTimestamp = getMonthEndTimestamp();

      // 更新订阅锚点,关闭自动计价避免首月金额拆分
      await stripe.subscriptions.update(subscriptionId, {
        billing_cycle_anchor: monthEndTimestamp,
        proration_behavior: 'none',
        payment_behavior: 'default_incomplete'
      });
    }
  }

  res.json({received: true});
});

注意:需在Stripe后台启用checkout.session.completed事件,确保Webhook地址能被Stripe正常访问。

方案B:提前创建订阅计划关联支付链接

在用户进入支付流程前,先定义好订阅周期规则,避免Webhook延迟:

  1. 创建自定义订阅计划:
async function createCustomSchedule(customerId, priceId) {
  const monthEndTimestamp = getMonthEndTimestamp();
  const nextMonthStart = monthEndTimestamp + 86400; // 下月第一天UTC时间戳

  return stripe.subscriptionSchedules.create({
    customer: customerId,
    start_date: 'now',
    end_behavior: 'release', // 计划结束后转为普通订阅
    phases: [
      {
        items: [{price: priceId}],
        end_date: monthEndTimestamp, // 首周期截止当月最后一天
      },
      {
        items: [{price: priceId}],
        iterations: null, // 无限循环自然月续费
        interval: 'month',
        interval_count: 1,
        start_date: nextMonthStart,
      }
    ]
  });
}
  1. 创建支付链接时关联该计划:
const paymentLink = await stripe.paymentLinks.create({
  line_items: [{
    price: priceId,
    quantity: 1,
  }],
  subscription_data: {
    schedule: '上述创建的订阅计划ID',
  },
});

3. 关键注意事项

  • 计价控制:若需首月收取全额月费而非按天拆分,需设置proration_behavior: 'none',或在创建价格时指定recurring.usage_type: 'licensed'。
  • 时区处理:所有日期计算使用UTC时间,避免时区差异导致周期错误。
  • 权限配置:确保Stripe API密钥拥有subscriptions:update和subscription_schedules:create权限。

替代方案

若不想依赖Webhook或订阅计划,可在用户发起订阅前手动创建临时价格:设置首月计费周期为订阅日到当月最后一天,后续自动切换至正常月度价格。但此方式需维护临时价格资源,灵活性不如前两种方案。

内容的提问来源于stack exchange,提问作者javed iqbal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 22:27:25