PAYATHON 2026

Cordova js library methods 在 iOS 上无法工作

支付小周

结论

Checkout.showEmbeddedPage() 在 Cordova iOS 中一直处于加载状态,通常不是 JavaScript 调用语法有误,而是支付页面依赖的外部脚本、iframe、网络请求、Cookie 或重定向受到 WKWebView 安全策略的限制。

建议重点检查:

  1. 支付网关是否明确支持嵌入 Cordova/WKWebView。
  2. Content-Security-Policy 是否允许加载网关的脚本、iframe 和接口域名。
  3. config.xml 是否允许相关导航和网络访问。
  4. 网关是否接受来自 Cordova 的 file://、ionic://localhost 或自定义 scheme。
  5. 支付流程是否依赖第三方 Cookie、跨站 iframe、弹窗或外部跳转。
  6. Checkout 脚本是否已经加载完成,挂载容器是否存在。

如果支付服务商不支持在 WKWebView 中嵌入支付页面,继续调整 Cordova 配置通常无法彻底解决问题。此时应改用服务商支持的重定向支付、系统浏览器或 InAppBrowser 流程。

先确认代码是否真的执行

“没有 console logs,也没有 network logs”说明首先要确认问题出在 showEmbeddedPage() 内部,还是支付库根本没有加载。

document.addEventListener('deviceready', function () {
  const container = document.querySelector('#embedded_page');

  console.log('deviceready fired');
  console.log('container:', container);
  console.log('Checkout:', window.Checkout);

  if (!container) {
    console.error('#embedded_page does not exist');
    return;
  }

  if (!window.Checkout || typeof Checkout.showEmbeddedPage !== 'function') {
    console.error('Checkout library is not loaded correctly');
    return;
  }

  try {
    Checkout.configure({
      order: {
        description: output.description,
        id: output.orderId
      },
      session: {
        id: output.session.id,
        version: output.session.version
      },
      interaction: {
        displayControl: {
          billingAddress: 'HIDE',
          shipping: 'HIDE'
        },
        merchant: {
          name: output.merchant
        }
      }
    });

    console.log('Checkout configured');

    Checkout.showEmbeddedPage('#embedded_page');

    console.log('showEmbeddedPage called');
  } catch (error) {
    console.error('Checkout initialization failed:', error);
  }
});

还要为外部支付脚本添加加载失败检查:

<script
  src="https://payment.example.com/checkout.js"
  onload="console.log('Checkout script loaded')"
  onerror="console.error('Checkout script failed to load')">
</script>

这里的域名仅供示例,必须替换为支付网关实际提供的脚本地址。

使用 Safari 检查真正的 iOS WebView

在桌面 Safari 中打开同一个网页,并不能证明它在 Cordova iOS WebView 中也能正常运行。两者的来源、Cookie 存储、导航权限和安全策略可能不同。

请在真机或模拟器中运行应用,再通过 macOS Safari 的”开发”菜单连接应用对应的 WebView,并检查:

  • Console 中是否出现 CSP、CORS、iframe 或 JavaScript 错误。
  • Network 中的支付脚本是否加载成功。
  • 是否有请求被取消或发生重定向。
  • iframe 的 src 是否已经设置。
  • Web Inspector 的 Storage/Cookies 中是否成功写入支付会话 Cookie。
  • 当前页面来源是 file://、ionic://localhost、app://localhost,还是其他自定义 scheme。

如果找不到 WebView,需要先在 iPhone 的 Safari 设置中启用”网页检查器”。具体入口可能随 iOS 版本变化。

检查 Content Security Policy

支付 SDK 往往不只加载一个 JavaScript 文件。它还可能创建 iframe、请求 API、加载图片或跳转到其他域名,因此只配置 script-src 通常不够。

下面的配置可用于排查:

<meta
  http-equiv="Content-Security-Policy"
  content="
    default-src 'self' data: blob:;
    script-src 'self' 'unsafe-inline' https://payment.example.com;
    connect-src 'self' https://api.payment.example.com https://payment.example.com;
    frame-src https://payment.example.com;
    img-src 'self' data: blob: https:;
    style-src 'self' 'unsafe-inline' https:;
  ">

具体配置必须根据 Safari 控制台中的报告和支付网关文档,列出实际使用的域名。不要在生产环境中长期使用以下配置:

default-src *

它会明显削弱应用的内容安全保护,也不一定能解决 Cookie、CORS 或 iframe 限制。

如果支付 SDK 会动态执行脚本,可能还要调整 script-src。是否允许 'unsafe-eval',应以支付网关的官方要求为准,不能在没有依据的情况下直接加入。

Cordova 白名单应配置在 config.xml

<access>、<allow-navigation> 等 Cordova 配置属于项目根目录中的 config.xml,不能放在 index.html 里。如果之前将这些标签写进了 HTML,Cordova 不会把它们当作原生白名单配置。

可以先按较小范围放行支付域名:

<access origin="https://payment.example.com" />
<access origin="https://api.payment.example.com" />

<allow-navigation href="https://payment.example.com/*" />
<allow-navigation href="https://api.payment.example.com/*" />

