'Error react-stripe CardElement is not rendering?
I am integrating stripe payment on my website but the card element is not rendering here is a code
Checkout and Elements Component
import React, { useState } from 'react';
import { loadStripe } from '@stripe/stripe-js/pure';
import { Elements, CardElement, ElementsConsumer,useStripe, useElements } from '@stripe/react-stripe-js';
let stripePromise = loadStripe('pk_test_TYooMQauvdEDq54NiTphI7jx')
const CARD_OPTIONS = {
iconStyle: "solid",
style: {
base: {
iconColor: "#c4f0ff",
color: "#fff",
fontWeight: 500,
fontFamily: "Roboto, Open Sans, Segoe UI, sans-serif",
fontSize: "16px",
fontSmoothing: "antialiased",
":-webkit-autofill": {
color: "#fce883"
},
"::placeholder": {
color: "#87bbfd"
}
},
invalid: {
iconColor: "#ffc7ee",
color: "#ffc7ee"
}
}
};
export const CheckoutForm = (props) => {
console.log(props);
const stripe = useStripe();
const elements = useElements();
const [cardComplete, setCardComplete] = useState(false);
console.log(stripe, elements, 'krishna');
console.log(CardElement);
return (
<form>
<CardElement
options={CARD_OPTIONS}
onReady={()=>{
console.log("Card Element is ready")
}}
onChange={props.onChange}
/>
<button type="submit" disabled={!stripe}>
Pay
</button>
</form>
);
};
const InjectedCheckoutForm = () => {
return (
<ElementsConsumer>
{({elements, stripe}) => (
<CheckoutForm elements={elements} stripe={stripe} />
)}
</ElementsConsumer>
);
};
export const StripeForm = (props) => {
console.log(stripePromise)
return (
<Elements stripe={stripePromise}>
<InjectedCheckoutForm />
</Elements>
);
};
Component Where I am using Stripe Element
import React, { Component, useEffect, useState } from 'react';
import InformationForm from './InformationForm';
import Style from '../../styles/checkout_information.module.scss';
import { StripeForm } from './CheckoutForm';
const InputField = ({ _placeholder, _name, _label, _required, _size }) => {
return (
<section className={[Style.input_wrapper, _size && Style[_size]].join(' ')}>
<label className={Style.label}>
{_label}
{_required && <sup className={Style.isRequired}>*</sup>}
</label>
<input className={Style.input} name={_name} required={_required} placeholder={_placeholder} />
</section>
);
};
const SelectField = ({ _placeholder, _name, _label, _required, _size }) => {
return (
<section className={[Style.input_wrapper, _size && Style[_size]].join(' ')}>
<label className={Style.label}>
{_label}
{_required && <sup className={Style.isRequired}>*</sup>}
</label>
<select
className={[Style.input, Style.select].join(' ')}
name={_name}
required={_required}
placeholder={_placeholder}
>
<option></option>
</select>
</section>
);
};
export default class PersonalDetails extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
// console.log(this.props);
}
render() {
return (
<div className={Style.details_container}>
<InformationForm title={'Personal Details'}>
<fieldset className={Style.form_group}>
<form className={Style.form}>
<div className={Style.row}>
<InputField
_type={'text'}
_label={'First Name'}
_required={true}
_placeholder={'Jane'}
_name={'firstName'}
_size={'large'}
/>
<InputField
_type={'text'}
_label={'Last Name'}
_required={true}
_placeholder={'Doe'}
_name={'lastName'}
_size={'large'}
/>
</div>
<div className={[Style.row, Style.mt_1].join(' ')}>
<InputField
_type={'email'}
_label={'Email Address'}
_required={true}
_placeholder={'[email protected]'}
_name={'email'}
_size="large"
/>
<InputField
_type={'text'}
_label={'Phone Number'}
_required={true}
_placeholder={'(941)-555-0123'}
_name={'phoneNumber'}
_size="large"
/>
</div>
</form>
</fieldset>
</InformationForm>
<InformationForm margin={'mt_1'} title={'Payment Details'}>
<StripeForm />
</InformationForm>
<InformationForm margin={'mt_1'} title={'Billing Details'}>
<fieldset className={Style.form_group}>
<div className={Style.row}>
<SelectField
_type={'text'}
_label={'Country'}
_required={true}
_placeholder={'Jane'}
_name={'country'}
_size="large"
/>
</div>
<div className={[Style.row, Style.mt_1].join(' ')}>
<InputField
_type={'text'}
_label={'Street Address'}
_required={true}
_placeholder={'Address Line 1'}
_name={'address'}
_size="large"
/>
<InputField
_type={'text'}
_label={'Seccondry Address'}
_required={false}
_placeholder={'Address Line 2'}
_name={'secondryLine'}
_size="large"
/>
</div>
<div className={[Style.row, Style.mt_1].join(' ')}>
<InputField
_type={'text'}
_label={'city'}
_required={true}
_placeholder={'Address Line 1'}
_name={'address'}
_size="large"
/>
<div className={Style.row}>
<SelectField
_type={'text'}
_label={'State'}
_required={true}
_placeholder={'Cincinati'}
_name={'secondryLine'}
_size="small"
/>
<InputField
_type={'text'}
_label={'Zip Code'}
_required={true}
_placeholder={'22121'}
_name={'zipCode'}
_size="small"
/>
</div>
</div>
</fieldset>
</InformationForm>
</div>
);
}
}
Its not showing any error on console. and also loadStripe is sending request to stripe API end but its promise is showing pending
version of sdks
"@stripe/react-stripe-js": "^1.4.0",
"@stripe/stripe-js": "^1.14.0",
Solution 1:[1]
As noted in the docs, the ElementsConsumer is meant to be used with Class-based components. Since your components are all functional, you should use the recommended pattern here:
export const StripeForm = (props) => {
console.log(stripePromise)
return (
<Elements stripe={stripePromise}>
<CheckoutForm />
</Elements>
);
};
And remove the InjectedCheckoutForm entirely.
Solution 2:[2]
I recently got this issue after upgrading to React 18 and having to specify async functions within useEffects instead of async directly on the useEffect callback. Or atleast I think that is when the issue occured. The problem I believe is that Stripe will attempt to render the CC input element once, if it does not have the stripe key and client secret, it will attempt to load with bad info and not attempt again, ensuring that you get instead of your stripe element. So you have to check if you have the client secret available first.
In any case, what worked for me is adding a "options" property to the Elements stripe element and defining the client_secret from server on it and rendering it only if that client secret had been defined already.
const stripePromise = loadStripe(keys.livekey);
const CreditCard = props => {
let { stripeSecret, ...rest } = props;
return (
stripeSecret && stripeSecret.client_secret ?
<Elements stripe={stripePromise} options={{ clientSecret: stripeSecret.client_secret}}>
<ElementsConsumer>
...
</ElementsConsumer>
</Elements>
: null
)
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Nolan H |
| Solution 2 |
