PAYATHON 2026

PCI Compliance 中 secured/hosted fields 的 WCAG 指南

支付阿杰

结论

有。即使每个 secure field 分别符合表单和 iframe 的 WCAG 要求,也不能说明整个支付流程无障碍。

还需要验证这些跨文档字段组合使用时,用户能否理解字段之间的关系,按合理顺序操作,发现并修正错误,并顺利完成后续的 3-D Secure、身份验证和订单确认。PCI 合规关注支付数据安全和责任范围,不能代替 WCAG 一致性评估。

重点风险

1. 父页面与 iframe 不属于同一个可访问性文档

父页面中的文字不能直接充当跨域 iframe 内输入框的 <label>、aria-describedby 或错误说明。这些名称和说明必须放在 iframe 内,或者由支付服务提供商通过 API 同步进去。

父页面中的 iframe 也应有明确的可访问名称:

<div class="payment-field">
  <iframe
    src="https://payments.example/fields/card-number"
    title="信用卡号安全输入框"
    allow="payment"
  ></iframe>
</div>

title 用来说明嵌入区域的用途,不能代替 iframe 内输入框自己的 <label>。

2. 多个字段应当作为一个支付表单来理解

如果卡号、有效期和 CVV 分别位于不同的 iframe 中,屏幕阅读器可能将它们呈现为多个独立文档。测试时需要确认:

  • 字段顺序是否与视觉顺序一致;
  • 按 Tab 键时,焦点是否依次进入卡号、有效期、CVV 和提交按钮;
  • 是否存在多余的焦点停靠点;
  • 用户离开一个 iframe 后,焦点能否可靠地进入下一个控件;
  • 页面是否使用了正数 tabindex。通常不应这样设置;
  • 页面更新、验证失败或重新渲染时,焦点是否会意外返回页面顶部。

不要拦截 Tab 键来强制切换字段。除非确有必要,否则应让浏览器按照 DOM 顺序管理焦点。

3. 错误应在正确的位置得到识别和播报

只显示红色边框是不够的。错误应有文字说明,并与对应的输入框建立程序化关联。

支付服务提供商控制的 iframe 内部可以采用类似结构:

<label for="card-number">信用卡号</label>

<input
  id="card-number"
  name="card-number"
  type="text"
  inputmode="numeric"
  autocomplete="cc-number"
  aria-describedby="card-number-hint card-number-error"
  aria-invalid="true"
>

<p id="card-number-hint">
  输入卡片正面的信用卡号。
</p>

<p id="card-number-error" role="alert">
  信用卡号不完整,请检查后重新输入。
</p>

需要注意以下事项:

  • 只有字段确实无效时,才应设置 aria-invalid;
  • 错误信息应告诉用户如何修正,不能只写“输入无效”;
  • 不要只依赖颜色、图标或占位符传达错误;
  • 用户尚未完成输入时,不宜反复播报错误;
  • 提交失败后,应把焦点移到错误摘要或第一个无效字段;
  • 错误摘要应与具体字段的错误信息一致。

跨域父页面通常不能直接控制 iframe 内的焦点,因此支付 SDK 需要提供 focus()、on("error")、on("validityChange") 等能力。实际 API 名称由供应商决定,不能假定所有 SDK 都支持这些方法。

4. 状态变化不能只发生在 iframe 内

支付字段可能处于加载、令牌化、验证失败、网络超时或服务不可用等状态。父页面需要在支付流程中向用户提供可感知的反馈:

<div id="payment-status" role="status" aria-live="polite"></div>
function updatePaymentStatus(message) {
  document.getElementById("payment-status").textContent = message;
}

updatePaymentStatus("正在安全处理支付信息。");

一般状态可以使用 role="status"。只有需要用户立即处理的错误,才考虑使用 role="alert"。即时播报过多会干扰用户输入。

如果跨域文档通过 postMessage 传递状态,父页面必须验证消息来源:

window.addEventListener("message", (event) => {
  if (event.origin !== "https://payments.example") {
    return;
  }

  if (event.data?.type === "payment-field-error") {
    updatePaymentStatus(event.data.message);
  }
});

不要通过消息传递完整卡号、CVV 等敏感数据。具体通信方式还应符合支付服务提供商的安全设计和适用的 PCI 要求。

5. 自动填充和输入方式需要实际测试

支付字段应尽量使用正确的自动填充标记,例如:

