返回

PayPal SDK iOS 12.5.7 信用卡按钮加载失败?解决方案

IOS

搞定这个麻烦事:PayPal SDK 信用卡按钮在 iOS 12.5.7 上加载失败 (Zombo 错误)

有朋友在集成 PayPal JavaScript SDK 时碰到了一个有点棘手的问题。情况是这样的:用一个基础的测试框架加载 PayPal SDK,显示两个按钮——“通过 PayPal 付款”和“通过信用卡或借记卡付款”。

这套东西在各种设备上都跑得好好的,偏偏在一台装着 iOS 12.5.7 的老 iPad Air 上(这已经是这台设备能装的最高版本了)出了岔子。用更新的 iPhone(比如 iOS 18)就一切正常。

怪就怪在,点那个“通过信用卡或借记卡付款”按钮时,它会先转个圈圈(表示正在加载卡信息表单),但紧接着就失败了。浏览器控制台里还报了俩错:Unexpected token "?"ReferenceError: Can't find variable: Zombo

点“通过 PayPal 付款”按钮倒是没问题,能正常跳转。就只有信用卡/借记卡这个选项歇菜了。

在 Macbook 的 Safari、Windows 的各种浏览器上测试,也都正常。网上搜了一圈,也没找到啥有用的信息。试了不少法子,都没用。

这是用来测试的代码片段:

<!-- PayPal SDK, 带 'components' 参数来显示信用卡/借记卡选项 -->
<script src='https://www.paypal.com/sdk/js?client-id=YOUR_CLIENT_ID&components=buttons&currency=CAD'></script>
<!-- 依赖了 jQuery, 虽然跟问题本身关系可能不大 -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

<!-- PayPal 按钮的容器 -->
<div id="paypal_container"></div>

<script type="text/javascript">
// 页面加载完成后执行
$(function() {
    // 定义 PayPal 按钮,集成信用卡/借记卡选项
    paypal.Buttons({
        style: {
            shape: 'rect',
            color: 'gold',
            layout: 'vertical', // 按钮垂直排列
            label: 'pay',       // 按钮上显示的文字
        },
        createOrder: function (data, actions) {
            // 设置订单详情 (金额, 货币等)
            return actions.order.create({
                intent: "CAPTURE", // 表示直接捕获付款
                purchase_units: [{
                    amount: {
                        currency_code: "CAD", // 货币代码
                        value: "100.00"       // 金额, 记得换成实际值
                    }
                }]
            });
        },
        onApprove: function (data, actions) {
            // 用户批准付款后,捕获这笔交易
            return actions.order.capture().then(function (details) {
                // 付款成功后的操作
                alert("付款成功! 谢谢你, " + details.payer.name.given_name);
            });
        },
        onError: function (err) {
            // 处理支付过程中出现的错误
            console.error("支付错误:", err);
            alert("处理您的付款时遇到问题。");
        }
    }).render('#paypal_container'); // 把按钮渲染到指定容器里
});
</script>

(注意:上面代码里的 client-id=YOUR_CLIENT_ID 要换成你自己的 PayPal Client ID)

那么,这到底是怎么回事?又该怎么解决呢?

问题根源在哪儿?

