#hayen_error

1 messages ยท Page 1 of 1 (latest)

pale spruceBOT
primal islandBOT
#

Below are links to other discussions we've had with you in the past week in case you want to review that information. If your question is related to one of these previous discussions, please provide a comprehensive summary of the current state and what you need help with now. We help many users simultaneously, so a summary allows us to resolve your issue as soon as possible.

pale spruceBOT
#

๐Ÿ‘‹ Welcome to your new thread!

โฒ๏ธ We'll be here soon! We typically respond in a few minutes, but in some cases we might need a bit more time (e.g., server's busy, you've got a complex question, etc.).

โฑ๏ธ We close idle threads, which makes them read-only. Once a thread is closed it won't be reopened, but you can start a new thread if you have another question.

๐Ÿ”— This thread will always be available, even after it's closed. You can find it again using Discord's search, or you can save this link: https://discord.com/channels/841573134531821608/1257434915041968221

๐Ÿ“ Have more to share? Add details, code, screenshots, videos, etc. below.

floral ingot
#

child component where the client secret is created, in the onSubmit function of the form:

        // Create payment intent
                let paymentIntent = await createPaymentIntentApi(cart.key, metadata, vatRate, vatData).catch(
                    (error) => {
                        setMessage(error.message);
                        setIsLoading(false);
                        return;
                    }
                );

                console.log("Created stripe payment intent: ", paymentIntent);

                const { clientSecret: clientSecret } = paymentIntent // response.data

                // Pass the client secret to the parent. clientSecret state will then be updated in parent (checkout.js)

                if (clientSecret) {

                    console.log(clientSecret);
                    onClientSecretGenerated(clientSecret); // Pass the client secret to the parent
                } else {
                    throw new Error('Client secret not found');
                }
#

checkout.js (the parent):

const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY);

export default function Checkout() {
    const {cart} = useContext(CartContext);
    const [clientSecret, setClientSecret] = useState('');

    const appearance = {
        theme: 'stripe',
    };

    const options = {
        appearance,
        mode: 'payment',
        amount: grandTotal,
        currency: 'eur',
        // paymentMethodTypes: ['card', 'ideal', 'paypal'],
        paymentMethodTypes: ['card', 'ideal', 'paypal'],
    };

    if (clientSecret) {
        options.clientSecret = clientSecret;
    }

    return (
        <Layout>
            { searchParams.get('redirect_status') === 'failed' && (
                <div className={styles.error}>
                    <p>There was an error processing your payment. Please try again.</p>
                </div>
            ) }
            <Container>
                <Elements options={options} stripe={stripePromise}>
                    <CheckoutContainer onClientSecretGenerated={setClientSecret} />
                </Elements>
            </Container>
        </Layout>
    )
}
zenith scroll
#

Hello, taking a step back, can you tell me more about what you are trying to do here? Are you trying to initialize your Elements page without a payment intent and then update the price or something later?

floral ingot
#

Oh yeah I forgot to say, later in the code of the child component (where the client secret is created) I am using fetchUpdates()

zenith scroll
#

fetchUpdates is intended for our flow where you start by creating a PaymentIntent before you initialize Elements. Basically the intent defines the price, available payment methods and such, so if those change on the intent you need to call fetchUpdates but if you are working without an intent, the Elements instance defines those things so you can just update the elements instance

floral ingot
#

and when I am not using fetchUpdates, everything works fine and it doesn't give me any errors about the clientsecret

#

But I need to use the fetchUpdates function because I have bug whenever the user is trying to pay with Google Pay , the price isn't correct for some reason (atleast that is what your collague told me)

#

But the fetchUpdates function requires a clientsecret that is being passed to the Elements component

#

So that's why I am trying to do it like this

pale spruceBOT
floral ingot
zenith scroll
#

fetchUpdates is only for the flow where you initialize Elements with a client secret

floral ingot
#

I am using this function because your collague told me that it would solve the problem when the price is not correct. Let's say I am from the Netherlands and the VAT is 21% and my product is 1.00, but when using Google Pay, and selecting the Netherlands it shows 1.00 instead of 1.21. But when selecting IDEAL it shows 1.21, which is correct. So it only happens when using Google Pay

#

And your collague gave as a solution, to use fetchUpdates

#

That's why I am a bit confused

zenith scroll
#

My colleague may have thought that you were using that other flow

#

fetchUpdates is only for the flow that you are not currently using. Updating the elements object is the way to indicate the pricing change with your current setup

floral ingot
#

Okay I understand

#

But I am bit confused because whenever selecting GooglePay in the PaymentElement, it shows 1.00 at first in the google payment screen. And after a succesfull checkout, when I look in my stripe dashboard it does show 1.21, which is correct

