Cordova js library methods 在 iOS 上无法工作
结论
Checkout.showEmbeddedPage() 在 Cordova iOS 中一直处于加载状态,通常不是 JavaScript 调用语法有误,而是支付页面依赖的外部脚本、iframe、网络请求、Cookie 或重定向受到 WKWebView 安全策略的限制。
建议重点检查:
- 支付网关是否明确支持嵌入 Cordova/WKWebView。
Content-Security-Policy是否允许加载网关的脚本、iframe 和接口域名。config.xml是否允许相关导航和网络访问。- 网关是否接受来自 Cordova 的
file://、ionic://localhost或自定义 scheme。 - 支付流程是否依赖第三方 Cookie、跨站 iframe、弹窗或外部跳转。
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 标签绕过。
注意第三方 Cookie 和 iframe 限制
嵌入式支付页通常运行在第三方 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 会将初始化状态保存在全局对象中,重复挂载可能使页面进入无法恢复的加载状态。具体行为应以该支付库的文档为准。
建议的排查顺序
- 在 iOS 真机 WebView 中启用 Safari 远程调试。
- 确认
deviceready、支付脚本加载和showEmbeddedPage()均已执行。 - 确认
#embedded_page存在,并且尺寸不为零。 - 检查 Console 中的 CSP、CORS、ATS、iframe 和 JavaScript 错误。
- 在 Network 中找到第一个未成功完成的请求或重定向。
- 根据实际域名修正 CSP 和
config.xml。 - 核对 WebView 的实际
Origin是否被支付网关允许。 - 检查第三方 Cookie、弹窗和 3-D Secure 跳转。
- 向支付服务商确认 embedded checkout 是否支持 Cordova iOS/WKWebView。
- 如果不支持,改用托管支付页、
InAppBrowser或原生 SDK。
排查时,应先找到第一个失败的脚本、请求或导航。添加范围宽泛的白名单标签,只能暂时掩盖部分配置问题,无法解决支付网关对 WebView 来源、第三方 Cookie 或嵌入式认证流程的限制。
备注:内容仅供参考。
