1 | <?php |
||||
2 | |||||
3 | // +---------------------------------------------------------------------- |
||||
4 | // | ThinkLibrary 6.0 for ThinkPhP 6.0 |
||||
5 | // +---------------------------------------------------------------------- |
||||
6 | // | 版权所有 2017~2020 [ https://www.dtapp.net ] |
||||
7 | // +---------------------------------------------------------------------- |
||||
8 | // | 官方网站: https://gitee.com/liguangchun/ThinkLibrary |
||||
9 | // +---------------------------------------------------------------------- |
||||
10 | // | 开源协议 ( https://mit-license.org ) |
||||
11 | // +---------------------------------------------------------------------- |
||||
12 | // | gitee 仓库地址 :https://gitee.com/liguangchun/ThinkLibrary |
||||
13 | // | github 仓库地址 :https://github.com/GC0202/ThinkLibrary |
||||
14 | // | Packagist 地址 :https://packagist.org/packages/liguangchun/think-library |
||||
15 | // +---------------------------------------------------------------------- |
||||
16 | |||||
17 | namespace DtApp\ThinkLibrary\service\wechat; |
||||
18 | |||||
19 | use DtApp\ThinkLibrary\exception\DtaException; |
||||
20 | use DtApp\ThinkLibrary\facade\Pregs; |
||||
21 | use DtApp\ThinkLibrary\facade\Randoms; |
||||
22 | use DtApp\ThinkLibrary\facade\Urls; |
||||
23 | use DtApp\ThinkLibrary\facade\Xmls; |
||||
24 | use DtApp\ThinkLibrary\Service; |
||||
25 | use DtApp\ThinkLibrary\service\curl\HttpService; |
||||
26 | use think\db\exception\DbException; |
||||
27 | |||||
28 | /** |
||||
29 | * 公众号 |
||||
30 | * Class WebAppService |
||||
31 | * @package DtApp\ThinkLibrary\service\WeChat |
||||
32 | */ |
||||
33 | class WebAppService extends Service |
||||
34 | { |
||||
35 | /** |
||||
36 | * 公众号的唯一标识 |
||||
37 | * @var |
||||
38 | */ |
||||
39 | private $app_id; |
||||
40 | |||||
41 | /** |
||||
42 | * 公众号的appsecret |
||||
43 | * @var |
||||
44 | */ |
||||
45 | private $app_secret; |
||||
46 | |||||
47 | /** |
||||
48 | * 授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理 |
||||
49 | * @var |
||||
50 | */ |
||||
51 | private $redirect_uri; |
||||
52 | |||||
53 | /** |
||||
54 | * 返回类型,请填写code |
||||
55 | * @var string |
||||
56 | */ |
||||
57 | private $response_type = 'code'; |
||||
58 | |||||
59 | /** |
||||
60 | * @var string |
||||
61 | */ |
||||
62 | private $scope = "snsapi_base"; |
||||
63 | |||||
64 | /** |
||||
65 | * @var string |
||||
66 | */ |
||||
67 | private $state = ""; |
||||
68 | |||||
69 | /** |
||||
70 | * @var string |
||||
71 | */ |
||||
72 | private $grant_type = "authorization_code"; |
||||
73 | |||||
74 | /** |
||||
75 | * 驱动方式 |
||||
76 | * @var string |
||||
77 | */ |
||||
78 | private $cache = "file"; |
||||
79 | |||||
80 | /** |
||||
81 | * 商户平台设置的密钥key |
||||
82 | * @var |
||||
83 | */ |
||||
84 | private $mch_key; |
||||
85 | |||||
86 | /** |
||||
87 | * @param string $mchKey |
||||
88 | * @return $this |
||||
89 | */ |
||||
90 | public function mchKey(string $mchKey) |
||||
91 | { |
||||
92 | $this->mch_key = $mchKey; |
||||
93 | return $this; |
||||
94 | } |
||||
95 | |||||
96 | /** |
||||
97 | * 商户号 |
||||
98 | * @var |
||||
99 | */ |
||||
100 | private $mch_id; |
||||
101 | |||||
102 | /** |
||||
103 | * 商户号 |
||||
104 | * @param string $mchId |
||||
105 | * @return $this |
||||
106 | */ |
||||
107 | public function mchId(string $mchId) |
||||
108 | { |
||||
109 | $this->mch_id = $mchId; |
||||
110 | return $this; |
||||
111 | } |
||||
112 | |||||
113 | /** |
||||
114 | * 公众号的唯一标识 |
||||
115 | * @param string $appId |
||||
116 | * @return $this |
||||
117 | */ |
||||
118 | public function appId(string $appId): self |
||||
119 | { |
||||
120 | $this->app_id = $appId; |
||||
121 | return $this; |
||||
122 | } |
||||
123 | |||||
124 | /** |
||||
125 | * 公众号的appsecret |
||||
126 | * @param string $appSecret |
||||
127 | * @return $this |
||||
128 | */ |
||||
129 | public function appSecret(string $appSecret): self |
||||
130 | { |
||||
131 | $this->app_secret = $appSecret; |
||||
132 | return $this; |
||||
133 | } |
||||
134 | |||||
135 | /** |
||||
136 | * 获取配置信息 |
||||
137 | * @return $this |
||||
138 | */ |
||||
139 | private function getConfig(): self |
||||
140 | { |
||||
141 | $this->cache = config('dtapp.wechat.webapp.cache'); |
||||
0 ignored issues
–
show
|
|||||
142 | $this->app_id = config('dtapp.wechat.webapp.app_id'); |
||||
143 | $this->app_secret = config('dtapp.wechat.webapp.app_secret'); |
||||
144 | return $this; |
||||
145 | } |
||||
146 | |||||
147 | /** |
||||
148 | * 授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理 |
||||
149 | * @param string $redirectUri |
||||
150 | * @return $this |
||||
151 | * @throws DtaException |
||||
152 | */ |
||||
153 | public function redirectUri(string $redirectUri) |
||||
154 | { |
||||
155 | if (empty(Pregs::isLink($redirectUri))) { |
||||
156 | throw new DtaException("请检查redirectUri,是否正确"); |
||||
157 | } |
||||
158 | $this->redirect_uri = Urls::lenCode($redirectUri); |
||||
159 | return $this; |
||||
160 | } |
||||
161 | |||||
162 | /** |
||||
163 | * 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 ) |
||||
164 | * @param string $scope |
||||
165 | * @return $this |
||||
166 | * @throws DtaException |
||||
167 | */ |
||||
168 | public function scope(string $scope): self |
||||
169 | { |
||||
170 | if ($scope === "snsapi_base") { |
||||
171 | $this->scope = $scope; |
||||
172 | } elseif ($scope === "snsapi_userinfo") { |
||||
173 | $this->scope = $scope; |
||||
174 | } else { |
||||
175 | throw new DtaException("请检查scope参数"); |
||||
176 | } |
||||
177 | return $this; |
||||
178 | } |
||||
179 | |||||
180 | /** |
||||
181 | * 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节 |
||||
182 | * @param string $state |
||||
183 | * @return $this |
||||
184 | */ |
||||
185 | public function state(string $state): self |
||||
186 | { |
||||
187 | $this->state = $state; |
||||
188 | return $this; |
||||
189 | } |
||||
190 | |||||
191 | /** |
||||
192 | * 驱动方式 |
||||
193 | * @param string $cache |
||||
194 | * @return $this |
||||
195 | */ |
||||
196 | public function cache(string $cache): self |
||||
197 | { |
||||
198 | $this->cache = $cache; |
||||
199 | return $this; |
||||
200 | } |
||||
201 | |||||
202 | /** |
||||
203 | * 网页授权 |
||||
204 | * https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html#0 |
||||
205 | * @return void |
||||
206 | * @throws DtaException |
||||
207 | */ |
||||
208 | public function oauth2() |
||||
209 | { |
||||
210 | if (empty($this->app_id)) { |
||||
211 | $this->getConfig(); |
||||
212 | } |
||||
213 | if (strlen($this->state) > 128) { |
||||
214 | throw new DtaException("请检查state参数,最多128字节"); |
||||
215 | } |
||||
216 | $params = Urls::toParams([ |
||||
217 | 'appid' => $this->app_id, |
||||
218 | 'redirect_uri' => $this->redirect_uri, |
||||
219 | 'response_type' => $this->response_type, |
||||
220 | 'scope' => $this->scope, |
||||
221 | 'state' => $this->state |
||||
222 | ]); |
||||
223 | return header("Location:https://open.weixin.qq.com/connect/oauth2/authorize?$params#wechat_redirect"); |
||||
0 ignored issues
–
show
Are you sure the usage of
header('Location:https:/...ams.'#wechat_redirect') is correct as it seems to always return null .
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||
224 | } |
||||
225 | |||||
226 | /** |
||||
227 | * 通过code换取网页授权access_token |
||||
228 | * @param string $code |
||||
229 | * @param bool $is |
||||
230 | * @return array|bool|mixed|string |
||||
231 | * @throws DtaException |
||||
232 | */ |
||||
233 | public function accessToken(string $code, bool $is = true) |
||||
234 | { |
||||
235 | if (empty($this->app_id) || empty($this->app_secret)) { |
||||
236 | $this->getConfig(); |
||||
237 | } |
||||
238 | if (empty($this->app_id)) { |
||||
239 | throw new DtaException('请检查app_id参数'); |
||||
240 | } |
||||
241 | if (empty($this->app_secret)) { |
||||
242 | throw new DtaException('请检查app_secret参数'); |
||||
243 | } |
||||
244 | return HttpService::instance() |
||||
245 | ->url("https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->app_id}&secret={$this->app_secret}&code={$code}&grant_type={$this->grant_type}") |
||||
246 | ->toArray($is); |
||||
247 | } |
||||
248 | |||||
249 | /** |
||||
250 | * 刷新access_token(如果需要) |
||||
251 | * @param string $refreshToken |
||||
252 | * @param bool $is |
||||
253 | * @return array|bool|mixed|string |
||||
254 | * @throws DtaException |
||||
255 | */ |
||||
256 | public function refreshToken(string $refreshToken, bool $is = true) |
||||
257 | { |
||||
258 | if (empty($this->app_id)) { |
||||
259 | $this->getConfig(); |
||||
260 | } |
||||
261 | if (empty($this->app_id)) { |
||||
262 | throw new DtaException('请检查app_id参数'); |
||||
263 | } |
||||
264 | $this->grant_type = "refresh_token"; |
||||
265 | return HttpService::instance() |
||||
266 | ->url("https://api.weixin.qq.com/sns/oauth2/refresh_token?appid={$this->app_id}&grant_type={$this->grant_type}&refresh_token={$refreshToken}") |
||||
267 | ->toArray($is); |
||||
268 | } |
||||
269 | |||||
270 | /** |
||||
271 | * 拉取用户信息(需scope为 snsapi_userinfo) |
||||
272 | * @param string $accessToken |
||||
273 | * @param string $openid |
||||
274 | * @param string $lang |
||||
275 | * @param bool $is |
||||
276 | * @return array|bool|mixed|string |
||||
277 | */ |
||||
278 | public function useInfo(string $accessToken, string $openid, $lang = "zh_CN", bool $is = true) |
||||
279 | { |
||||
280 | if (empty($this->app_id) || empty($this->app_secret)) { |
||||
281 | $this->getConfig(); |
||||
282 | } |
||||
283 | return HttpService::instance() |
||||
284 | ->url("https://api.weixin.qq.com/sns/userinfo?access_token={$accessToken}&openid={$openid}&lang={$lang}") |
||||
285 | ->toArray($is); |
||||
286 | } |
||||
287 | |||||
288 | /** |
||||
289 | * 检验授权凭证(access_token)是否有效 |
||||
290 | * @param string $accessToken |
||||
291 | * @param string $openid |
||||
292 | * @param bool $is |
||||
293 | * @return array|bool|mixed|string |
||||
294 | */ |
||||
295 | public function auth(string $accessToken, string $openid, bool $is = true) |
||||
296 | { |
||||
297 | if (empty($this->app_id) || empty($this->app_secret)) { |
||||
298 | $this->getConfig(); |
||||
299 | } |
||||
300 | return HttpService::instance() |
||||
301 | ->url("https://api.weixin.qq.com/sns/auth?access_token={$accessToken}&openid={$openid}") |
||||
302 | ->toArray($is); |
||||
303 | } |
||||
304 | |||||
305 | /** |
||||
306 | * 分享 |
||||
307 | * @param string $url |
||||
308 | * @return array |
||||
309 | * @throws DbException |
||||
310 | * @throws DtaException |
||||
311 | * @throws \Exception |
||||
312 | */ |
||||
313 | public function share($url = '') |
||||
314 | { |
||||
315 | if (empty($this->app_id)) { |
||||
316 | $this->getConfig(); |
||||
317 | } |
||||
318 | if (empty($this->app_id)) { |
||||
319 | throw new DtaException('请检查app_id参数'); |
||||
320 | } |
||||
321 | // 获取数据 |
||||
322 | $accessToken = $this->getAccessToken(); |
||||
323 | if (!isset($accessToken['access_token'])) { |
||||
324 | throw new DtaException("获取access_token错误," . $accessToken['errmsg']); |
||||
325 | } |
||||
326 | $res = HttpService::instance() |
||||
327 | ->url("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={$accessToken['access_token']}&type=jsapi") |
||||
328 | ->toArray(); |
||||
329 | if (!empty($res['errcode'])) { |
||||
330 | // 获取数据 |
||||
331 | $accessToken = $this->getAccessToken(); |
||||
332 | if (!isset($accessToken['access_token'])) { |
||||
333 | throw new DtaException("获取access_token错误," . $accessToken['errmsg']); |
||||
334 | } |
||||
335 | $res = HttpService::instance() |
||||
336 | ->url("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={$accessToken['access_token']}&type=jsapi") |
||||
337 | ->toArray(); |
||||
338 | if (!empty($res['errcode'])) { |
||||
339 | throw new DtaException('accessToken已过期'); |
||||
340 | } |
||||
341 | } |
||||
342 | if (empty($url)) { |
||||
343 | // 注意 URL 一定要动态获取,不能 hardcode. |
||||
344 | $protocol = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] === 443) ? "https://" : "http://"; |
||||
345 | $url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"; |
||||
346 | } |
||||
347 | $timestamp = time(); |
||||
348 | $nonceStr = $this->createNonceStr(); |
||||
349 | // 获得jsapi_ticket之后,就可以生成JS-SDK权限验证的签名了。 |
||||
350 | $jsapiTicket = $res['ticket']; |
||||
351 | // 这里参数的顺序要按照 key 值 ASCII 码升序排序 |
||||
352 | $string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr×tamp=$timestamp&url=$url"; |
||||
353 | return [ |
||||
354 | "appId" => $this->app_id, |
||||
355 | "nonceStr" => $nonceStr, |
||||
356 | "timestamp" => $timestamp, |
||||
357 | "url" => $url, |
||||
358 | "signature" => sha1($string), |
||||
359 | "rawString" => $string |
||||
360 | ]; |
||||
361 | } |
||||
362 | |||||
363 | /** |
||||
364 | * @param int $length |
||||
365 | * @return string |
||||
366 | * @throws \Exception |
||||
367 | */ |
||||
368 | private function createNonceStr($length = 16): string |
||||
369 | { |
||||
370 | $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; |
||||
371 | $str = ""; |
||||
372 | for ($i = 0; $i < $length; $i++) { |
||||
373 | $str .= $chars[random_int(0, strlen($chars) - 1)]; |
||||
374 | } |
||||
375 | return $str; |
||||
376 | } |
||||
377 | |||||
378 | /** |
||||
379 | * 生成二维码 |
||||
380 | * @param array $data |
||||
381 | * @return array|bool|mixed|string |
||||
382 | * @throws DbException |
||||
383 | * @throws DtaException |
||||
384 | */ |
||||
385 | public function qrCode(array $data) |
||||
386 | { |
||||
387 | // 获取数据 |
||||
388 | $accessToken = $this->getAccessToken(); |
||||
389 | return HttpService::instance() |
||||
390 | ->url("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={$accessToken['access_token']}") |
||||
391 | ->data($data) |
||||
392 | ->post() |
||||
393 | ->toArray(); |
||||
394 | } |
||||
395 | |||||
396 | /** |
||||
397 | * 发送模板消息 |
||||
398 | * @param array $data |
||||
399 | * @return array|bool|mixed|string |
||||
400 | * @throws DbException |
||||
401 | * @throws DtaException |
||||
402 | */ |
||||
403 | public function messageTemplateSend(array $data = []) |
||||
404 | { |
||||
405 | // 获取数据 |
||||
406 | $accessToken = $this->getAccessToken(); |
||||
407 | $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={$accessToken['access_token']}"; |
||||
408 | return HttpService::instance() |
||||
409 | ->url($url) |
||||
410 | ->data($data) |
||||
411 | ->toArray(); |
||||
412 | } |
||||
413 | |||||
414 | /** |
||||
415 | * 设置所属行业 |
||||
416 | * https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html#0 |
||||
417 | * @param string $access_token |
||||
418 | * @param array $data |
||||
419 | * @return bool|mixed|string |
||||
420 | * @throws DbException |
||||
421 | * @throws DtaException |
||||
422 | */ |
||||
423 | public function setIndustry(string $access_token, array $data = []) |
||||
0 ignored issues
–
show
The parameter
$access_token is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||
424 | { |
||||
425 | // 获取数据 |
||||
426 | $accessToken = $this->getAccessToken(); |
||||
427 | $url = "https://api.weixin.qq.com/cgi-bin/template/api_set_industry?access_token={$accessToken['access_token']}"; |
||||
428 | return HttpService::instance() |
||||
429 | ->url($url) |
||||
430 | ->data($data) |
||||
431 | ->toArray(); |
||||
432 | } |
||||
433 | |||||
434 | /** |
||||
435 | * 将一条长链接转成短链接 |
||||
436 | * @param string $long_url |
||||
437 | * @return bool |
||||
438 | * @throws DbException |
||||
439 | * @throws DtaException |
||||
440 | */ |
||||
441 | public function shortUrl(string $long_url) |
||||
442 | { |
||||
443 | // 获取数据 |
||||
444 | $accessToken = $this->getAccessToken(); |
||||
445 | $url = "https://api.weixin.qq.com/cgi-bin/shorturl?access_token={$accessToken['access_token']}"; |
||||
446 | return HttpService::instance() |
||||
0 ignored issues
–
show
|
|||||
447 | ->url($url) |
||||
448 | ->data([ |
||||
449 | 'action' => 'long2short', |
||||
450 | 'long_url' => $long_url |
||||
451 | ]) |
||||
452 | ->toArray(); |
||||
453 | } |
||||
454 | |||||
455 | /** |
||||
456 | * 连Wi-Fi完成页跳转小程序 |
||||
457 | * https://developers.weixin.qq.com/doc/offiaccount/WiFi_via_WeChat/WiFi_mini_programs.html |
||||
458 | * @param array $data |
||||
459 | * @return array|bool|mixed|string |
||||
460 | * @throws DbException |
||||
461 | * @throws DtaException |
||||
462 | */ |
||||
463 | public function fiNihPageSet(array $data = []) |
||||
464 | { |
||||
465 | // 获取数据 |
||||
466 | $accessToken = $this->getAccessToken(); |
||||
467 | $url = "https://api.weixin.qq.com/bizwifi/finishpage/set?access_token={$accessToken['access_token']}"; |
||||
468 | return HttpService::instance() |
||||
469 | ->url($url) |
||||
470 | ->post() |
||||
471 | ->data($data) |
||||
472 | ->toArray(); |
||||
473 | } |
||||
474 | |||||
475 | /** |
||||
476 | * 自定义菜单 获取自定义菜单配置 |
||||
477 | * https://developers.weixin.qq.com/doc/offiaccount/Custom_Menus/Getting_Custom_Menu_Configurations.html |
||||
478 | * @return array|bool|mixed|string |
||||
479 | * @throws DbException |
||||
480 | * @throws DtaException |
||||
481 | */ |
||||
482 | public function menuGet() |
||||
483 | { |
||||
484 | // 获取数据 |
||||
485 | $accessToken = $this->getAccessToken(); |
||||
486 | $url = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token={$accessToken['access_token']}"; |
||||
487 | return HttpService::instance() |
||||
488 | ->url($url) |
||||
489 | ->toArray(); |
||||
490 | } |
||||
491 | |||||
492 | /** |
||||
493 | * 自定义菜单 创建个性化菜单 |
||||
494 | * https://developers.weixin.qq.com/doc/offiaccount/Custom_Menus/Personalized_menu_interface.html |
||||
495 | * @param array $data |
||||
496 | * @return array|bool|mixed|string |
||||
497 | * @throws DbException |
||||
498 | * @throws DtaException |
||||
499 | */ |
||||
500 | public function menuAddConditional(array $data = []) |
||||
501 | { |
||||
502 | // 获取数据 |
||||
503 | $accessToken = $this->getAccessToken(); |
||||
504 | $url = "https://api.weixin.qq.com/cgi-bin/menu/addconditional?access_token={$accessToken['access_token']}"; |
||||
505 | return HttpService::instance() |
||||
506 | ->url($url) |
||||
507 | ->post() |
||||
508 | ->data($data) |
||||
509 | ->toArray(); |
||||
510 | } |
||||
511 | |||||
512 | /** |
||||
513 | * 自定义菜单 删除个性化菜单 |
||||
514 | * https://developers.weixin.qq.com/doc/offiaccount/Custom_Menus/Personalized_menu_interface.html |
||||
515 | * @param array $data |
||||
516 | * @return array|bool|mixed|string |
||||
517 | * @throws DbException |
||||
518 | * @throws DtaException |
||||
519 | */ |
||||
520 | public function menuDelConditional(array $data = []) |
||||
521 | { |
||||
522 | // 获取数据 |
||||
523 | $accessToken = $this->getAccessToken(); |
||||
524 | $url = "https://api.weixin.qq.com/cgi-bin/menu/delconditional?access_token={$accessToken['access_token']}"; |
||||
525 | return HttpService::instance() |
||||
526 | ->url($url) |
||||
527 | ->post() |
||||
528 | ->data($data) |
||||
529 | ->toArray(); |
||||
530 | } |
||||
531 | |||||
532 | /** |
||||
533 | * 自定义菜单 测试个性化菜单匹配结果 |
||||
534 | * https://developers.weixin.qq.com/doc/offiaccount/Custom_Menus/Personalized_menu_interface.html |
||||
535 | * @param array $data |
||||
536 | * @return array|bool|mixed|string |
||||
537 | * @throws DbException |
||||
538 | * @throws DtaException |
||||
539 | */ |
||||
540 | public function menuTryMatch(array $data = []) |
||||
541 | { |
||||
542 | // 获取数据 |
||||
543 | $accessToken = $this->getAccessToken(); |
||||
544 | $url = "https://api.weixin.qq.com/cgi-bin/menu/trymatch?access_token={$accessToken['access_token']}"; |
||||
545 | return HttpService::instance() |
||||
546 | ->url($url) |
||||
547 | ->post() |
||||
548 | ->data($data) |
||||
549 | ->toArray(); |
||||
550 | } |
||||
551 | |||||
552 | /** |
||||
553 | * 自定义菜单 删除接口 |
||||
554 | * https://developers.weixin.qq.com/doc/offiaccount/Custom_Menus/Deleting_Custom-Defined_Menu.html |
||||
555 | * @return array|bool|mixed|string |
||||
556 | * @throws DbException |
||||
557 | * @throws DtaException |
||||
558 | */ |
||||
559 | public function menuDelete() |
||||
560 | { |
||||
561 | // 获取数据 |
||||
562 | $accessToken = $this->getAccessToken(); |
||||
563 | $url = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token={$accessToken['access_token']}"; |
||||
564 | return HttpService::instance() |
||||
565 | ->url($url) |
||||
566 | ->toArray(); |
||||
567 | } |
||||
568 | |||||
569 | /** |
||||
570 | * 自定义菜单 查询接口 |
||||
571 | * https://developers.weixin.qq.com/doc/offiaccount/Custom_Menus/Querying_Custom_Menus.html |
||||
572 | * @return array|bool|mixed|string |
||||
573 | * @throws DbException |
||||
574 | * @throws DtaException |
||||
575 | */ |
||||
576 | public function getCurrentSelfmenuInfo() |
||||
577 | { |
||||
578 | // 获取数据 |
||||
579 | $accessToken = $this->getAccessToken(); |
||||
580 | $url = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token={$accessToken['access_token']}"; |
||||
581 | return HttpService::instance() |
||||
582 | ->url($url) |
||||
583 | ->toArray(); |
||||
584 | } |
||||
585 | |||||
586 | /** |
||||
587 | * 自定义菜单 创建接口 |
||||
588 | * https://developers.weixin.qq.com/doc/offiaccount/Custom_Menus/Creating_Custom-Defined_Menu.html |
||||
589 | * @param array $data |
||||
590 | * @return array|bool|mixed|string |
||||
591 | * @throws DbException |
||||
592 | * @throws DtaException |
||||
593 | */ |
||||
594 | public function menuCreate(array $data = []) |
||||
595 | { |
||||
596 | // 获取数据 |
||||
597 | $accessToken = $this->getAccessToken(); |
||||
598 | $url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token={$accessToken['access_token']}"; |
||||
599 | return HttpService::instance() |
||||
600 | ->url($url) |
||||
601 | ->post() |
||||
602 | ->data($data) |
||||
603 | ->toArray(); |
||||
604 | } |
||||
605 | |||||
606 | /** |
||||
607 | * 获取access_token信息 |
||||
608 | * @return array|bool|mixed|string|string[] |
||||
609 | * @throws DbException |
||||
610 | * @throws DtaException |
||||
611 | */ |
||||
612 | private function getAccessToken() |
||||
613 | { |
||||
614 | if (empty($this->cache) || empty($this->app_id) || empty($this->app_secret)) { |
||||
615 | $this->getConfig(); |
||||
616 | } |
||||
617 | if (empty($this->cache)) { |
||||
618 | throw new DtaException('请检查cache参数'); |
||||
619 | } |
||||
620 | if (empty($this->app_id)) { |
||||
621 | throw new DtaException('请检查app_id参数'); |
||||
622 | } |
||||
623 | if (empty($this->app_secret)) { |
||||
624 | throw new DtaException('请检查app_secret参数'); |
||||
625 | } |
||||
626 | |||||
627 | $this->grant_type = "client_credential"; |
||||
628 | if ($this->cache === "file") { |
||||
629 | // 文件名 |
||||
630 | $file = "{$this->app->getRootPath()}runtime/{$this->app_id}_access_token.json"; |
||||
631 | // 获取数据 |
||||
632 | $accessToken = file_exists($file) ? json_decode(file_get_contents($file), true) : []; |
||||
633 | if (empty($accessToken) || !is_array($accessToken)) { |
||||
634 | $accessToken = [ |
||||
635 | 'access_token' => '', |
||||
636 | 'expires_in' => '', |
||||
637 | 'expires_time' => '', |
||||
638 | ]; |
||||
639 | } |
||||
640 | if (empty($accessToken['expires_time'])) { |
||||
641 | // 文件不存在 |
||||
642 | $accessToken_res = HttpService::instance() |
||||
643 | ->url("https://api.weixin.qq.com/cgi-bin/token?grant_type={$this->grant_type}&appid={$this->app_id}&secret={$this->app_secret}") |
||||
644 | ->toArray(); |
||||
645 | $accessToken_res['expires_time'] = time() + 6000; |
||||
646 | file_put_contents($file, json_encode($accessToken_res, JSON_UNESCAPED_UNICODE)); |
||||
647 | $accessToken = $accessToken_res; |
||||
648 | } else if (!isset($accessToken['access_token'])) { |
||||
649 | // 内容不存在 |
||||
650 | $accessToken_res = HttpService::instance() |
||||
651 | ->url("https://api.weixin.qq.com/cgi-bin/token?grant_type={$this->grant_type}&appid={$this->app_id}&secret={$this->app_secret}") |
||||
652 | ->toArray(); |
||||
653 | $accessToken_res['expires_time'] = time() + 6000; |
||||
654 | file_put_contents($file, json_encode($accessToken_res, JSON_UNESCAPED_UNICODE)); |
||||
655 | $accessToken = $accessToken_res; |
||||
656 | } else if ($accessToken['expires_time'] <= time()) { |
||||
657 | $accessToken_res = HttpService::instance() |
||||
658 | ->url("https://api.weixin.qq.com/cgi-bin/token?grant_type={$this->grant_type}&appid={$this->app_id}&secret={$this->app_secret}") |
||||
659 | ->toArray(); |
||||
660 | $accessToken_res['expires_time'] = time() + 6000; |
||||
661 | file_put_contents($file, json_encode($accessToken_res, JSON_UNESCAPED_UNICODE)); |
||||
662 | $accessToken = $accessToken_res; |
||||
663 | } |
||||
664 | if (isset($accessToken['access_token'])) { |
||||
665 | $judge = HttpService::instance() |
||||
666 | ->url("https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token={$accessToken['access_token']}") |
||||
667 | ->toArray(); |
||||
668 | if (!isset($judge['ip_list'])) { |
||||
669 | $accessToken_res = HttpService::instance() |
||||
670 | ->url("https://api.weixin.qq.com/cgi-bin/token?grant_type={$this->grant_type}&appid={$this->app_id}&secret={$this->app_secret}") |
||||
671 | ->toArray(); |
||||
672 | $accessToken_res['expires_time'] = time() + 6000; |
||||
673 | file_put_contents($file, json_encode($accessToken_res, JSON_UNESCAPED_UNICODE)); |
||||
674 | $accessToken = $accessToken_res; |
||||
675 | } |
||||
676 | } else { |
||||
677 | $accessToken_res = HttpService::instance() |
||||
678 | ->url("https://api.weixin.qq.com/cgi-bin/token?grant_type={$this->grant_type}&appid={$this->app_id}&secret={$this->app_secret}") |
||||
679 | ->toArray(); |
||||
680 | $accessToken_res['expires_time'] = time() + 6000; |
||||
681 | file_put_contents($file, json_encode($accessToken_res, JSON_UNESCAPED_UNICODE)); |
||||
682 | $accessToken = $accessToken_res; |
||||
683 | } |
||||
684 | return $accessToken; |
||||
685 | } |
||||
686 | |||||
687 | if ($this->cache === "mysql") { |
||||
688 | $access_token = []; |
||||
689 | // 文件名 |
||||
690 | $file = "{$this->app_id}_access_token"; |
||||
691 | // 获取数据 |
||||
692 | $cache_mysql_value = dtacache($file); |
||||
693 | if (!empty($cache_mysql_value)) { |
||||
694 | $access_token['access_token'] = $cache_mysql_value; |
||||
695 | } else { |
||||
696 | // 获取远程Token |
||||
697 | $accessToken_res = HttpService::instance() |
||||
698 | ->url("https://api.weixin.qq.com/cgi-bin/token?grant_type={$this->grant_type}&appid={$this->app_id}&secret={$this->app_secret}") |
||||
699 | ->toArray(); |
||||
700 | // 保存到数据库 |
||||
701 | dtacache($file, $accessToken_res['access_token'], 6000); |
||||
702 | $access_token['access_token'] = $accessToken_res['access_token']; |
||||
703 | } |
||||
704 | // 判断token是否可以使用 |
||||
705 | $judge = HttpService::instance() |
||||
706 | ->url("https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token={$access_token['access_token']}") |
||||
707 | ->toArray(); |
||||
708 | if (!isset($judge['ip_list'])) { |
||||
709 | $accessToken_res = HttpService::instance() |
||||
710 | ->url("https://api.weixin.qq.com/cgi-bin/token?grant_type={$this->grant_type}&appid={$this->app_id}&secret={$this->app_secret}") |
||||
711 | ->toArray(); |
||||
712 | dtacache($file, $accessToken_res['access_token'], 6000); |
||||
713 | $access_token['access_token'] = $accessToken_res['access_token']; |
||||
714 | } |
||||
715 | return $access_token; |
||||
716 | } |
||||
717 | |||||
718 | throw new DtaException("驱动方式错误"); |
||||
719 | } |
||||
720 | |||||
721 | /** |
||||
722 | * 微信支付 |
||||
723 | * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1 |
||||
724 | * @param array $array |
||||
725 | * @return bool|string |
||||
726 | */ |
||||
727 | public function payUnfIedOrder(array $array) |
||||
728 | { |
||||
729 | $array['appid'] = $this->app_id; |
||||
730 | $array['mch_id'] = $this->mch_id; |
||||
731 | $array['nonce_str'] = Randoms::generate(32, 3); |
||||
732 | $array['sign_type'] = 'HMAC-SHA256'; |
||||
733 | $array['sign'] = $this->paySign($array); |
||||
734 | $res = $this->postXmlCurl(Xmls::toXml($array)); |
||||
735 | return Xmls::toArray($res); |
||||
0 ignored issues
–
show
It seems like
$res can also be of type true ; however, parameter $xml of DtApp\ThinkLibrary\facade\Xmls::toArray() 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
![]() |
|||||
736 | } |
||||
737 | |||||
738 | /** |
||||
739 | * 微信内H5调起支付 |
||||
740 | * @param string $prepay_id |
||||
741 | * @return array |
||||
742 | */ |
||||
743 | public function h5Pay(string $prepay_id) |
||||
744 | { |
||||
745 | $array['appId'] = $this->app_id; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
746 | $array['timeStamp'] = time(); |
||||
747 | $array['nonceStr'] = Randoms::generate(32, 3); |
||||
748 | $array['package'] = "prepay_id={$prepay_id}"; |
||||
749 | $array['signType'] = 'HMAC-SHA256'; |
||||
750 | $array['paySign'] = $this->paySign($array); |
||||
751 | return $array; |
||||
752 | } |
||||
753 | |||||
754 | /** |
||||
755 | * 生成支付签名 |
||||
756 | * @param array $array 参与签名的内容组成的数组 |
||||
757 | * @param bool $hmacsha256 是否使用 HMAC-SHA256算法,否则使用MD5 |
||||
758 | * @return string |
||||
759 | */ |
||||
760 | private function paySign(array $array, bool $hmacsha256 = true): string |
||||
761 | { |
||||
762 | // 排序 |
||||
763 | ksort($array); |
||||
764 | // 转成字符串 |
||||
765 | $stringA = Urls::toParams($array); |
||||
766 | // 在字符串接商户支付秘钥 |
||||
767 | $stringSignTemp = "{$stringA}&key=" . $this->mch_key; |
||||
768 | //步骤四:MD5或HMAC-SHA256C加密 |
||||
769 | if ($hmacsha256) { |
||||
770 | $str = hash_hmac("sha256", $stringSignTemp, $this->mch_key); |
||||
771 | } else { |
||||
772 | $str = md5($stringSignTemp); |
||||
773 | } |
||||
774 | //符转大写 |
||||
775 | return strtoupper($str); |
||||
776 | } |
||||
777 | |||||
778 | /** |
||||
779 | * @param $xml |
||||
780 | * @return bool|string |
||||
781 | */ |
||||
782 | private function postXmlCurl($xml) |
||||
783 | { |
||||
784 | $ch = curl_init(); |
||||
785 | //设置超时 |
||||
786 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); |
||||
787 | |||||
788 | curl_setopt($ch, CURLOPT_URL, "https://api.mch.weixin.qq.com/pay/unifiedorder"); |
||||
789 | //设置header |
||||
790 | curl_setopt($ch, CURLOPT_HEADER, FALSE); |
||||
791 | //要求结果为字符串且输出到屏幕上 |
||||
792 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); |
||||
793 | |||||
794 | //试试手气新增,增加之后 curl 不报 60# 错误,可以请求到微信的响应 |
||||
795 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //不验证 SSL 证书 |
||||
796 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);//不验证 SSL 证书域名 |
||||
797 | //post提交方式 |
||||
798 | curl_setopt($ch, CURLOPT_POST, TRUE); |
||||
799 | curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); |
||||
800 | //运行curl |
||||
801 | $data = curl_exec($ch); |
||||
802 | //返回结果 |
||||
803 | if ($data) { |
||||
804 | curl_close($ch); |
||||
805 | return $data; |
||||
806 | } |
||||
807 | |||||
808 | $error = curl_errno($ch); |
||||
809 | curl_close($ch); |
||||
810 | return "curl error, error code " . $error; |
||||
811 | } |
||||
812 | } |
||||
813 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.