zenith scroll
#

Basically the Apple and Google Pay sheet don't have a concept of the Stripe payment. Elements has to tell them what price to display, so if Elements has an outdated idea of what the price is, so will the google or apple pay sheet

floral ingot
#

But how come IDEAL and paypal etc. show the price correctly immediately?

#

It just happens with Google Pay

zenith scroll
#

Immediately when what happens? before you call fetchUpdates or update the elements object?

floral ingot
#

No I haven't even used fetchUpdates, and I am definitely not going to use it anymore because you told me it doesn't work with my flow right?

zenith scroll
#

Oh, are iDEAL and PayPal on an entirely different page? It may be that our server properly coordinates things before the redirect if that is the case

#

And right, but I am trying to understand where you are updating the price that updates it for iDEAL and PayPal. I was not aware that that could happen but am happy to look in to what is happening when I have more details of what your integration is doing here

floral ingot
#

No it's not on a different page

#

Wait I can show you a screenshot

#

Or wait eh

#

Not it is on a different page sorry

#

When selecting ideal it sends me to a different page

#

Same goes for paypal

#

It's just google pay because google doesn't redirect to a different page

zenith scroll
#

Gotcha, that makes sense. So by that point there is a PaymentIntent with the amount that you actually want to charge, so at that point my guess is that we reference the actual price when talking to PayPal or iDEAL. We could probably also retrieve the price on the frontend as well, but at the moment we expect you to update the price as it changes

floral ingot
#

Oh, so for Google Pay price to show the correct price I MUST update the price using the elements.update function

#

Hmm it is still not updating in google pay screen

#

Can you check my code?:

export default function Checkout() {
    const {cart} = useContext(CartContext);
    const [grandTotal, setGrandTotal] = useState(0);
    const [grandTotalFetched, setGrandTotalFetched] = useState(false); // New state variable
    const searchParams = useSearchParams();
    const elementsRef = useRef(null); // Ref to store the Elements instance

    useEffect(() => {
        if (cart && cart.items) {
            // Calculate grand total
            const newGrandTotal = cart.items.reduce(
                (total, item) => total + item.quantity * item.price,
                0
            );

            setGrandTotal(newGrandTotal);
        }
    }, [cart]);

    useEffect(() => {
        if (elementsRef.current) {
            elementsRef.current.update({
                amount: grandTotal
            });
        }
    }, [grandTotal]);

    if(!grandTotal) {
        return (
            <Layout>
                <Container>
                    <div className={styles.successPage}>
                        <h1>Your cart is empty!</h1>
                        <center>Return to the <Link href="/">homepage</Link></center>
                    </div>
                </Container>
            </Layout>
        )
    }

    const appearance = {
        theme: 'stripe',
    };

    const options = {
        // clientSecret,
        appearance,
        mode: 'payment',
        amount: grandTotal,
        currency: 'eur',
        // paymentMethodTypes: ['card', 'ideal', 'paypal'],
        paymentMethodTypes: ['card', 'ideal', 'paypal'],
    };

    return (
        <Layout>
                <Elements options={options} stripe={stripePromise}  onReady={(elements) => {
                        elementsRef.current = elements; // Store the Elements instance
                    }}>
                    <CheckoutContainer />
                </Elements>
            </Container>
        </Layout>
    )
}
rancid kernel
#

๐Ÿ‘‹

#

Stepping in for my teammate. I'm not a React expert so may need to pull in another teammate. Are you able to log grandTotal?

floral ingot
#

Yes, before I answer your question i think i'd have to ask this first to be sure i am not using element.update in the wrong place

#

should elements.update be placed in the same file as where the options are passed to the Elements component?

#

because useElements is used in another file

rancid kernel
#

Let's take step back for a second. Could you summarize for me where you're blocked? Also, to be clear, are you using both the PaymentElement and the ExpressCheckoutElement or just the PaymentElement?

floral ingot
#

I am just using PaymentElement

#

I think I am just confused on where to use elements.update

#

Before I didn't use elements.update and the total amount was correctly shown when selecting IDEAL or Paypal, but not in the Google Pay screen

#

When using IDEAL or Paypal it redirect you to a different page

#

when selecting Google Pay you just stay on the same page

rancid kernel
#

Sorry, I'm having a bit of trouble understanding where you're blocked.

#

So you're creating a PaymentIntent and using its client secret to render the PaymentElement. Is that correct? If so, are customers doing something client side (e.g., updating their cart) to require an update to the PaymentIntent's amount?

floral ingot
#

