I'm trying to integrate a Stripe payments page using the React "Elements". I'm following the tutorial from https://stripe.com/docs/payments/accept-a-payment?platform=web&ui=elements#web-submit-payment and I've gotten to step 5, "Submit the payment to Stripe". My code doesn't look much different from the example, but whenever I try to submit a payment this error:
Invalid value for stripe.confirmPayment(): elements should have a mounted Payment Element.
Thrown from stripe.confirmPayment. I've included the <Elements/> and <PaymentElement/> on the page, and passed the return value from useElements() so I'm really not sure what I'm missing.
Here's my checkout form:
function StripeCheckoutForm({paymentIntent,booking}: StripeCheckoutFormProps) {
const stripe = useStripe();
const elements = useElements();
const [confirming,setConfirming] = useState(false)
const handleSubmit = async (ev: FormEvent<HTMLFormElement>) => {
ev.preventDefault();
if (!stripe || !elements) {
notifyWarning("Still loading. Please wait a few seconds and then try again.");
return;
}
setConfirming(true)
try {
const {error} = await stripe.confirmPayment({
//`Elements` instance that was used to create the Payment Element
elements,
confirmParams: {
return_url: resolveRoute('customerPaymentReceived',{key:booking.key}),
},
})
if(error) {
notifyError(error.message)
setConfirming(false)
}
} catch(error) {
setConfirming(false)
if(error?.message) {
notifyError(error.message) // <-- error shown here
} else {
notifyError("Something went wrong");
}
}
}
return (
<form onSubmit={handleSubmit}>
<PaymentElement/>
<BlockSpacer height="1rem"/>
<ActionButton disabled={!stripe || !elements || confirming} type="submit" className="btn-phone btn-fullwidth">Pay {paymentIntent.amountFormatted}</ActionButton>
</form>
)
}
And that form is inside this component, similar to the example shown in step 4:
import {Elements as StripeElements} from '@stripe/react-stripe-js';
import {useStripe, useElements, PaymentElement} from '@stripe/react-stripe-js';
function StripePaymentForm({stripeAccountId,paymentIntent,booking}: StripePaymentFormProps) {
const options = {
clientSecret: paymentIntent.clientSecret,
loader: 'always',
}
return (
<StripeElements stripe={getStripeConnect(stripeAccountId)} options={options}>
<StripeCheckoutForm paymentIntent={paymentIntent} booking={booking}/>
</StripeElements>
)
}
The only thing I can see that's different is that I'm using a Connect account, so I'm passing in account ID in when I load Stripe. getStripeConnect is basically
loadStripe(STRIPE_PUBLIC_KEY, {stripeAccount: CONNECTED_ACCOUNT_ID})
You can see the React component tree here if it helps:

What am I missing?
I'm guessing this stems from useElements() is not finding any elements:

But I still don't know why.
In my case, the issue is that during the stripe request stripe.confirmPayment, the component that has the Elements component must be being rendered on page.
If you remove this component from the DOM to show a loading animation during the request, for example, the error will be thrown.
I believe it's a recent bug in react-stripe-js:
https://github.com/stripe/react-stripe-js/issues/296
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With