Stripe checkout.session.completed Webhook异常:支付成功但订单未创建
订单未创建及票券不显示问题排查
一、订单未创建的核心原因
1. createOrder函数异常被吞且日志无效
createOrder中console.log("order created")写在return语句之后,永远不会执行,无法确认订单是否创建成功。同时如果handleError仅打印错误而不重新抛出,会导致数据库保存失败时无任何报错反馈,webhook仍返回200,Stripe认为处理成功但实际订单未写入。
修复:
export const createOrder = async (order: CreateOrderParams) => { try { await connectToDatabase(); console.log("creating order"); const newOrder = await Order.create({ ...order, event: order.eventId, buyer: order.buyerId, }); console.log("order created"); // 移至return前 return JSON.parse(JSON.stringify(newOrder)); } catch (error) { handleError(error); throw error; // 重新抛出异常,让上层捕获 } };
2. ObjectId类型不匹配
从Stripe metadata获取的eventId和buyerId是字符串,而Mongoose模型中event和buyer字段应为ObjectId类型,直接传入字符串会导致数据库保存失败(若模型开启严格类型校验)。
修复:
import { ObjectId } from "mongodb"; export const createOrder = async (order: CreateOrderParams) => { try { await connectToDatabase(); console.log("creating order"); const newOrder = await Order.create({ ...order, event: new ObjectId(order.eventId), // 转换为ObjectId buyer: new ObjectId(order.buyerId), // 转换为ObjectId }); console.log("order created"); return JSON.parse(JSON.stringify(newOrder)); } catch (error) { handleError(error); throw error; } };
3. Webhook未捕获createOrder异常
当前webhook未对createOrder的调用做异常捕获,若创建失败会直接返回500,但如果handleError吞了异常,会导致Stripe误以为处理成功。
修复webhook代码:
if (eventType === "checkout.session.completed") { const { id, amount_total, metadata } = event.data.object; const order = { stripeId: id, eventId: metadata?.eventId || "", buyerId: metadata?.buyerId || "", totalAmount: amount_total ? (amount_total / 100).toString() : "0", createdAt: new Date(), }; try { const newOrder = await createOrder(order); return NextResponse.json({ message: "OK", order: newOrder }); } catch (err) { console.error("Failed to create order:", err); return NextResponse.json({ message: "Failed to create order", error: err }, { status: 500 }); } }
二、票券未显示的额外问题
即使订单创建成功,getOrdersByUser函数存在语法错误,导致无法正确查询用户订单:
1. Order.distinct("event._id").find(...)调用错误
distinct方法返回数组,无法链式调用find、sort等查询方法,会导致查询失败返回空数据。
修复:
export async function getOrdersByUser({ userId, limit = 3, page, }: GetOrdersByUserParams) { try { await connectToDatabase(); const skipAmount = (Number(page) - 1) * limit; const conditions = { buyer: new ObjectId(userId) }; // 转换为ObjectId const orders = await Order.find(conditions) .sort({ createdAt: "desc" }) .skip(skipAmount) .limit(limit) .populate({ path: "event", model: Event, populate: { path: "organizer", model: User, select: "_id firstName lastName", }, }); const ordersCount = await Order.countDocuments(conditions); return { data: JSON.parse(JSON.stringify(orders)), totalPages: Math.ceil(ordersCount / limit), }; } catch (error) { handleError(error); throw error; } }
三、额外排查步骤
- 确认MongoDB连接正常,检查
connectToDatabase函数无错误; - 在Stripe Dashboard查看
checkout.session.completed事件的请求体,确认metadata中eventId和buyerId是否正确传递; - 查看服务器日志(如Vercel/Netlify日志),检查
createOrder的日志是否执行,是否有数据库错误信息。
内容的提问来源于stack exchange,提问作者Caio
相关产品推荐
相关产品推荐

