Ahambramhasamii
Ahambramhasamii

Reputation: 53

Stripe : Could not find Elements context error in nextjs

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

Answers (1)

HaryanviDeveloper
HaryanviDeveloper

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

Related Questions