1 | <?php |
||||
2 | /** |
||||
3 | * 蚂蚁金服 https://openhome.alipay.com |
||||
4 | * 支付宝第三方应用授权文档 |
||||
5 | * https://docs.open.alipay.com/20160728150111277227 |
||||
6 | * 1.设置:回调地址/加签方式(RSA(SHA256)密钥)/接口内容加密方式(AES密钥) |
||||
7 | * 应用公钥(SHA256withRsa)生成方法 |
||||
8 | * 1.1 在liunx 使用 openssl |
||||
9 | * 1.2 OpenSSL> genrsa -out rsa_private_key.pem 2048 #生成私钥 |
||||
10 | * 1.3 OpenSSL> pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt -out rsa_private_key_pkcs8.pem #Java开发者需要将私钥转换成PKCS8格式 |
||||
11 | * 1.4 OpenSSL> rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem #生成公钥 |
||||
12 | * 1.5 OpenSSL> exit #退出OpenSSL程序 |
||||
13 | * 1.6 复制rsa_public_key.pem 中间那一段到支付宝填写(注: 不要复制头和尾) |
||||
14 | * 2.1.PC登录需要签约: 第三方应用授权/获取会员信息 |
||||
15 | * 2.2.APP登录需要签约: APP支付宝登录/获取会员信息 |
||||
16 | * |
||||
17 | * 网页授权文档:https://opendocs.alipay.com/open/284/web |
||||
18 | */ |
||||
19 | namespace tinymeng\OAuth2\Gateways; |
||||
20 | |||||
21 | use tinymeng\OAuth2\Connector\Gateway; |
||||
22 | use tinymeng\OAuth2\Exception\OAuthException; |
||||
23 | use tinymeng\OAuth2\Helper\ConstCode; |
||||
24 | use tinymeng\OAuth2\Helper\Str; |
||||
25 | |||||
26 | /** |
||||
27 | * Class Alipay |
||||
28 | * @package tinymeng\OAuth2\Gateways |
||||
29 | * @Author: TinyMeng <[email protected]> |
||||
30 | * @Created: 2018/11/9 |
||||
31 | */ |
||||
32 | class Alipay extends Gateway |
||||
33 | { |
||||
34 | const RSA_PRIVATE = 1; |
||||
35 | const RSA_PUBLIC = 2; |
||||
36 | |||||
37 | const API_BASE = 'https://openapi.alipay.com/gateway.do'; |
||||
38 | protected $AuthorizeURL = 'https://openauth.alipay.com/oauth2/publicAppAuthorize.htm'; |
||||
39 | |||||
40 | /** |
||||
41 | * 非必须参数。接口权限值,目前只支持 auth_user 和 auth_base 两个值。以空格分隔的权限列表,若不传递此参数,代表请求的数据访问操作权限与上次获取Access Token时一致。通过Refresh Token刷新Access Token时所要求的scope权限范围必须小于等于上次获取Access Token时授予的权限范围。 |
||||
42 | * @var string |
||||
43 | */ |
||||
44 | protected $scope; |
||||
45 | |||||
46 | /** |
||||
47 | * 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2. |
||||
48 | * @var string |
||||
49 | */ |
||||
50 | protected $sign_type = 'RSA2'; |
||||
51 | |||||
52 | /** |
||||
53 | * @param $config |
||||
54 | */ |
||||
55 | public function __construct($config) |
||||
56 | { |
||||
57 | parent::__construct($config); |
||||
58 | $this->AccessTokenURL = static::API_BASE; |
||||
59 | } |
||||
60 | |||||
61 | /** |
||||
62 | * Description: 得到跳转地址 |
||||
63 | * @author: JiaMeng <[email protected]> |
||||
64 | * Updater: |
||||
65 | * @return string |
||||
66 | */ |
||||
67 | public function getRedirectUrl() |
||||
68 | { |
||||
69 | //存储state |
||||
70 | $this->saveState(); |
||||
71 | //登录参数 |
||||
72 | $params = [ |
||||
73 | 'app_id' => $this->config['app_id'], |
||||
74 | 'redirect_uri' => $this->config['callback'], |
||||
75 | 'scope' => $this->config['scope'], |
||||
76 | 'state' => $this->config['state'], |
||||
77 | ]; |
||||
78 | |||||
79 | if ($this->config['is_sandbox'] == true) { |
||||
80 | //使用沙箱环境url |
||||
81 | $this->AuthorizeURL = str_replace("alipay", "alipaydev", $this->AuthorizeURL); |
||||
82 | } |
||||
83 | |||||
84 | return $this->AuthorizeURL . '?' . http_build_query($params); |
||||
85 | } |
||||
86 | |||||
87 | /** |
||||
88 | * Description: 获取当前授权用户的openid标识 |
||||
89 | * @author: JiaMeng <[email protected]> |
||||
90 | * Updater: |
||||
91 | * @return mixed |
||||
92 | * @throws OAuthException |
||||
93 | */ |
||||
94 | public function openid() |
||||
95 | { |
||||
96 | $this->getToken(); |
||||
97 | |||||
98 | if (isset($this->token['openid'])) { |
||||
99 | return $this->token['openid']; |
||||
100 | } else { |
||||
101 | throw new OAuthException('没有获取到支付宝用户ID!'); |
||||
102 | } |
||||
103 | } |
||||
104 | |||||
105 | /** |
||||
106 | * Description: 获取格式化后的用户信息 |
||||
107 | * @author: JiaMeng <[email protected]> |
||||
108 | * Updater: |
||||
109 | * @return array |
||||
110 | */ |
||||
111 | public function userInfo() |
||||
112 | { |
||||
113 | $result = $this->getUserInfo(); |
||||
114 | |||||
115 | $userInfo = [ |
||||
116 | 'open_id' => $this->openid(), |
||||
117 | 'union_id' => $this->token['union_id']??'', |
||||
118 | 'channel' => ConstCode::TYPE_ALIPAY, |
||||
119 | 'nickname' => $result['nick_name'], |
||||
120 | 'gender' => isset($result['gender']) ? $this->getGender($result['gender']) : ConstCode::GENDER, |
||||
121 | 'avatar' => $result['avatar'], |
||||
122 | // 拓展字段 |
||||
123 | 'access_token' => $this->token['access_token']??'', |
||||
124 | 'user_id' => $this->token['user_id'], |
||||
125 | 'native' => $result, |
||||
126 | ]; |
||||
127 | $userInfo['type'] = ConstCode::getTypeConst($userInfo['channel'],$this->type); |
||||
128 | return $userInfo; |
||||
129 | } |
||||
130 | |||||
131 | /** |
||||
132 | * Description: 获取原始接口返回的用户信息 |
||||
133 | * @author: JiaMeng <[email protected]> |
||||
134 | * Updater: |
||||
135 | * @return mixed |
||||
136 | * @throws OAuthException |
||||
137 | */ |
||||
138 | public function getUserInfo() |
||||
139 | { |
||||
140 | if($this->type == 'app'){//App登录 |
||||
141 | if(!isset($_REQUEST['access_token']) ){ |
||||
142 | throw new OAuthException("AliPay APP登录 需要传输access_token参数! "); |
||||
143 | } |
||||
144 | $this->token['access_token'] = $_REQUEST['access_token']; |
||||
145 | }else { |
||||
146 | /** 获取token信息 */ |
||||
147 | $this->getToken(); |
||||
148 | } |
||||
149 | |||||
150 | $params = [ |
||||
151 | 'app_id' => $this->config['app_id'], |
||||
152 | 'method' => 'alipay.user.info.share', |
||||
153 | 'charset' => 'UTF-8', |
||||
154 | 'sign_type' => $this->sign_type, |
||||
155 | 'timestamp' => date("Y-m-d H:i:s"), |
||||
156 | 'version' => '1.0', |
||||
157 | 'auth_token' => $this->token['access_token'], |
||||
158 | ]; |
||||
159 | $params['sign'] = $this->signature($params); |
||||
160 | |||||
161 | $data = $this->post(self::API_BASE, $params); |
||||
162 | $data = mb_convert_encoding($data, 'utf-8', 'gbk'); |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
163 | $result = json_decode($data, true); |
||||
0 ignored issues
–
show
It seems like
$data can also be of type array ; however, parameter $json of json_decode() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
164 | return $result['alipay_user_info_share_response']; |
||||
165 | } |
||||
166 | |||||
167 | /** |
||||
168 | * Description: 重写 获取的AccessToken请求参数 |
||||
169 | * @author: JiaMeng <[email protected]> |
||||
170 | * Updater: |
||||
171 | * @return array |
||||
172 | */ |
||||
173 | protected function accessTokenParams() |
||||
174 | { |
||||
175 | $params = [ |
||||
176 | 'app_id' => $this->config['app_id'], |
||||
177 | 'method' => 'alipay.system.oauth.token', |
||||
178 | 'charset' => 'UTF-8', |
||||
179 | 'sign_type' => $this->sign_type, |
||||
180 | 'timestamp' => date("Y-m-d H:i:s"), |
||||
181 | 'version' => '1.0', |
||||
182 | 'grant_type' => $this->config['grant_type'], |
||||
183 | 'code' => $this->getCode(), |
||||
184 | ]; |
||||
185 | $params['sign'] = $this->signature($params); |
||||
186 | return $params; |
||||
187 | } |
||||
188 | |||||
189 | /** |
||||
190 | * Description: 支付宝签名 |
||||
191 | * @author: JiaMeng <[email protected]> |
||||
192 | * Updater: |
||||
193 | * @param array $data |
||||
194 | * @return string |
||||
195 | * @throws OAuthException |
||||
196 | */ |
||||
197 | private function signature($data = []) |
||||
198 | { |
||||
199 | ksort($data); |
||||
200 | $str = Str::buildParams($data); |
||||
201 | |||||
202 | $rsaKey = $this->getRsaKeyVal(self::RSA_PRIVATE); |
||||
203 | $res = openssl_get_privatekey($rsaKey); |
||||
204 | if ($res !== false) { |
||||
205 | $sign = ''; |
||||
206 | openssl_sign($str, $sign, $res, OPENSSL_ALGO_SHA256); |
||||
207 | openssl_free_key($res); |
||||
208 | return base64_encode($sign); |
||||
209 | } |
||||
210 | throw new OAuthException('支付宝RSA私钥不正确'); |
||||
211 | } |
||||
212 | |||||
213 | /** |
||||
214 | * Description: 获取密钥 |
||||
215 | * @author: JiaMeng <[email protected]> |
||||
216 | * Updater: |
||||
217 | * @param int $type |
||||
218 | * @return string |
||||
219 | * @throws OAuthException |
||||
220 | */ |
||||
221 | private function getRsaKeyVal($type = self::RSA_PUBLIC) |
||||
222 | { |
||||
223 | if ($type === self::RSA_PUBLIC) { |
||||
224 | $keyname = 'pem_public'; |
||||
225 | $header = '-----BEGIN PUBLIC KEY-----'; |
||||
226 | $footer = '-----END PUBLIC KEY-----'; |
||||
227 | } else { |
||||
228 | $keyname = 'pem_private'; |
||||
229 | $header = '-----BEGIN RSA PRIVATE KEY-----'; |
||||
230 | $footer = '-----END RSA PRIVATE KEY-----'; |
||||
231 | } |
||||
232 | $rsa = $this->config[$keyname]; |
||||
233 | if (is_file($rsa)) { |
||||
234 | $rsa = file_get_contents($rsa); |
||||
235 | } |
||||
236 | if (empty($rsa)) { |
||||
237 | throw new OAuthException('支付宝RSA密钥未配置'); |
||||
238 | } |
||||
239 | $rsa = str_replace([PHP_EOL, $header, $footer], '', $rsa); |
||||
240 | $rsaVal = $header . PHP_EOL . chunk_split($rsa, 64, PHP_EOL) . $footer; |
||||
241 | return $rsaVal; |
||||
242 | } |
||||
243 | |||||
244 | /** |
||||
245 | * Description: 解析access_token方法 |
||||
246 | * @author: JiaMeng <[email protected]> |
||||
247 | * Updater: |
||||
248 | * @param $token |
||||
249 | * @return mixed |
||||
250 | * @throws OAuthException |
||||
251 | */ |
||||
252 | protected function parseToken($token) |
||||
253 | { |
||||
254 | $token = mb_convert_encoding($token, 'utf-8', 'gbk'); |
||||
255 | $data = json_decode($token, true); |
||||
0 ignored issues
–
show
It seems like
$token can also be of type array ; however, parameter $json of json_decode() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
256 | |||||
257 | if (isset($data['alipay_system_oauth_token_response'])) { |
||||
258 | $data = $data['alipay_system_oauth_token_response']; |
||||
259 | $data['openid'] = $data['open_id']??$data['user_id']; |
||||
260 | return $data; |
||||
261 | } else { |
||||
262 | throw new OAuthException("获取支付宝 ACCESS_TOKEN 出错:{$token}"); |
||||
263 | } |
||||
264 | } |
||||
265 | } |
||||
266 |