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

React集成Stripe结账传参时触发CORS跨域问题求助

React集成Stripe结账的CORS问题排查与解决

问题场景

我在React应用里集成Stripe结账功能,购物车页面的结账按钮调用以下表单提交函数:

const handleSubmit = async(e) => {
    e.preventDefault();
    try {
      const response = await fetch('/create-checkout-session', {
        method: 'POST',
        // mode: "no-cors", // no-cors, *cors, same-origin
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({productId}),
      });
  
      if (response.ok) {
        console.log('Request sent successfully');
      } else {
        console.log('Request failed');
      }
    } catch (error) {
      console.log('Error occurred during the request', error);
    }
  }

该请求对应后端API:

app.post('/create-checkout-session', async (req, res) => {
    // Get the selected product ID from the frontend
    const { productId } = req.body;

    console.log(productId)

    // Define a mapping of product IDs to prices
    const priceMap = {
        bricky_id: 'price_1NA7O1JVu65LdnVcBljr473q',
        sub_id: 'price_1NA64tJVu65LdnVcfgUwbhUA',
        // Add more product IDs and their corresponding prices here
      };
    
    //   Get the price based on the selected product ID
      const price = priceMap[productId];

      console.log(price);
    
      if (!price) {
        // Handle invalid product ID
        return res.status(400).json({ error: 'Invalid product selected' });
      }
const session = await stripe.checkout.sessions.create({
    line_items: [
        {
            price: price,
            quantity: 1,
        },
        {
            price: 'price_1NA64tJVu65LdnVcfgUwbhUA',
            quantity: 1
        }
    ],
    mode: 'subscription',
    success_url: `http://localhost:4000/order-success`,
    cancel_url: `http://localhost:4000/order-preview`,
});
res.redirect(303, session.url);
// res.redirect({url: session.url});
// res.redirect(session.url);
});

React已配置代理:"proxy": "http://localhost:4000",服务端也添加了CORS配置:

app.use(cors())
app.use(function(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
res.setHeader('Access-Control-Allow-Credentials', true);
next();
  });

无请求体的简单调用能正常跳转Stripe结账页,但传递参数后出现CORS错误:

Access to fetch at 'https://checkout.stripe.com/c/pay/...' (redirected from 'http://localhost:3000/create-checkout-session') from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

OrderPreview.js:11 GET https://checkout.stripe.com/c/pay/... net::ERR_FAILED

问题根源

核心不是后端CORS配置问题,而是前端用fetch发起请求后,后端直接重定向到Stripe域名——fetch会自动跟随重定向去请求Stripe的URL,但Stripe服务器不会给你的前端域名添加CORS响应头,因此浏览器拦截了这个跨域请求。

另外,React代理只作用于/create-checkout-session的请求阶段,重定向后的请求直接从浏览器发往Stripe,不再走代理。

解决方法

方案1:后端返回会话URL,前端主动跳转(Stripe推荐)

这是最直接的解决方案,避开fetch处理跨域重定向:

  1. 修改后端API,替换重定向逻辑,返回会话URL:
// 删除res.redirect(303, session.url);
res.json({ url: session.url });
  1. 修改前端handleSubmit函数,拿到URL后主动跳转:
const handleSubmit = async(e) => {
    e.preventDefault();
    try {
      const response = await fetch('/create-checkout-session', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({productId}),
      });
  
      if (response.ok) {
        const { url } = await response.json();
        // 直接让浏览器跳转Stripe结账页
        window.location.href = url;
      } else {
        console.log('Request failed');
      }
    } catch (error) {
      console.log('Error occurred during the request', error);
    }
  }

方案2:清理冗余CORS配置,避免冲突

当前服务端同时使用了cors()包和自定义CORS中间件,两者可能产生冲突,建议只保留一种:

  • 保留cors包的配置,更简洁且不易出错:
const cors = require('cors');
// 生产环境建议替换为实际前端域名,不要用*
app.use(cors({ origin: 'http://localhost:3000', credentials: true }));
// 删除自定义的CORS中间件代码

方案3:确认React代理生效

检查代理配置并重启服务:

  • 确认package.json中的proxy字段为"http://localhost:4000"
  • 重启React开发服务器,避免缓存导致代理未生效

关键注意事项

  • 禁止用fetch/axios处理Stripe的重定向,必须让浏览器直接发起跳转请求,绕过跨域限制
  • 生产环境不要用*作为Access-Control-Allow-Origin,指定具体前端域名更安全
  • Stripe结账会话URL是一次性的,不要缓存,每次结账都需重新创建会话

内容的提问来源于stack exchange,提问作者jamesj9005

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 13:54:56