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

亚马逊克隆项目Stripe支付后出现Axios网络错误求助

问题描述

开发亚马逊克隆项目时,基于Stripe实现了支付功能。输入卡片信息后页面成功跳转到订单页(符合预期),但屏幕弹出错误提示:

"Network Error AxiosError: Network Error at XMLHttpRequest.handleError (http://localhost:3000/static/js/bundle.js:105169:14)"
同时Stripe后台显示异常。以下是相关代码文件,需排查网络错误原因并解决。

相关代码

Payment.js

import React, { useEffect, useState } from "react";
import "./Payment.css";
import { useStateValue } from "./StateProvider";
import CheckoutProduct from "./CheckoutProduct";
import { Link } from "react-router-dom";
import { CardElement, useElements, useStripe } from "@stripe/react-stripe-js";
import CurrencyFormat from "react-currency-format";
import { getBasketTotal } from "./reducer";
import axios from "./axios";
import { useNavigate } from "react-router-dom";

function Payment() {
  const [{ basket, user }, dispatch] = useStateValue();
  const navigate = useNavigate();

  const stripe = useStripe();
  const elements = useElements();

  const [succeeded, setSucceeded] = useState(false);
  const [processing, setProcessing] = useState("");
  const [error, setError] = useState(null);
  const [disabled, setDisabled] = useState(true);
  const [clientSecret, setClientSecret] = useState(true);

  useEffect(() => {
    //生成用于向客户收费的专属Stripe密钥

    const getClientSecret = async () => {
      const response = await axios({
        method: "post",
        url: `/payments/create?total=${getBasketTotal(basket) * 100}`,
      });
      setClientSecret(response.data.clientSecret);
      //Stripe要求金额以货币 subunit 为单位
    };

    getClientSecret();
  }, [basket]);

  const handleSubmit = async (event) => {
    event.preventDefault();
    setProcessing(true);

    const payload = await stripe
      .confirmCardPayment(clientSecret, {
        payment_method: {
          card: elements.getElement(CardElement),
        },
      })
      .then(({ payment }) => {
        //paymentIntent = 支付确认
        setSucceeded(true);
        setError(null);
        setProcessing(false);

        dispatch({
          type: "EMPTY_BASKET",
        });

        navigate("/orders", { replace: true });
        //这里使用replace而非push,因为支付成功后不希望回到支付页面
      });
  };

  const handleChange = (event) => {
    //监听Card Element的变化,显示客户输入卡片信息时的错误
    setDisabled(event.empty);
    setError(event.error ? event.error.message : "");
  };

  return (
    <div className="payment">
      <div className="payment__container">
        <h1>
          Checkout (<Link to="/checkout">{basket?.length} Items</Link>)
        </h1>

        <div className="payment__section">
          <div className="payment__title">
            <h3>Delivery Address</h3>
          </div>
          <div className="payment__address">
            <p>{user?.email}</p>
            <p>76/4A Rajpur Road</p>
            <p>Dehradun</p>
          </div>
        </div>

        <div className="payment__section">
          <div className="payment__title">
            <h3>Review Items & Delivery</h3>
          </div>

          <div className="payment__items">
            {basket.map((item) => (
              <CheckoutProduct
                id={item.id}
                title={item.title}
                image={item.image}
                price={item.price}
                rating={item.rating}
              />
            ))}
          </div>
        </div>

        <div className="payment__section">
          <div className="payment__title">
            <h3>Payment Method</h3>
          </div>

          <div className="payment__details">
            <form onSubmit={handleSubmit}>
              <CardElement onChange={handleChange} />

              <div className="payment__priceContainer">
                <CurrencyFormat
                  renderText={(value) => <h3>Order Total : {value}</h3>}
                  decimalScale={2}
                  value={getBasketTotal(basket)}
                  displayType={"text"}
                  thousandSeparator={true}
                  prefix={"$"}
                />
                <button disabled={processing || disabled || succeeded}>
                  <span>{processing ? <p>Processing</p> : "Buy Now"}</span>
                </button>
              </div>

              {error && <div>{error}</div>}
            </form>
          </div>
        </div>
      </div>
    </div>
  );
}

export default Payment;

App.js

import "./App.css";
import Checkout from "./Checkout";
import Header from "./Header";
import Home from "./Home";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Login from "./Login";
import React, { useEffect } from "react";
import { auth } from "./firebase";
import { useStateValue } from "./StateProvider";
import Payment from "./Payment";
import { loadStripe } from "@stripe/stripe-js";
import { Elements } from "@stripe/react-stripe-js";
import Orders from "./Orders";

const promise = loadStripe(
  "pk_test_51NRKdkSGdggXhtrctnXw7BsWnkeXoSwRsRYKxZ7o17Nqx55wvPdMQJwSpm4BCXZoSPiIFW9KOrPAFAwoUylvfEt100BhWELocF"
);

