如何在用户完成Stripe付款后再创建NEXT.JS订单?
解决Stripe结账取消后产生无效订单的问题(Next.js环境)
当前代码的核心问题是在跳转到Stripe结账前就创建了订单,不管后续支付是否成功,都会留下无效订单。正确的做法是仅在支付确认完成后再创建订单,这需要借助Stripe的Webhook机制来实现。
具体解决方案步骤
1. 修改现有/api/stripe路由,仅创建Stripe结账会话
删除当前代码里创建Order的逻辑,只负责生成Stripe结账会话,并将订单所需的关键数据存在会话的metadata中(方便后续Webhook获取)。
修改后的代码示例:
import stripe from '@stripe/stripe-js'; import { NextApiRequest, NextApiResponse } from 'next'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method !== 'POST') { return res.status(405).json({ error: 'Method not allowed' }); } const stripeInstance = stripe(process.env.STRIPE_SECRET_KEY!); const { items, userId, userEmail } = req.body; // 创建Stripe结账会话 const session = await stripeInstance.checkout.sessions.create({ payment_method_types: ['card'], line_items: items.map(item => ({ price_data: { currency: 'usd', product_data: { name: item.name }, unit_amount: item.price * 100, // Stripe以分为单位计算金额 }, quantity: item.quantity, })), mode: 'payment', success_url: `${process.env.NEXT_PUBLIC_URL}/success?session_id={CHECKOUT_SESSION_ID}`, cancel_url: `${process.env.NEXT_PUBLIC_URL}/cancel`, // 存储订单关键数据,供Webhook使用 metadata: { userId, userEmail, // 可额外存储商品ID列表等信息,比如:itemIds: items.map(i => i.id).join(',') }, }); // 仅返回Stripe结账地址,不创建订单 return res.status(200).json({ checkoutUrl: session.url }); }
2. 新建Stripe Webhook路由,监听支付成功事件
创建/api/stripe-webhook路由,专门处理Stripe发送的支付状态通知,仅在收到checkout.session.completed事件时才创建订单。
代码示例:
import stripe from '@stripe/stripe-js'; import { NextApiRequest, NextApiResponse } from 'next'; import { buffer } from 'micro'; import Order from '../../models/Order'; // 替换为你的Order模型路径 // 关闭Next.js默认的body解析,需要原始请求体验证Stripe签名 export const config = { api: { bodyParser: false } }; const stripeInstance = stripe(process.env.STRIPE_SECRET_KEY!); export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method !== 'POST') { return res.status(405).json({ error: 'Method not allowed' }); } // 获取原始请求体与Stripe签名 const requestBuffer = await buffer(req); const stripeSignature = req.headers['stripe-signature'] as string; const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!; let stripeEvent; try { // 验证签名,确保请求来自Stripe stripeEvent = stripeInstance.webhooks.constructEvent( requestBuffer, stripeSignature, webhookSecret ); } catch (err) { return res.status(400).json({ error: `Webhook验证失败: ${(err as Error).message}` }); } // 处理支付成功事件 if (stripeEvent.type === 'checkout.session.completed') { const checkoutSession = stripeEvent.data.object as stripe.Checkout.Session; const { userId, userEmail } = checkoutSession.metadata!; // 从会话中提取订单数据并创建订单 const newOrder = new Order({ userId, userEmail, stripeSessionId: checkoutSession.id, totalAmount: checkoutSession.amount_total! / 100, // 转换回元单位 status: 'paid', // 补充商品列表等字段,可从metadata的itemIds查询数据库获取 }); await newOrder.save(); console.log(`订单已创建: ${newOrder._id}`); } return res.status(200).json({ received: true }); }
3. 配置Stripe后台Webhook
- 登录Stripe Dashboard,进入Webhooks页面;
- 添加端点,地址填写你的线上域名
/api/stripe-webhook; - 选择监听
checkout.session.completed事件; - 复制生成的Webhook Secret,存入你的环境变量
STRIPE_WEBHOOK_SECRET。
关键注意事项
- 本地调试时,可使用Stripe CLI转发Webhook请求到本地服务;
- 必须验证Stripe签名,防止伪造请求触发订单创建;
- 用户取消支付时,因为订单从未创建,自然不会产生无效订单,无需额外处理。
内容的提问来源于stack exchange,提问作者janko hrasko
相关产品推荐
相关产品推荐

