Stripe报错:Checkout Session不存在但ID已在控制台显示
解决StripeInvalidRequestError:No such checkout.session错误
问题根源
你错误地将Payment Intent的ID(pi_开头的标识)当成了Checkout Session的ID传入stripe.checkout.sessions.retrieve()方法。从Webhook日志可以看到,pi_3OjTAGAHrCXdaN2D11vQGxvG的object字段是payment_intent,并非checkout.session,所以用这个ID查询结账会话必然报错。
修复方案
1. 推荐方案:监听checkout.session.completed事件
这是最直接的方式,当结账会话完成时,Stripe会触发checkout.session.completed事件,事件对象中包含完整的会话信息,无需额外查询:
修改Webhook的事件处理逻辑:
// Stripe webhooks! app.post('/stripe/webhooks', express.raw({type: 'application/json'}), async (req, res) => { const sig = req.headers['stripe-signature']; let event; // Function to update plan in supabase db const updateUserPlan = async (customerEmail) => { console.log("Trying to update user plan using the updateUserPlan function"); try { console.log("inside of updateUserPlan try block"); const { data: updateData, error: updateError } = await supabase .from('accounts') .update({ Plan: 'Standard' }) .eq('email', customerEmail) if (updateError) { console.error("Error updating user plan", updateError); } if (updateData) { console.log("updated user plan", updateData); } } catch (error) { console.error("Error with the updating userplan function", error); } } try { event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret); } catch (error) { return res.status(400).send(`Webhook Error: ${error.message}`); } // Handle the event switch (event.type) { // 新增/替换为该事件监听 case 'checkout.session.completed': { const completedSession = event.data.object; console.log("checkout session completed", completedSession); // 获取客户邮箱 const customerId = completedSession.customer; const customer = await stripe.customers.retrieve(customerId); const customerEmail = customer.email; // 直接判断会话模式 if (completedSession.mode === "subscription") { console.log("checkout mode is subscription"); updateUserPlan(customerEmail); } else { console.log("Skipping user plan update for payment mode"); } break; } case 'payment_intent.succeeded': { // 保留原逻辑(如果有其他需求),否则可以移除 const paymentIntentSucceeded = event.data.object; console.log("webhooks: payment succeeded", paymentIntentSucceeded); break; } default: // Unexpected event type return res.status(400).end(); } // Return a 200 response to acknowledge receipt of the event res.status(200).send("webhook recieved request."); })
2. 备选方案:通过元数据关联Payment Intent和Checkout Session
如果需要保留payment_intent.succeeded事件监听,可以在创建Checkout Session时,将会话ID存入Payment Intent的元数据:
修改创建Checkout Session的代码:
// Endpoint to create checkout sessions for one-off payments app.post("/create-checkout-payment", jsonParser, urlencodedParser, async (req, res) => { const { customerId, priceId } = req.body; console.log("recieved customer and priceid for credits", customerId, priceId); const session = await stripe.checkout.sessions.create({ customer: customerId, payment_method_types: [ "card", ], line_items: [ { price: priceId, quantity: 1 } ], mode: "payment", success_url: 'https://localhost:5173/payment/success', cancel_url: 'https://localhost:5173/pricing', // 添加元数据存储会话ID payment_intent_data: { metadata: { checkout_session_id: session.id } } }) res.json({ id: session.id }) })
然后在Webhook中从Payment Intent取出会话ID查询:
// 在payment_intent.succeeded事件处理中 const sessionId = paymentIntentSucceeded.metadata.checkout_session_id; if (sessionId) { const session = await stripe.checkout.sessions.retrieve(sessionId); console.log("session:", session); // 后续判断模式的逻辑不变 if (session.mode === "subscription") { console.log("checkout mode is subscription"); updateUserPlan(customerEmail); } else { console.log("Skipping user plan update for payment mode"); } }
验证步骤
部署修改后的代码后,触发一次支付流程,检查Webhook日志是否能正确获取到Checkout Session信息,同时确认用户套餐更新逻辑正常执行。
内容的提问来源于stack exchange,提问作者anon
相关产品推荐
相关产品推荐

