Stripe支付集成遇StripeAuthenticationError,附代码求排查
Stripe PaymentIntent 创建时出现 StripeAuthenticationError 排查与解决
首次基于Stripe搭建支付API,严格遵循官方文档操作,但创建PaymentIntent时触发Error creating payment intent: StripeAuthenticationError错误,请求排查原因及解决方法。
相关代码
Payment.jsx
import React from "react"; import CheckoutForm from "./CheckoutForm"; import { Elements } from "@stripe/react-stripe-js"; import { loadStripe } from "@stripe/stripe-js"; import useCart from "../../hooks/useCart"; const stripePromise = loadStripe(import.meta.env.VITE_Stripe_PK); const Payment = () => { const [cart] = useCart(); console.log(cart); // calculate the checkout prices const cartTotal = cart.reduce((sum,item)=> sum + item.price ,0) const totalPrice = parseFloat(cartTotal.toFixed(2)); return ( <div className="max-w-screen-2xl container mx-auto xl:px-24 px-4 py-28"> <Elements stripe={stripePromise}> <CheckoutForm price={totalPrice} cart={cart} /> </Elements> </div> ); }; export default Payment;
CheckoutForm.jsx
import React, { useEffect, useState } from "react"; import { CardElement, useStripe, useElements } from "@stripe/react-stripe-js"; import { FaPaypal } from "react-icons/fa"; import useAxiosSecure from "../../hooks/useAxiosSecure"; import useAuth from "../../hooks/useAuth"; const CheckoutForm = ({ price, cart }) => { const stripe = useStripe(); const elements = useElements(); const axiosSecure = useAxiosSecure(); const { user } = useAuth(); const [cardError, setCardError] = useState(""); const [clientSecret, setClientSecret] = useState(""); useEffect(() => { if (typeof price !== "number" || price < 1) { console.log("Price is not a number or less than 1"); return; } axiosSecure.post(`/create-payment-intent`, { price }).then((res) => { console.log(res.data.clientSecret); setClientSecret(res.data.clientSecret); }); }, [price, axiosSecure]); const handleSubmit = async (event) => { event.preventDefault(); if (!stripe || !elements) { // Stripe.js has not loaded yet. Make sure to disable // form submission until Stripe.js has loaded. return; } // create a card element const card = elements.getElement(CardElement); if (card == null) { return; } // Use your card Element with other Stripe.js APIs const { error, paymentMethod } = await stripe.createPaymentMethod({ type: 'card', card, }); if (error) { console.log("[error]", error); setCardError(error.message); } else { setCardError("success!"); console.log("[PaymentMethod]", paymentMethod); } // Correct the usage of clientSecret const { paymentIntent, error:confirmError } = await stripe.confirmCardPayment( `${clientSecret}_secret`, // Corrected usage of clientSecret { payment_method: { card: card, billing_details: { name: user?.displayName || "anonymous", email: user?.email || "unknown", }, }, } ); if (confirmError) { console.log(confirmError); } console.log(paymentIntent); if (paymentIntent.status === "succeeded") { console.log(paymentIntent.id); setCardError(`Your transactionId is${paymentIntent.id} `) // payment info data const paymentInfo ={ email: user.email, transitionId:paymentIntent.id, price, quantity: cart.length, status:"Order pending", itemName: cart.map(item => item.name), cartItems: cart.map(item => item._id), menuItem: cart.map(item => item.menuItem) } console.log(paymentInfo); // send information to backend axiosSecure.post('/payments',paymentInfo) .then( res =>{ console.log(res.data); }) } }; return ( <div className="flex flex-col sm:flex-row justify-start items-start gap-8"> {/*left side */} <div className="md:w-1/2 w-full space-y-3 "> <h4 className="text-lg font-semibold">Order Summary</h4> <p>Total Price: ${price}</p> <p>Number of Items: {cart.length}</p> </div> {/* right side*/} <div className="md:w-1/3 w-full space-y-3 card shrink-0 max-w-sm shadow-2xl bg-base-100 px-4 py-8"> <h4 className="text-lg font-semibold">Process Your Payment!</h4> <h5 className="font-medium">Credit/Debit Card</h5> {/* Stripe Form */} <form onSubmit={handleSubmit}> <CardElement options={{ style: { base: { fontSize: "16px", color: "#424770", "::placeholder": { color: "#aab7c4", }, }, invalid: { color: "#9e2146", }, }, }} /> <button type="submit" disabled={!stripe} className="btn btn-sm mt-5 btn-primary w-full text-white" > Pay </button> </form> {cardError ? ( <p className="text-red text-center italic text-xs">{cardError}</p> ) : ( "" )} {/* Paypal */} <div className="mt-5 text-center"> <hr /> <button type="submit" disabled={!stripe} className="btn btn-sm mt-5 bg-orange-500 text-white" > <FaPaypal /> Pay With Paypal </button> </div> </div> </div> ); }; export default CheckoutForm;
服务端代码
const express = require("express"); const app = express(); const cors = require("cors"); const port = process.env.PORT || 6001; const mongoose = require("mongoose"); const jwt = require("jsonwebtoken"); const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY); require("dotenv").config(); // middleware app.use(cors()); app.use(express.json()); //mongodb configuration using mongoose mongoose .connect( `mongodb+srv://${process.env.DB_USER}:${process.env.DB_PASSWORD}@demo-foodi-client.xsy4pne.mongodb.net/demo-foodi-client?retryWrites=true&w=majority` ) .then(console.log("MongoDB Connected Successfully")) .catch((error) => console.log("Error connecting to MongoDB", error)); // jwt authentication app.post("/jwt", async (req, res) => { const user = req.body; const token = jwt.sign(user, process.env.ACCESS_TOKEN_SECRET, { expiresIn: "1hr", }); res.send({ token }); }); // import routes here const menuRoutes = require("./api/routes/menuRoutes"); const cartRoutes = require("./api/routes/cartRoutes"); const userRoutes = require("./api/routes/userRoutes"); const paymentRoutes = require("./api/routes/paymentRoutes"); app.use("/menu", menuRoutes); app.use("/carts", cartRoutes); app.use("/users", userRoutes); app.use("/payments", paymentRoutes); app.post("/create-payment-intent", async (req, res) => { try { const { price } = req.body; const amount = price * 100; // Convert price to cents // Create a PaymentIntent with the order amount and currency const paymentIntent = await stripe.paymentIntents.create({ amount: amount, currency: "usd", payment_method_types: ["card"], }); res.send({ clientSecret: paymentIntent.client_secret }); } catch (error) { console.error("Error creating payment intent:", error); res.status(500).send({ error: "Failed to create payment intent" }); } }); app.get("/", (req, res) => { res.send("Hello Foodi Client Server!"); }); app.listen(port, () => { console.log(`Example app listening on port ${port}`); });
错误截图

排查方向与解决方法
1. 检查Stripe Secret Key配置
- 确认环境变量
STRIPE_SECRET_KEY的值是Stripe控制台提供的Secret Key(测试环境以sk_test_开头,生产环境以sk_live_开头),绝对不能使用Publishable Key(pk_开头)。 - 检查.env文件中变量名是否拼写正确,比如是否写成
STRIPE_SECRET而非STRIPE_SECRET_KEY,同时确保密钥前后无多余空格。 - 登录Stripe控制台,确认该Secret Key未被禁用或删除,状态为有效。
2. 验证服务器网络与API访问权限
- 测试服务器能否正常连接Stripe API:在服务器终端执行以下curl命令,替换
sk_test_your_secret_key为实际密钥:
如果请求失败,说明服务器存在网络限制(防火墙、代理等),需要开放对curl -u sk_test_your_secret_key: https://api.stripe.com/v1/payment_intents -d amount=100 -d currency=usdhttps://api.stripe.com的访问权限。 - 确保使用的Stripe Node.js库版本为稳定版,过时版本可能存在API兼容性问题,可通过
npm update stripe更新。
3. 修复代码中的潜在问题
- 前端clientSecret拼接错误:
confirmCardPayment中代码写了${clientSecret}_secret,但paymentIntent.client_secret本身已经是完整的密钥格式(如pi_xxx_secret_xxx),额外拼接会导致密钥无效,引发认证错误。修改为直接使用clientSecret:const { paymentIntent, error:confirmError } = await stripe.confirmCardPayment( clientSecret, // 移除多余的"_secret"拼接 { /* ... */ } ); - 金额精度问题:前端计算
totalPrice时使用parseFloat(cartTotal.toFixed(2)),后端计算amount = price * 100可能出现浮点精度丢失(比如0.1 *100 =9.999999999999998),导致Stripe拒绝请求。建议后端使用Math.round(price *100)处理金额:const amount = Math.round(price * 100);
内容的提问来源于stack exchange,提问作者Yosef Elsersy
相关产品推荐
相关产品推荐

