子比主题支付宝登入设置好了登录失败修复-资讯论坛-小武站

子比主题支付宝登入设置好了登录失败修复

从支付宝返回的响应数据来看,问题的核心原因是:支付宝接口已将用户唯一标识从 user_id 变更为 open_id,但当前代码仍在尝试获取 user_id 字段,导致字段缺失错误。

为什么会有这个变更?

支付宝近年来逐步推进 open_id 作为用户唯一标识,原因包括:

 

  • 增强用户数据安全性,open_id 与应用绑定,不同应用获取的 open_id 不同,避免用户标识泄露。
  • 统一多平台账号体系,open_id 格式更适配跨端登录场景。

 

这一变更在支付宝开放平台的最新文档中已有说明,旧版 user_id 字段可能已被废弃。

后续建议

  1. 更新接口文档参考:查阅支付宝开放平台最新的 《支付宝 OAuth 授权文档》,确认其他字段是否有变更。
  2. 检查用户信息接口:在 getUserInfo 方法中,也可能需要同步适配字段变更(如用户昵称、头像等字段是否调整)。
  3. 联系插件开发者:如果使用的是第三方插件(yurun-oauth-login),建议反馈此问题,获取官方更新版本,避免后续接口变更再次出现兼容性问题。

主要修改说明

  1. 核心字段替换:将所有原本使用user_id的地方统一替换为open_id,包括:
    • __getAccessToken方法中的字段检查和赋值
    • refreshToken方法中的字段检查和赋值
  2. 错误信息更新:调整了错误提示文本,明确提示缺少open_id字段,便于后期问题排查
  3. 保持兼容性:其他功能逻辑保持不变,确保代码整体功能不受影响

使用建议

  1. 替换文件后,建议测试完整的支付宝登录流程,确保授权、获取用户信息等功能正常
  2. 检查系统中其他依赖openid的模块,确保它们能正确处理支付宝返回的open_id格式
  3. 定期关注支付宝开放平台的接口变更通知,及时适配新的接口规范

 

这个修改版本完全适配了你提供的支付宝接口返回数据格式,应该能解决之前的 "缺少 user_id" 错误。
<?php
namespace Yurun\OAuthLogin\Alipay;

use Yurun\OAuthLogin\Base;
use Yurun\OAuthLogin\ApiException;

/**
 * 支付宝授权
 * 网页授权文档:https://docs.open.alipay.com/53/104114
 */
class OAuth2 extends Base
{
	/**
	 * api域名
	 */
	const API_DOMAIN = 'https://openapi.alipay.com/gateway.do';

	/**
	 * 非必须参数。以空格分隔的权限列表
	 * @var string
	 */
	public $scope;

	/**
	 * 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2
	 *
	 * @var string
	 */
	public $signType = 'RSA2';

	/**
	 * 详见应用授权概述:https://docs.open.alipay.com/common/105193
	 *
	 * @var string
	 */
	public $appAuthToken;

	/**
	 * 私有证书文件内容
	 * @var string
	 */
	public $appPrivateKey;

	/**
	 * 私有证书文件地址,不为空时优先使用文件地址
	 * @var string
	 */
	public $appPrivateKeyFile;

	/**
	 * 第一步:获取登录页面跳转url
	 * @param string $callbackUrl 登录回调地址
	 * @param string $state 用于保持请求和回调的状态
	 * @param array $scope 权限列表
	 * @return string
	 */
	public function getAuthUrl($callbackUrl = null, $state = null, $scope = null)
	{
		$option = array(
			'app_id'			=>	$this->appid,
			'scope'				=>	$scope ? $scope : 'auth_user',
			'redirect_uri'		=>	null === $callbackUrl ? $this->callbackUrl : $callbackUrl,
			'state'				=>	$this->getState($state),
		);
		if(null === $this->loginAgentUrl)
		{
			return 'https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?' . $this->http_build_query($option);
		}
		else
		{
			return $this->loginAgentUrl . '?' . $this->http_build_query($option);
		}
	}

	/**
	 * 第二步:处理回调并获取access_token
	 * @param string $storeState 存储的正确的state
	 * @param string $code 回调地址中传过来的code
	 * @param string $state 回调接收到的state
	 * @return string
	 */
	protected function __getAccessToken($storeState, $code = null, $state = null)
	{
		$params = array(
			'app_id'		=>	$this->appid,
			'method'		=>	'alipay.system.oauth.token',
			'charset'		=>	'utf-8',
			'sign_type'		=>	$this->signType,
			'timestamp'		=>	date('Y-m-d H:i:s'),
			'version'		=>	'1.0',
			'grant_type'	=>	'authorization_code',
			'code'			=>	isset($code) ? $code : (isset($_GET['auth_code']) ? $_GET['auth_code'] : ''),
		);
		if($this->appAuthToken)
		{
			$params['app_auth_token'] = $this->appAuthToken;
		}
		$params['sign'] = $this->sign($params);
		$response = $this->http->get(static::API_DOMAIN, $params);
		$this->result = $response->json(true);

		if(!isset($this->result['alipay_system_oauth_token_response']) && isset($this->result['error_response']))
		{
			throw new ApiException(sprintf('%s %s', $this->result['error_response']['msg'], $this->result['error_response']['sub_msg']), $this->result['error_response']['code']);
		}
		$this->result = $responseData = $this->result['alipay_system_oauth_token_response'];
		if(isset($responseData['code']))
		{
			throw new ApiException(sprintf('%s %s', $responseData['msg'], $responseData['sub_msg']), $responseData['code']);
		}
		
		// 检查open_id是否存在(替代原user_id)
		if (!isset($responseData['open_id'])) {
		    $errorMsg = "支付宝接口返回数据中缺少'open_id'字段,响应数据: " . json_encode($responseData);
		    error_log($errorMsg);
		    throw new ApiException($errorMsg, 500);
		}
		
		$this->openid = $responseData['open_id']; // 使用open_id作为用户标识
		return $this->accessToken = $responseData['access_token'];
	}

