'Stripe CardElement not rendering
I am attempting to integrate Stripe into my application, but I am having issues getting the CardElement to render within my Payment Form.
Here is my code for my Payment Form:
import React from "react";
import { Typography, Button, Divider } from "@material-ui/core";
import {
Elements,
CardElement,
ElementsConsumer,
} from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js";
import Review from "./Review";
const stripePromise = loadStripe(process.env.REACT_APP_STRIPE_PUBLIC_KEY);
console.log(process.env.REACT_APP_STRIPE_PUBLIC_KEY);
const PaymentForm = ({
checkoutToken,
nextStep,
backStep,
shippingData,
onCaptureCheckout,
}) => {
const handleSubmit = async (event, elements, stripe) => {
event.preventDefault();
if (!stripe || !elements) return;
const cardElement = elements.getElement(CardElement);
const { error, paymentMethod } = await stripe.createPaymentMethod({
type: "card",
card: cardElement,
});
console.log("[Payment Method]", paymentMethod);
if (error) {
console.log("[error]", error);
} else {
const orderData = {
line_items: checkoutToken.live.line_items,
customer: {
firstname: shippingData.firstName,
lastname: shippingData.lastName,
email: shippingData.email,
},
shipping: {
name: "International",
street: shippingData.address1,
town_city: shippingData.city,
county_state: shippingData.shippingSubdivision,
postal_zip_code: shippingData.zip,
country: shippingData.shippingCountry,
},
fulfillment: { shipping_method: shippingData.shippingOption },
payment: {
gateway: "stripe",
stripe: {
payment_method_id: paymentMethod.id,
},
},
};
onCaptureCheckout(checkoutToken.id, orderData);
nextStep();
}
};
return (
<>
<Review checkoutToken={checkoutToken} />
<Divider />
<Typography variant="h6" gutterBottom style={{ margin: "20px 0" }}>
Payment method
</Typography>
<Elements stripe={stripePromise}>
<ElementsConsumer>
{({ elements, stripe }) => (
<form onSubmit={(e) => handleSubmit(e, elements, stripe)}>
<CardElement />
<br /> <br />
<div style={{ display: "flex", justifyContent: "space-between" }}>
<Button variant="outlined" onClick={backStep}>
Back
</Button>
<Button
type="submit"
variant="contained"
disabled={!stripe}
color="primary"
>
Pay {checkoutToken.live.subtotal.formatted_with_symbol}
</Button>
</div>
</form>
)}
</ElementsConsumer>
</Elements>
</>
);
};
export default PaymentForm;
Here is an image of the payment form as it stands now. Note that the CardElement is not present.
I do not get any error messages in the console related to Stripe or anything that would indicate something wrong. I have even given the CardElement a class name and checked it within the Inspector and found that it exists, but as an empty Div. Following that, I took a look at Stripe's documentation on the matter, but it would seem that I have followed everything correctly.
If someone might know what's up and can point me in the right direction, I would appreciate it!
Solution 1:[1]
I finally figured out the issue. Apparently loadStripe()
resolves null
in a server environment. And since I was running a local server for my application, it returned null and thus my CardElement
did not render. After deploying the app to AWS, it now works as intended.
Thanks for everyone's help!
Solution 2:[2]
loadStripe()
is an asynch function that returns a promise. It is noted in the props section of the ElementsConsumer docs that:
"Note that if you pass a Promise to the Elements provider and the Promise has not yet resolved, then stripe and elements will be null."
In your code above your promise is not resolving before your component renders which is leading to the above returning null and your CardElement not mounting.
To fix this it is recommended that you call loadStripe()
and wrap your PaymentForm
component within the Elements
provider at the root of your app as opposed to within your PaymentForm
component.
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 | Paul B |
Solution 2 | bismarck |