亚马逊克隆项目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
相关产品推荐
相关产品推荐

