PAYATHON 2026

如何自定义 Airwallex DropIn 输入框和按钮样式?

支付老李

结论

Airwallex DropIn 通常在 Airwallex 创建的 iframe 中运行。页面中的 CSS、CSS Modules、Tailwind 类名和 container inherit 都无法穿透 iframe,因此不能用外层容器选择器直接修改内部输入框或 Proceed 按钮。

有两种处理方式:

  1. 使用 DropIn 初始化参数中的 appearance 或 style 配置主题样式。
  2. 如果需要精确控制输入框结构、按钮尺寸和悬停状态等细节,改用独立 Payment Element,并在 React 中自行创建提交按钮。

具体有哪些配置字段,取决于 @airwallex/components-sdk 的版本。请以当前安装版本的 TypeScript 类型定义和对应版本文档为准。

为什么继承样式不起作用

DropIn 挂载后的实际结构大致如下:

<div id="payment-element">
  <iframe>
    <!-- Airwallex 托管的支付表单 -->
  </iframe>
</div>

外层页面的样式只能作用于 iframe 容器:

#payment-element {
  width: 100%;
  max-width: 480px;
}

下面的规则无法选中 iframe 内部的输入框:

#payment-element input {
  border-radius: 8px;
}

即使设置了继承属性:

#payment-element {
  font-family: inherit;
  color: inherit;
}

这些属性也不会自动跨越 iframe 边界。这不是 CSS 优先级问题,增加 !important 也无法解决。

使用 DropIn 的主题配置

如果当前 SDK 版本支持 appearance,需要在创建 DropIn 时传入,不能等挂载完成后再修改 DOM。

import { useEffect, useRef } from 'react';
import {
  createElement,
  init,
} from '@airwallex/components-sdk';

interface PaymentFormProps {
  intentId: string;
  clientSecret: string;
  currency: string;
}

export function PaymentForm({
  intentId,
  clientSecret,
  currency,
}: PaymentFormProps) {
  const initialized = useRef(false);

  useEffect(() => {
    if (initialized.current) return;
    initialized.current = true;

    let dropIn: Awaited<ReturnType<typeof createElement>> | undefined;

    async function mountPaymentForm() {
      await init({
        env: 'prod',
      });

      dropIn = await createElement('dropIn', {
        intent_id: intentId,
        client_secret: clientSecret,
        currency,
        appearance: {
          variables: {
            colorPrimary: '#2563eb',
            colorBackground: '#ffffff',
            colorText: '#111827',
            borderRadius: '8px',
          },
        },
      });

      dropIn.mount('airwallex-dropin');
    }

    void mountPaymentForm();

    return () => {
      dropIn?.unmount();
    };
  }, [intentId, clientSecret, currency]);

  return <div id="airwallex-dropin" className="payment-container" />;
}

宽度、间距和页面布局仍可由外层 CSS 控制:

.payment-container {
  width: 100%;
  max-width: 520px;
  margin: 0 auto;
}

colorPrimary 一般控制主要操作色,可能同时影响主按钮和选中状态。borderRadius、colorBackground、colorText 可用于统一控件外观。不过,这些变量是否存在以及会影响哪些控件,仍要以当前 SDK 版本的 appearance 类型定义为准。

如果 TypeScript 报出类似错误:

Object literal may only specify known properties

说明当前版本不支持示例中的某个字段。不要用 as any 强行绕过类型检查,应先确认当前版本允许哪些配置:

type DropInOptions = Parameters<typeof createElement>[1];

也可以直接查看 IDE 对 createElement('dropIn', ...) 的参数提示。

部分版本使用 style,而不是 appearance。遇到这种情况,应按照该版本的类型传入配置,例如:

const dropIn = await createElement('dropIn', {
  intent_id: intentId,
  client_secret: clientSecret,
  currency,
  style: {
    base: {
      color: '#111827',
      fontSize: '16px',
      fontFamily: 'Inter, Arial, sans-serif',
    },
    invalid: {
      color: '#dc2626',
    },
  },
});

不要默认 appearance 和 style 都能使用。具体选择哪一种,要看实际安装的 SDK 版本以及 DropIn 元素支持的参数。

需要完全自定义时改用独立 Element

DropIn 用于快速提供完整的支付界面,通常只开放有限的主题变量,不支持任意 CSS 选择器。如果设计中有以下要求,建议改用独立 Element,而不是继续尝试覆盖 DropIn:

  • 自定义按钮文案、图标和加载动画;
  • 分别设置输入框的边框、阴影和高度;
  • 控制 hover、focus、disabled 等状态;
  • 调整字段的排列方式;
  • 让按钮与网站现有组件完全一致。

可以只让 Airwallex 托管敏感支付字段,由 React 页面渲染按钮:

<button
  type="submit"
  className="proceed-button"
  disabled={submitting}
>
  {submitting ? '处理中…' : '继续支付'}
</button>
.proceed-button {
  width: 100%;
  min-height: 48px;
  border: 0;
  border-radius: 8px;
  background: #2563eb;
  color: #fff;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition:
    background-color 150ms ease,
    box-shadow 150ms ease;
}

.proceed-button:hover:not(:disabled) {
  background: #1d4ed8;
}

.proceed-button:focus-visible {
  outline: 3px solid rgb(37 99 235 / 30%);
  outline-offset: 2px;
}

.proceed-button:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}

提交时,需要调用当前 SDK 版本提供的支付确认 API。支付 Element、intent_id 和 client_secret 必须来自同一次 PaymentIntent 流程,不能只依靠普通 HTML 表单提交。

注意事项

  • 不要通过查找 iframe、修改 contentDocument 或注入 CSS 来覆盖表单。跨域限制通常会阻止这些操作,而且实现并不稳定。
  • 不要把 client_secret 写死在前端代码中或提交到仓库。它应由服务端创建 PaymentIntent 后返回。
  • React 开发模式下,useEffect 可能重复执行。应避免对同一容器重复调用 mount,并在组件卸载时调用 unmount()。
  • DropIn 只支持主题级定制,无法完全控制内部 DOM。
  • 如果当前版本没有为 Proceed 按钮提供独立的样式配置,就无法通过外层 CSS 单独修改它。此时可以接受统一的主题样式,或者改用独立 Payment Element 和自定义按钮。

备注:内容仅供参考。