autocomplete="cc-name"
autocomplete="cc-number"
autocomplete="cc-exp"
autocomplete="cc-csc"

浏览器、密码管理器和第三方 iframe 对自动填充的支持并不一致,跨域场景尤其如此。因此,不能只确认属性是否存在,还必须进行实际测试。

同时应检查:

  • 移动设备是否显示合适的键盘;
  • 卡号是否接受空格或连字符;
  • 是否可以粘贴;
  • 用户能否逐字修改,而不是只能清空后重新输入;
  • 有效期格式是否明确;
  • 是否为 CVV 的位置和含义提供了可访问的说明;
  • 自动格式化是否会导致光标跳动,或让屏幕阅读器重复播报。

出于安全考虑,CVV 通常不应持久化或自动保存。具体行为应遵循支付服务提供商和适用的 PCI 要求。

6. 缩放、高对比度和自定义样式可能不起作用

iframe 内的样式不一定会继承父页面设置。即使父页面支持深色模式、高对比度或 200% 缩放,secure field 仍可能使用固定字号,出现对比度不足或内容被裁切的问题。

应检查:

  • 文本放大后是否被截断;
  • 在 320 CSS 像素宽度下是否需要双向滚动;
  • 焦点指示是否清楚;
  • 焦点是否会被固定页头、Cookie 横幅或底部操作栏遮挡;
  • 浏览器使用强制颜色模式时,边框和错误状态是否仍然可见;
  • 用户自定义字体、字距和行距后,标签及错误信息是否完整;
  • iframe 高度能否随错误文字的增加而调整。

固定高度的 iframe 很容易裁掉错误信息。支付 SDK 最好提供安全的自动高度调整机制。

7. 测试范围不能止于 secure fields

完整的测试还应覆盖:

  • 支付方式选择;
  • 账单地址;
  • 优惠码和订单金额更新;
  • “提交订单”按钮的禁用状态;
  • 加载遮罩;
  • 支付失败后的恢复路径;
  • 3-D Secure 或银行身份验证弹窗;
  • 验证超时、取消和返回;
  • 订单确认与支付结果;
  • 重复提交和重复扣款预防。

对于第三方验证窗口,应重点检查焦点能否正确进入,焦点是否被合理约束,用户如何关闭窗口,以及关闭后焦点能否回到原来的位置。即使 secure fields 本身合格,只要后续身份验证界面无法操作,用户仍然不能完成购买。

支付或订单提交通常涉及金融或法律承诺。应根据适用的一致性级别评估 WCAG 的错误预防要求,例如在提交前提供检查和确认机会,或者允许用户撤销操作。

建议的验收方法

不要只审核静态 markup。至少应完成以下测试:

  1. 仅使用键盘完成一次成功支付,并完成一次支付失败后的错误修正。
  2. 使用屏幕阅读器逐个检查字段的名称、角色、状态、说明和错误信息。
  3. 在 Windows 强制颜色模式和浏览器高对比度设置下检查页面。
  4. 将页面放大到 200%,并在窄视口下测试重排和内容裁切。
  5. 使用移动端屏幕阅读器测试虚拟光标、软键盘和自动填充。
  6. 测试加载失败、网络超时、字段重建和支付服务不可用等情况。
  7. 测试 3-D Secure、银行验证,以及返回商户页面后的焦点位置。
  8. 使用真实浏览器与辅助技术组合进行测试,不能只依赖自动扫描工具。

自动化工具可以发现缺少 title、可访问名称或部分对比度问题,但通常无法判断跨 iframe 的操作顺序和播报时机,也无法验证错误恢复及第三方验证流程是否可用。

与支付服务提供商确认的能力

集成前,建议要求供应商明确说明:

  • 如何命名 iframe 和内部输入框;
  • 是否支持传入标签、说明和本地化错误文本;
  • 是否公开有效性变化和错误事件;
  • 是否提供聚焦无效字段的方法;
  • 是否支持自动填充属性;
  • 是否能够调整 iframe 高度;
  • 是否支持缩放、强制颜色和用户样式;
  • SDK 更新后是否会保留这些无障碍行为;
  • 其合规声明覆盖哪些 WCAG 版本、级别、浏览器和辅助技术组合。

供应商的合规声明可以作为证据,但不能代替商户对最终集成页面的测试。最终是否符合 WCAG,取决于 secure fields、父页面、支付 SDK 和后续验证流程共同构成的实际用户体验。

备注:内容仅供参考。