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

Laravel Cashier:订阅续订时重置使用额度的实现方案

解决方案:订阅周期续订时重置额度

1. 调整数据库结构(核心前提)

当前users表仅存tokens_left无法满足周期重置需求,必须补充订阅周期相关信息,以下两种方案任选:

方案A:扩展users表

新增字段关联订阅周期与额度规则:

idtokens_leftsubscription_plancurrent_cycle_startcurrent_cycle_endquota_per_cycle
15premium2024-05-01 00:00:002024-06-01 00:00:0010
29premium2024-05-05 00:00:002024-06-05 00:00:0010

字段说明:

  • subscription_plan:标记用户当前订阅类型(如premium)
  • current_cycle_start/end:记录当前订阅周期的起止时间
  • quota_per_cycle:该订阅计划的周期额度上限

方案B:单独创建subscription_quotas表

若需保留历史额度使用记录,建议拆分出独立表:

iduser_idplan_typecycle_startcycle_endtotal_quotaused_quota
11premium2024-05-01 00:00:002024-06-01 00:00:00105
22premium2024-05-05 00:00:002024-06-05 00:00:00101

剩余额度可通过total_quota - used_quota直接计算,同时便于回溯用户过往周期的使用情况。

2. 两种重置触发方式

方式一:实时触发(推荐,基于Stripe Webhook)

Stripe订阅续订会触发invoice.paid事件(确保订阅成功扣费),通过监听该事件可实时重置额度:

  1. 在Stripe控制台配置Webhook端点,接收invoice.paid事件
  2. 解析事件中的subscription对象,获取customer(关联你的用户ID)、current_period_start/current_period_end(新周期时间)
  3. 对应更新数据库:
    • 方案A:将tokens_left设为quota_per_cycle,同时更新周期起止时间
    • 方案B:为用户创建新的额度记录,used_quota设为0,填充新周期时间

示例伪代码(Python):

def handle_stripe_webhook(request):
    event = stripe.Webhook.construct_event(
        request.data, request.headers['Stripe-Signature'], WEBHOOK_SECRET
    )
    if event.type == 'invoice.paid':
        subscription = event.data.object.subscription
        # 通过Stripe客户ID关联自有用户
        user = User.objects.get(stripe_customer_id=subscription.customer)
        # 重置额度并更新周期
        user.tokens_left = user.quota_per_cycle
        user.current_cycle_start = datetime.fromtimestamp(subscription.current_period_start)
        user.current_cycle_end = datetime.fromtimestamp(subscription.current_period_end)
        user.save()
    return HttpResponse(status=200)

方式二:定时任务(兜底方案)

为避免Webhook延迟或失败,可配合每日定时任务检查到期周期:

  1. 每日执行脚本,查询所有current_cycle_end早于当前时间的用户
  2. 重置其额度并更新新周期时间(按原周期长度顺延,比如加30天)

示例SQL查询(筛选需重置用户):

SELECT id, quota_per_cycle FROM users 
WHERE subscription_plan = 'premium' AND current_cycle_end < NOW();

重置操作SQL:

UPDATE users 
SET tokens_left = quota_per_cycle, 
    current_cycle_start = NOW(), 
    current_cycle_end = DATE_ADD(NOW(), INTERVAL 30 DAY) 
WHERE id = {user_id};

3. 实时校验时的兼容处理

在用户触发任务使用时,先检查是否已进入新周期,若已到期则自动重置额度,避免Webhook延迟导致的业务异常:

def use_task(user_id):
    user = User.objects.get(id=user_id)
    # 检查周期是否过期
    if datetime.now() > user.current_cycle_end:
        user.tokens_left = user.quota_per_cycle
        user.current_cycle_start = datetime.now()
        user.current_cycle_end = datetime.now() + timedelta(days=30)
        user.save()
    # 扣减额度
    if user.tokens_left > 0:
        user.tokens_left -= 1
        user.save()
        return True
    return False

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 21:45:27