Next.js电商Stripe Checkout批量结算购物车商品问题求助
问题分析与修复方案
前端代码核心问题及修复
- 表单提交未阻止默认行为:点击按钮后触发表单默认提交逻辑,导致页面重载、购物车清空。
- 函数未实际执行:
updateState和postData仅做了引用,未调用执行,且未处理异步请求的等待逻辑。 - JSON序列化错误:
JSON.stringify传入多个参数时只会序列化第一个,需传递完整的对象结构。 - 冗余状态拆分:无需将购物车数据拆分为单独的
id/price等数组,直接传递完整购物车数据更高效。
修改后的前端代码:
const stripePromise = loadStripe( process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY ); function Checkout() { const [basket] = useAppContext(); // 假设basket是购物车商品数组,每个商品包含id、price、description、title字段 const { data: session } = useSession(); const postData = async () => { const response = await fetch("/api/checkout_sessions", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ basket }), }); const { url } = await response.json(); window.location.href = url; }; React.useEffect(() => { const query = new URLSearchParams(window.location.search); if (query.get('success')) { console.log('订单已提交!你将收到确认邮件。'); // 可选:结算成功后清空购物车 // addToBasket([]); } if (query.get('canceled')) { console.log('订单已取消 -- 继续购物,准备好后再结算。'); } }, []); return ( <form onSubmit={async (e) => { e.preventDefault(); await postData(); }}> <button type="submit" role="link" className="button" > 前往结算 </button> </form> ); }
后端代码核心问题及修复
- Line Items结构错误:同时使用
price和price_data(两者互斥),且未处理多商品数组的遍历生成。 - 参数接收错误:未正确接收前端传来的购物车数组,而是尝试提取单个字段。
- 金额单位错误:Stripe的
unit_amount要求以分为单位,需将美元金额转换为整数分。
修改后的后端代码:
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY); export default async function handler(req, res) { if (req.method === 'POST') { try { const { basket } = req.body; const lineItems = basket.map(item => ({ quantity: 1, price_data: { currency: 'usd', unit_amount: Math.round(item.price * 100), // 转换为分(假设item.price是美元金额) product_data: { description: item.description, name: item.title, } } })); const session = await stripe.checkout.sessions.create({ shipping_address_collection: { allowed_countries: ["US", "CA", "GB"], }, line_items: lineItems, mode: 'payment', success_url: `${req.headers.origin}/?success=true`, cancel_url: `${req.headers.origin}/?canceled=true`, }); res.status(200).json({ url: session.url }); } catch (err) { res.status(err.statusCode || 500).json(err.message); } } else { res.setHeader('Allow', 'POST'); res.status(405).end('Method Not Allowed'); } }
额外注意事项
- 确保
basket数组中的每个商品对象包含price(数字类型的美元金额)、title、description字段。 - 如果商品已在Stripe后台创建了价格ID,可将
price_data替换为price: item.stripePriceId(移除price_data),更符合Stripe最佳实践。 - 测试时使用Stripe测试卡号(如
4242 4242 4242 4242)模拟支付流程。
内容的提问来源于stack exchange,提问作者Cameron Austin
相关产品推荐
相关产品推荐

