am using reactjs redux to manage my states and stripe to integrate payments and i am getting this error while trying handle payment confirmation in stripe stripe.confirmPayment elements should have mounted a payment element or an express checkout element.
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import {
PaymentElement,
useStripe,
useElements,
} from "@stripe/react-stripe-js";
import styles from "./CheckoutForm.module.scss";
import Card from "../card/Card";
import CheckoutSummary from "../checkoutSummary/CheckoutSummary";
import spinnerImg from "../../assets/spinner (1).jpg";
import { toast } from "react-toastify";
import { useDispatch, useSelector } from "react-redux";
import { selectEmail, selectUserID } from "../../redux/slices/authSlice";
import {
CLEAR_CART,
selectCartItems,
selectCartTotalAmount,
} from "../../redux/slices/cartSlice";
import { selectShippingAddress } from "../../redux/slices/checkoutSlice";
import { addDoc, collection, Timestamp } from "firebase/firestore";
import { db } from "../../firebase/config";
const CheckoutForm = () => {
const [message, setMessage] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const stripe = useStripe();
const elements = useElements();
const dispatch = useDispatch();
const navigate = useNavigate();
const userID = useSelector(selectUserID);
const userEmail = useSelector(selectEmail);
const cartItems = useSelector(selectCartItems);
const cartTotalAmount = useSelector(selectCartTotalAmount);
const shippingAddress = useSelector(selectShippingAddress);
useEffect(() => {
if (!stripe) {
return;
}
const clientSecret = new URLSearchParams(window.location.search).get(
"payment_intent_client_secret"
);
if (!clientSecret) {
return;
}
}, [stripe]);
const saveOrder = () => {
const today = new Date();
const date = today.toDateString();
const time = today.toLocaleTimeString();
const orderConfig = {
userID,
userEmail,
orderDate: date,
orderTime: time,
orderAmount: cartTotalAmount,
orderStatus: "Order Status",
cartItems,
shippingAddress,
createdAt: Timestamp.now().toDate(),
};
try {
addDoc(collection(db, "orders"), orderConfig);
dispatch(CLEAR_CART());
toast.success("your order has been saved")
navigate("/checkout-success");
} catch (error) {
toast.error(error.message);
}
};
const handleSubmit = async (e) => {
e.preventDefault();
setMessage(null);
if (!stripe || !elements) {
return;
}
setIsLoading(true);
const confirmPayment = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: "http::/localhost:3000/checkout-success",
},
redirect: "if_required",
})
.then((result) => {
if (result.error) {
toast.error(result.error.message);
//maybe the customer has entered wrong account details
setMessage(result.error.message);
return;
}
if (result.paymentIntent) {
if (result.paymentIntent.status === "succeeded") {
setIsLoading(false);
//customer will be directed to the return url you provided or redirected if it needs inter mediate
toast.success("payment successful");
saveOrder();
}
}
});
setIsLoading(false);
};
return (
<section>
<div className={`container ${styles.checkout}`}>
<h2>Checkout</h2>
<form onSubmit={handleSubmit}>
<div>
<Card cardClass={styles.card}>
<CheckoutSummary />
</Card>
</div>
<div>
<Card cardClass={`${styles.card} ${styles.pay}`}>
<h3>Stripe Checkout</h3>
{isLoading ? (<img src={spinnerImg} style={{ witdh: "20px" }} /> ) : (
<div>
<PaymentElement id={styles["payment-element"]} />
<button
disabled={isLoading || !stripe || !elements}
id="submit"
className={styles.button}
>
<span id="button-text">
{isLoading ? (
<img src={spinnerImg} alt="loading.." style={{ witdh: "20px" }} />
) : (
"Pay Now"
)}
</span>
</button>
</div>
)}
{/* s*/}
{message && <div id={styles["payment-message"]}>{message}</div>}
</Card>
</div>
</form>
</div>
</section>
);
}
export default CheckoutForm;
in my logs in the stripe dashboard i am getting status 200 ok and incomplete under payments my response from the backend has both client_secret and paymentintent id help out i have tried to go through this issue here text which is related to the error that am getting but i am still stack.
This is how am initializing stripe
import { useEffect, useState } from "react";
import { loadStripe } from "@stripe/stripe-js";
import { Elements } from "@stripe/react-stripe-js";
import { useDispatch, useSelector } from "react-redux";
import {
CALCULATE_SUB_TOTAL,
CALCULATE_TOTAL_QUANTITY,
selectCartItems,
selectCartTotalAmount,
} from "../../redux/slices/cartSlice";
import { selectEmail } from "../../redux/slices/authSlice";
import {
selectBillingAddress,
selectShippingAddress,
} from "../../redux/slices/checkoutSlice";
import { toast } from "react-toastify";
import CheckoutForm from "../../components/checkoutForm/CheckoutForm";
const stripePromise = loadStripe(`${process.env.STRIPE_PUBLISHABLE_KEY}`);
const Checkout = () => {
const [message, setMessage] = useState("Initializing checkout....");
const [clientSecret, setClientSecret] = useState("");
const cartItems = useSelector(selectCartItems);
const totalAmount = useSelector(selectCartTotalAmount);
const customerEmail = useSelector(selectEmail);
const shippingAddress = useSelector(selectShippingAddress);
const billingAddress = useSelector(selectBillingAddress);
const dispatch = useDispatch();
useEffect(() => {
dispatch(CALCULATE_SUB_TOTAL());
dispatch(CALCULATE_TOTAL_QUANTITY());
}, [dispatch, cartItems]);
const description = `commerce platform Payment: email: ${customerEmail}`;
useEffect(() => {
//create a payment intent as soon as the page loads
fetch("http://localhost:4242/create-payment-intent", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
items: cartItems,
userEmail: customerEmail,
shipping: shippingAddress,
billing: billingAddress,
description,
}),
})
.then((res) => {
if (res.ok) {
return res.json();
}
return res.json().then((json) => Promise.reject(json));
})
.then((data) => {
setClientSecret(data.clientSecret);
})
.catch((error) => {
setMessage("Failled to initialize checkout");
toast.error(error.message);
});
}, []);
const appearance = {
theme: "stripe",
};
const options = {
clientSecret,
appearance,
};
return (
<div>
<section>
<div className="container">{!clientSecret && <h3>{ message}</h3>}</div>
</section>
{clientSecret && (
<Elements options={options} stripe={stripePromise}>
<CheckoutForm/>
</Elements>
)}
</div>
)
}
export default Checkout