PAYATHON 2026

PHP Easy版支付验签报错:Call to a member function common() on null

支付老李

结论

报错出现在调用 common() 之前。错误信息是:

Call to a member function common() on null

在下面的调用中,Factory::payment() 返回了 null:

Factory::payment()->common()->verifyNotify($params)

所以应先检查 SDK 初始化、Factory 类引用、依赖版本和封装代码,确认 Factory::payment() 为什么没有返回支付客户端。客户端创建正常后,再排查回调参数和支付宝配置。

先定位具体是哪一步为空

先拆开链式调用,分别检查每一步的返回值:

use Alipay\EasySDK\Kernel\Factory;

protected function verifyNotify($params)
{
    try {
        $payment = Factory::payment();

        if ($payment === null) {
            throw new \RuntimeException(
                'Factory::payment() 返回 null,请检查 SDK 初始化、Factory 类和依赖版本'
            );
        }

        $common = $payment->common();

        if ($common === null) {
            throw new \RuntimeException(
                'payment()->common() 返回 null,请检查 SDK 版本及依赖完整性'
            );
        }

        return $common->verifyNotify($params);
    } catch (\Throwable $e) {
        \think\facade\Log::error(
            '支付宝验签失败:' . $e->getMessage()
        );

        return false;
    }
}

这里应捕获 \Throwable,不要只捕获 \Exception。在 PHP 7 及以上版本中,对 null 调用成员方法产生的是 Error,不属于 Exception,因此原来的 catch (\Exception $e) 可能无法捕获这个错误。

检查 SDK 是否完成初始化

调用支付宝 Easy SDK 的支付功能前,通常需要执行:

Factory::setOptions($options);

例如:

use Alipay\EasySDK\Kernel\Factory;
use Alipay\EasySDK\Kernel\Config;

$options = new Config();

$options->protocol = 'https';
$options->gatewayHost = 'openapi.alipay.com';
$options->signType = 'RSA2';

$options->appId = '实际的支付宝应用 ID';
$options->merchantPrivateKey = '实际的应用私钥';
$options->alipayPublicKey = '实际的支付宝公钥';

Factory::setOptions($options);

具体字段要以项目当前安装的 SDK 版本为准。检查时注意以下几点:

  • Factory::setOptions() 是否确实在验签前执行。
  • 支付宝异步通知请求是否也会加载初始化代码。
  • 初始化过程是否被配置文件、环境变量或条件分支跳过。
  • appId、应用私钥和支付宝公钥是否读取成了空值。
  • 生产环境与沙箱环境的配置是否混用。

如果初始化只写在某个控制器的构造方法或普通支付流程中,而异步通知使用了另一个入口,就可能出现“下单正常,回调验签异常”。

检查 Factory 是否引用了正确的类

项目中可能有多个名为 Factory 的类。检查文件顶部的 use 声明,确认引用的是支付宝 Easy SDK 的 Factory:

use Alipay\EasySDK\Kernel\Factory;

也可以临时记录实际类名:

\think\facade\Log::info('Factory class: ' . Factory::class);

日志中的类名应该是:

Alipay\EasySDK\Kernel\Factory

如果不是,需要修正命名空间,或者直接使用完整类名:

$payment = \Alipay\EasySDK\Kernel\Factory::payment();

检查 Composer 依赖和 SDK 版本

示例代码可能来自其他版本,项目实际安装的 SDK 未必支持相同的调用链。可以在项目环境中运行:

composer show alipay/easysdk

同时检查:

  • SDK 是否通过 Composer 完整安装。
  • vendor/autoload.php 是否正常加载。
  • composer.lock 是否与服务器实际安装的依赖一致。
  • 部署时是否漏传了 vendor 文件,或依赖安装是否失败。
  • 是否同时安装了多个支付宝 SDK,并引用了其他包中的类。
  • 当前版本是否支持 payment()->common()->verifyNotify()。

如果依赖经过升级、降级或重新安装,应以当前版本的源码和对应文档为准,不要直接套用其他版本的示例。

检查是否修改或封装过 Factory::payment()

正常情况下,支付工厂方法应该返回支付客户端对象。如果项目对 SDK 做过二次封装,或者定义了同名的自定义 Factory,需要直接查看 payment() 的实现。

例如,某些分支可能没有 return:

public static function payment()
{
    // 某些条件下没有 return
}

也可能在缺少配置时主动返回 null:

public static function payment()
{
    if (empty(self::$options)) {
        return null;
    }

    return new Payment(...);
}

如果某个分支没有返回对象,PHP 会默认返回 null,接着调用 common() 就会触发当前错误。

验证回调参数的获取方式

修复 Factory::payment() 返回 null 的问题后,如果验签仍然失败,再检查 $params。

支付宝异步通知通常使用表单参数。ThinkPHP 项目应读取完整的原始通知参数,例如:

$params = request()->post();

$result = $this->verifyNotify($params);

if (!$result) {
    return 'failure';
}

return 'success';

验签前不要随意修改参数,例如:

  • 删除 sign 或 sign_type。
  • 对参数额外执行 URL 编码或解码。
  • 修改字段内容、字符编码或空格。
  • 将数组转换成 JSON 后再传给验签方法。
  • 只选择部分业务字段参与验签。

排查时可以记录字段名称,但回调数据可能包含订单号、账号标识等敏感信息,不宜长期把完整参数和签名写入日志:

\think\facade\Log::info(
    '支付宝回调字段:' . implode(',', array_keys($params))
);

推荐的完整处理方式

use Alipay\EasySDK\Kernel\Factory;

protected function verifyNotify(array $params): bool
{
    try {
        $payment = Factory::payment();

        if (!is_object($payment)) {
            throw new \RuntimeException(
                '支付宝 Easy SDK 支付客户端未创建,请检查 Factory 初始化和 SDK 版本'
            );
        }

        $common = $payment->common();

        if (!is_object($common)) {
            throw new \RuntimeException(
                '支付宝 Easy SDK common 客户端未创建'
            );
        }

        return (bool) $common->verifyNotify($params);
    } catch (\Throwable $e) {
        \think\facade\Log::error(sprintf(
            '支付宝回调验签异常:%s,文件:%s,行号:%d',
            $e->getMessage(),
            $e->getFile(),
            $e->getLine()
        ));

        return false;
    }
}

建议按以下顺序排查:

  1. 确认实际引用的是 Alipay\EasySDK\Kernel\Factory。
  2. 确认回调入口执行过 Factory::setOptions()。
  3. 单独检查 Factory::payment() 的返回值和实际类型。
  4. 核对 Composer 中安装的 SDK 名称和版本。
  5. 检查是否存在自定义封装、同名类或不完整部署。
  6. 支付客户端创建正常后,再核对支付宝公钥、应用私钥和回调参数。

不能通过跳过验签来绕过异常。异步通知必须先通过验签,并完成订单号、金额、商户身份和订单状态校验,之后才能进入业务处理流程。

备注:内容仅供参考。