[BUG]: IntegrationError: Invalid value for stripe.confirmSetup(): elements should have a mounted Payment Element or Express Checkout Element.
Closed this issue · 2 comments
What happened?
I have developed a form to save payment details for the user.
I'm rendering this form inside a Mui Popup component on my page.
When I try to fill details submit for the first time I get this error: "An unhandled error was caught from submitForm() IntegrationError: Invalid value for stripe.confirmSetup(): elements should have a mounted Payment Element or Express Checkout Element."
When I try for the second time it works. Can you help me with this problem.
I'm pasting the working code below
Note: I observed that if comment setIsSubmitting setter functions, it works fine. But when I set state its having this issue.
import { Formik, useFormikContext } from 'formik'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useAddressOptions } from '../../../hooks'
import useCreateNewAddress from '../../../hooks/useCreateNewAddress'
import { PaymentElement, useElements, useStripe } from '@stripe/react-stripe-js'
import {
AddressForm,
Button,
Error,
FormGroup,
MenuDropdown,
} from '../../../components'
import { Plus } from '../../../icons'
import { CLR_PRIMARY } from '../../../constants/colors'
const DEFAULT_RETURN_URL = '/my-profile/payment-details'
function PaymentForm({ onClose, returnTo, zIndex }) {
const stripe = useStripe()
const elements = useElements()
const { t } = useTranslation(['myProfile'])
const { defaultBillingAddress } = useAddressOptions()
const [showAddressForm, setShowAddressForm] = useState(false)
const [errorMessage, setErrorMessage] = useState(null)
const [isSubmitting, setIsSubmitting] = useState(false)
const {
mutate: createNewAddress,
isLoading,
isError,
} = useCreateNewAddress(
// onSuccess
() => {
closeAddressForm()
}
)
const openAddressForm = () => {
setShowAddressForm(true)
}
const closeAddressForm = () => {
setShowAddressForm(false)
}
const validateForm = (values) => {
return validate(values, t)
}
const handleFormSubmit = async (values) => {
const { name, selectedAddress, isDefaultPaymentMethod } = values
setErrorMessage(null)
setIsSubmitting(false)
if (!stripe || !elements) {
return null
}
setIsSubmitting(true)
const urlPath = returnTo ? returnTo : DEFAULT_RETURN_URL
let returnURL = `${window.location.origin}${urlPath}`
if (isDefaultPaymentMethod) {
returnURL += '?default_payment_method=true'
}
const { error } = await stripe.confirmSetup({
elements,
confirmParams: {
return_url: returnURL,
payment_method_data: {
billing_details: {
name,
address: {
city: selectedAddress?.value?.city,
country: selectedAddress?.value?.countryCode,
line1: selectedAddress?.value?.address,
line2: selectedAddress?.value?.flatSuite,
postal_code: selectedAddress?.value?.zipCode,
state: selectedAddress?.value?.stateCode,
},
},
},
},
})
setIsSubmitting(false)
if (error) {
setErrorMessage(error.message)
}
}
const isSaveBtnDisabled = !stripe
return (
<Formik
initialValues={{
name: '',
selectedAddress: defaultBillingAddress,
isDefaultPaymentMethod: false,
}}
onSubmit={handleFormSubmit}
validate={validateForm}
validateOnChange={true}
>
{({
values,
errors,
touched,
handleChange,
handleSubmit,
handleBlur,
}) => (
<form className="payment-details__form" onSubmit={handleSubmit}>
<div className="payment-details__method-info">
<div className="payment-details__left-col">
<FormGroup>
<FormGroup.Label htmlFor="name">
{t('fullName')}
</FormGroup.Label>
<FormGroup.Input
id="name"
name="name"
value={values?.name}
onChange={handleChange}
onBlur={handleBlur}
/>
{touched.name && errors?.name ? (
<Error mt="0.4rem">{errors?.name}</Error>
) : null}
</FormGroup>
<PaymentElement
options={{
fields: {
billingDetails: {
address: {
country: 'never',
postalCode: 'never',
},
},
},
}}
/>
<FormGroup className="payment-details__default-payment">
<FormGroup.Input
type="checkbox"
id="default-payment-method"
name="isDefaultPaymentMethod"
checked={values?.isDefaultPaymentMethod}
onChange={handleChange}
/>
<FormGroup.Label htmlFor="default-payment-method">
{t('setAsDefaultPaymentMethod')}
</FormGroup.Label>
</FormGroup>
</div>
<FormGroup className="payment-details__billing-details">
<FormGroup.Label>{t('billingAddress')}</FormGroup.Label>
<AddressField />
{touched.selectedAddress && errors?.selectedAddress ? (
<Error mt="0.4rem">{errors?.selectedAddress}</Error>
) : null}
<AddressForm
isOpen={showAddressForm}
handleClose={closeAddressForm}
title={t('addBillingAddress')}
error={t('genericErrMsg')}
isError={isError}
isLoading={isLoading}
action={createNewAddress}
zIndex={zIndex}
isBillingAddress
/>
<Button primaryBordered onClick={openAddressForm} type="button">
{t('addNewAddress')} <Plus color={CLR_PRIMARY} />
</Button>
</FormGroup>
</div>
<ErrorMessage errorMessage={errorMessage} />
<Footer
onClose={onClose}
isSaveBtnDisabled={isSaveBtnDisabled}
isSubmitting={isSubmitting}
/>
</form>
)}
</Formik>
)
}
function AddressField() {
const { addresses, defaultBillingAddress } = useAddressOptions()
const { values, setFieldValue, touched, setTouched } = useFormikContext()
useEffect(() => {
setFieldValue('selectedAddress', defaultBillingAddress)
}, [defaultBillingAddress, setFieldValue])
return (
<MenuDropdown
options={addresses}
value={values?.selectedAddress}
onChange={(newAddress) => {
setFieldValue('selectedAddress', newAddress)
}}
onBlur={() => {
if (!touched?.selectedAddress) {
setTouched({ selectedAddress: true })
}
}}
/>
)
}
function ErrorMessage({ errorMessage }) {
if (!errorMessage) return null
return (
<Error centered mt="1.5rem" mb="-1rem">
{errorMessage}
</Error>
)
}
function Footer({ onClose, isSaveBtnDisabled, isSubmitting }) {
const { t } = useTranslation(['myProfile'])
return (
<footer className="payment-details__footer">
<div>
<Button primaryBordered onClick={onClose}>
{t('cancel')}
</Button>
<Button
secondary
type="submit"
disabled={isSaveBtnDisabled}
loading={isSubmitting}
>
{t('save')}
</Button>
</div>
</footer>
)
}
function validate(values, t) {
const { name, selectedAddress } = values
const errors = {}
if (!name || name?.trim() === '') {
errors.name = t('nameIsIncomplete')
}
if (
!selectedAddress ||
!selectedAddress?.value ||
Object.keys(selectedAddress?.value).length === 0
) {
errors.selectedAddress = t('billingAddressIsIncomplete')
}
return errors
}
export default PaymentForm
Environment
Chrome on Windows 10
Reproduction
No response
Hi, this looks like an integration question, and we aren’t able to answer those here on GitHub. Please reach out to Stripe Support for help.
Hi @fruchtose-stripe It seems like its an issue from stripe. I saw similar issue reported in past hence raised the issue.
Can you please check it once?