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

使用Stripe Payment API出现Failed to Fetch错误的排查求助

问题:React+Express+Stripe集成时出现Failed to Fetch错误

我用React、Express和Stripe API构建了一个简单Web应用,点击跳转Stripe外部支付页面时触发了Failed to Fetch错误,除这个问题外,前端UI等其他功能都正常,不确定问题出在哪。

错误信息

Uncaught runtime errors:
×
ERROR
Failed to fetch
TypeError: Failed to fetch
    at checkout (http://localhost:3000/static/js/bundle.js:479:11)
    at HTMLUnknownElement.callCallback (http://localhost:3000/static/js/bundle.js:22436:18)
    at Object.invokeGuardedCallbackDev (http://localhost:3000/static/js/bundle.js:22480:20)
    at invokeGuardedCallback (http://localhost:3000/static/js/bundle.js:22537:35)
    at invokeGuardedCallbackAndCatchFirstError (http://localhost:3000/static/js/bundle.js:22551:29)
    at executeDispatch (http://localhost:3000/static/js/bundle.js:26694:7)
    at processDispatchQueueItemsInOrder (http://localhost:3000/static/js/bundle.js:26720:11)
    at processDispatchQueue (http://localhost:3000/static/js/bundle.js:26731:9)
    at dispatchEventsForPlugins (http://localhost:3000/static/js/bundle.js:26740:7)
    at http://localhost:3000/static/js/bundle.js:26900:16
import {Button, Container, Navbar, Modal} from 'react-bootstrap';
import { useState, useContext } from 'react';
import { CartContext } from "../CartContext";
import CartProduct from './CartProduct';

function NavbarComponent() {
    const cart = useContext(CartContext);
    const [show, setShow] = useState(false); // 初始不显示购物车弹窗
    const handleClose = () => setShow(false);
    const handleShow = () => setShow(true);

    const checkout = async () => {
        await fetch('http://localhost:4000/checkout', {
            method: "POST",
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({items: cart.items})
        }).then((response) => {
            return response.json();
        }).then((response) => {
            if(response.url) {
                window.location.assign(response.url); // 跳转至Stripe支付页
            }
        });
    }

    const productsCount = cart.items.reduce((sum, product) => sum + product.quantity, 0);

    return (
        <>
            <Navbar expand="sm">
                <Navbar.Brand href="/">Test</Navbar.Brand>
                <Navbar.Toggle />
                <Navbar.Collapse className="justify-content-end">
                    <Button onClick={handleShow}>Cart ({productsCount} Items)</Button>
                </Navbar.Collapse>
            </Navbar>
            <Modal show={show} onHide={handleClose}>
                <Modal.Header closeButton>
                    <Modal.Title>Shopping Cart</Modal.Title>
                </Modal.Header>
                <Modal.Body>
                    {productsCount > 0 ?
                        <>
                            <p>Items in your cart:</p>
                            {cart.items.map( (currentProduct, idx) => (
                                <CartProduct key={idx} id={currentProduct.id} quantity={currentProduct.quantity}></CartProduct>
                            ))}

                            <h1>Total: {cart.getTotalCost().toFixed(2)}</h1>

                            <Button variant="success" onClick={checkout}>
                                Purchase items!
                            </Button>
                        </>
                    :
                        <h1>There are no items in your cart!</h1>
                    }
                </Modal.Body>
            </Modal>
        </>
    )
}

export default NavbarComponent;

server.js代码

const express = require('express');
var cors = require('cors');
const stripe = require('stripe')('');

const app = express();
app.use(cors());
app.use(express.static("public"));
app.use(express.json());

app.post("/checkout", async (req, res) => {
 
    console.log(req.body);
    const items = req.body.items;
    let lineItems = [];
    items.forEach((item)=> {
        lineItems.push(
            {
                price: item.id,
                quantity: item.quantity
            }
        );
    });

    const session = await stripe.checkout.sessions.create({
        line_items: lineItems,
        mode: 'payment',
        success_url: "http://localhost:3000/success",
        cancel_url: "http://localhost:3000/cancel"
    });

    res.send(JSON.stringify({
        url: session.url
    }));
});

app.listen(4000, () => console.log("Listening on port 4000"));

疑问

请问该问题是否与本地主机/连接配置有关?


排查与解决方案

  • 修复Stripe密钥配置:你的server.js中stripe('')是空的,必须填入有效的Stripe测试/生产密钥(如sk_test_xxxxxx),空密钥会直接导致Stripe API调用失败,后端无法生成支付会话,前端自然会收到网络错误。这是最核心的问题。

  • 完善CORS配置:虽然你使用了cors()中间件,但可以指定明确的允许源来避免潜在的跨域问题:

    app.use(cors({ origin: 'http://localhost:3000' }));
    
  • 添加后端错误捕获:当前后端没有错误处理逻辑,Stripe调用失败时会直接抛出错误导致请求中断,给/checkout接口添加try-catch:

    app.post("/checkout", async (req, res) => {
        try {
            console.log(req.body);
            const items = req.body.items;
            let lineItems = [];
            items.forEach((item)=> {
                lineItems.push({ price: item.id, quantity: item.quantity });
            });
    
            const session = await stripe.checkout.sessions.create({
                line_items: lineItems,
                mode: 'payment',
                success_url: "http://localhost:3000/success",
                cancel_url: "http://localhost:3000/cancel"
            });
    
            res.json({ url: session.url });
        } catch (error) {
            console.error("Stripe会话创建失败:", error);
            res.status(500).json({ error: error.message });
        }
    });
    

    这样能在控制台看到具体错误信息,方便定位问题。

  • 增强前端错误处理:当前前端fetch没有处理错误状态,添加try-catch捕获网络或HTTP错误:

    const checkout = async () => {
        try {
            const response = await fetch('http://localhost:4000/checkout', {
                method: "POST",
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({items: cart.items})
            });
            if (!response.ok) {
                throw new Error(`请求失败,状态码:${response.status}`);
            }
            const data = await response.json();
            if(data.url) {
                window.location.assign(data.url);
            }
        } catch (error) {
            console.error('结账失败:', error);
            alert('支付请求失败,请稍后重试');
        }
    }
    
  • 验证本地连接:确认后端服务确实在4000端口运行,可通过Postman或curl测试POST http://localhost:4000/checkout接口,检查是否返回有效的Stripe会话URL。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 20:04:56