Closed wecessary closed 11 months ago
Hi @wecessary ,
Thanks for pointing it out, we will investigate this soon. I am assuming that you are facing this problem on all Next 14.x versions.. Did you also test on 14.0.0?
Hi @ribeiroguilherme thanks for taking notice. I actually just tested on Next js 13.5.6 and the same issue occurred. So I think the problem has to to with at least Next js 13.5.6 and up.
@ribeiroguilherme Just to add. I just tested on Nextjs v13.5.1 and same issue also occurred. So v13.4.19 happens to be the last compatible version.
Also if it is relevant. I mount the drop in component by doing the following. Simplified code:
"use client";
import AdyenCheckout from "@adyen/adyen-web";
function Page(){
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
async function initPaymentSession() {
if (!ref.current) return;
const res = await fetch("/api/checkout/get-payment-methods", {
method: "POST",
body: JSON.stringify({ cartId, locale }),
});
const data = await res.json();
const checkout = await AdyenCheckout(
getConfig({
router,
paymentMethodsResponse: data,
cartId,
})
);
checkout.create("dropin").mount(ref.current);
}
initPaymentSession();
}, [router, cartId, locale]);
}
function getConfig({
router,
paymentMethodsResponse,
cartId,
}: {
router: AppRouterInstance;
paymentMethodsResponse: any;
cartId: string;
}) {
return {
paymentMethodsResponse,
clientKey: process.env.NEXT_PUBLIC_ADYEN_CLIENT_KEY as string,
locale: "en-US",
environment: "test",
analytics: {
enabled: true,
},
amount:{ value:1000, currency:"GBP"},
onSubmit: async (state: any, dropin: any) => {
const res = await fetch("/api/checkout/make-payment", {
method: "POST",
body: JSON.stringify({
cartId,
paymentData: state.data,
}),
});
const data = await res.json();
if (data.action) {
dropin.handleAction(data.action);
} else {
if (data.resultCode) {
router.push(
`/uk/checkout/result?resultCode=${data.resultCode}&order=${data.merchantReference}`
);
}
}
},
onAdditionalDetails: async (state: any, dropin: any) => {
const res = await fetch("/api/checkout/additional-details", {
method: "POST",
body: JSON.stringify({
additionalDetails: state.data.details,
}),
});
const data = await res.json();
if (data.action) {
dropin.handleAction(data.action);
} else {
if (data.resultCode) {
router.push(
`/uk/checkout/result?resultCode=${data.resultCode}&order=${data.merchantReference}`
);
}
}
},
setStatusAutomatically: true,
onError: (error: any, component: any) => {
console.error("fail", error.name, error.message, error.stack, component);
},
threeDS2: {
challengeWindowSize: "05",
},
paymentMethodsConfiguration: {
card: {
hasHolderName: true,
holderNameRequired: true,
billingAddressRequired: true,
name: "Credit or debit card",
},
},
};
}
Hi @wecessary
Thanks for sharing the code. Is it happening specifically with the 3DS action, or also with other action types? Can you let me know which other payment methods is this happening?
Did you try only Drop-in, or also with standalone Component?
@ribeiroguilherme When /payments
response didn't have an action object, it worked fine. It also wasn't working when action.type was redirect
. I only tested action types that have to do with 3ds. We are only planning on taking Visa, Mastercard and Amex so I think these are the only two action types I need to be concerned with...?
I only tried Drop-in.
Just a hunch so feel free to ignore it: According to the React doc, changing a ref doesn't trigger re-render. So refs are apparently not appropriate for storing information that need to change visually. But Adyen doc says to use ref. So I was actually surprised when I saw 3ds or even loading state work for the first time. Maybe something to do with this? But actually if this was the issue that even the older Next js versions should have the same problem I guess
Hey @wecessary ,
I created a repo trying to reproduce this problem using adyen-web with Nextjs 14.0.3, and everything seems to work as expected, including handleAction
part. You can check it here
Can you take a look and let me know if I am missing something?
Thanks @ribeiroguilherme! I'll look at it today - will get back to you today or tomorrow (UK time)
Hey @ribeiroguilherme, I found the issue!
In the following code, if you get rid of the if statement, and just run loadAdyen()
in the useEffect, somehow, 3ds UI will not work with NextJs 14, but will work with Next 13.4.19.
useEffect(() => {
if (!isAdyenWebInitialized.current) {
isAdyenWebInitialized.current = true;
loadAdyen();
}
}, [loadAdyen]);
I have made two branches for you to see:
I also updated React to 18.2.0 in both branches, just beacuse that's what I'm using.
This is super interesting! Keen to know your thinking! Also, may I ask your reason behind using isAdyenWebInitialized
?
FYI, the card I have been using to test is: 5454 5454 5454 5454 03/30 737
That is the reason that I added the isAdyenWebInitialized
.
I believe the issue is that React is running on strict mode , which causes the effects to re-run an extra time.
Re-running the effect can lead the library to be reinitialized twice or even multiple times depending on your implementation. This can lead to many bugs. One problem for example is a race condition situation, where you might end up rendering your checkout
using one AdyenCheckout
instance, but then handling actions using another AdyenCheckout
instance. The code is asynchronous, so you can't know which one will finish/render first.
For the reason above, we need to add a mechanism to prevent the initialization to happen multiple times.
Can you confirm that this 'check' fixes the problem on Nextjs 13 too?
That makes sense. I can also confirm that this 'check' fixes the problem on Nextjs 13.4.19 too.
I think you're right on this one. Nextjs doc says
Since Next.js 13.4, Strict Mode is true by default with app router, so the above configuration is only necessary for pages. You can still disable Strict Mode by setting reactStrictMode: false.
This explains why the issue appeared only in dev mode and only on Next 14 but not on Next 13.4. I just wasn't aware...
Perhaps the use of this 'check' is worth adding to the doc? The doc currently just warns React and Vue developers to "make sure that you use references instead of selectors" (advanced flow or simple flow)
Thank you so so much for your help @ribeiroguilherme! I learnt something in the process too!
Glad that it worked @wecessary 🙏 I will forward your feedback to the docs team so they can update the documentation accordingly. Cheers!
Is your feature request related to a problem? Please describe.
I'm using web-dropin intergration:
@adyen/adyen-web": "5.53.3
,@adyen/api-library": "15.0.0-beta
When I use Next Js 13.4.19, calling
dropin.handleAction
makes the UI to change or redirect to 3D Secure. I.e. It works as expected in both dev and production mode.When I use Next Js 14.0.1 or 14.0.3, calling
dropin.handleAction
does not work as expected anymore in dev mode. But it works as expected in production mode - on http localhost or deployed on https./payments response with action is correct with all of the cases above - as described by doc.
I'm too lazy to make a re-producible repo so I don't feel like I deserve to call it a bug. Also this seems like it might be a Next Js bug. But still want to put this out here in case someone was facing the same issue. And in case the Adyen team wants to look into it.
Describe the solution you'd like It would be nice if it also works in dev mode on Next Js 14.0.1 or 14.0.3. (I mention these 2 versions because I was developing on 14.0.1 and discovered the issue, so I tried upgrading to latest version)
Describe alternatives you've considered Just to reiterate, downgrading Next Js seems to fix the problem for me.
Additional context Add any other context or screenshots about the feature request here.