WebAppService::state()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 4
rs 10
cc 1
nc 1
nop 1
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
Documentation Bug introduced by
It seems like config('dtapp.wechat.webapp.cache') can also be of type boolean. However, the property $cache is declared as type string. Maybe add an additional type check?

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 the id property of an instance of the Account 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.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
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
Bug introduced by
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 getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
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&timestamp=$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
Unused Code introduced by
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 ignore-unused  annotation

423
    public function setIndustry(/** @scrutinizer ignore-unused */ string $access_token, array $data = [])

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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
Bug Best Practice introduced by
The expression return DtApp\ThinkLibrar... $long_url))->toArray() also could return the type array|string which is incompatible with the documented return type boolean.
Loading history...
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
Bug introduced by
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 ignore-type  annotation

735
        return Xmls::toArray(/** @scrutinizer ignore-type */ $res);
Loading history...
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
$array was never initialized. Although not strictly required by PHP, it is generally a good practice to add $array = array(); before regardless.
Loading history...
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