如何自定义 Airwallex DropIn 输入框和按钮样式?
结论
Airwallex DropIn 通常在 Airwallex 创建的 iframe 中运行。页面中的 CSS、CSS Modules、Tailwind 类名和 container inherit 都无法穿透 iframe,因此不能用外层容器选择器直接修改内部输入框或 Proceed 按钮。
有两种处理方式:
- 使用 DropIn 初始化参数中的
appearance或style配置主题样式。 - 如果需要精确控制输入框结构、按钮尺寸和悬停状态等细节,改用独立 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 和自定义按钮。
备注:内容仅供参考。