Only after the payment is done it will show up correctly with VAT included in the stripe dashboard

#

So in order

#

I am initializing the PaymentElement by doing this:

    const options = {
        // clientSecret,
        appearance,
        mode: 'payment',
        amount: grandTotal,
        currency: 'eur',
        // paymentMethodTypes: ['card', 'ideal', 'paypal'],
        paymentMethodTypes: ['card', 'ideal', 'paypal'],
    };

    return (
        <Layout>
            { searchParams.get('redirect_status') === 'failed' && (
                <div className={styles.error}>
                    <p>There was an error processing your payment. Please try again.</p>
                </div>
            ) }
            <Container>
                <Elements options={options} stripe={stripePromise}  onReady={(elements) => {
                        elementsRef.current = elements; // Store the Elements instance
                    }}>
                    <CheckoutContainer />
                </Elements>
            </Container>
        </Layout>
    )
#

As you can see Elements component is placed in the Container and the options are passed to the Elements component

#

In the CheckoutContainer. component I am creating the payment intent and the clientSecret are created in the handleSubmit function:

const metadata = {
                    selectedPaymentMethod,
                    firstName: formValues.firstName,
                    lastName: formValues.lastName,
                    email: formValues.email,
                    address: formValues.address,
                    address2: formValues.address2,
                    postalCode: formValues.postalCode,
                    city: formValues.city,
                    country: countryCodes[formValues.countryCode].name,
                    countryCode: formValues.countryCode,
                    priceTotal: totalAmount,
                    priceTotalVAT: totalVAT,
                    vatNumber: formValues.vatNumber,
                    vatValid: vatData ? vatData.Valid : false,
                    vatName: vatData ? vatData.Name : null,
                    vatAddress: vatData ? vatData.Address + vatData.CountryCode : null,
                    vatCountryCode: vatData ? vatData.CountryCode : null,
                    newsletterSignup: typeof formValues.newsletterSignup.checked !== 'boolean' ? true : formValues.newsletterSignup.checked,
                };

                // Create payment intent
                let paymentIntent = await createPaymentIntentApi(cart.key, metadata, vatRate, vatData).catch(
                    (error) => {
                        setMessage(error.message);
                        setIsLoading(false);
                        return;
                    }
                );

    console.log("Created stripe payment intent: ", paymentIntent);

                const { clientSecret: clientSecret } = paymentIntent // response.data

#

and after that the payment is confirmed:

 //Confirm the payment
                const { error } = await stripe.confirmPayment({
                    elements,
                    clientSecret,
                    confirmParams: {
                        return_url: returnURL,
                        payment_method_data: {
                            billing_details: {
                                address: {
                                    city: formValues.city,
                                    country: formValues.countryCode,
                                    line1: formValues.address,
                                    line2: formValues.address2, // Optional?
                                    postal_code: formValues.postalCode,
                                    // state: TODO! add state in inputs
                                },
                                name: formValues.firstName,
                                email: formValues.email,
                                // phone: formValues.phone // Optional

                            }
                        },
                    },
                });
#

Here you have a screenshot of what I am expecting to see in Google Pay, IDEAL, Paypal etc

#

So price that has to be shown is 1.21 including 21% vat, because I've selected the Netherlands. Price without vat is 1.00

#

But for some reason, when selecting Google Pay on production it is showing me this:

#

And when I click on 'pay' and stripe.confirmPayment is executed, when I check my stripe dashboard it succesfully charged me 1.21

#

So your collague advised me to use elements.update() to solve this problem

#

I am stuck because I am stuck on using elements.update()

#

I've tried doing something like elements.update(amount: ...) and passed an amount, but it is not showing any changes. Amount is still 1.00 on the Google pay screen

rancid kernel
#

Just to make sure I follow. In the GooglePay screenshot above, the price shows 1.00 but you're saying that if you complete the payment from that screen, you see a 1.21 charge?

floral ingot
pale spruceBOT
iron zinc
#

Hi there ๐Ÿ‘‹ taking over, as my colleague needs to step away

Give me a few minutes to get caught up.

floral ingot
#

Yes, no problem

iron zinc
#

Can you paste the relevant code with the element.update() line?

floral ingot
#

Yes ofcourse

#

I just tested with amount: 500 but that doesn't change anything

iron zinc
floral ingot
#

this documentation looks different

#

But do I even need to use element.update or elements.update for my problem?

#

Another colleague advised me to use elements.fetchUpdates(), but that also didn't work

floral ingot
#

It's getting pretty late here, can you please leave this open? I have put so much effort into explaining evrything to you guys and it hasn't been solved yet. Don't want to explain everything all over again tomorrow