Stripe结账流程获取customer_id遇错误:ReferenceError: session is not defined
如何在Stripe结账流程(Node.js/Mongoose)中获取customer_id并存入数据库
我为此问题耗时数周,查阅了多篇Stack Overflow相关问答,但现有方案都无法解决问题。
在用户注册后,我通过Stripe的create-checkout-session引导用户进入结账门户,希望获取Stripe生成的customer_id并存入数据库,以此监控订阅状态(作为入门/中级开发者,目前对Webhooks概念理解困难)。
创建结账会话的控制器代码
module.exports.createCheckoutSession = async (req, res) => { if (req.isAuthenticated()) { try { const session = await stripe.checkout.sessions.create({ mode: "subscription", line_items: [ { price: priceId, quantity: 1, }, ], success_url: `${webUrl}/users/${req.user._id}/activations/?session_id={CHECKOUT_SESSION_ID}`, cancel_url: `${webUrl}/canceled.html`, }); console.log("customer ID ", session.customer_id); console.log("query params ", req.query); console.log("req params ", req.params); console.log("session", session); return res.redirect(303, session.url); } catch (e) { res.status(400); return res.send({ error: { message: e.message, } }); } } req.flash('error', "You must login before changing billing"); res.render('/login'); }
激活页面控制器代码
module.exports.showActivations = async (req, res) => { // ...其他代码 console.log("req params ", req.params); console.log("req headers ", req.headers) console.log("session ", session); res.render('users/activations', { activeEvents, qr, qrAgg }); // ...其他代码 };
控制台输出情况
req.params仅包含用户注册时存入数据库的user_id(跳转至Stripe门户前的ID)req.headers仅包含Cookie,无Stripe相关信息- 打印
session时出现ReferenceError: session is not defined错误
解决方案
方法一:在成功页面通过Session ID查询获取Customer ID(临时方案)
你已经在success_url里带上了session_id={CHECKOUT_SESSION_ID},可以在showActivations函数中提取该ID,调用Stripe API查询完整会话信息来获取customer_id:
修改showActivations代码:
module.exports.showActivations = async (req, res) => { try { // 从查询参数中提取Stripe会话ID const sessionId = req.query.session_id; if (!sessionId) { throw new Error("Missing Stripe session ID"); } // 调用Stripe API获取完整会话详情 const stripeSession = await stripe.checkout.sessions.retrieve(sessionId); const customerId = stripeSession.customer; // 注意:Stripe API返回字段为`customer`而非`customer_id` // 将customerId存入Mongoose用户文档 const user = await User.findById(req.params._id); user.stripeCustomerId = customerId; await user.save(); // ...你的其他业务代码 console.log("Stripe Customer ID:", customerId); res.render('users/activations', { activeEvents, qr, qrAgg, customerId }); } catch (err) { console.error("Error retrieving Stripe session:", err); req.flash('error', "Failed to activate subscription"); res.redirect('/some-error-page'); } };
注意事项
- 该方案依赖前端跳转后的查询参数,存在被篡改的风险,仅适合快速验证,不建议作为长期方案。
- 确保你的Stripe API密钥拥有查询Checkout Sessions的权限。
方法二:使用Webhooks获取Customer ID(推荐长期方案)
Webhooks是Stripe官方推荐的可靠方式,不受前端跳转影响,能确保你不会错过订阅完成事件,也是监控后续订阅状态(到期、付款失败等)的基础。
步骤1:配置Stripe Webhook端点
- 在Stripe Dashboard的【Webhooks】页面,添加一个接收端点(例如
https://your-domain.com/stripe-webhook)。 - 选择监听事件:
checkout.session.completed(用户成功完成结账时触发)。 - 复制Webhook签名密钥(用于验证请求来自Stripe)。
步骤2:实现Webhook处理函数
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY); const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET; module.exports.handleStripeWebhook = async (req, res) => { const sig = req.headers['stripe-signature']; let event; try { // 验证Webhook请求签名,确保来自Stripe event = stripe.webhooks.constructEvent(req.rawBody, sig, endpointSecret); } catch (err) { console.error("Webhook signature verification failed:", err.message); return res.status(400).send(`Webhook Error: ${err.message}`); } // 处理结账完成事件 if (event.type === 'checkout.session.completed') { const session = event.data.object; const customerId = session.customer; // 从metadata中提取你的用户ID(需提前存入) const userId = session.metadata.userId; if (userId && customerId) { // 将customerId更新到用户数据库 await User.findByIdAndUpdate(userId, { stripeCustomerId: customerId }); console.log(`Saved customer ID ${customerId} for user ${userId}`); } } // 返回200表示成功接收事件 res.status(200).json({ received: true }); };
步骤3:修改Checkout Session创建代码,添加用户ID到metadata
在createCheckoutSession函数中,给Session添加metadata字段,关联你的用户ID:
const session = await stripe.checkout.sessions.create({ mode: "subscription", line_items: [ { price: priceId, quantity: 1, }, ], success_url: `${webUrl}/users/${req.user._id}/activations/?session_id={CHECKOUT_SESSION_ID}`, cancel_url: `${webUrl}/canceled.html`, // 存入用户ID,用于Webhook中关联数据库用户 metadata: { userId: req.user._id.toString() } });
为什么推荐Webhooks?
- 可靠性高:无论用户是否跳转回你的网站,Stripe都会发送通知,确保不会遗漏订阅事件。
- 安全性强:通过签名验证过滤恶意请求。
- 扩展性好:后续处理订阅到期、付款失败等事件,都可以复用Webhook机制。
内容的提问来源于stack exchange,提问作者econobro
相关产品推荐
相关产品推荐

