'CardElement not showing ui components in react app ie `Card Number, MM/YY, CVC?
So my problem is that I cannot see the CardElement
Ui component in the image below. It should be above the Order Total: $0 which is beside the payment method.
I have tried many other ways of debugging it but no luck. As of posting this, I am still debugging it and external help from experienced developers would be appreciated. I will provide my code below ie Payment.js
Payment.css
App.js
this is Payment.js
import React, { useState, useEffect } from "react";
import "./Payment.css";
import { useStateValue } from "./StateProvider";
import CheckoutProduct from "./CheckoutProduct";
import { Link, useNavigate } from "react-router-dom";
import { CardElement, useStripe, useElements } from "@stripe/react-stripe-js";
import CurrencyFormat from "react-currency-format";
import { getBasketTotal } from "./reducer";
import axios from "./axios";
function Payment() {
const [{ basket, user }, dispatch] = useStateValue();
const navigate = useNavigate();
const stripe = useStripe();
const elements = useElements();
const [succeeded, setSucceeded] = useState(false);
const [processing, setProcessing] = useState("");
const [error, setError] = useState(null);
const [disabled, setDisabled] = useState(true);
const [clientSecret, setClientSecret] = useState(true);
useEffect(() => {
// generate the special stripe secret which allows us to charge a customer
const getClientSecret = async () => {
const response = await axios({
method: "post",
// Stripe expects the total in a currencies sub-units
url: `/payments/create?total=${getBasketTotal(basket) * 100}`,
});
setClientSecret(response.data.clientSecret);
};
getClientSecret();
}, [basket]);
const handleSubmit = async (event) => {
// do all the fancy stripe stuff...
event.preventDefault();
setProcessing(true);
const payload = await stripe
.confirmCardPayment(clientSecret, {
payment_method: {
card: elements.getElement(CardElement),
},
})
.then(({ paymentIntent }) => {
// paymentIntent = payment confirmation
setSucceeded(true);
setError(null);
setProcessing(false);
dispatch({
type: "EMPTY_BASKET",
});
navigate.replace("/orders");
});
};
const handleChange = (event) => {
// Listen for changes in the CardElement
// and display any errors as the customer types their card details
setDisabled(event.empty);
setError(event.error ? event.error.message : "");
};
return (
<div className="payment">
<div className="payment__container">
<h1>
Checkout (<Link to="/checkout">{basket?.length} items</Link>)
</h1>
<div className="payment__section">
<div className="payment__title">
<h3>Delivery Address</h3>
</div>
<div className="payment__address">
<p>{user?.email}</p>
<p>123 React Lane</p>
<p>Los Angeles, CA</p>
</div>
</div>
<div className="payment__section">
<div className="payment__title">
<h3>Review items and delivery</h3>
</div>
<div className="payment__items">
{basket.map((item) => (
<CheckoutProduct
id={item.id}
title={item.title}
image={item.image}
price={item.price}
rating={item.rating}
/>
))}
</div>
</div>
<div className="payment__section">
<div className="payment__title">
<h3>Payment Method</h3>
</div>
<div className="payment__details">
<form onSubmit={handleSubmit}>
<CardElement onChange={handleChange} />
<div className="payment__priceContainer">
<CurrencyFormat
renderText={(value) => <h3>Order Total: {value}</h3>}
decimalScale={2}
value={getBasketTotal(basket)}
displayType={"text"}
thousandSeparator={true}
prefix={"$"}
/>
<button disabled={processing || disabled || succeeded}>
<span>{processing ? <p>Processing</p> : "Buy Now"}</span>
</button>
</div>
{error && <div>{error}</div>}
</form>
</div>
</div>
</div>
</div>
);
}
export default Payment;
this is Payment.css
.payment {
background-color: white;
}
.payment__container > h1 {
text-align: center;
padding: 10px;
font-weight: 400;
background-color: rgb(234, 237, 237);
border-bottom: 1px solid lightgray;
}
.payment__container > h1 a {
text-decoration: none;
}
.payment__section {
display: flex;
padding: 20px;
margin: 0 20px;
border-bottom: 1px solid lightgray;
}
.payment__title {
flex: 0.2;
}
.payment__address,
.payment__items,
.payment__details {
flex: 0.8;
}
this is App.js
import React, { useEffect } from "react";
import "./App.css";
import Header from "./Header";
import Home from "./Home";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Checkout from "./Checkout";
import Login from "./Login";
import Payment from "./Payment";
import { auth } from "./firebase.js";
import { useStateValue } from "./StateProvider";
import { loadStripe } from "@stripe/stripe-js";
import { Elements } from "@stripe/react-stripe-js";
const stripePromise = loadStripe(
"pk_test_51Ku8iiSHAST6F5x2egU8YwHdYkqKAOcJIfrfXKa0b7kxeHm0ECkvFOLVW0KeaiudzfAfU31m954rOkIKGsoXhiAE00EQx04Did"
);
function App() {
const [{}, dispatch] = useStateValue();
useEffect(() => {
// will only run once when the app component loads...
auth.onAuthStateChanged((authUser) => {
console.log("THE USER IS >>> ", authUser);
if (authUser) {
// the user just logged in / the user was logged in
dispatch({
type: "SET_USER",
user: authUser,
});
} else {
// the user is logged out
dispatch({
type: "SET_USER",
user: null,
});
}
});
}, []);
return (
//BEM
<Router>
<div className="app">
<Routes>
<Route path="/login" element={[<Login />]} />
<Route path="/checkout" element={[<Header />, <Checkout />]} />
<Route
path="/payment"
element={[
<Header />,
<Elements stripe={stripePromise}>
<Payment />
</Elements>,
]}
/>
<Route path="/" element={[<Header />, <Home />]} />
</Routes>
</div>
</Router>
);
}
export default App;
//npm install <package-name> --legacy-peer-deps
this is Package.json of the src folder
{
"name": "amazon-clone",
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@mui/icons-material": "^5.6.1",
"@mui/material": "^5.6.1",
"@stripe/react-stripe-js": "^1.7.2",
"@stripe/stripe-js": "^1.29.0",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.1.1",
"@testing-library/user-event": "^13.5.0",
"axios": "^0.27.2",
"firebase": "^9.6.11",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "set PORT=3000 && react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
One last thing could anyone write why express is not working with node v.17 / 18 current version while writing this post it is express is not supported if I downgrade to node v16 will it cause trouble to other js code in my project. Please explain
package.json of the function folder
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "16"
},
"main": "index.js",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.1",
"firebase-admin": "^10.0.2",
"firebase-functions": "^3.18.0",
"stripe": "^8.219.0"
},
"devDependencies": {
"eslint": "^8.9.0",
"eslint-config-google": "^0.14.0",
"firebase-functions-test": "^0.2.0"
},
"private": true
}
In some tutorial videos I can see they get it very quickly I don't know why I cannot debug. Kindly help me find the problem here. Thank you
Solution 1:[1]
In your App.js
file, where you declare your Route
to /payment
, try to pass a single component, instead of passing your component already wrapped in <Elements>
component.
I've faced this problem and for some reason, Stripe elements won't work that way. So instead try to make a component that will render your Payment
component wrapped in <Elements>
, I'll write some pseudocode for you: ParentComponent.js
const ParentComponent = () => {
return (
<Elements>
<Payments />
</Elements>
);
}
So now, you would just do this in your App.js
component:
App.js
<Route
path="/payment"
element={[
<Header />,
<ParentComponent />
]}
/>
Solution 2:[2]
I was finally able to find a proper solution. Older versions of stripe/react-stripe-js are not working with react18. To solve issue, please install latest version of react-stripe-js.
npm install stripe/react-stripe-js@latest
Worked for me.
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 | DharmanBot |
Solution 2 | Urmez |