function App() {
  const [{}, dispatch] = useStateValue();

  useEffect(() => {
    //仅在App组件加载时运行一次
    auth.onAuthStateChanged((authUser) => {
      console.log("The user is : ", authUser);

      if (authUser) {
        //用户已登录/刚登录
        dispatch({
          type: "SET_USER",
          user: authUser,
        });
      } else {
        //用户已登出
        dispatch({
          type: "SET_USER",
          user: null,
        });
      }
    });
  }, []);

  return (
    <Router>
      <div className="app">
        <Routes>
          <Route
            path="/orders"
            element={
              <>
                <Header />
                <Orders />
              </>
            }
          />
          <Route path="/login" element={<Login />} />
          <Route
            path="/checkout"
            element={
              <>
                <Header />
                <Checkout />
              </>
            }
          ></Route>

          <Route
            path="/payment"
            element={
              <>
                <Header />
                <Elements stripe={promise}>
                  <Payment />
                </Elements>
              </>
            }
          ></Route>

          <Route
            path="/"
            element={
              <>
                <Header />
                <Home />
              </>
            }
          />
        </Routes>
      </div>
    </Router>
  );
}

export default App;

后端index.js

/**
 * 从各自子模块导入函数触发器:
 *
 * const {onCall} = require("firebase-functions/v2/https");
 * const {onDocumentWritten} = require("firebase-functions/v2/firestore");
 *
 * 查看完整支持的触发器列表:https://firebase.google.com/docs/functions
 *
 */

const { onRequest } = require("firebase-functions/v2/https");
const logger = require("firebase-functions/logger");

// 创建并部署你的第一个函数
// https://firebase.google.com/docs/functions/get-started

// exports.helloWorld = onRequest((request, response) => {
//   logger.info("Hello logs!", {structuredData: true});
//   response.send("Hello from Firebase!");
// });
const functions = require("firebase-functions");
const express = require("express");
const cors = require("cors");
const stripe = require("stripe")(
  "sk_test_51NRKdkSGdggXhtrcB8ZbigCqipRaMbRZVIJGYGGhAr7tM9fkCqXMaBJL7ubJhfxgDfxaPhYxG3nrtUarNhvhQ76U00zgF0sXPa"
);

//API

//应用配置
const app = express();

//中间件
app.use(cors({ origin: true }));
app.use(express.json());

// API路由
app.get("/", (request, response) => response.status(200).send("hello world"));

app.post("/payments/create", async (request, response) => {
  const total = request.query.total;

  console.log("Payment Request Received BOOM!!! for this amount -> ", total);

  try {
    const paymentIntent = await stripe.paymentIntents.create({
      amount: total,
      currency: "usd",
    });

    // 返回客户端密钥
    response.status(201).send({
      clientSecret: paymentIntent.client_secret,
    });
  } catch (error) {
    console.error(error.response.data);
    response.status(500).send({
      error: "创建支付意图时发生错误。",
    });
  }
});

// 监听命令
exports.api = functions.https.onRequest(app);

//http://127.0.0.1:5001/challenge-940e1/us-central1/api

axios.js

import axios from "axios";

const instance = axios.create({
  baseURL: "http://127.0.0.1:5001/challenge-940e1/us-central1/api", //API(云函数)URL
});

export default instance;

排查与解决方法

1. 修正clientSecret初始值错误

Payment.js中clientSecret的初始值被设为布尔值true,但Stripe要求clientSecret为字符串类型,使用布尔值会导致支付请求时触发网络错误。

修改代码:

// 将初始值从true改为null
const [clientSecret, setClientSecret] = useState(null);

2. 修正Stripe支付确认的解构错误

stripe.confirmCardPayment返回的对象中,支付意图的属性是paymentIntent而非payment,错误的解构会导致无法正确处理支付结果,进而引发未捕获的错误。

修改handleSubmit函数:

const handleSubmit = async (event) => {
  event.preventDefault();
  setProcessing(true);

  try {
    // 正确解构paymentIntent
    const { paymentIntent } = await stripe.confirmCardPayment(clientSecret, {
      payment_method: {
        card: elements.getElement(CardElement),
      },
    });

    setSucceeded(true);
    setError(null);
    setProcessing(false);

    dispatch({
      type: "EMPTY_BASKET",
    });

    navigate("/orders", { replace: true });
  } catch (err) {
    // 捕获并处理支付错误
    setError(err.message);
    setProcessing(false);
    console.error("支付失败:", err);
  }
};

3. 验证本地Firebase函数运行状态

确保本地Firebase云函数已启动:

  • 执行命令:firebase emulators:start --only functions
  • 访问http://127.0.0.1:5001/challenge-940e1/us-central1/api,确认接口可正常返回内容
  • 测试/payments/create接口,确保能正确生成clientSecret

4. 检查CORS与HTTPS配置

  • 本地开发时,后端已配置cors({ origin: true }),确保允许前端跨域请求
  • 生产环境需使用HTTPS协议,Stripe不支持HTTP环境下的支付请求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 19:36:56