如果支付过程还会跳转到验证服务、银行页面或 3-D Secure 页面,也可能需要加入相应域名:

<allow-navigation href="https://verification.example.com/*" />

排查时可以暂时扩大范围,以确认问题是否来自白名单:

<access origin="*" />
<allow-navigation href="https://*/*" />

找到原因后,应将范围收紧到支付流程实际使用的域名。修改 config.xml 后,需要重新构建并安装 iOS 应用。只刷新网页内容,可能不会应用原生配置的变化。

检查 App Transport Security

iOS 的 App Transport Security(ATS)会限制不安全的 HTTP 连接,以及不符合要求的 TLS 配置。

如果支付脚本、接口或重定向链中存在以下情况,WKWebView 可能拒绝访问:

  • 使用 http://。
  • HTTPS 证书无效或证书链不完整。
  • TLS 配置过旧。
  • 页面中包含 HTTP 混合内容。
  • 重定向最终指向 HTTP 地址。

支付系统应优先全程使用有效的 HTTPS。不要把全局关闭 ATS 当作正式解决方案。NSAllowsArbitraryLoads 只能用于定位问题,而且可能带来安全和审核风险。如果确实需要设置 ATS 例外,应将其限制在明确的域名,并先确认支付服务商的安全要求。

Cordova 页面来源可能不被网关接受

普通浏览器网页一般使用正常的 HTTPS 来源,例如:

https://merchant.example.com

Cordova 应用的来源却可能是:

file://
ionic://localhost
app://localhost

支付网关可能根据 Origin、Referer 或商户后台配置的允许域名判断请求是否合法。如果后台只登记了正式网站域名,来自 Cordova WebView 的请求就可能被拒绝。

需要在远程调试工具中检查实际请求头和响应,并向支付服务商确认:

  • 是否支持 Cordova/WKWebView 集成。
  • 是否接受 file:// 或自定义 scheme。
  • 是否要求登记应用来源。
  • 是否必须从 HTTPS 页面初始化 checkout。
  • 是否限制在 iframe 中运行。
  • 3-D Secure 验证是否支持嵌入式 WebView。

这些限制无法只靠添加 HTML 标签绕过。

嵌入式支付页通常运行在第三方 iframe 中。如果支付会话依赖跨站 Cookie,WKWebView 的 Cookie 和隐私策略可能导致会话无法建立,页面便会一直处于加载状态。

常见表现包括:

  • iframe 已经加载,但支付页面不断刷新。
  • 会话接口请求成功,后续请求却判断用户没有会话。
  • 在浏览器中直接打开正常,嵌入 WebView 后失败。
  • 3-D Secure 或银行验证页面无法返回。
  • 支付流程尝试打开新窗口,但应用没有处理该导航。

即使 CSP 和 Cordova 白名单都配置正确,也未必能解决这类问题。通常应采用支付服务商明确支持的移动端流程,例如:

  • 跳转到托管支付页。
  • 使用系统浏览器完成认证。
  • 使用 Cordova InAppBrowser。
  • 使用支付网关提供的原生 iOS SDK。
  • 通过 Universal Link 或自定义 URL scheme 返回应用。

不要自行将支付页迁移到其他容器。采用任何替代方案前,都要先确认它符合网关的集成和安全要求。

检查初始化时机和容器状态

调用 showEmbeddedPage() 时,#embedded_page 必须已经存在,并且具有可见尺寸:

<div id="embedded_page"></div>
#embedded_page {
  width: 100%;
  min-height: 600px;
}

如果该元素位于尚未显示的路由、弹窗或标签页中,iOS WebView 可能会在容器尺寸为零时初始化 iframe。可以等页面显示完成后再调用:

requestAnimationFrame(function () {
  Checkout.showEmbeddedPage('#embedded_page');
});

同时要避免重复调用 Checkout.configure() 或 Checkout.showEmbeddedPage()。部分 SDK 会将初始化状态保存在全局对象中,重复挂载可能使页面进入无法恢复的加载状态。具体行为应以该支付库的文档为准。

建议的排查顺序

  1. 在 iOS 真机 WebView 中启用 Safari 远程调试。
  2. 确认 deviceready、支付脚本加载和 showEmbeddedPage() 均已执行。
  3. 确认 #embedded_page 存在,并且尺寸不为零。
  4. 检查 Console 中的 CSP、CORS、ATS、iframe 和 JavaScript 错误。
  5. 在 Network 中找到第一个未成功完成的请求或重定向。
  6. 根据实际域名修正 CSP 和 config.xml。
  7. 核对 WebView 的实际 Origin 是否被支付网关允许。
  8. 检查第三方 Cookie、弹窗和 3-D Secure 跳转。
  9. 向支付服务商确认 embedded checkout 是否支持 Cordova iOS/WKWebView。
  10. 如果不支持,改用托管支付页、InAppBrowser 或原生 SDK。

排查时,应先找到第一个失败的脚本、请求或导航。添加范围宽泛的白名单标签,只能暂时掩盖部分配置问题,无法解决支付网关对 WebView 来源、第三方 Cookie 或嵌入式认证流程的限制。

备注:内容仅供参考。