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

基于Stripe Python实现带按比例计费的订阅升级/降级方案咨询

实现订阅升级/降级并保持计费周期的解决方案

针对你的两个核心需求,结合Stripe Python库的特性,以下是具体实现方案和代码修改建议:

核心逻辑说明

场景2:保持原有计费周期

直接使用原订阅的current_period_end作为新订阅的billing_cycle_anchor,这样后续计费会严格遵循原周期时间点(比如原周期结束日是9月3日12:45,新订阅也会在该时间点进行下一次全额计费)。不要依赖本地存储的过期日期,优先从Stripe订阅对象中获取,避免数据不一致。

场景1:按比例差额计费

通过设置proration_behavior: 'create_prorations',Stripe会自动计算:

新套餐剩余周期费用 - 旧套餐剩余周期费用 = 需立即支付的差额

这个计算逻辑完全匹配你要求的「新套餐价格-旧套餐价格-已使用时长的按比例计费金额」,Stripe会自动生成对应的 prorations 条目,用户在Checkout会话中完成支付。

修改后的代码

@stripe_blueprint.route('/stripe/<string:product_id>/<string:recurring_type>', methods=['POST', 'GET'])
def stripe_payment(product_id, recurring_type):
    global sidebar_links
    user = None
    subscription = None
    billing_cycle_anchor = None

    if current_user.is_authenticated:
        user = load_user_or_sub(current_user)
        # 检查并创建Stripe客户
        try:
            if user.stripe_customer_id:
                customer = stripe.Customer.retrieve(user.stripe_customer_id)
                if customer.deleted:
                    user.stripe_customer_id = None
            if not user.stripe_customer_id:
                logger.info(f'用户 {user.id} 无有效Stripe客户ID,创建新客户')
                create_stripe_customer(user)
        except Exception as e:
            logger.error(f'获取Stripe客户失败: {e}')
            return str(e)

    # 验证产品和周期类型
    product = PRODUCTS_JSON.get(product_id)
    if not product:
        return abort(404)
    
    if recurring_type == 'monthly':
        price_id = product['month']['id']
    elif recurring_type == 'yearly':
        price_id = product['year']['id']
    else:
        return abort(404)

    try:
        # 获取用户现有有效订阅
        if user and user.stripe_subscription_id:
            subscription = stripe.Subscription.retrieve(user.stripe_subscription_id)
            if subscription.status in ['active', 'trialing']:
                # 使用Stripe返回的当前周期结束时间作为新订阅的计费锚点
                billing_cycle_anchor = subscription.current_period_end

        # 构建Checkout会话参数
        checkout_params = {
            'line_items': [{"price": price_id, "quantity": 1}],
            'client_reference_id': user.user_hash_identifier if user else None,
            'payment_method_types': ['card'],
            'mode': 'subscription',
            'billing_address_collection': 'required',
            'allow_promotion_codes': True,
            'customer': user.stripe_customer_id if user else None,
            'metadata': {'recurring_type': recurring_type},
            'locale': 'auto',
            'success_url': url_for('stripe_blueprint.order_success', _external=True) + '?session_id={CHECKOUT_SESSION_ID}',
            'cancel_url': url_for('stripe_blueprint.order_cancel', _external=True),
        }

        # 如果有有效订阅,添加订阅升级/降级相关配置
        if billing_cycle_anchor:
            checkout_params['subscription_data'] = {
                "billing_cycle_anchor": billing_cycle_anchor,
                "proration_behavior": "create_prorations",
                "payment_behavior": "allow_incomplete"  # 允许通过Checkout支付差额
            }
            # 指定要替换的现有订阅(确保Stripe关联原订阅进行计算)
            checkout_params['subscription'] = user.stripe_subscription_id

        checkout_session = stripe.checkout.Session.create(**checkout_params)

    except Exception as e:
        logger.error(f'用户 {user.id if user else "匿名"} 创建Checkout会话失败: {e}')
        return str(e)

    return redirect(checkout_session.url, code=303)

关键修改点

  1. 替换本地过期日期为Stripe订阅的current_period_end:避免本地数据与Stripe后台不一致,确保计费周期完全准确。
  2. 添加subscription参数到Checkout会话:告诉Stripe这是对现有订阅的升级/降级操作,自动关联原订阅进行差额计算。
  3. 设置payment_behavior: 'allow_incomplete':允许用户通过Checkout完成差额支付,而不是自动尝试扣款(避免支付失败导致订阅变更失败)。
  4. 优化客户ID检查逻辑:更严谨地处理已删除的客户ID,确保创建新客户的流程可靠。

注意事项

  • 确保你的Stripe价格对象($42和$70)都是同一种计费周期(比如都是月度),否则需要额外处理跨周期的升级/降级逻辑。
  • 测试时可以使用Stripe测试卡号模拟支付,验证差额计算是否符合预期:
    • 升级时:用户需支付「新套餐剩余周期费用 - 旧套餐剩余周期费用」的正数差额
    • 降级时:如果剩余周期费用差额为负,Stripe会生成信用额度,自动抵扣下一次账单金额

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 18:06:05