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

如何验证Stripe订阅为活跃付费状态?Webhook与数据库存储咨询

关于Stripe订阅状态管理的方案建议

你的当前方案有一定基础,但仅依赖invoice.paid事件和period_end判断订阅状态,会漏掉不少关键场景,无法全面确保状态的准确性。下面是具体的优化建议:

必须监听的核心Webhook事件

  • invoice.paid:这是基础触发事件,当订阅成功完成付费(首次或续费)时触发,此时你需要同步更新数据库里的订阅到期时间(current_period_end),并将订阅状态标记为active。
  • invoice.payment_failed:续费失败时触发(比如用户银行卡过期、余额不足),此时订阅会进入past_due状态,不能直接判定为失效——用户可能会在宽限期内更新支付方式重试,所以要把状态同步为past_due,并提示用户处理。
  • customer.subscription.updated:订阅状态变更时触发(比如用户升级/降级订阅、订阅被暂停/恢复、手动更新支付方式后状态恢复),需要同步更新数据库里的订阅状态、新的到期时间等核心字段。
  • customer.subscription.deleted:订阅被永久取消时触发,直接将数据库里的订阅状态标记为canceled。

数据库存储的关键字段建议

为了准确追踪订阅状态,数据库至少要存这些字段:

  • stripe_subscription_id:Stripe返回的订阅唯一ID,用来关联Stripe端的数据。
  • subscription_status:直接存储Stripe返回的状态值(比如active、past_due、canceled、unpaid),不要自己造状态。
  • current_period_end:订阅当前周期的到期时间(datetime格式)。
  • stripe_customer_id:关联Stripe的客户ID,方便后续查询用户的支付记录。
  • last_sync_at:本地最后同步状态的时间,用于排查问题。

你的当前方案的局限性

  1. 忽略付费失败场景:如果用户续费失败,period_end会过期,但订阅实际处于past_due(待付款)状态,用户还有机会修复支付问题,直接标记为inactive会误判。
  2. 未处理主动取消:用户手动取消订阅后,即使period_end还没到,订阅状态已经是canceled,此时应该标记为失效,但你的逻辑会认为还是active。

优化后的订阅状态判断逻辑

不要只依赖period_end,要结合Stripe返回的订阅状态:

$currentTime = date('Y-m-d H:i:s');
$subscriptionStatus = $db->get('subscription_status'); // 从数据库取Stripe返回的状态
$periodEnd = $db->get('current_period_end'); // 从数据库取到期时间

if ($subscriptionStatus === 'active' && $periodEnd > $currentTime) {
    // 订阅处于活跃且已付费状态
} elseif ($subscriptionStatus === 'past_due') {
    // 付费失败,处于待付款宽限期,提示用户更新支付方式
} elseif (in_array($subscriptionStatus, ['canceled', 'unpaid', 'inactive'])) {
    // 订阅已失效
}

额外注意事项

  • 一定要验证Webhook的签名,防止伪造请求,Stripe官方提供了各语言的验证工具。
  • 处理Webhook时要做幂等性控制:用Stripe返回的event_id作为唯一标识,避免重复处理同一事件。
  • 建议用异步队列处理Webhook事件,避免因Stripe超时导致重复发送。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 12:39:50