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

基于NextJs+Sanity+Stripe的电商:Webhook无法获取原产品CMS ID问题

解决方案:在Stripe会话中保留CMS产品ID

方案1:给结账会话的Line Item添加自定义Metadata

这是最直接的方式,创建Stripe结账会话时,给每个line_item附加包含Sanity产品ID的metadata,支付成功后在Webhook中直接读取。

修改创建结账会话的代码

在你的/api/route.js中,构建line_items数组时,为每个条目添加metadata字段,把Sanity的产品ID传进去:

// 假设购物车/CM S返回的产品数据包含sanityId字段
const lineItems = cartItems.map(item => ({
  price_data: {
    currency: 'usd',
    product_data: {
      name: item.name,
      images: [item.imageUrl]
    },
    unit_amount: item.price * 100 // Stripe以分为单位计价
  },
  quantity: item.quantity,
  // 核心:给line_item绑定Sanity产品ID
  metadata: {
    sanityProductId: item.sanityId
  }
}));

const session = await stripe.checkout.sessions.create({
  payment_method_types: ['card'],
  line_items: lineItems,
  mode: 'payment',
  success_url: `${process.env.NEXT_PUBLIC_URL}/success`,
  cancel_url: `${process.env.NEXT_PUBLIC_URL}/cart`,
});

Webhook中读取Metadata

在支付成功的Webhook处理逻辑中,先展开session的line_items,再从每个条目的metadata里取出Sanity ID:

// Webhook支付成功事件处理示例
const handlePaymentSuccess = async (session) => {
  // 主动展开line_items,Webhook默认返回的session不包含该字段
  const expandedSession = await stripe.checkout.sessions.retrieve(session.id, {
    expand: ['line_items']
  });

  for (const item of expandedSession.line_items.data) {
    const sanityProductId = item.metadata.sanityProductId;
    if (sanityProductId) {
      // 调用Sanity API更新产品库存/状态
      await fetch(`https://${process.env.SANITY_PROJECT_ID}.api.sanity.io/v2021-06-07/data/mutate/${process.env.SANITY_DATASET}`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${process.env.SANITY_API_TOKEN}`
        },
        body: JSON.stringify({
          mutations: [{
            patch: {
              id: sanityProductId,
              set: {
                // 示例:减少库存
                stock: `currentStock - ${item.quantity}`
              }
            }
          }]
        })
      });
    }
  }
};

方案2:关联Stripe Product与Sanity产品ID

如果需要在Stripe后台也能关联到Sanity产品,可以在创建Stripe Product时就把Sanity ID存入其metadata,后续通过Stripe API查询获取。

创建Stripe Product时附加Metadata

若你是提前从Sanity同步产品到Stripe,或是动态生成Stripe Product,添加metadata绑定:

const stripeProduct = await stripe.products.create({
  name: item.name,
  images: [item.imageUrl],
  metadata: {
    sanityProductId: item.sanityId
  }
});

// 基于该Product创建价格
const stripePrice = await stripe.prices.create({
  product: stripeProduct.id,
  unit_amount: item.price * 100,
  currency: 'usd'
});

Webhook中通过Product ID查询Metadata

在Webhook中,通过line_item的price.product字段查询对应的Stripe Product,提取Sanity ID:

const handlePaymentSuccess = async (session) => {
  const expandedSession = await stripe.checkout.sessions.retrieve(session.id, {
    expand: ['line_items']
  });

  for (const item of expandedSession.line_items.data) {
    // 查询对应Stripe产品
    const stripeProduct = await stripe.products.retrieve(item.price.product);
    const sanityProductId = stripeProduct.metadata.sanityProductId;
    
    if (sanityProductId) {
      // 执行Sanity产品状态更新操作
    }
  }
};

注意事项

  • 使用use-shopping-cart时,确保购物车存储的产品数据包含Sanity ID字段,避免传递到API路由时丢失。
  • Stripe Webhook必须配置签名验证,防止伪造请求触发错误的产品更新。
  • 若为动态生成Stripe Product的场景,方案1的line_item metadata方式更高效,无需额外API调用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 16:38:45