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

React Router Loader实现Stripe嵌入结账返回401错误求助

Stripe嵌入式结账表单401未授权错误排查

问题详情

按照Stripe官方文档实现Node/Express+React的嵌入式结账表单,前端改用React Router v6的loader替代示例中的useEffect。后端创建支付会话的接口返回200,前端能获取到有效clientSecret,但加载嵌入表单时,https://api.stripe.com/v1/payment_pages/ 端点返回401(未授权)错误,而Stripe日志中仅显示200响应,无401相关记录。已确认测试API密钥正确,求排查问题原因。

后端代码

const stripe = require('stripe')(`${process.env.STRIPE_SECRET_KEY}`);

router.post('/create-payment-session', requireLogin, async (req, res) => {
  try {
    const orderId = req.query.order_id;
    const orderData = await db.getOrderById(orderId);

    // Generate checkout session Price objects (line items)
    const orderItemsData = orderData.order_items.map(item => {
      return {
        price_data: {
          currency: 'usd',
          product_data: {
            name: item.product_name,
          },
          unit_amount: (Number(item.product_price.substring(1)) * 100),  // Price in cents
        },
        quantity: Number(item.product_quantity),
      }
    });

    const session = await stripe.checkout.sessions.create({
      ui_mode: 'embedded',
      line_items: orderItemsData,
      mode: 'payment',
      return_url: `${process.env.FRONT_END_BASE_URL}/checkout/${orderId}/payment-return?session_id={CHECKOUT_SESSION_ID}`,
    });
  
    res.send({clientSecret: session.client_secret});

  } catch(err) {
    console.log(err);
    res.status(500).send();
  }
});

router.get('/payment-session-status', requireLogin, async (req, res) => {
  const session = await stripe.checkout.sessions.retrieve(req.query.session_id);
  res.send({ status: session.status });
});

前端代码

import { useLoaderData, useRouteLoaderData } from "react-router-dom";
import { loadStripe } from "@stripe/stripe-js";
import { EmbeddedCheckoutProvider, EmbeddedCheckout } from "@stripe/react-stripe-js";

import InlineErrorPage from "../../components/InlineErrorPage/InlineErrorPage";

const stripePromise = loadStripe(`${process.env.REACT_APP_STRIPE_PUBLIC_KEY}`);

export async function paymentSessionLoader({ params }) {
  try {
    // Create a Checkout Session as soon as the page loads
    const baseEndpoint = `${process.env.REACT_APP_API_BASE_URL}/checkout/create-payment-session`;
    const res = await fetch(`${baseEndpoint}?order_id=${params.orderId}`,
    {
      method: "POST",
      credentials: "include",
    });
    console.log(res);

    if (res.ok) {
      const data = await res.json();
      return { clientSecret: data.clientSecret };
    }
    throw new Error("Unexpected status code.");

  } catch (error) {
    console.log(error);
    return { error: "Sorry, payment is not currently possible. Please try again later." };
  }
}

export function PaymentPage() {

  const authData = useRouteLoaderData("app");
  const { clientSecret, error } = useLoaderData();
  console.log(clientSecret);

  if (!authData.logged_in) {
    return <InlineErrorPage pageName="Order details" type="login_required" loginRedirect="/orders" />;
  } else if (error) {
    return <InlineErrorPage pageName="Order failed" message={error} />;
  }

  return (
    <div id="checkout">
      {clientSecret && (
        <EmbeddedCheckoutProvider
          stripe={stripePromise}
          options={{clientSecret}}
        >
          <EmbeddedCheckout />
        </EmbeddedCheckoutProvider>
      )}
    </div>
  )
}

排查方向

  • 确认clientSecret完整性:对比前端控制台打印的clientSecret与后端返回的session.client_secret,确保没有截断、空格或字符错误。
  • 验证密钥环境一致性:检查前端REACT_APP_STRIPE_PUBLIC_KEY是否为测试环境公钥(以pk_test开头),且与后端使用的测试密钥(以sk_test开头)属于同一Stripe账号。
  • 检查CORS配置:后端需允许前端域名发起跨域请求,若CORS配置缺失或错误,可能导致Stripe嵌入式表单的请求授权信息传递异常。
  • 排查请求拦截/代理干扰:检查前端是否有全局请求拦截器,或使用了代理工具(如Webpack Dev Server代理),这类工具可能修改Stripe请求的头信息,导致授权失败。
  • 核对Checkout Session配置:确认创建会话时ui_mode: 'embedded'设置正确,return_url为HTTPS且属于Stripe后台已配置的域名(测试环境可暂时跳过域名验证,但正式环境必须配置)。
  • 隔离测试会话创建流程:直接在前端使用Stripe测试密钥创建简单的Checkout Session,拿到clientSecret后初始化表单,验证是否仍出现401,以此排除后端会话创建的问题。
  • 分析浏览器请求头:在浏览器开发者工具的Network面板中,查看payment_pages请求的Authorization头,确认其格式为Bearer {你的公钥},且公钥正确。若授权头缺失或错误,会直接导致401。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 05:25:59