Reputation: 53
I am using stripe following package in nextjs 12
import { loadStripe } from "@stripe/stripe-js";
import { Elements } from "@stripe/react-stripe-js";
It is working well in developement mode but while production build it is throwing this error
Error: Could not find Elements context; You need to wrap the part of your app that calls useStripe() in an <Elements> provider.
I am using Elements as a parent container and Checkoutform inside Elements as a child
import React from 'react'
import { useAppContext } from '@/context/AppContext';
import { loadStripe } from "@stripe/stripe-js";
import { Elements } from "@stripe/react-stripe-js";
import Checkoutform from './Checkoutform';
import AppLoader from '@/components/common/Loader';
const Payment = () => {
const { state } = useAppContext();
const { user, planType } = state;
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_KEY);
const options = {
clientSecret: planType.orderData.client_secret,
appearance: {
theme: 'stripe',
}
};
return (
options.clientSecret && options.clientSecret.length > 0 ? (
<Elements stripe={stripePromise} options={options} >
<Checkoutform userData={user} planType={planType} />
</Elements >
) : (
<AppLoader />
)
)
}
export default Payment
Checkoutform.js
import React, { useEffect, useState } from "react";
import {
PaymentElement,
useStripe,
useElements
} from "@stripe/react-stripe-js";
import AppLoader from "@/components/common/Loader";
import { toast } from "react-toastify";
import { baseURL } from "@/constants/utils/universal";
export default function Checkoutform(props) {
const { userData, planType } = props;
const stripe = useStripe();
const elements = useElements();
const [message, setMessage] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [isInitialized, setIsInitialized] = useState(true);
const redirectUrl = baseURL + "/pricing/payment/success";
useEffect(() => {
if (typeof window === "undefined" && !stripe) {
return;
}
const clientSecret = new URLSearchParams(window.location.search).get(
"payment_intent_client_secret"
);
if (!clientSecret) {
return;
}
stripe.retrievePaymentIntent(clientSecret).then(({ paymentIntent }) => {
switch (paymentIntent.status) {
case "succeeded":
toast.success("Thankyou!, Payment Successful, please check your email for your receipt");
setMessage("Payment succeeded!");
break;
case "processing":
setMessage("Your payment is processing.");
break;
case "requires_payment_method":
setMessage("Your payment was not successful, please try again.");
break;
default:
setMessage("Something went wrong.");
break;
}
});
}, [stripe]);
useEffect(() => {
setTimeout(() => {
if (typeof window !== "undefined") {
setIsInitialized(false);
}
}, 1000);
}, [])
const handleSubmit = async (e) => {
e.preventDefault();
if (!stripe || !elements) {
return;
}
setIsLoading(true);
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: redirectUrl,
},
});
if (error.type === "card_error" || error.type === "validation_error") {
toast.error("Oops! some error occurred, please try again");
setMessage(error.message);
} else {
toast.error("Oops! some error occurred, please try again");
setMessage("An unexpected error occured.");
}
setIsLoading(false);
};
return (
<div className='container my-5 pt-5'>
<div className='row'>
<div className='col-md-6 col-12 mx-auto'>
<div className='card shadow-sm p-5'>
{
!isInitialized ?
<form id="payment-form" onSubmit={handleSubmit}>
<PaymentElement id="payment-element" className="mb-5" />
<div className="mt-3 d-grid">
<button disabled={isLoading || !stripe || !elements} id="submit" className="btn app-cta">
<span id="button-text">
{isLoading ? "processing please wait..." : "Pay now"}
</span>
</button>
</div>
{/* Show any error or success messages */}
{message && <div id="payment-message" className='my-2 small fw-light text-danger'>{message}</div>}
</form>
:
<AppLoader />
}
</div>
</div>
</div>
</div>
);
}
please help me what i am doing wrong or what i have to add for production build
Upvotes: 5
Views: 1731
Reputation: 1135
Jonathan Steele is right if you are using checkoutform.js as a component then make sure you kept it inside components folder which should be outside pages folder. Because if it is inside pages folder then during build nextjs will trying to pre-render it by considering it as a page which will give you this error
Upvotes: 7