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

Stripe PaymentElement在React项目中间歇性无法显示求助

React集成Stripe PaymentElement间歇性失效问题

问题现象

PaymentElement组件随机无法正常显示,无明确规律,多次刷新/重启应用、强制刷新(CTRL+F5)均无法解决。

  • 正常状态:支付表单完整展示
    正常显示的支付表单
  • 异常状态:
    1. 表单字段为空
      字段为空的支付表单
    2. 承载表单的iframe完全空白,body标签内无任何HTML内容
      空白iframe
  • 补充异常(已反馈给Stripe支持):
    • iframe内已加载完整表单,但opacity设为0
      opacity为0的iframe
    • iframe的body内仅包含空的root div,无其他内容
      body为空的iframe

已排查的内容

  1. 严格按照Stripe官方文档集成Elements和PaymentElement组件
  2. 切换试用账户与付费账户的密钥,问题依旧存在
  3. 已在组件渲染外部初始化stripePromise,符合文档要求
  4. 通过useEffect+空依赖数组在页面加载时创建PaymentIntent,后端为.NET6 REST API,负责调用Stripe并返回clientSecret
  5. 外层Bootstrap模态框始终正常显示,仅PaymentElement间歇性失效
  6. 控制台间歇性出现如下错误,但该错误与组件失效无明确关联(有时组件正常显示也会报错,有时失效时无错误)
    控制台错误

核心代码细节

全局Stripe初始化

import { useState, useEffect, useRef } from 'react';
import './App.css';
import DynamicTextArea from './components/DynamicTextArea';
import MultiSelect from "./components/MultiSelect";
import CheckoutForm from './components/CheckoutForm';
import ReCAPTCHA from "react-google-recaptcha";

import { loadStripe } from "@stripe/stripe-js";
import { Elements } from "@stripe/react-stripe-js";

const stripePromise = loadStripe("pk_test_xxxxxxxx");


function App() {

PaymentIntent创建逻辑

const createPaymentIntent = () => {
    // Create PaymentIntent as soon as the page loads
    fetch("/payments/create-payment-intent", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ items: [{ id: "product001" }] }),
    })
        .then((res) => res.json())
        .then((data) => setClientSecret(data.clientSecret));
}

const showPaymentForm = async () => {
    setPaymentModalOptions({...paymentModalOptions, visible: true});
}

useEffect(() => {
    createPaymentIntent();
}, []);

Elements配置与模态框嵌套

const options = {
    clientSecret: clientSecret,
    appearance: {
        theme: 'stripe',
    }
};
<div id="payment-modal" className={`modal ${paymentModalOptions.visible ? 'modal-show' : ''}`} role="dialog">
    <div className="modal-dialog" role="document">
        <div className="modal-content">
            <div className="modal-body">
                {clientSecret && (
                    <Elements options={options} stripe={stripePromise}>
                        <CheckoutForm onPaymentSucceeded={handlePaymentSucceeded} onCancelPayment={handlePaymentCancelled}/>
                    </Elements>
                )}
            </div>
        </div>
    </div>
</div>

CheckoutForm组件代码

import { useEffect, useState } from "react";
import {
  PaymentElement,
  useStripe,
  useElements
} from "@stripe/react-stripe-js";

import './CheckoutForm.css';

export default function CheckoutForm(props) {
  const stripe = useStripe();
  const elements = useElements();
  const [message, setMessage] = useState(null);
  const [paymentIsLoading, setPaymentIsLoading] = useState(false);

  const paymentElementOptions = {
    layout: "tabs"
  }

  const delay = async (ms) => new Promise(res => setTimeout(res, ms));

  const handleSubmit = async (evt) => {
    evt.preventDefault();

    if (!stripe || !elements) {
      // Stripe.js has not yet loaded.
      // Make sure to disable form submission until Stripe.js has loaded.
      return;
    }

    setPaymentIsLoading(true);

    const paymentResult = await stripe.confirmPayment({
      elements,
      confirmParams: {
        // Make sure to change this to your payment completion page
        return_url: "http://localhost:44472",
      },
      redirect: 'if_required'
    });
    
    if (paymentResult.paymentIntent.status === 'succeeded') {
      setMessage("Payment successful!");
      
      // wait two seconds, then close the payment modal
      await delay(2000);
      props.onPaymentSucceeded();
      
    } else {
      setMessage("Payment unsuccessful");
    }

    setPaymentIsLoading(false);
  }

  return (
    <form id="payment-form" onSubmit={handleSubmit}>
        <PaymentElement id="payment-element" options={paymentElementOptions} />
          <div id="payment-buttons-wrapper">
              <div className="payment-button-group">
                  {paymentIsLoading ?
                      <div className="spinner-border text-secondary" id="payment-spinner" role="status"></div>
                      : <button disabled={paymentIsLoading || !stripe || !elements} id="submit" className="btn btn-light border">
                          <span id="button-text">Pay now</span>
                        </button>
                  }
              </div>
              
              <button onClick={props.onCancelPayment} className="btn btn-light border">Cancel</button>
          </div>
      
      {/* Show any error or success messages */}
      {message && <div id="payment-message">{message}</div>}
    </form>
  );
}

之前遇到过React中间歇性渲染问题,通常由异步调用未完成或useEffect使用不当导致,但本次未发现问题所在,寻求解决方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 02:56:11