Stripe PaymentElement在React项目中间歇性无法显示求助
React集成Stripe PaymentElement间歇性失效问题
问题现象
PaymentElement组件随机无法正常显示,无明确规律,多次刷新/重启应用、强制刷新(CTRL+F5)均无法解决。
- 正常状态:支付表单完整展示

- 异常状态:
- 表单字段为空

- 承载表单的iframe完全空白,body标签内无任何HTML内容

- 表单字段为空
- 补充异常(已反馈给Stripe支持):
- iframe内已加载完整表单,但
opacity设为0
- iframe的body内仅包含空的root div,无其他内容

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

核心代码细节
全局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
相关产品推荐
相关产品推荐