这个问题的核心,大概率出在浏览器兼容性 上。

  1. 老旧的 WebKit 内核: iOS 12.5.7 自带的 Safari 浏览器,用的是一个比较老的 WebKit 渲染引擎。这个引擎对一些现代 JavaScript 的新特性支持不佳。

  2. Unexpected token "?" 错误: 这个错误信息通常指向 JavaScript 的可选链操作符(Optional Chaining Operator, ?. 或者 空值合并操作符(Nullish Coalescing Operator, ?? 。这些都是 ES2020 标准引入的新语法,老旧的 JavaScript 引擎不认识它们,遇到就会直接报错。PayPal SDK 的内部代码(尤其是在加载信用卡表单的那个部分)很可能用了这些新语法。

  3. ReferenceError: Can't find variable: Zombo 错误: 这个 Zombo 听起来像是个内部变量名或者函数名。这个错误很可能是前面那个语法错误 (?) 导致的“连锁反应”——因为代码在 ? 那里就崩了,后面定义的 Zombo 就没机会执行,导致找不到它。或者,也有可能是 PayPal 加载的某些特定于信用卡功能的脚本里,还有其他不兼容老旧 iOS Safari 的代码。

说白了,就是 PayPal SDK(或者它动态加载的用于处理信用卡支付的部分)用了些比较新的 JavaScript 写法,而 iOS 12.5.7 上的 Safari 太老了,“看不懂”。结果就是,加载信用卡表单的那一步直接卡壳报错。

怎么解决?

既然知道了大概原因,我们就可以对症下药了。有几种思路可以尝试:

方案一:对老版本 iOS 条件性隐藏信用卡/借记卡选项

这是最直接也可能最稳妥的方法:检测用户的操作系统版本,如果是已知的有问题的 iOS 12.5.x,干脆就不给他们显示那个“通过信用卡/借记卡付款”的按钮,只保留标准的 PayPal 按钮。这样虽然牺牲了一部分用户直接输卡的便利性,但至少保证了基础支付功能可用,并且避免了报错。

原理与作用:
通过 JavaScript 检测 navigator.userAgent 字符串,判断是否为 iOS 12。如果是,就在初始化 PayPal Buttons 时,明确告诉 SDK 只展示 PayPal 这一个支付方式,禁用掉卡片支付(Card)。

操作步骤与代码示例:

  1. 添加一个简单的 iOS 版本检测函数:

    function isProblematicIOS() {
        // navigator.userAgent 包含了浏览器和操作系统信息
        const ua = window.navigator.userAgent;
        // 正则表达式匹配 iPhone, iPad, iPod 并且跟着 OS 12_5 (可能还有后续小版本号)
        // 注意: \b 是单词边界,确保匹配的是完整的版本号,避免匹配到 OS 125
        const iOS12_5 = /\b(iPhone|iPad|iPod).* OS 12_5([\._]\d+)* like Mac OS X/i;
        return iOS12_5.test(ua);
    }
    

    说明: 这个检测函数不是百分百精确,但对于定位到 iOS 12.5.x 来说够用了。userAgent 可能会变,但这个模式覆盖了常见情况。

  2. 在初始化 PayPal Buttons 时加入判断:

    $(function() {
        // 先判断是不是有问题的 iOS 版本
        if (isProblematicIOS()) {
            console.log("检测到 iOS 12.5.x,将禁用信用卡/借记卡按钮");
            // 对于有问题的 iOS 版本,只显示 PayPal 按钮
            paypal.Buttons({
                style: { /* ... 样式配置保持不变 ... */ },
                createOrder: function (data, actions) { /* ... createOrder 配置保持不变 ... */ },
                onApprove: function (data, actions) { /* ... onApprove 配置保持不变 ... */ },
                onError: function (err) { /* ... onError 配置保持不变 ... */ },
                // 关键点:使用 fundingSource 指定只允许 PayPal 付款方式
                // 这会有效地移除信用卡/借记卡按钮
                fundingSource: paypal.FUNDING.PAYPAL
            }).render('#paypal_container');
        } else {
            // 其他设备和系统,正常显示所有按钮
            console.log("非 iOS 12.5.x 或检测失败,正常加载所有按钮");
            paypal.Buttons({
                style: { /* ... 样式配置保持不变 ... */ },
                createOrder: function (data, actions) { /* ... createOrder 配置保持不变 ... */ },
                onApprove: function (data, actions) { /* ... onApprove 配置保持不变 ... */ },
                onError: function (err) { /* ... onError 配置保持不变 ... */ }
                // 注意:这里不设置 fundingSource 或者根据需要配置
                // PayPal SDK 默认会根据 eligibility 决定显示哪些按钮
            }).render('#paypal_container');
        }
    });
    

优点:

  • 简单有效,直接绕开了出错的部分。
  • 保证了在老设备上至少 PayPal 付款可用。

缺点:

  • 受影响的用户失去了直接使用信用卡支付的选项。

安全建议:
这个方案本身不涉及额外的安全风险,只是功能上的取舍。

方案二:尝试引入 Polyfill

如果问题确实只是因为缺少对 Optional Chaining (?.) 或 Nullish Coalescing (??) 等新语法的支持,那么理论上可以通过引入 Polyfill 来“教会”老浏览器认识这些语法。

原理与作用:
Polyfill 是一段 JavaScript 代码,它能检测当前浏览器是否原生支持某个新特性,如果不支持,就用已有的、兼容性好的老语法来模拟实现这个新特性。这样,即使 PayPal SDK 内部用了这些新语法,经过 Polyfill 的“翻译”,老浏览器也能尝试去理解和执行。

操作步骤与代码示例:

  1. 选择并引入 Polyfill 库:
    有很多提供 Polyfill 的库,core-js 是一个非常全面和常用的选择。你可以通过 npm/yarn 安装它并用 Webpack 等工具打包,或者直接引入一个提供所需功能的 CDN 链接。

    • 如果你使用打包工具 (Webpack, Rollup等):
      通常会在项目入口文件或配置文件 (如 Babel 配置) 中配置 core-js 按需引入。例如,在 Babel 中配置 useBuiltIns: 'usage'corejs: 3
    • 如果想直接在 HTML 中引入:
      可以找一个包含所需 Polyfill (主要是 ES2020 特性) 的 core-js 单独文件或集合包的 CDN 链接。或者使用一些 Polyfill 服务,比如 polyfill.io (虽然它近期有些维护上的不确定性,需要注意风险或寻找替代品),它可以根据用户的 userAgent 动态返回所需的 Polyfill。

    示例 (使用 polyfill.io - 请注意其服务状态):
    在加载 PayPal SDK 之前 ,引入 Polyfill 脚本。

    <!-- 首先加载 Polyfill -->
    <!-- 这个 URL 会自动检测浏览器并提供所需 polyfills, 包括可选链等 -->
    <script src="https://polyfill.io/v3/polyfill.min.js?features=es2020"></script>
    
    <!-- 然后加载 PayPal SDK -->
    <script src='https://www.paypal.com/sdk/js?client-id=YOUR_CLIENT_ID&components=buttons&currency=CAD'></script>
    <!-- 再加载你的 jQuery 和应用逻辑脚本 -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="your_paypal_script.js"></script> <!-- 假设你的 PayPal 初始化代码在这个文件里 -->
    

注意事项:

  • 不一定能解决所有问题: Zombo 错误的存在暗示可能不只是语法问题,也可能有更复杂的 API 或逻辑不兼容。Polyfill 主要解决的是标准语法层面的缺失,不一定能搞定所有内部实现差异。
  • 性能开销: 引入 Polyfill 会增加页面需要加载和执行的代码量,对性能有一定影响,尤其是在老旧设备上。
  • 维护和来源: 使用第三方 Polyfill 服务需要关注其稳定性和维护情况。自己打包 core-js 更可控但配置稍复杂。

安全建议:

  • 确保你使用的 Polyfill 库或服务是可信赖的,避免引入包含恶意代码的 Polyfill。

方案三:检查 PayPal SDK 版本或等待官方修复

软件开发总是在迭代,PayPal 也在不断更新他们的 SDK。

原理与作用:

  • 也许 PayPal 在后续的 SDK 版本中已经意识到了对老旧浏览器的兼容性问题,并进行了修复。
  • 或者,如果这是一个普遍问题,反馈给 PayPal 后,他们可能会在未来的版本中修复。

操作步骤:

  1. 检查并尝试更新 SDK:
    查看你当前使用的 SDK 版本 (虽然上面代码没指定版本,默认可能是最新稳定版)。去 PayPal 开发者文档网站看看是否有更新的版本发布说明提到了关于 iOS 兼容性或特定 bug 的修复。
    尝试在 SDK 的 URL 后面加上 &commit=true 来获取最新的可用构建(但这通常用于测试,稳定性可能不如正式版本),或者查找是否有指定版本的参数。

  2. 向 PayPal 报告问题:
    如果在 PayPal 的开发者论坛、GitHub issues(如果他们有开放的话)或者官方支持渠道没有找到相关信息,可以考虑将你遇到的问题(包括设备、OS版本、错误信息、重现步骤)详细地反馈给 PayPal。提供的信息越全,他们定位和解决问题的可能性越大。

现实考量:

  • 依赖外部修复通常比较被动,时间不可控。
  • 对于非常老的操作系统版本(iOS 12 在现在看来已经相当老了),开发者通常会逐渐降低支持优先级,所以不保证一定会有官方修复。

方案四:友好提示用户

如果以上技术方案都不可行或者投入产出比不高,还有一个最后的选择:接受现实,并给用户一个交代。

原理与作用:
使用与方案一相同的用户环境检测方法,但不是直接隐藏按钮,而是显示一条友好的提示信息。

操作步骤:

  1. 检测 isProblematicIOS()
  2. 如果是,不渲染 PayPal 按钮 (或者只渲染 PayPal 标准按钮)。
  3. #paypal_container 或旁边显示一条消息, 比如:“抱歉,您的设备系统版本较旧,暂不支持直接通过信用卡/借记卡支付。请尝试使用 PayPal 账户登录支付,或在更新的设备/浏览器上完成购买。”

用户体验考量:
这种方式虽然不能解决根本问题,但至少告知了用户情况,避免了他们反复点击一个坏掉的按钮而感到困惑或沮丧。

总结一下思路

面对 PayPal SDK 在老 iOS 上信用卡按钮加载失败的问题:

  1. 最稳妥: 检测 iOS 12.5.x,然后条件性地禁用信用卡按钮(方案一),只提供 PayPal 按钮。
  2. 可以一试: 引入 Polyfill(方案二),看看能否“填补”缺失的 JavaScript 特性,但效果不保证。
  3. 着眼未来: 检查有无更新的 PayPal SDK 版本,或向 PayPal 反馈问题(方案三),但别抱太大期望很快解决。
  4. 最后手段: 如果实在搞不定,就检测设备并给用户一个明确的提示(方案四)。

建议优先尝试方案一,因为它直接、可控且能立刻改善受影响用户的体验(尽管是功能降级)。方案二和三可以作为补充尝试。