skip to Main Content

I am using the PaymentElement to accept payments. The problem is after a clientSecret is generated, and the amount is updated on my website it doesn’t reflect the update amount. The only way it will reflect if the page is reloaded.

Is there anyway where i can have the amount updated? Below is my code. Any help would be really appreciated. Below is my code

import React, { useState, useEffect } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import CheckoutForm from '../../components/CheckoutForm'; // Adjust the path as needed
import { loadStripe } from '@stripe/stripe-js';
import { Elements } from '@stripe/react-stripe-js';
import { useParams } from 'next/navigation';
import { useSelector } from 'react-redux';

export default function CheckoutPage() {
    const storeInfo = useSelector((state) => state.storeInfo);
    const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY);

    const [clientSecret, setClientSecret] = useState(null);
    const params = useParams();
    const storeId = params.store_id;

    const grandTotal = useSelector((state) => state.cart.grandTotal); // Assuming it's in the cart slice

    useEffect(() => {
        const grandTotalInCents = Math.round(grandTotal * 100);

        const fetchClientSecret = async () => {
            try {
                const response = await fetch('/api/checkout/create-payment-intent', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        amount: grandTotalInCents, // Dummy amount in cents
                        name: 'Dummy User', // Dummy name
                        email : "[email protected]"
                    }),
                });

                const data = await response.json();



                if (data.clientSecret) {
                    setClientSecret(data.clientSecret);
                } else {
                    console.error('Failed to retrieve client secret:', data);
                }
            } catch (error) {
                console.error('Error fetching client secret:', error);
            }
        };

        fetchClientSecret();
    }, [grandTotal]); // Dependency array ensures this runs when grandTotal changes



    return (
        <div className="container mt-5">
            <div className="d-flex justify-content-end mb-3"></div>
            {stripePromise && clientSecret && (
                <Elements stripe={stripePromise} options={{ clientSecret }}>
                    <CheckoutForm clientSecrett={clientSecret} />
                </Elements>
            )}
        </div>
    );
}

2

Answers


  1. Stripe’s Elements doesn’t reinitialize automatically when the clientSecret changes. To handle this:

    Key Prop on Elements Component: React will treat Elements as a new component if the key prop changes. By using clientSecret as the key, React will re-render the Elements component whenever clientSecret changes.

    Update your Elements wrapper like so:

    {stripePromise && clientSecret && (
        <Elements stripe={stripePromise} options={{ clientSecret }} key={clientSecret}>
            <CheckoutForm clientSecret={clientSecret} />
        </Elements>
    )}

    This ensures the Elements component re-initializes when a new clientSecret is fetched.

    import React, { useState, useEffect } from 'react';
    import 'bootstrap/dist/css/bootstrap.min.css';
    import CheckoutForm from '../../components/CheckoutForm'; // Adjust the path as needed
    import { loadStripe } from '@stripe/stripe-js';
    import { Elements } from '@stripe/react-stripe-js';
    import { useParams } from 'next/navigation';
    import { useSelector } from 'react-redux';
    
    export default function CheckoutPage() {
        const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY);
    
        const [clientSecret, setClientSecret] = useState(null);
        const params = useParams();
        const storeId = params.store_id;
    
        const grandTotal = useSelector((state) => state.cart.grandTotal); // Assuming it's in the cart slice
    
        useEffect(() => {
            const grandTotalInCents = Math.round(grandTotal * 100);
    
            const fetchClientSecret = async () => {
                try {
                    const response = await fetch('/api/checkout/create-payment-intent', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify({
                            amount: grandTotalInCents,
                            name: 'Dummy User',
                            email: '[email protected]',
                        }),
                    });
    
                    const data = await response.json();
    
                    if (data.clientSecret) {
                        setClientSecret(data.clientSecret);
                    } else {
                        console.error('Failed to retrieve client secret:', data);
                    }
                } catch (error) {
                    console.error('Error fetching client secret:', error);
                }
            };
    
            fetchClientSecret();
        }, [grandTotal]); // Dependency array ensures this runs when grandTotal changes
    
        return (
            <div className="container mt-5">
                <div className="d-flex justify-content-end mb-3"></div>
                {stripePromise && clientSecret && (
                    <Elements stripe={stripePromise} options={{ clientSecret }} key={clientSecret}>
                        <CheckoutForm clientSecret={clientSecret} />
                    </Elements>
                )}
            </div>
        );
    }

    Make sure your backend endpoint /api/checkout/create-payment-intent correctly creates a PaymentIntent with the provided amount.

    const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
    
    app.post('/api/checkout/create-payment-intent', async (req, res) => {
        const { amount, name, email } = req.body;
    
        try {
            const paymentIntent = await stripe.paymentIntents.create({
                amount,
                currency: 'usd',
                metadata: { name, email },
            });
    
            res.send({ clientSecret: paymentIntent.client_secret });
        } catch (error) {
            console.error('Error creating payment intent:', error);
            res.status(500).send({ error: 'Failed to create payment intent' });
        }
    });
    Login or Signup to reply.
  2. You should just call elements.fetchUpdates() to reflect the updates on the existing Payment Element.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search