从支付宝返回的响应数据来看,问题的核心原因是:支付宝接口已将用户唯一标识从 user_id
变更为 open_id
,但当前代码仍在尝试获取 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, '&');
}
}
没有回复内容