You need to enable JavaScript to run this app. NextJS集成Adyen Dropin报window未定义、AdyenCheckout非构造函数求助
优惠活动
大模型
产品
解决方案
定价
更多

NextJS集成Adyen Dropin报window未定义、AdyenCheckout非构造函数求助

Next.js 集成Adyen Dropin 常见报错原因和正确实现

报错根源

  • window is not defined:Adyen Web是纯浏览器端SDK,顶层静态导入时会在SSR(服务端渲染)阶段执行代码,此时Node环境不存在window对象,直接触发报错。
  • TypeError: AdyenCheckout is not a constructor:
    • 尝试用Next.js的dynamic()加载SDK的用法存在本质错误:dynamic()的设计目标是代码分割加载React组件,返回值是可被React树渲染的组件包装,不是SDK本身导出的构造函数。就算设置ssr:false,拿到的依然是React组件,用new调用自然会触发报错。
    • 直接在组件渲染阶段执行import('@adyen/adyen-web').default是同步取值,import()返回的是未解析的Promise对象,根本没等到SDK加载完成,拿到的自然是无法作为构造函数的Promise值。
    • @adyen/adyen-web的AdyenCheckout是具名导出,不是默认导出,就算等待Promise resolve后,错误取.default属性也拿不到正确的构造函数。
    • 原代码存在ref绑定错误:把Adyen组件实例作为ref绑定到DOM上,和实际要挂载的容器DOM混淆了。

正确实现步骤

  • 保留顶层导入Adyen的CSS文件,纯样式导入没有副作用,不会触发window报错。
  • 所有涉及SDK加载、初始化的逻辑全部放到useEffect内部执行:useEffect的触发时机是浏览器端DOM挂载完成后,此时window对象存在,完全规避SSR环境的报错。
  • 等支付方法接口的返回结果、DOM容器实际渲染完成后,再异步导入Adyen SDK,完成初始化和挂载。
  • 客户端可访问的Adyen clientKey需要使用NEXT_PUBLIC_前缀的环境变量,否则在浏览器端拿不到值。

修正后的完整代码

import Head from 'next/head';
import { useRef, useEffect, useState } from 'react';
import { callServer, handleSubmission } from '../util/serverHelpers';
import '@adyen/adyen-web/dist/adyen.css';

export default function Dropin() {
  // 存放挂载Dropin的真实DOM容器
  const dropinContainerRef = useRef(null);
  // 存储从服务端获取的支付方法列表
  const [paymentMethods, setPaymentMethods] = useState(null);

  // 页面挂载完成后拉取支付方法数据
  useEffect(() => {
    const fetchPaymentMethods = async () => {
      const res = await callServer(`${process.env.BASE_URL}/api/getPaymentMethods`);
      setPaymentMethods(res);
    };
    fetchPaymentMethods();
  }, []);

  // 数据和DOM就绪后初始化Adyen Dropin
  useEffect(() => {
    // 前置校验:支付方法没拿到、DOM容器不存在时直接跳过
    if (!paymentMethods || !dropinContainerRef.current) return;

    const initCheckout = async () => {
      // 浏览器端异步加载SDK,拿到具名导出的AdyenCheckout构造函数
      const { AdyenCheckout } = await import('@adyen/adyen-web');

      const configuration = {
        paymentMethodsResponse: paymentMethods,
        clientKey: process.env.NEXT_PUBLIC_ADYEN_CLIENT_KEY,
        locale: "en_AU",
        environment: "test",
        paymentMethodsConfiguration: {
          card: {
            showPayButton: true,
            hasHolderName: true,
            holderNameRequired: true,
            name: "Credit or debit card",
            amount: {
              value: 2000,
              currency: "AUD"
            }
          }
        },
        onSubmit: (state, component) => {
          if (state.isValid) {
            handleSubmission(state, component, "/api/initiatePayment");
          }
        },
        onAdditionalDetails: (state, component) => {
          handleSubmission(state, component, "/api/submitAdditionalDetails");
        },
      };

      // 初始化实例,创建Dropin并挂载到真实DOM容器
      const checkoutInstance = new AdyenCheckout(configuration);
      checkoutInstance.create('dropin').mount(dropinContainerRef.current);
    };

    initCheckout();
  }, [paymentMethods]);

  return (
    <div>
      <Head>
        <title>Dropin支付</title>
      </Head>
      {/* 绑定DOM容器ref */}
      <div ref={dropinContainerRef}></div>
    </div>
  );
}

额外注意事项

  • 不要在组件渲染阶段直接执行SDK初始化逻辑,渲染逻辑会同时在服务端和客户端执行,必然触发window is not defined。
  • 若后续需要卸载Dropin,可在组件的清理函数(useEffect的return回调)中调用dropin.unmount(),避免内存泄漏。

内容的提问来源于stack exchange,提问作者Darragh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.01 18:36:03