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

如何让Stripe Checkout识别客户历史购买量以应用阶梯定价?

Stripe Checkout阶梯定价追加购买时价格计算错误的解决方法

问题场景

现有Stripe Checkout会话代码配置了阶梯定价的价格ID:

  • 首次购买60个许可证时,结算页面价格符合预期;
  • 同一客户完成首次购买后,再次购买5个许可证,预期价格为100美元,但结算页面显示错误价格。

现有代码

exports.createCheckoutSession = async (totalLicenses, currentURL, organizationID) => {
  try {
    const stripeCustomerID = await database.getStripeCustomerIDFromOrganizationID(organizationID);

    // 如果客户不存在则返回错误
    if (!stripeCustomerID) {
      throw new Error('Stripe客户不存在。');
    }

    const session = await stripe.checkout.sessions.create({
      customer: stripeCustomerID,
      mode: 'subscription',
      line_items: [
        {
          price: 'price_2321srdspiOm5Cg52a',
          quantity: 60,
        },
      ],
      custom_text: {
        submit: {
          message: '我们会通过邮件发送课程注册指南文档',
        },
      },
      allow_promotion_codes: true,
      success_url: `${currentURL}/admin/licenses?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${currentURL}/cancel.html`,
    });

    return {
      url: session.url,
    };
  } catch (err) {
    console.trace(err);
    throw err;
  }
};

问题原因

Stripe默认不会自动将客户现有订阅的数量与新结账会话的购买数量合并计算阶梯定价,新会话只会基于本次传入的quantity单独计算价格,导致阶梯层级判断错误。

解决方法

需要手动查询客户现有订阅的数量,通过更新现有订阅的方式让Stripe基于总数量计算阶梯定价,具体修改如下:

修改后的完整代码

exports.createCheckoutSession = async (totalLicenses, currentURL, organizationID) => {
  try {
    const stripeCustomerID = await database.getStripeCustomerIDFromOrganizationID(organizationID);

    if (!stripeCustomerID) {
      throw new Error('Stripe客户不存在。');
    }

    // 查询客户该价格的活跃订阅
    const subscriptions = await stripe.subscriptions.list({
      customer: stripeCustomerID,
      price: 'price_2321srdspiOm5Cg52a',
      status: 'active'
    });

    let session;
    if (subscriptions.data.length > 0) {
      // 存在现有订阅:追加购买,更新订阅数量
      const existingSub = subscriptions.data[0];
      const existingItem = existingSub.items.data[0];

      session = await stripe.checkout.sessions.create({
        customer: stripeCustomerID,
        mode: 'subscription',
        subscription: existingSub.id, // 指定要更新的现有订阅
        line_items: [
          {
            price: 'price_2321srdspiOm5Cg52a',
            // 直接设置总数量,或用add_quantity追加(二选一)
            quantity: existingItem.quantity + totalLicenses,
          },
        ],
        subscription_data: {
          // 按需设置 prorate 策略:create_prorations/none/always_invoice
          proration_behavior: 'none',
          // 也可以用add_quantity方式,无需计算总数量:
          // items: [
          //   { id: existingItem.id, add_quantity: totalLicenses }
          // ]
        },
        custom_text: {
          submit: {
            message: '我们会通过邮件发送课程注册指南文档',
          },
        },
        allow_promotion_codes: true,
        success_url: `${currentURL}/admin/licenses?session_id={CHECKOUT_SESSION_ID}`,
        cancel_url: `${currentURL}/cancel.html`,
      });
    } else {
      // 无现有订阅:创建新订阅
      session = await stripe.checkout.sessions.create({
        customer: stripeCustomerID,
        mode: 'subscription',
        line_items: [
          {
            price: 'price_2321srdspiOm5Cg52a',
            quantity: totalLicenses,
          },
        ],
        custom_text: {
          submit: {
            message: '我们会通过邮件发送课程注册指南文档',
          },
        },
        allow_promotion_codes: true,
        success_url: `${currentURL}/admin/licenses?session_id={CHECKOUT_SESSION_ID}`,
        cancel_url: `${currentURL}/cancel.html`,
      });
    }

    return {
      url: session.url,
    };
  } catch (err) {
    console.trace(err);
    throw err;
  }
};

关键说明

  1. 查询现有订阅:通过stripe.subscriptions.list获取客户对应价格的活跃订阅,确认是否已有购买记录。
  2. 更新订阅而非创建新订阅:指定subscription参数指向现有订阅,让Stripe基于总数量(现有+本次购买)重新计算阶梯定价。
  3. Prorate策略调整:proration_behavior可根据业务需求设置,比如create_prorations会生成 prorate 账单,none则不会立即收取 prorate 费用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 23:28:11