基于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_
相关产品推荐
相关产品推荐