	/**
	 * 获取用户资料
	 * @param string $accessToken
	 * @return array
	 */
	public function getUserInfo($accessToken = null)
	{
		$params = array(
			'app_id'		=>	$this->appid,
			'method'		=>	'alipay.user.info.share',
			'charset'		=>	'utf-8',
			'sign_type'		=>	$this->signType,
			'timestamp'		=>	date('Y-m-d H:i:s'),
			'version'		=>	'1.0',
			'auth_token'	=>	null === $accessToken ? $this->accessToken : $accessToken,
		);
		if($this->appAuthToken)
		{
			$params['app_auth_token'] = $this->appAuthToken;
		}
		$params['sign'] = $this->sign($params);
		$response = $this->http->get(static::API_DOMAIN, $params);
		$this->result = $response->json(true);

		if(!isset($this->result['alipay_user_info_share_response']) && isset($this->result['error_response']))
		{
			throw new ApiException(sprintf('%s %s', $this->result['error_response']['msg'], $this->result['error_response']['sub_msg']), $this->result['error_response']['code']);
		}
		$this->result = $responseData = $this->result['alipay_user_info_share_response'];
		if(isset($responseData['code']) && 10000 != $responseData['code'])
		{
			throw new ApiException(sprintf('%s %s', $responseData['msg'], $responseData['sub_msg']), $responseData['code']);
		}
		return $responseData;
	}

	/**
	 * 刷新AccessToken续期
	 * @param string $refreshToken
	 * @return bool
	 */
	public function refreshToken($refreshToken)
	{
		$params = array(
			'app_id'		=>	$this->appid,
			'method'		=>	'alipay.system.oauth.token',
			'charset'		=>	'utf-8',
			'sign_type'		=>	$this->signType,
			'timestamp'		=>	date('Y-m-d H:i:s'),
			'version'		=>	'1.0',
			'grant_type'	=>	'refresh_token',
			'refresh_token'	=>	$refreshToken,
		);
		if($this->appAuthToken)
		{
			$params['app_auth_token'] = $this->appAuthToken;
		}
		$params['sign'] = $this->sign($params);
		$response = $this->http->get(static::API_DOMAIN, $params);
		$this->result = $response->json(true);

		if(!isset($this->result['alipay_system_oauth_token_response']) && isset($this->result['error_response']))
		{
			throw new ApiException(sprintf('%s %s', $this->result['error_response']['msg'], $this->result['error_response']['sub_msg']), $this->result['error_response']['code']);
		}
		$this->result = $responseData = $this->result['alipay_system_oauth_token_response'];
		if(isset($responseData['code']))
		{
			throw new ApiException(sprintf('%s %s', $responseData['msg'], $responseData['sub_msg']), $responseData['code']);
		}
		
		// 检查open_id是否存在(替代原user_id)
		if (!isset($responseData['open_id'])) {
		    $errorMsg = "支付宝刷新令牌接口返回数据中缺少'open_id'字段,响应数据: " . json_encode($responseData);
		    error_log($errorMsg);
		    throw new ApiException($errorMsg, 500);
		}
		
		$this->openid = $responseData['open_id']; // 使用open_id作为用户标识
		return $this->accessToken = $responseData['access_token'];
	}

	/**
	 * 检验授权凭证AccessToken是否有效
	 * @param string $accessToken
	 * @return bool
	 */
	public function validateAccessToken($accessToken = null)
	{
		try
		{
			$this->getUserInfo($accessToken);
			return true;
		}
		catch(ApiException $e)
		{
			return false;
		}
	}

	/**
	 * 签名
	 * @param $data
	 * @return string
	 */
	public function sign($data)
	{
		$content = $this->parseSignData($data);
		if(empty($this->appPrivateKeyFile))
		{
			$key = $this->appPrivateKey;
			$method = 'signPrivate';
		}
		else
		{
			$key = $this->appPrivateKeyFile;
			$method = 'signPrivateFromFile';
		}
		switch($this->signType)
		{
			case 'RSA':
				$result = \Yurun\OAuthLogin\Lib\RSA::$method($content, $key);
				break;
			case 'RSA2':
				$result = \Yurun\OAuthLogin\Lib\RSA2::$method($content, $key);
				break;
			default:
				throw new \Exception('未知的加密方式:' . $this->signType);
		}
		return \base64_encode($result);
	}

	/**
	 * 处理验证数据
	 *
	 * @param array $data
	 * @return string
	 */
	public function parseSignData($data)
	{
		if(isset($data['sign']))
		{
			unset($data['sign']);
		}
		\ksort($data);
		$content = '';
		foreach ($data as $k => $v){
			if($v !== '' && $v !== null && !is_array($v)){
				$content .= $k . '=' . $v . '&';
			}
		}
		return trim($content, '&');
	}

}