PAYATHON 2026

Easy版手机网站支付DEMO提交订单时报错

支付小周

结论

这次报错并不是因为没有配置 接口内容加密方式,而是 DEMO 使用的旧版 AopClient.php 与 PHP 8 不兼容。

具体表现如下:

  • PHP 8 已移除 each()。程序因此发生致命错误,订单还没有真正提交到支付宝。
  • 必填参数位于可选参数之后 是 PHP 8 针对旧式函数参数声明给出的兼容性警告。
  • config.php 没有内容加密配置,与这次报错没有直接关系。不过,sign_type、应用私钥等签名配置仍须正确填写。

建议优先更新整套支付宝 PHP SDK 或手机网站支付 DEMO。如果暂时无法升级,可以修改旧代码以兼容 PHP 8,也可以暂时使用 PHP 7.4 运行旧 DEMO。

原因分析

从调用栈看,程序在以下调用过程中中断:

AopClient->buildRequestForm()
AopClient->pageExecute()
AlipayTradeService->wapPay()

导致程序停止的是这个错误:

Call to undefined function each()

旧版 PHP SDK 通常会用类似代码遍历请求参数:

while (list($key, $val) = each($para_temp)) {
    // 生成表单字段
}

PHP 7.2 已废弃 each(),PHP 8.0 则将其彻底移除。因此,在 PHP 8 环境中调用该函数会直接抛出 Fatal error。

另外三条 Deprecated 信息来自类似下面的函数声明:

function example($value, $n = 1, $subnum, $charset)

这里先声明了可选参数 $n,后面却还有必填参数 $subnum 和 $charset,所以新版本 PHP 会发出兼容性警告。

推荐解决方法:更新完整 SDK

从支付宝开放平台目前提供的 PHP SDK 或相应支付产品示例中,获取兼容当前 PHP 版本的代码,然后整体替换旧 DEMO 中的 SDK。

不要只替换 AopClient.php。SDK 中的请求类、签名处理类和辅助文件可能存在版本依赖,混用不同版本容易引发新的兼容问题。

替换后,重点检查以下配置:

$config = [
    'app_id'            => '你的应用 APPID',
    'merchant_private_key' => '你的应用私钥',
    'alipay_public_key' => '支付宝公钥',
    'sign_type'         => 'RSA2',
    'charset'           => 'UTF-8',
    'gatewayUrl'        => 'https://openapi.alipay.com/gateway.do',
];

具体键名应以当前 SDK 的配置结构为准,不同版本的键名可能并不完全相同。

临时修改旧版 AopClient.php

如果暂时不能更新 SDK,可以先修复 each() 的兼容问题。

在报错的第 413 行附近找到类似代码:

while (list($key, $val) = each($para_temp)) {
    if (false === $this->checkEmpty($val)) {
        $html_text .= "<input type='hidden' name='" . $key .
            "' value='" . $val . "'/>";
    }
}

将遍历方式改为 foreach:

foreach ($para_temp as $key => $val) {
    if (false === $this->checkEmpty($val)) {
        $html_text .= "<input type='hidden' name='" . $key .
            "' value='" . $val . "'/>";
    }
}

修改前要先核对原循环体的完整内容。循环体里的转义、编码和过滤逻辑都应原样保留,只替换参数遍历方式。

处理参数顺序警告

检查第 808 行和第 821 行对应的函数声明。必填参数应放在可选参数之前,也可以根据实际情况为后续参数提供合理的默认值。

例如,旧声明可能是:

function splitText($text, $n = 1, $subnum, $charset)

如果实际调用不依赖 $n 的默认值,可以调整为:

function splitText($text, $n, $subnum, $charset)

如果现有调用依赖默认值,则可以为后续参数也提供默认值:

function splitText(
    $text,
    $n = 1,
    $subnum = null,
    $charset = 'UTF-8'
) {
    // 原有实现
}

另一个函数如果采用类似声明:

function cutText($text, $start = 0, $length, $charset = 'UTF-8')

可以改成:

function cutText(
    $text,
    $start = 0,
    $length = null,
    $charset = 'UTF-8'
) {
    if ($length === null) {
        // 根据原函数语义处理省略长度的情况
    }

    // 原有实现
}

不要为了消除警告而随意交换参数位置,因为现有代码可能通过位置参数调用这些函数。修改前应搜索全部调用位置,确认参数的顺序和数量。

关于 接口内容加密方式

alipay.trade.wap.pay 的请求签名与接口内容加密是两套不同的机制:

  • sign_type、应用私钥和支付宝公钥用于请求签名与验签。
  • 只有应用和具体接口确实启用了内容加密,才需要配置接口内容加密,例如设置对应的 AES 密钥和加密类型。
  • 当前错误发生在本地生成跳转表单的阶段,请求还没有到达支付宝网关,因此与缺少内容加密参数无关。

不能因为 config.php 里没有 接口内容加密方式,就随意填写 AES 密钥,更不能把它与 RSA2 混为一谈。

注意事项

修改完成后,如果页面上只剩兼容性警告,可以在开发环境中保留完整的错误日志,但不要把错误详情直接输出到支付页面。生产环境尤其不能暴露私钥路径、服务器目录或调用栈。

修复 each() 后,如果请求已经发送到支付宝,但支付宝返回签名错误、密钥错误或参数错误,再逐项检查:

  • app_id 是否属于当前应用;
  • 应用私钥与上传到支付宝开放平台的应用公钥是否配对;
  • alipay_public_key 是否误填成应用公钥;
  • sign_type 是否与开放平台配置一致;
  • 请求参数是否采用 UTF-8 编码;
  • 是否混用了沙箱网关与正式网关。

这次问题应先处理 PHP 8 与旧版 SDK 的兼容性,不需要通过补充内容加密配置来解决。

备注:内容仅供参考。