如何修复Stripe结账会话显示全部商品而非选中单品的问题?
解决Stripe结账会话显示全部商品而非选中单品的问题
我在网站项目中集成Stripe支付系统时遇到问题:点击商品添加至购物车并发起结账,Stripe结账会话显示全部商品(含价格及总计),而非我选中的单品。
问题根源
- 前端
checkout.js中硬编码了所有商品的数组,发起结账时直接把所有商品传给后端,完全没有使用用户实际添加到购物车的内容 - 后端
server.js直接将前端传来的items数组传给Stripe的line_items,但Stripe要求line_items的每个元素必须包含price(对应Stripe的价格ID)和quantity,而非自定义的id
解决方案
1. 实现前端购物车逻辑
首先需要在前端维护购物车数据,记录用户选择的商品和数量:
修改shop.html(添加购物车交互)
给加入购物车按钮绑定点击事件:
<!--SHOP--> <section class="shop-section"> <div class="card-wrapper"> <!--Item 1--> <div data-id="1" class="card-item"> <img src="images/Shop/IMG-5288.jpg"> <div class="details"> <h3>Black Sweatsuit</h3> <p> <span>Kingdom</span> <span class="price">Price: $85.00</span> <span class="add-to-cart-btn" onclick="addToCart(1)"><i class="fa-solid fa-bag-shopping"></i></span> </p> </div> </div> <!-- 其他商品节点同理添加onclick事件 --> </div> <button id="checkout-button">结账</button> </section>
添加购物车管理JS
在页面中添加(或单独创建文件引入)购物车逻辑:
// 初始化购物车,用localStorage持久化 let cart = JSON.parse(localStorage.getItem('cart')) || {}; // 添加商品到购物车 function addToCart(itemId) { cart[itemId] = cart[itemId] ? cart[itemId] + 1 : 1; localStorage.setItem('cart', JSON.stringify(cart)); } // 获取当前购物车的商品列表 function getCartItems() { return Object.entries(cart).map(([id, quantity]) => ({ id: parseInt(id), quantity: quantity })); }
2. 修改checkout.js,发送实际购物车内容
替换硬编码的商品数组,改为发送用户实际添加的购物车内容:
const button = document.getElementById("checkout-button") button.addEventListener("click", () => { const cartItems = getCartItems(); // 购物车为空时提示用户 if (cartItems.length === 0) { alert("购物车为空,请先添加商品"); return; } fetch('/create-checkout-session', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ items: cartItems }), }) .then(res => { if (res.ok) return res.json() return res.json().then(json => Promise.reject(json)) }) .then(({ url }) => { window.location = url }) .catch(e => { console.error(e.error) }) })
3. 修改server.js,转换为Stripe要求的格式
后端需要将前端传来的商品ID映射为Stripe的价格ID,并构造符合要求的line_items数组:
require("dotenv").config() const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY); // 改用环境变量存储密钥更安全 const express = require('express'); const app = express(); app.use(express.static('public')); app.use(express.json()); // 必须添加,用于解析JSON请求体 const YOUR_DOMAIN = 'http://localhost:4242'; const storeItems = new Map ([ [1, { priceId: 'price_XXXXX', name: "Black Sweatsuit" }], [2, { priceId: 'price_XXXXX', name: "Grey Sweatsuit" }], [3, { priceId: 'price_XXXXX', name: "Red Sweatsuit" }], [4, { priceId: 'price_XXXXX', name: "Blue Sweatsuit" }], [5, { priceId: 'price_XXXXX', name: "Black T-Shirt" }], [6, { priceId: 'price_XXXXX', name: "Blue T-Shirt" }], [7, { priceId: 'price_XXXXX', name: "Purple T-Shirt" }], ]); app.post('/create-checkout-session', async (req, res) => { try { // 转换为Stripe要求的line_items格式 const lineItems = req.body.items.map(item => { const storeItem = storeItems.get(item.id); if (!storeItem) throw new Error(`商品ID ${item.id}不存在`); return { price: storeItem.priceId, quantity: item.quantity }; }); const session = await stripe.checkout.sessions.create({ line_items: lineItems, payment_method_types: ['card'], mode: 'payment', success_url: `${YOUR_DOMAIN}/success.html`, cancel_url: `${YOUR_DOMAIN}/cancel.html`, automatic_tax: {enabled: true}, }); res.redirect(303, session.url); } catch (error) { res.status(400).json({ error: error.message }); } }); app.listen(4242, () => console.log('Running on port 4242'));
关键注意事项
- 后端必须添加
app.use(express.json()),否则无法解析前端传来的JSON请求体 - 禁止硬编码Stripe密钥,使用
.env环境变量存储更安全 - 要处理购物车为空的情况,避免发起无效的结账会话
- 后端需校验前端传来的商品ID是否存在,防止非法请求
内容的提问来源于stack exchange,提问作者user16147870
相关产品推荐
相关产品推荐

