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 的兼容性,不需要通过补充内容加密配置来解决。
备注:内容仅供参考。
