You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js + Express集成Stripe结账时无法正常跳转至结账页面的问题

Node.js + Express集成Stripe结账时无法正常跳转至结账页面的问题

Hey,我帮你梳理下代码里的问题,应该能解决你现在的跳转异常以及潜在的Stripe配置错误:

首先说核心问题:为什么点击按钮后显示JSON而不是跳转?

你提到的现象,大概率是两种情况之一:

  1. 你可能直接在浏览器地址栏访问了GET /cart/checkout路由——因为你同时定义了GET和POST两个checkout接口,GET请求会直接返回JSON,浏览器就会显示这个字符串;
  2. 前端fetch请求因为后端的Stripe配置错误触发了异常,没走到跳转逻辑。

下面一步步解决:


1. 前端代码的致命错误:无法直接访问后端的req.session

在你的checkout.ejs里,fetch请求的body写了:

body: JSON.stringify({
  items: req.session.cart,
}),

前端浏览器完全拿不到后端的req.session对象,这里的req.session.cart在前端会变成undefined!虽然你的后端没用到这个参数,但这是无效代码,还可能干扰后续逻辑。建议改成从EJS渲染购物车数据到前端:

<script>
// 先把后端购物车数据渲染到前端全局变量
const cartItems = <%- JSON.stringify(req.session.cart) %>;
</script>

然后在fetch里用cartItems代替req.session.cart。


2. Stripe Session配置的关键错误:金额和数量搞混了

后端创建Stripe会话的代码里,有两个严重错误:

line_items: req.session.cart.map(item => {
  return {
    price_data: {
      // ...
      unit_amount: item.itemPrice // 错误:Stripe要求传入最小货币单位(英镑是便士)
    },
    quantity: item.quantity * 100 // 错误:数量不需要乘100!
  }
}),
  • unit_amount:如果你的item.itemPrice是英镑数值(比如10代表10英镑),必须乘以100转换成便士,也就是unit_amount: item.itemPrice * 100;
  • quantity:直接写item.quantity就行,乘100会导致购买数量变成实际的100倍,Stripe肯定会报错。

这个错误会直接导致Stripe会话创建失败,后端返回500错误,前端自然无法拿到跳转URL。


修复后的完整代码参考

后端控制器(customer_cart.js)

const AllProduct = require('../database_models/allproduct');
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY)

module.exports.checkout = async (req, res) => {
  if (!req.session.cart) {
    req.flash('error', 'There are no items to be checked out');
    return res.redirect('/'); // 加return避免后续代码执行
  }

  try {
    const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      mode: 'payment',
      line_items: req.session.cart.map(item => {
        return {
          price_data: {
            currency: 'gbp',
            product_data: {
              name: item.productName
            },
            unit_amount: item.itemPrice * 100 // 转换为便士
          },
          quantity: item.quantity // 正确的购买数量
        }
      }),
      success_url: `${process.env.SERVER_URL}/success`,
      cancel_url: `${process.env.SERVER_URL}/cancel` // 确保/cancel路由已定义,去掉.html后缀
    })
    res.json({ url: session.url })
  } catch (error) {
    res.status(500).json({ error: error.message })
  }
};

前端页面(checkout.ejs)

<% layout('layouts/productboilerplate')%>
<h1>TEST</h1>

<button>Checkout</button>

<script>
// 从EJS渲染后端购物车数据到前端
const cartItems = <%- JSON.stringify(req.session.cart) %>;

const button = document.querySelector("button")
button.addEventListener("click", () => {
  fetch('/cart/checkout', {
    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 }) => {
    console.log(url)
    window.location.href = url // 这里会正常跳转至Stripe结账页
  }).catch(error => {
    console.error(error)
    alert('结账请求失败:' + (error.error || '未知错误')) // 给用户提示错误
  })
})
</script>

额外排查建议

  1. 测试时打开浏览器开发者工具(F12),看Network标签页的fetch请求响应是否正常,Console标签页有没有报错;
  2. 确保process.env.SERVER_URL是正确的(比如http://localhost:3000),并且/success和/cancel路由已经在后端定义好;
  3. 不要再直接访问GET /cart/checkout,只能通过按钮触发POST请求。

备注:内容来源于stack exchange,提问作者NewtoJavaHelp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 10:14:30