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

如何区分Stripe中customer.subscription.updated事件的新旧订阅信息

区分Stripe订阅更新事件的方法

先对比你提供的两个事件内容(已翻译为中文):

旧套餐订阅事件对象

{
"id": "sub_NA2KPh90",
"object": "subscription",
"status": "canceled",
"cancel_at_period_end": true,
"current_period_end": 1699823999,
"customer": "cus_XXXXXX",
"items": {
"data": [
{
"id": "si_XXXXXX",
"price": {
"id": "price_OLD",
"nickname": "基础版套餐",
"currency": "usd",
"unit_amount": 1000
}
}
]
},
"created": 1697231999,
"ended_at": 1699823999
}

新套餐订阅事件对象

{
"id": "sub_qF9g1wDs",
"object": "subscription",
"status": "active",
"cancel_at_period_end": false,
"current_period_end": 1702415999,
"customer": "cus_XXXXXX",
"items": {
"data": [
{
"id": "si_YYYYYY",
"price": {
"id": "price_NEW",
"nickname": "专业版套餐",
"currency": "usd",
"unit_amount": 2000
}
}
]
},
"created": 1699824000,
"start_date": 1699824000
}

从以上内容的核心差异出发,你可以通过以下方法过滤旧事件,只处理新套餐的更新:

  • 筛选活跃状态的订阅:旧事件的status为canceled,新事件为active,直接忽略status不是active的事件。
  • 检查到期取消标记:旧事件的cancel_at_period_end为true(表示该订阅会在当前周期结束后取消),新事件为false,可作为辅助过滤条件。
  • 对比价格ID:新事件的items.data[0].price.id与旧事件不同,结合你数据库中存储的客户当前价格ID,只有当事件中的价格ID与数据库不一致时,才执行更新操作。
  • 订阅时间字段:旧事件包含ended_at(订阅结束时间),新事件包含start_date(新订阅开始时间),可通过字段存在性快速区分。

示例代码(Node.js)

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

app.post('/stripe-webhook', async (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;

  // 验证Webhook签名,防止伪造事件
  try {
    event = stripe.webhooks.constructEvent(
      req.rawBody,
      sig,
      process.env.STRIPE_WEBHOOK_SECRET
    );
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  if (event.type === 'customer.subscription.updated') {
    const subscription = event.data.object;
    // 过滤条件:仅处理活跃、非到期取消的订阅
    if (subscription.status !== 'active' || subscription.cancel_at_period_end) {
      return res.sendStatus(200);
    }

    // 从数据库获取客户当前的套餐信息
    const customer = await getCustomerByStripeId(subscription.customer);
    if (!customer) return res.sendStatus(200);

    const newPriceId = subscription.items.data[0].price.id;
    // 如果价格ID有变化,更新数据库
    if (customer.subscription_price_id !== newPriceId) {
      await updateCustomerSubscription(customer.id, {
        subscription_price_id: newPriceId,
        plan_name: subscription.items.data[0].price.nickname,
        current_period_end: subscription.current_period_end
      });
    }
  }

  res.sendStatus(200);
});

关键提醒

  • 必须验证Stripe Webhook的签名,这是防止恶意数据篡改的核心步骤。
  • 如果你的业务允许客户拥有多个订阅,需要结合subscription.id和客户ID进行更精准的匹配,避免更新错误的订阅信息。
  • 对于套餐变更后立即生效的场景,旧订阅会直接被标记为canceled,此时仅通过status字段即可完成过滤。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 10:01:16