├── LICENSE
├── MpWechat.php
├── QyWechat.php
├── README.md
├── Wechat.php
├── components
├── BaseWechat.php
├── MessageCrypt.php
├── WechatComponent.php
└── messageCrypt
│ ├── ReadMe.txt
│ ├── demo.php
│ ├── errorCode.php
│ ├── pkcs7Encoder.php
│ ├── sha1.php
│ ├── wxBizMsgCrypt.php
│ └── xmlparse.php
├── composer.json
└── mp
├── Card.php
├── CustomService.php
├── DataCube.php
├── Merchant.php
└── ShakeAround.php
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 callmez
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MpWechat.php:
--------------------------------------------------------------------------------
1 | 开发者中心
42 | * @var string
43 | */
44 | public $token;
45 | /**
46 | * 公众号消息加密键值
47 | * @var string
48 | */
49 | public $encodingAesKey;
50 |
51 | /**
52 | * @inheritdoc
53 | * @throws InvalidConfigException
54 | */
55 | public function init()
56 | {
57 | if ($this->appId === null) {
58 | throw new InvalidConfigException('The "appId" property must be set.');
59 | } elseif ($this->appSecret === null) {
60 | throw new InvalidConfigException('The "appSecret" property must be set.');
61 | } elseif ($this->token === null) {
62 | throw new InvalidConfigException('The "token" property must be set.');
63 | } elseif ($this->encodingAesKey === null) {
64 | throw new InvalidConfigException('The "encodingAesKey" property must be set.');
65 | }
66 | }
67 |
68 | /**
69 | * 获取缓存键值
70 | * @param $name
71 | * @return string
72 | */
73 | protected function getCacheKey($name)
74 | {
75 | return $this->cachePrefix . '_' . $this->appId . '_' . $name;
76 | }
77 |
78 | /**
79 | * 增加微信基本链接
80 | * @inheritdoc
81 | */
82 | protected function httpBuildQuery($url, array $options)
83 | {
84 | if (stripos($url, 'http://') === false && stripos($url, 'https://') === false) {
85 | $url = self::WECHAT_BASE_URL . $url;
86 | }
87 | return parent::httpBuildQuery($url, $options);
88 | }
89 |
90 | /**
91 | * @inheritdoc
92 | * @param bool $force 是否强制获取access_token, 该设置会在access_token使用错误时, 是否再获取一次access_token并再重新提交请求
93 | */
94 | public function parseHttpRequest(callable $callable, $url, $postOptions = null, $force = true)
95 | {
96 | $result = call_user_func_array($callable, [$url, $postOptions]);
97 | if (isset($result['errcode']) && $result['errcode']) {
98 | $this->lastError = $result;
99 | Yii::warning([
100 | 'url' => $url,
101 | 'result' => $result,
102 | 'postOptions' => $postOptions
103 | ], __METHOD__);
104 | switch ($result ['errcode']) {
105 | case 40001: //access_token 失效,强制更新access_token, 并更新地址重新执行请求
106 | if ($force) {
107 | $url = preg_replace_callback("/access_token=([^&]*)/i", function(){
108 | return 'access_token=' . $this->getAccessToken(true);
109 | }, $url);
110 | $result = $this->parseHttpRequest($callable, $url, $postOptions, false); // 仅重新获取一次,否则容易死循环
111 | }
112 | break;
113 | }
114 | }
115 | return $result;
116 | }
117 |
118 | /**
119 | * 解析微信服务器请求的xml数据, 如果是加密数据直接自动解密
120 | * @param string $xml 微信请求的XML信息主体, 默认取$_GET数据
121 | * @param string $messageSignature 加密签名, 默认取$_GET数据
122 | * @param string $timestamp 加密时间戳, 默认取$_GET数据
123 | * @param string $nonce 加密随机串, 默认取$_GET数据
124 | * @param string $encryptType 加密类型, 默认取$_GET数据
125 | * @return array
126 | */
127 | public function parseRequestXml($xml = null, $messageSignature = null, $timestamp = null , $nonce = null, $encryptType = null)
128 | {
129 | $xml === null && $xml = Yii::$app->request->getRawBody();
130 | $return = [];
131 | if (!empty($xml)) {
132 | $messageSignature === null && isset($_GET['msg_signature']) && $messageSignature = $_GET['msg_signature'];
133 | $encryptType === null && isset($_GET['encrypt_type']) && $encryptType = $_GET['encrypt_type'];
134 | if ($messageSignature !== null && $encryptType == 'aes') { // 自动解密
135 | $timestamp === null && isset($_GET['timestamp']) && $timestamp = $_GET['timestamp'];
136 | $nonce === null && isset($_GET['nonce']) && $nonce = $_GET['nonce'];
137 | $xml = $this->decryptXml($xml, $messageSignature, $timestamp, $nonce);
138 | if ($xml === false) {
139 | return $return;
140 | }
141 | }
142 | $return = (array)simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
143 | }
144 | return $return;
145 | }
146 |
147 | /**
148 | * 创建消息加密类
149 | * @return object
150 | */
151 | protected function createMessageCrypt()
152 | {
153 | return Yii::createObject(MessageCrypt::className(), [$this->token, $this->encodingAesKey, $this->appId]);
154 | }
155 |
156 | /* =================== 基础接口 =================== */
157 |
158 | /**
159 | * access token获取
160 | */
161 | const WECHAT_ACCESS_TOKEN_PREFIX = '/cgi-bin/token';
162 | /**
163 | * 请求服务器access_token
164 | * @param string $grantType
165 | * @return array|bool
166 | */
167 | protected function requestAccessToken($grantType = 'client_credential')
168 | {
169 | $result = $this->httpGet(self::WECHAT_ACCESS_TOKEN_PREFIX, [
170 | 'appid' => $this->appId,
171 | 'secret' => $this->appSecret,
172 | 'grant_type' => $grantType
173 | ]);
174 | return isset($result['access_token']) ? $result : false;
175 | }
176 |
177 | /**
178 | * 获取微信服务器IP地址
179 | */
180 | const WECHAT_IP_PREFIX = '/cgi-bin/getcallbackip';
181 | /**
182 | * 获取微信服务器IP地址
183 | * @return array|bool
184 | * @throws \yii\web\HttpException
185 | */
186 | public function getIp()
187 | {
188 | $result = $this->httpGet(self::WECHAT_IP_PREFIX, [
189 | 'access_token' => $this->getAccessToken()
190 | ]);
191 | return isset($result['ip_list']) ? $result['ip_list'] : false;
192 | }
193 |
194 | /* =================== 接收消息 =================== */
195 |
196 | /**
197 | * 微信服务器请求签名检测
198 | * @param string $signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
199 | * @param string $timestamp 时间戳
200 | * @param string $nonce 随机数
201 | * @return bool
202 | */
203 | public function checkSignature($signature = null, $timestamp = null, $nonce = null)
204 | {
205 | $signature === null && isset($_GET['signature']) && $signature = $_GET['signature'];
206 | $timestamp === null && isset($_GET['timestamp']) && $timestamp = $_GET['timestamp'];
207 | $nonce === null && isset($_GET['nonce']) && $nonce = $_GET['nonce'];
208 | $tmpArr = [$this->token, $timestamp, $nonce];
209 | sort($tmpArr, SORT_STRING);
210 | $tmpStr = implode($tmpArr);
211 | return sha1($tmpStr) == $signature;
212 | }
213 |
214 | /* =================== 发送消息 =================== */
215 |
216 | // 多客服部分 @see self::getCustomService()
217 |
218 | /**
219 | * 发送客服消息
220 | */
221 | const WECHAT_CUSTOM_MESSAGE_SEND_PREFIX = '/cgi-bin/message/custom/send';
222 | /**
223 | * 发送客服消息
224 | * @param array $data
225 | * @return bool
226 | * @throws \yii\web\HttpException
227 | */
228 | public function sendMessage(array $data)
229 | {
230 | $result = $this->httpRaw(self::WECHAT_CUSTOM_MESSAGE_SEND_PREFIX, $data, [
231 | 'access_token' => $this->getAccessToken()
232 | ]);
233 | return isset($result['errmsg']) && $result['errmsg'] == 'ok';
234 | }
235 |
236 | /**
237 | * 消息上传
238 | */
239 | const WECHAT_ARTICLES_UPLOAD_PREFIX = '/cgi-bin/media/uploadnews';
240 | /**
241 | * 上传图文消息素材【订阅号与服务号认证后均可用】
242 | * @param array $articles
243 | * ~~~
244 | * $articles = [
245 | * [
246 | * 'thumb_media_id' => 'qI6_Ze_6PtV7svjolgs-rN6stStuHIjs9_DidOHaj0Q-mwvBelOXCFZiq2OsIU-p',
247 | * 'author' => 'xxx',
248 | * 'title' => 'Happy Day',
249 | * 'content_source_url' => 'www.qq.com',
250 | * 'content' => 'content',
251 | * 'digest' => 'digest',
252 | * 'show_cover_pic' => '1'
253 | * ]
254 | * ...
255 | * ];
256 | *
257 | * ~~~
258 | * @return array|bool
259 | */
260 | public function uploadArticles(array $articles)
261 | {
262 | $result = $this->httpRaw(self::WECHAT_ARTICLES_UPLOAD_PREFIX, [
263 | 'articles' => $articles
264 | ], [
265 | 'access_token' => $this->getAccessToken()
266 | ]);
267 | return isset($result['media_id']) ? $result : false;
268 | }
269 |
270 | /**
271 | * 上传视频
272 | */
273 | const WECHAT_VIDEO_UPLOAD_URL = 'https://file.api.weixin.qq.com/cgi-bin/media/uploadvideo';
274 | /**
275 | * 上传视频
276 | * @param $videoPath
277 | * @return bool|mixed
278 | * @throws \yii\web\HttpException
279 | */
280 | public function uploadVideo($videoPath)
281 | {
282 | $result = $this->httpPost(self::WECHAT_VIDEO_UPLOAD_URL, [
283 | 'media' => $this->uploadFile($videoPath)
284 | ], [
285 | 'access_token' => $this->getAccessToken()
286 | ]);
287 | return isset($result['media_id']) ? $result : false;
288 | }
289 |
290 | /**
291 | * 高级群发接口
292 | */
293 | const WECHAT_SEND_ALL_PREFIX = '/cgi-bin/message/mass/sendall';
294 | /**
295 | * 高级群发接口
296 | * @return bool
297 | * @throws \yii\web\HttpException
298 | */
299 | public function sendAll(array $data)
300 | {
301 | $result = $this->httpRaw(self::WECHAT_SEND_ALL_PREFIX, $data, [
302 | 'access_token' => $this->getAccessToken()
303 | ]);
304 | return isset($result['errcode']) && !$result['errcode'];
305 | }
306 |
307 | /**
308 | * 删除群发【订阅号与服务号认证后均可用】
309 | */
310 | const WECHAT_SENDED_ALL_DELETE_PREFIX = '/cgi-bin/message/mass/delete';
311 | /**
312 | * 删除群发【订阅号与服务号认证后均可用】
313 | * @param $messageId
314 | * @return bool
315 | * @throws \yii\web\HttpException
316 | */
317 | public function deleteSendedAll($messageId)
318 | {
319 | $result = $this->httpRaw(self::WECHAT_SENDED_ALL_DELETE_PREFIX, [
320 | 'msgid' => $messageId
321 | ], [
322 | 'access_token' => $this->getAccessToken()
323 | ]);
324 | return isset($result['errmsg']) && $result['errmsg'] == 'ok';
325 | }
326 |
327 | /**
328 | * 群发预览接口【订阅号与服务号认证后均可用】
329 | */
330 | const WECHAT_SEND_ALL_PREVIEW_PREFIX = '/cgi-bin/message/mass/preview';
331 | /**
332 | * 群发预览接口【订阅号与服务号认证后均可用】
333 | * @param array $data
334 | * @return bool
335 | * @throws \yii\web\HttpException
336 | */
337 | public function previewSendAll(array $data)
338 | {
339 | $result = $this->httpRaw(self::WECHAT_SEND_ALL_PREVIEW_PREFIX, $data, [
340 | 'access_token' => $this->getAccessToken()
341 | ]);
342 | return isset($result['errcode']) && !$result['errcode'];
343 | }
344 |
345 | /**
346 | * 查询群发消息发送状态【订阅号与服务号认证后均可用】
347 | */
348 | const WECHAT_SEND_ALL_STATUS = '/cgi-bin/message/mass/get';
349 | /**
350 | * 查询群发消息发送状态【订阅号与服务号认证后均可用】
351 | * @param $messageId
352 | * @return bool|mixed
353 | * @throws \yii\web\HttpException
354 | */
355 | public function getSendAllStatus($messageId)
356 | {
357 | $result = $this->httpRaw(self::WECHAT_SEND_ALL_STATUS, [
358 | 'msgid' => $messageId
359 | ], [
360 | 'access_token' => $this->getAccessToken()
361 | ]);
362 | return isset($result['msg_status']) ? $result : false;
363 | }
364 |
365 | /**
366 | * 设置所属行业
367 | */
368 | const WECHAT_TEMPLATE_INDUSTRY_SET_PREFIX = '/cgi-bin/template/api_set_industry';
369 | /**
370 | * 设置所属行业
371 | * @param array $data
372 | * @return bool
373 | * @throws \yii\web\HttpException
374 | */
375 | public function setTemplateIndustry(array $data)
376 | {
377 | $result = $this->httpRaw(self::WECHAT_TEMPLATE_INDUSTRY_SET_PREFIX, $data, [
378 | 'access_token' => $this->getAccessToken()
379 | ]);
380 | return isset($result['errcode']) && !$result['errcode'];
381 | }
382 |
383 | /**
384 | *获得模板ID
385 | */
386 | const WECHAT_TEMPLATE_ID_GET_PREFIX = '/cgi-bin/template/api_add_template';
387 | /**
388 | * 获得模板ID
389 | * @param $shortId
390 | * @return bool
391 | * @throws \yii\web\HttpException
392 | */
393 | public function getTemplateId($shortId)
394 | {
395 | $result = $this->httpRaw(self::WECHAT_TEMPLATE_ID_GET_PREFIX, [
396 | 'template_id_short' => $shortId
397 | ], [
398 | 'access_token' => $this->getAccessToken()
399 | ]);
400 | return isset($result['template_id']) ? $result['template_id'] : false;
401 | }
402 |
403 | /**
404 | * 发送模板消息
405 | */
406 | const WECHAT_TEMPLATE_MESSAGE_SEND_PREFIX = '/cgi-bin/message/template/send';
407 | /**
408 | * 发送模板消息
409 | * @param array $data 模板需要的数据
410 | * @return int|bool
411 | */
412 | public function sendTemplateMessage(array $data)
413 | {
414 | $result = $this->httpRaw(self::WECHAT_TEMPLATE_MESSAGE_SEND_PREFIX, array_merge([
415 | 'url' => null,
416 | 'topcolor' => '#FF0000'
417 | ], $data), [
418 | 'access_token' => $this->getAccessToken()
419 | ]);
420 | return isset($result['msgid']) ? $result['msgid'] : false;
421 | }
422 |
423 | /**
424 | * 获取自动回复规则
425 | */
426 | const WECHAT_AUTO_REPLY_INFO_GET_PREFIX = '/cgi-bin/get_current_autoreply_info';
427 | /**
428 | * 获取自动回复规则
429 | * @return bool|mixed
430 | * @throws \yii\web\HttpException
431 | */
432 | public function getAutoReplyInfo()
433 | {
434 | $result = $this->httpGet(self::WECHAT_AUTO_REPLY_INFO_GET_PREFIX, [
435 | 'access_token' => $this->getAccessToken()
436 | ]);
437 | return !array_key_exists('errcode', $result) ? $result : false;
438 | }
439 |
440 | /* =================== 素材管理 =================== */
441 |
442 | /**
443 | * 新增临时素材(上传临时多媒体文件)
444 | */
445 | const WECHAT_MEDIA_UPLOAD_PREFIX = '/cgi-bin/media/upload';
446 | /**
447 | * 新增临时素材(上传临时多媒体文件)
448 | * @param $mediaPath
449 | * @param $type
450 | * @return bool|mixed
451 | * @throws \yii\web\HttpException
452 | */
453 | public function uploadMedia($mediaPath, $type)
454 | {
455 | $result = $this->httpPost(self::WECHAT_MEDIA_UPLOAD_PREFIX, [
456 | 'media' => $this->uploadFile($mediaPath)
457 | ], [
458 | 'access_token' => $this->getAccessToken(),
459 | 'type' => $type
460 | ]);
461 | return isset($result['media_id']) ? $result : false;
462 | }
463 |
464 | /**
465 | * 获取临时素材(下载多媒体文件)
466 | */
467 | const WECHAT_MEDIA_GET_PREFIX = '/cgi-bin/media/get';
468 | /**
469 | * 获取临时素材(下载多媒体文件)
470 | * @param $mediaId
471 | * @return bool|string
472 | * @throws \yii\web\HttpException
473 | */
474 | public function getMedia($mediaId)
475 | {
476 | $result = $this->httpGet(self::WECHAT_MEDIA_GET_PREFIX, [
477 | 'access_token' => $this->getAccessToken(),
478 | 'media_id' => $mediaId
479 | ]);
480 | return is_string($result) ? $result : false;
481 | }
482 |
483 | /**
484 | * 新增永久图文素材
485 | */
486 | const WECHAT_NEWS_MATERIAL_ADD_PREFIX = '/cgi-bin/material/add_news';
487 | /**
488 | * 新增永久图文素材
489 | * @param array $articles
490 | * @return string|bool
491 | * @throws \yii\web\HttpException
492 | */
493 | public function addNewsMaterial(array $articles)
494 | {
495 | $result = $this->httpRaw(self::WECHAT_NEWS_MATERIAL_ADD_PREFIX, [
496 | 'articles' => $articles
497 | ], [
498 | 'access_token' => $this->getAccessToken()
499 | ]);
500 | return isset($result['media_id']) ? $result['media_id'] : false;
501 | }
502 |
503 | /**
504 | * 新增其他类型永久素材
505 | */
506 | const WECHAT_MATERIAL_ADD_PREFIX = '/cgi-bin/material/add_material';
507 | /**
508 | * 新增其他类型永久素材
509 | * @param string $mediaPath
510 | * @param string $type
511 | * @param array $data 视频素材需要description
512 | * @return bool|mixed
513 | * @throws \yii\web\HttpException
514 | */
515 | public function addMaterial($mediaPath, $type, $data = [])
516 | {
517 | $result = $this->httpPost(self::WECHAT_MATERIAL_ADD_PREFIX, array_merge($data, [
518 | 'media' => $this->uploadFile($mediaPath)
519 | ]), [
520 | 'access_token' => $this->getAccessToken(),
521 | 'type' => $type
522 | ]);
523 | return isset($result['media_id']) ? $result : false;
524 | }
525 |
526 | /**
527 | * 获取永久素材
528 | */
529 | const WECHAT_MATERIAL_GET_PREFIX = '/cgi-bin/material/get_material';
530 | /**
531 | * 获取永久素材
532 | * @param $mediaId
533 | * @return bool|string
534 | * @throws \yii\web\HttpException
535 | */
536 | public function getMaterial($mediaId)
537 | {
538 | $result = $this->httpGet(self::WECHAT_MATERIAL_GET_PREFIX, [
539 | 'access_token' => $this->getAccessToken(),
540 | 'media_id' => $mediaId
541 | ]);
542 | return !array_key_exists('errcode', $result) ? $result : false;
543 | }
544 |
545 | /**
546 | * 删除永久素材
547 | */
548 | const WECHAT_MATERIAL_DELETE_PREFIX = '/cgi-bin/material/del_material';
549 | /**
550 | * 删除永久素材
551 | * @param $mediaId
552 | * @return bool
553 | * @throws \yii\web\HttpException
554 | */
555 | public function deleteMaterial($mediaId)
556 | {
557 | $result = $this->httpRaw(self::WECHAT_MATERIAL_DELETE_PREFIX, [
558 | 'media_id' => $mediaId
559 | ], [
560 | 'access_token' => $this->getAccessToken()
561 | ]);
562 | return isset($result['errcode']) && !$result['errcode'];
563 | }
564 |
565 | /**
566 | * 修改永久图文素材
567 | */
568 | const WECHAT_NEWS_MATERIAL_UPDATE_PREFIX = '/cgi-bin/material/update_news';
569 | /**
570 | * 修改永久图文素材
571 | * @param array $data
572 | * @return bool
573 | * @throws \yii\web\HttpException
574 | */
575 | public function updateNewsMaterial(array $data)
576 | {
577 | $result = $this->httpRaw(self::WECHAT_MATERIAL_DELETE_PREFIX, $data, [
578 | 'access_token' => $this->getAccessToken()
579 | ]);
580 | return isset($result['errcode']) && !$result['errcode'];
581 | }
582 |
583 | /**
584 | * 获取素材总数
585 | */
586 | const WECHAT_MATERIAL_COUNT_GET_PREFIX = '/cgi-bin/material/get_materialcount';
587 | /**
588 | * 获取素材总数
589 | * @return bool|mixed
590 | * @throws \yii\web\HttpException
591 | */
592 | public function getMaterialCount()
593 | {
594 | $result = $this->httpGet(self::WECHAT_MATERIAL_COUNT_GET_PREFIX, [
595 | 'access_token' => $this->getAccessToken()
596 | ]);
597 | return !array_key_exists('errorcode', $result) ? $result : false;
598 | }
599 |
600 | /**
601 | * 获取素材列表
602 | */
603 | const WECHAT_MATERIAL_LIST_GET_PREFIX = '/cgi-bin/material/batchget_material';
604 | /**
605 | * 获取素材列表
606 | * @param $data
607 | * @return bool|mixed
608 | * @throws \yii\web\HttpException
609 | */
610 | public function getMaterialList($data)
611 | {
612 | $result = $this->httpRaw(self::WECHAT_MATERIAL_LIST_GET_PREFIX, $data, [
613 | 'access_token' => $this->getAccessToken()
614 | ]);
615 | return !isset($result['errodcode']) ? $result : false;
616 | }
617 |
618 | /* =================== 用户管理 =================== */
619 |
620 | /**
621 | * 创建分组
622 | */
623 | const WECHAT_GROUP_CREATE_PREFIX = '/cgi-bin/groups/create';
624 | /**
625 | * 创建分组
626 | * @param $group
627 | * @return bool
628 | * @throws \yii\web\HttpException
629 | */
630 | public function createGroup($group)
631 | {
632 | $result = $this->httpRaw(self::WECHAT_GROUP_CREATE_PREFIX, [
633 | 'group' => $group
634 | ], [
635 | 'access_token' => $this->getAccessToken()
636 | ]);
637 | return isset($result['group']) ? $result['group'] : false;
638 | }
639 |
640 | /**
641 | * 查询所有分组
642 | */
643 | const WECHAT_GROUP_LIST_GET_PREFIX = '/cgi-bin/groups/get';
644 | /**
645 | * 查询所有分组
646 | * @return bool
647 | * @throws \yii\web\HttpException
648 | */
649 | public function getGroupList()
650 | {
651 | $result = $this->httpGet(self::WECHAT_GROUP_LIST_GET_PREFIX, [
652 | 'access_token' => $this->getAccessToken()
653 | ]);
654 | return isset($result['groups']) ? $result['groups'] : false;
655 | }
656 |
657 | /**
658 | * 查询用户所在分组
659 | */
660 | const WECHAT_USER_GROUP_ID_GET_PREFIX = '/cgi-bin/groups/getid';
661 | /**
662 | * 查询用户所在分组
663 | * @param $openId
664 | * @return bool
665 | * @throws \yii\web\HttpException
666 | */
667 | public function getUserGroupId($openId)
668 | {
669 | $result = $this->httpRaw(self::WECHAT_USER_GROUP_ID_GET_PREFIX, [
670 | 'openid' => $openId
671 | ], [
672 | 'access_token' => $this->getAccessToken()
673 | ]);
674 | return isset($result['groupid']) ? $result['groupid'] : false;
675 | }
676 |
677 | /**
678 | * 修改分组名
679 | */
680 | const WECHAT_GROUP_UPDATE_PREFIX = '/cgi-bin/groups/update';
681 | /**
682 | * 修改分组名
683 | * @param array $group
684 | * @return bool
685 | * @throws \yii\web\HttpException
686 | */
687 | public function updateGroup(array $group)
688 | {
689 | $result = $this->httpRaw(self::WECHAT_GROUP_UPDATE_PREFIX, [
690 | 'group' => $group
691 | ], [
692 | 'access_token' => $this->getAccessToken()
693 | ]);
694 | return isset($result['errmsg']) && $result['errmsg'] == 'ok';
695 | }
696 |
697 | /**
698 | * 移动用户分组
699 | */
700 | const WECHAT_USER_GROUP_UPDATE_PREFIX = '/cgi-bin/groups/members/update';
701 | /**
702 | * 移动用户分组
703 | * @param array $data
704 | * @return bool
705 | * @throws \yii\web\HttpException
706 | */
707 | public function updateUserGroup(array $data)
708 | {
709 | $result = $this->httpRaw(self::WECHAT_MEMBER_GROUP_UPDATE_PREFIX, $data, [
710 | 'access_token' => $this->getAccessToken()
711 | ]);
712 | return isset($result['errmsg']) && $result['errmsg'] == 'ok';
713 | }
714 |
715 | /**
716 | * 批量移动用户分组
717 | */
718 | const WECHAT_USERS_GROUP_UPDATE_PREFIX = '/cgi-bin/groups/members/batchupdate';
719 | /**
720 | * 批量移动用户分组
721 | * @param array $data
722 | * @return bool
723 | * @throws \yii\web\HttpException
724 | */
725 | public function updateUsersGroup(array $data)
726 | {
727 | $result = $this->httpRaw(self::WECHAT_USERS_GROUP_UPDATE_PREFIX, $data, [
728 | 'access_token' => $this->getAccessToken()
729 | ]);
730 | return isset($result['errmsg']) && $result['errmsg'] == 'ok';
731 | }
732 |
733 | /**
734 | * 删除分组
735 | */
736 | const WECHAT_GROUP_DELETE_PREFIX = '/cgi-bin/groups/delete';
737 | /**
738 | * 删除分组
739 | * @param $groupId
740 | * @return bool
741 | * @throws \yii\web\HttpException
742 | */
743 | public function deletGroup($groupId)
744 | {
745 | $result = $this->httpRaw(self::WECHAT_GROUP_DELETE_PREFIX, [
746 | 'group' => [
747 | 'id' => $groupId
748 | ]
749 | ], [
750 | 'access_token' => $this->getAccessToken()
751 | ]);
752 | return isset($result['errmsg']) && $result['errmsg'] == 'ok';
753 | }
754 |
755 | /**
756 | * 设置用户备注名
757 | */
758 | const WEHCAT_USER_MARK_UPDATE = '/cgi-bin/user/info/updateremark';
759 | /**
760 | * 设置用户备注名
761 | * @param array $data
762 | * @return bool
763 | * @throws \yii\web\HttpException
764 | */
765 | public function updateUserMark(array $data)
766 | {
767 | $result = $this->httpRaw(self::WEHCAT_USER_MARK_UPDATE, $data, [
768 | 'access_token' => $this->getAccessToken()
769 | ]);
770 | return isset($result['errmsg']) && $result['errmsg'] == 'ok';
771 | }
772 |
773 | /**
774 | * 获取用户基本信息(UnionID机制)
775 | */
776 | const WECHAT_USER_INFO_GET = '/cgi-bin/user/info';
777 | /**
778 | * 获取用户基本信息(UnionID机制)
779 | * @param $openId
780 | * @param string $lang
781 | * @return bool|mixed
782 | * @throws \yii\web\HttpException
783 | */
784 | public function getUserInfo($openId, $lang = 'zh_CN')
785 | {
786 | $result = $this->httpGet(self::WECHAT_USER_INFO_GET, [
787 | 'access_token' => $this->getAccessToken(),
788 | 'openid' => $openId,
789 | 'lang' => $lang
790 | ]);
791 | return !array_key_exists('errcode', $result) ? $result : false;
792 | }
793 |
794 | /**
795 | * 获取用户列表
796 | */
797 | const WECHAT_USER_LIST_GET_PREFIX = '/cgi-bin/user/get';
798 | /**
799 | * 获取用户列表
800 | * @param $nextOpenId
801 | * @return bool|mixed
802 | * @throws \yii\web\HttpException
803 | */
804 | public function getUserList($nextOpenId)
805 | {
806 | $result = $this->httpGet(self::WECHAT_USER_LIST_GET_PREFIX, [
807 | 'access_token' => $this->getAccessToken(),
808 | 'next_openid' => $nextOpenId,
809 | ]);
810 | return array_key_exists('errcode', $result) ? $result : false;
811 | }
812 |
813 | /* ==== 网页授权 ===== */
814 |
815 | /**
816 | * 用户同意授权,获取code
817 | */
818 | const WECHAT_OAUTH2_AUTHORIZE_URL = 'https://open.weixin.qq.com/connect/oauth2/authorize';
819 | /**
820 | * 用户同意授权,获取code:第一步
821 | * 通过此函数生成授权url
822 | * @param $redirectUrl 授权后重定向的回调链接地址,请使用urlencode对链接进行处理
823 | * @param string $state 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值
824 | * @param string $scope 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),
825 | * snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息)
826 | * @return string
827 | */
828 | public function getOauth2AuthorizeUrl($redirectUrl, $state = 'authorize', $scope = 'snsapi_base')
829 | {
830 | return $this->httpBuildQuery(self::WECHAT_OAUTH2_AUTHORIZE_URL, [
831 | 'appid' => $this->appId,
832 | 'redirect_uri' => $redirectUrl,
833 | 'response_type' => 'code',
834 | 'scope' => $scope,
835 | 'state' => $state,
836 | ]) . '#wechat_redirect';
837 | }
838 |
839 | /**
840 | * 通过code换取网页授权access_token
841 | */
842 | const WECHAT_OAUTH2_ACCESS_TOKEN_PREFIX = '/sns/oauth2/access_token';
843 | /**
844 | * 通过code换取网页授权access_token:第二步
845 | * 通过跳转到getOauth2AuthorizeUrl返回的授权code获取用户资料 (该函数和getAccessToken函数作用不同.请参考文档)
846 | * @param $code
847 | * @param string $grantType
848 | * @return array
849 | */
850 | public function getOauth2AccessToken($code, $grantType = 'authorization_code')
851 | {
852 | $result = $this->httpGet(self::WECHAT_OAUTH2_ACCESS_TOKEN_PREFIX, [
853 | 'appid' => $this->appId,
854 | 'secret' => $this->appSecret,
855 | 'code' => $code,
856 | 'grant_type' => $grantType
857 | ]);
858 | return !array_key_exists('errcode', $result) ? $result : false;
859 | }
860 |
861 | /**
862 | * 刷新access_token
863 | */
864 | const WECHAT_OAUTH2_ACCESS_TOKEN_REFRESH_PREFIX = '/sns/oauth2/refresh_token';
865 | /**
866 | * 刷新access_token:第三步(非必须)
867 | * 由于access_token拥有较短的有效期,当access_token超时后,可以使用refresh_token进行刷新
868 | * refresh_token拥有较长的有效期(7天、30天、60天、90天),当refresh_token失效的后,需要用户重新授权。
869 | * @param $refreshToken
870 | * @param string $grantType
871 | * @return array|bool
872 | */
873 | public function refreshOauth2AccessToken($refreshToken, $grantType = 'refresh_token')
874 | {
875 | $result = $this->httpGet(self::WECHAT_OAUTH2_ACCESS_TOKEN_REFRESH_PREFIX, [
876 | 'appid' => $this->appId,
877 | 'grant_type' => $grantType,
878 | 'refresh_token' => $refreshToken
879 | ]);
880 | return !array_key_exists('errcode', $result) ? $result : false;
881 | }
882 |
883 | /**
884 | * 拉取用户信息(需scope为 snsapi_userinfo)
885 | */
886 | const WEHCAT_SNS_USER_INFO_PREFIX = '/sns/userinfo';
887 | /**
888 | * 拉取用户信息(需scope为 snsapi_userinfo):第四步
889 | * @param $openId
890 | * @param string $oauth2AccessToken
891 | * @param string $lang
892 | * @return array|bool
893 | */
894 | public function getSnsUserInfo($openId, $oauth2AccessToken, $lang = 'zh_CN')
895 | {
896 | $result = $this->httpGet(self::WEHCAT_SNS_USER_INFO_PREFIX, [
897 | 'access_token' => $oauth2AccessToken,
898 | 'openid' => $openId,
899 | 'lang' => $lang
900 | ]);
901 | return !array_key_exists('errcode', $result) ? $result : false;
902 | }
903 |
904 | /**
905 | * 检验授权凭证(access_token)是否有效
906 | */
907 | const WECHAT_SNS_AUTH_PREFIX = '/sns/auth';
908 | /**
909 | * 检验授权凭证(access_token)是否有效
910 | * @param $accessToken
911 | * @param $openId
912 | * @return bool
913 | */
914 | public function checkOauth2AccessToken($accessToken, $openId)
915 | {
916 | $result = $this->httpGet(self::WECHAT_SNS_AUTH_PREFIX, [
917 | 'access_token' => $accessToken,
918 | 'openid' => $openId
919 | ]);
920 | return isset($result['errmsg']) && $result['errmsg'] == 'ok';
921 | }
922 |
923 | /* =================== 自定义管理 =================== */
924 |
925 | /**
926 | * 自定义菜单创建接口
927 | */
928 | const WECHAT_MENU_CREATE_PREFIX = '/cgi-bin/menu/create';
929 | /**
930 | * 自定义菜单创建接口(创建菜单)
931 | * @param array $buttons 菜单结构字符串
932 | * ~~~
933 | * $this->createMenu([
934 | * [
935 | * 'type' => 'click',
936 | * 'name' => '今日歌曲',
937 | * 'key' => 'V1001_TODAY_MUSIC'
938 | * ],
939 | * [
940 | * 'type' => 'view',
941 | * 'name' => '搜索',
942 | * 'url' => 'http://www.soso.com'
943 | * ]
944 | * ...
945 | * ]);
946 | * ~~~
947 | * @return bool
948 | */
949 | public function createMenu(array $buttons)
950 | {
951 | $result = $this->httpRaw(self::WECHAT_MENU_CREATE_PREFIX, [
952 | 'button' => $buttons
953 | ], [
954 | 'access_token' => $this->getAccessToken()
955 | ]);
956 | return isset($result['errmsg']) && $result['errmsg'] == 'ok';
957 | }
958 |
959 | /**
960 | * 自定义菜单查询
961 | */
962 | const WECHAT_MENU_GET_PREFIX = '/cgi-bin/menu/get';
963 | /**
964 | * 自定义菜单查询接口(获取菜单)
965 | * @return bool
966 | */
967 | public function getMenu()
968 | {
969 | $result = $this->httpGet(self::WECHAT_MENU_GET_PREFIX, [
970 | 'access_token' => $this->getAccessToken()
971 | ]);
972 | return isset($result['menu']['button']) ? $result['menu']['button'] : false;
973 | }
974 |
975 | /**
976 | * 自定义菜单删除接口(删除菜单)
977 | */
978 | const WECHAT_MENU_DELETE_PREFIX = '/cgi-bin/menu/delete';
979 | /**
980 | * 自定义菜单删除接口(删除菜单)
981 | * @return bool
982 | */
983 | public function deleteMenu()
984 | {
985 | $result = $this->httpGet(self::WECHAT_MENU_DELETE_PREFIX, [
986 | 'access_token' => $this->getAccessToken()
987 | ]);
988 | return isset($result['errmsg']) && $result['errmsg'] == 'ok';
989 | }
990 |
991 | /**
992 | * 获取自定义菜单配置接口
993 | */
994 | const WECHAT_MENU_INFO_GET_PREFIX = '/cgi-bin/get_current_selfmenu_info';
995 | /**
996 | * 获取自定义菜单配置接口
997 | * @return bool|mixed
998 | * @throws \yii\web\HttpException
999 | */
1000 | public function getMenuInfo()
1001 | {
1002 | $result = $this->httpGet(self::WECHAT_MENU_INFO_GET_PREFIX, [
1003 | 'access_token' => $this->getAccessToken()
1004 | ]);
1005 | return !array_key_exists('errcode', $result) ? $result : false;
1006 | }
1007 |
1008 | /* =================== 账号管理 =================== */
1009 |
1010 | /**
1011 | * 创建二维码ticket
1012 | */
1013 | const WECHAT_QR_CODE_CREATE_PREFIX = '/cgi-bin/qrcode/create';
1014 | /**
1015 | * 创建二维码ticket
1016 | * @param arary $data
1017 | * @return bool|mixed
1018 | * @throws \yii\web\HttpException
1019 | */
1020 | public function createQrCode(array $data)
1021 | {
1022 | $result = $this->httpRaw(self::WECHAT_QR_CODE_CREATE_PREFIX, $data, [
1023 | 'access_token' => $this->getAccessToken()
1024 | ]);
1025 | return !array_key_exists('errcode', $result) ? $result : false;
1026 | }
1027 |
1028 | /**
1029 | * 通过ticket换取二维码
1030 | */
1031 | const WECHAT_QR_CODE_GET_URL = 'https://mp.weixin.qq.com/cgi-bin/showqrcode';
1032 | /**
1033 | * 通过ticket换取二维码
1034 | * ticket正确情况下,http 返回码是200,是一张图片,可以直接展示或者下载。
1035 | * @param $ticket
1036 | * @return string
1037 | */
1038 | public function getQrCode($ticket)
1039 | {
1040 | return $this->httpBuildQuery(self::WECHAT_QR_CODE_GET_URL, ['ticket' => $ticket]);
1041 | }
1042 |
1043 | /**
1044 | * 长链接转短链接接口
1045 | */
1046 | const WECHAT_SHORT_URL_CREATE_PREFIX = '/cgi-bin/shorturl';
1047 | /**
1048 | * 长链接转短链接接口
1049 | * @param $longUrl 需要转换的长链接,支持http://、https://、weixin://wxpay 格式的url
1050 | * @return bool
1051 | */
1052 | public function getShortUrl($longUrl)
1053 | {
1054 | $result = $this->httpRaw(self::WECHAT_SHORT_URL_CREATE_PREFIX, [
1055 | 'action' => 'long2short',
1056 | 'long_url' => $longUrl,
1057 | ], [
1058 | 'access_token' => $this->getAccessToken()
1059 | ]);
1060 | return isset($result['errmsg']) && $result['errmsg'] == 'ok' ? $result['short_url'] : false;
1061 | }
1062 |
1063 | /* =================== 数据统计接口 =================== */
1064 |
1065 | /**
1066 | * @var object
1067 | */
1068 | private $_dataCube;
1069 | /**
1070 | * 数据统计组件
1071 | * @return object
1072 | */
1073 | public function getDataCube()
1074 | {
1075 | if ($this->_dataCube === null) {
1076 | $this->_dataCube = Yii::createObject(DataCube::className(), [$this]);
1077 | }
1078 | return $this->_dataCube;
1079 | }
1080 |
1081 | /* =================== 微信JS-SDK =================== */
1082 |
1083 | /**
1084 | * js api ticket 获取
1085 | */
1086 | const WECHAT_JS_API_TICKET_PREFIX = '/cgi-bin/ticket/getticket';
1087 | /**
1088 | * 请求服务器jsapi_ticket
1089 | * @param string $type
1090 | * @return array|bool
1091 | */
1092 | protected function requestJsApiTicket($type = 'jsapi')
1093 | {
1094 | $result = $this->httpGet(self::WECHAT_JS_API_TICKET_PREFIX, [
1095 | 'access_token' => $this->getAccessToken(),
1096 | 'type' => $type
1097 | ]);
1098 | return isset($result['ticket']) ? $result : false;
1099 | }
1100 |
1101 | /**
1102 | * 生成js 必需的config
1103 | * 只需在视图文件输出JS代码:
1104 | * wx.config(= json_encode($wehcat->jsApiConfig()) ?>); // 默认全权限
1105 | * wx.config(= json_encode($wehcat->jsApiConfig([ // 只允许使用分享到朋友圈功能
1106 | * 'jsApiList' => [
1107 | * 'onMenuShareTimeline'
1108 | * ]
1109 | * ])) ?>);
1110 | * @param array $config
1111 | * @return array
1112 | * @throws HttpException
1113 | */
1114 | public function jsApiConfig(array $config = [])
1115 | {
1116 | $data = [
1117 | 'jsapi_ticket' => $this->getJsApiTicket(),
1118 | 'noncestr' => Yii::$app->security->generateRandomString(16),
1119 | 'timestamp' => $_SERVER['REQUEST_TIME'],
1120 | 'url' => explode('#', Yii::$app->request->getAbsoluteUrl())[0]
1121 | ];
1122 | return array_merge([
1123 | 'debug' => YII_DEBUG,
1124 | 'appId' => $this->appId,
1125 | 'timestamp' => $data['timestamp'],
1126 | 'nonceStr' => $data['noncestr'],
1127 | 'signature' => sha1(urldecode(http_build_query($data))),
1128 | 'jsApiList' => [
1129 | 'checkJsApi',
1130 | 'onMenuShareTimeline',
1131 | 'onMenuShareAppMessage',
1132 | 'onMenuShareQQ',
1133 | 'onMenuShareWeibo',
1134 | 'hideMenuItems',
1135 | 'showMenuItems',
1136 | 'hideAllNonBaseMenuItem',
1137 | 'showAllNonBaseMenuItem',
1138 | 'translateVoice',
1139 | 'startRecord',
1140 | 'stopRecord',
1141 | 'onRecordEnd',
1142 | 'playVoice',
1143 | 'pauseVoice',
1144 | 'stopVoice',
1145 | 'uploadVoice',
1146 | 'downloadVoice',
1147 | 'chooseImage',
1148 | 'previewImage',
1149 | 'uploadImage',
1150 | 'downloadImage',
1151 | 'getNetworkType',
1152 | 'openLocation',
1153 | 'getLocation',
1154 | 'hideOptionMenu',
1155 | 'showOptionMenu',
1156 | 'closeWindow',
1157 | 'scanQRCode',
1158 | 'chooseWXPay',
1159 | 'openProductSpecificView',
1160 | 'addCard',
1161 | 'chooseCard',
1162 | 'openCard'
1163 | ]
1164 | ], $config);
1165 | }
1166 |
1167 | /* =================== 微信小店接口(基于手册V1.15) =================== */
1168 |
1169 | /**
1170 | * @var object
1171 | */
1172 | private $_merchant;
1173 | /**
1174 | * 微信小店组件
1175 | * @return object
1176 | */
1177 | public function getMerchant()
1178 | {
1179 | if ($this->_merchant === null) {
1180 | $this->_merchant = Yii::createObject(Merchant::className(), [$this]);
1181 | }
1182 | return $this->_merchant;
1183 | }
1184 |
1185 | /* =================== 微信卡卷接口 =================== */
1186 |
1187 | /**
1188 | * @var object
1189 | */
1190 | private $_card;
1191 | /**
1192 | * @return object
1193 | */
1194 | public function getCard()
1195 | {
1196 | if ($this->_card === null) {
1197 | $this->_card = Yii::createObject(Card::className(), [$this]);
1198 | }
1199 | return $this->_card;
1200 | }
1201 |
1202 | /* =================== 微信智能接口 =================== */
1203 |
1204 | /**
1205 | * 语义理解
1206 | */
1207 | const WECHAT_SEMANTIC_SEMPROXY_PREFIX = '/semantic/semproxy/search';
1208 | /**
1209 | * 语义理解
1210 | * @param array $data
1211 | * @return bool|mixed
1212 | * @throws \yii\web\HttpException
1213 | */
1214 | public function searchSemantic(array $data)
1215 | {
1216 | $result = $this->httpRaw(self::WECHAT_SEMANTIC_SEMPROXY_PREFIX, $data, [
1217 | 'access_token' => $this->getAccessToken()
1218 | ]);
1219 | return isset($result['errcode']) && !$result['errcode'] ? $result : false;
1220 | }
1221 |
1222 | /* =================== 设备功能(物联网, 欢迎PR) =================== */
1223 |
1224 | /* =================== 多客服功能(部分功能实现在[发送消息]区域内) =================== */
1225 |
1226 | /**
1227 | * @var object
1228 | */
1229 | private $_customService;
1230 | /**
1231 | * 多客服组件
1232 | * @return object
1233 | */
1234 | public function getCustomService()
1235 | {
1236 | if ($this->_customService === null) {
1237 | $this->_customService = Yii::createObject(CustomService::className(), [$this]);
1238 | }
1239 | return $this->_customService;
1240 | }
1241 |
1242 | /* =================== 摇一摇周边 =================== */
1243 |
1244 | /**
1245 | * @var object
1246 | */
1247 | private $_shakeAround;
1248 | /**
1249 | * 摇一摇组件
1250 | * @return object
1251 | */
1252 | public function getShakeAround()
1253 | {
1254 | if ($this->_shakeAround === null) {
1255 | $this->_shakeAround = Yii::createObject(ShakeAround::className(), [$this]);
1256 | }
1257 | return $this->_shakeAround;
1258 | }
1259 | }
--------------------------------------------------------------------------------
/QyWechat.php:
--------------------------------------------------------------------------------
1 | httpGet(self::WECHAT_ACCESS_TOKEN_PREFIX, [
57 | 'corpid' => $this->corpId,
58 | 'corpsecret' => $this->secret
59 | ]);
60 | return isset($result['access_token']) ? $result : false;
61 | }
62 |
63 | /**
64 | * 获取微信服务器IP地址
65 | */
66 | const WECHAT_IP_PREFIX = '/cgi-bin/getcallbackip';
67 | /**
68 | * 获取微信服务器IP地址
69 | * @return array|bool
70 | * @throws \yii\web\HttpException
71 | */
72 | public function getIp()
73 | {
74 | $result = $this->httpGet(self::WECHAT_IP_PREFIX, [
75 | 'access_token' => $this->getAccessToken()
76 | ]);
77 | return isset($result['ip_list']) ? $result['ip_list'] : false;
78 | }
79 |
80 | /* =================== 管理通讯录 =================== */
81 |
82 | /**
83 | * 二次验证
84 | */
85 | const WECHAT_USER_AUTH_SUCCESS_PREFIX = '/cgi-bin/user/authsucc';
86 | /**
87 | * 二次验证
88 | * @param $userId
89 | * @return bool
90 | * @throws \yii\web\HttpException
91 | */
92 | public function userAuthSuccess($userId)
93 | {
94 | $result = $this->httpGet(self::WECHAT_USER_AUTH_SUCCESS_PREFIX, [
95 | 'access_token' => $this->getAccessToken(),
96 | 'userid' => $userId
97 | ]);
98 | return isset($result['errmsg']) && $result['errmsg'] == 'ok';
99 | }
100 |
101 | /**
102 | * 创建部门
103 | */
104 | const WECHAT_DEPARTMENT_CREATE_PREFIX = '/cgi-bin/department/create';
105 | /**
106 | * 创建部门
107 | * @param array $data
108 | * @return bool
109 | * @throws \yii\web\HttpException
110 | */
111 | public function createDepartment(array $data)
112 | {
113 | $result = $this->httpRaw(self::WECHAT_DEPARTMENT_CREATE_PREFIX, $data, [
114 | 'access_token' => $this->getAccessToken()
115 | ]);
116 | return isset($result['errcode']) && !$result['errcode'] ? $result['id'] : false;
117 | }
118 |
119 | /**
120 | * 创建部门
121 | */
122 | const WECHAT_DEPARTMENT_UPDATE_PREFIX = '/cgi-bin/department/update';
123 | /**
124 | * 创建部门
125 | * @param array $data
126 | * @return bool
127 | * @throws \yii\web\HttpException
128 | */
129 | public function updateDepartment(array $data)
130 | {
131 | $result = $this->httpRaw(self::WECHAT_DEPARTMENT_CREATE_PREFIX, $data, [
132 | 'access_token' => $this->getAccessToken()
133 | ]);
134 | return isset($result['errcode']) && !$result['errcode'];
135 | }
136 |
137 | /**
138 | * 删除部门
139 | */
140 | const WECHAT_DEPARTMENT_DELETE_PREFIX = '/cgi-bin/department/delete';
141 | /**
142 | * 删除部门
143 | * @param $id
144 | * @return bool
145 | * @throws \yii\web\HttpException
146 | */
147 | public function deleteDepartment($id)
148 | {
149 | $result = $this->httpGet(self::WECHAT_DEPARTMENT_DELETE_PREFIX, [
150 | 'access_token' => $this->getAccessToken(),
151 | 'id' => $id
152 | ]);
153 | return isset($result['errcode']) && !$result['errcode'];
154 | }
155 |
156 | /**
157 | * 获取部门列表
158 | */
159 | const WECHAT_DEPARTMENT_LIST = '/cgi-bin/department/list';
160 | /**
161 | * 获取部门列表
162 | * @param null $id 部门id。获取指定部门id下的子部门
163 | * @return bool
164 | * @throws \yii\web\HttpException
165 | */
166 | public function getDepartmentList($id = null)
167 | {
168 | $result = $this->httpGet(self::WECHAT_DEPARTMENT_DELETE_PREFIX, [
169 | 'access_token' => $this->getAccessToken(),
170 | ] + ($id === null ? [] : [
171 | 'id' => $id
172 | ]));
173 | return isset($result['errmsg']) && $result['errmsg'] == 'ok' ? $result['department'] : false;
174 | }
175 |
176 | /**
177 | * 创建成员
178 | */
179 | const WECHAT_USER_CREATE_PREFIX = '/cgi-bin/user/create';
180 | /**
181 | * 创建成员
182 | * @param array $data
183 | * @return bool
184 | * @throws \yii\web\HttpException
185 | */
186 | public function createUser(array $data)
187 | {
188 | $result = $this->httpRaw(self::WECHAT_USER_CREATE_PREFIX, $data, [
189 | 'access_token' => $this->getAccessToken()
190 | ]);
191 | return isset($result['errcode']) && !$result['errcode'];
192 | }
193 |
194 | /**
195 | * 创建成员
196 | */
197 | const WECHAT_USER_UPDATE_PREFIX = '/cgi-bin/user/create';
198 | /**
199 | * 创建成员
200 | * @param array $data
201 | * @return bool
202 | * @throws \yii\web\HttpException
203 | */
204 | public function updateUser(array $data)
205 | {
206 | $result = $this->httpRaw(self::WECHAT_USER_UPDATE_PREFIX, $data, [
207 | 'access_token' => $this->getAccessToken()
208 | ]);
209 | return isset($result['errcode']) && !$result['errcode'];
210 | }
211 |
212 | /**
213 | * 删除成员
214 | */
215 | const WECHAT_USER_DELETE_PREFIX = '/cgi-bin/user/delete';
216 | /**
217 | * 删除成员
218 | * @param $userId
219 | * @return bool
220 | * @throws \yii\web\HttpException
221 | */
222 | public function deleteUser($userId)
223 | {
224 | $result = $this->httpGet(self::WECHAT_USER_DELETE_PREFIX, [
225 | 'access_token' => $this->getAccessToken(),
226 | 'userid' => $userId
227 | ]);
228 | return isset($result['errcode']) && !$result['errcode'];
229 | }
230 |
231 | /**
232 | * 批量删除成员
233 | */
234 | const WECHAT_USER_BATCH_DELETE_PREFIX = '/cgi-bin/user/batchdelete';
235 | /**
236 | * 批量删除成员
237 | * @param array $userIdList
238 | * @return bool
239 | * @throws \yii\web\HttpException
240 | */
241 | public function batchDeleteUser(array $userIdList)
242 | {
243 | $result = $this->httpRaw(self::WECHAT_USER_BATCH_DELETE_PREFIX, [
244 | 'useridlist' => $userIdList
245 | ], [
246 | 'access_token' => $this->getAccessToken()
247 | ]);
248 | return isset($result['errcode']) && !$result['errcode'];
249 | }
250 |
251 | /**
252 | * 获取部门成员(详情)
253 | */
254 | const WECHAT_USER_GET_PREFIX = '/cgi-bin/user/get';
255 | /**
256 | * 获取部门成员(详情)
257 | * @param $userId
258 | * @return bool|mixed
259 | * @throws \yii\web\HttpException
260 | */
261 | public function getUser($userId)
262 | {
263 | $result = $this->httpGet(self::WECHAT_USER_GET_PREFIX, [
264 | 'access_token' => $this->getAccessToken(),
265 | 'userid' => $userId
266 | ]);
267 | return isset($result['errmsg']) && $result['errmsg'] == 'ok' ? $result : false;
268 | }
269 |
270 | /**
271 | * 获取部门成员
272 | */
273 | const WECHAT_DEPARTMENT_USER_LIST_GET_PREFIX = '/cgi-bin/user/simplelist';
274 | /**
275 | * 获取部门成员
276 | * @param $departmentId
277 | * @param int $fetchChild
278 | * @param int $status
279 | * @return bool|mixed
280 | * @throws \yii\web\HttpException
281 | */
282 | public function getDepartmentUserList($departmentId, $fetchChild = 0, $status = 0)
283 | {
284 | $result = $this->httpGet(self::WECHAT_DEPARTMENT_USER_LIST_GET_PREFIX, [
285 | 'access_token' => $this->getAccessToken(),
286 | 'department_id' => $departmentId,
287 | 'fetch_child' => $fetchChild,
288 | 'status' => $status,
289 | ]);
290 | return isset($result['errmsg']) && $result['errmsg'] == 'ok' ? $result['userlist'] : false;
291 | }
292 |
293 | /**
294 | * 获取部门成员(详情)
295 | */
296 | const WECHAT_DEPARTMENT_USERS_INFO_LIST_GET_PREFIX = '/cgi-bin/user/list';
297 | /**
298 | * 获取部门成员(详情)
299 | * @param $departmentId
300 | * @param int $fetchChild
301 | * @param int $status
302 | * @return bool|mixed
303 | * @throws \yii\web\HttpException
304 | */
305 | public function getDepartmentUserInfoList($departmentId, $fetchChild = 0, $status = 0)
306 | {
307 | $result = $this->httpGet(self::WECHAT_DEPARTMENT_USERS_INFO_LIST_GET_PREFIX, [
308 | 'access_token' => $this->getAccessToken(),
309 | 'department_id' => $departmentId,
310 | 'fetch_child' => $fetchChild,
311 | 'status' => $status,
312 | ]);
313 | return isset($result['errmsg']) && $result['errmsg'] == 'ok' ? $result['userlist'] : false;
314 | }
315 |
316 | /**
317 | * 邀请成员关注
318 | */
319 | const WECHAT_USER_INVITE_PREFIX = '/cgi-bin/invite/send';
320 | /**
321 | * 邀请成员关注
322 | * @param $userId
323 | * @return bool
324 | * @throws \yii\web\HttpException
325 | */
326 | public function inviteUser($userId)
327 | {
328 | $result = $this->httpRaw(self::WECHAT_USER_INVITE_PREFIX, [
329 | 'userid' => $userId
330 | ], [
331 | 'access_token' => $this->getAccessToken(),
332 | ]);
333 | return isset($result['errmsg']) && $result['errmsg'] == 'ok' ? $result['type'] : false;
334 | }
335 |
336 | /**
337 | * 创建标签
338 | */
339 | const WECHAT_TAG_CREATE_PREFIX = '/cgi-bin/tag/create';
340 | /**
341 | * 创建标签
342 | * @param $tagName
343 | * @return int|bool
344 | * @throws \yii\web\HttpException
345 | */
346 | public function createTag($tagName)
347 | {
348 | $result = $this->httpRaw(self::WECHAT_TAG_CREATE_PREFIX, [
349 | 'tagname' => $tagName
350 | ], [
351 | 'access_token' => $this->getAccessToken()
352 | ]);
353 | return isset($result['errmsg']) && $result['errmsg'] == 'ok' ? $result['tagid'] : false;
354 | }
355 |
356 | /**
357 | * 更新标签名字
358 | */
359 | const WECHAT_TAG_NAME_UPDATE_PREFIX = '/cgi-bin/tag/update';
360 | /**
361 | * 更新标签名字
362 | * @param $tagId
363 | * @param $tagName
364 | * @return bool
365 | * @throws \yii\web\HttpException
366 | */
367 | public function updateTagName($tagId, $tagName)
368 | {
369 | $result = $this->httpRaw(self::WECHAT_TAG_CREATE_PREFIX, [
370 | 'tagid' => $tagId,
371 | 'tagname' => $tagName
372 | ], [
373 | 'access_token' => $this->getAccessToken()
374 | ]);
375 | return isset($result['errcode']) && !$result['errcode'];
376 | }
377 |
378 | /**
379 | * 删除标签
380 | */
381 | const WECHAT_TAG_DELETE_PREFIX = '/cgi-bin/tag/delete';
382 | /**
383 | * 删除标签
384 | * @param $tagId
385 | * @return bool
386 | * @throws \yii\web\HttpException
387 | */
388 | public function deleteTag($tagId)
389 | {
390 | $result = $this->httpGet(self::WECHAT_TAG_DELETE_PREFIX, [
391 | 'access_token' => $this->getAccessToken(),
392 | 'tagid' => $tagId
393 | ]);
394 | return isset($result['errcode']) && !$result['errcode'];
395 | }
396 |
397 | /**
398 | * 获取标签成员
399 | */
400 | const WECHAT_TAG_USER_LIST_GET_PREFIX = '/cgi-bin/tag/get';
401 | /**
402 | * 获取标签成员
403 | * @param $tagId
404 | * @return bool|mixed
405 | * @throws \yii\web\HttpException
406 | */
407 | public function getTagUserList($tagId)
408 | {
409 | $result = $this->httpGet(self::WECHAT_TAG_USER_LIST_GET_PREFIX, [
410 | 'access_token' => $this->getAccessToken(),
411 | 'tagid' => $tagId
412 | ]);
413 | return isset($result['errmsg']) && $result['errmsg'] == 'ok' ? $result : false;
414 | }
415 |
416 | /**
417 | * 增加标签成员
418 | */
419 | const WECHAT_TAG_USERS_ADD_PREFIX = '/cgi-bin/tag/addtagusers';
420 | /**
421 | * 增加标签成员
422 | * @param array $data
423 | * @return bool
424 | * @throws \yii\web\HttpException
425 | */
426 | public function addTagUsers(array $data)
427 | {
428 | $result = $this->httpRaw(self::WECHAT_TAG_USERS_ADD_PREFIX, $data, [
429 | 'access_token' => $this->getAccessToken(),
430 | ]);
431 | return isset($result['errmsg']) && $result['errmsg'] == 'ok';
432 | }
433 |
434 | /**
435 | * 删除标签成员
436 | */
437 | const WECHAT_TAG_USERS_DELETE_PREFIX = '/cgi-bin/tag/deltagusers';
438 | /**
439 | * 删除标签成员
440 | * @param array $data
441 | * @return bool
442 | * @throws \yii\web\HttpException
443 | */
444 | public function deleteTagUsers(array $data)
445 | {
446 | $result = $this->httpRaw(self::WECHAT_TAG_USERS_DELETE_PREFIX, $data, [
447 | 'access_token' => $this->getAccessToken(),
448 | ]);
449 | return isset($result['errmsg']) && $result['errmsg'] == 'ok';
450 | }
451 |
452 | /**
453 | * 获取标签列表
454 | */
455 | const WECHAT_TAG_LIST_GET_PREFIX = '/cgi-bin/tag/list';
456 | /**
457 | * 获取标签列表
458 | * @return bool|mixed
459 | * @throws \yii\web\HttpException
460 | */
461 | public function getTagList()
462 | {
463 | $result = $this->httpGet(self::WECHAT_TAG_LIST_GET_PREFIX, [
464 | 'access_token' => $this->getAccessToken()
465 | ]);
466 | return isset($result['errmsg']) && $result['errmsg'] == 'ok' ? $result['taglist'] : false;
467 | }
468 |
469 | /**
470 | * 邀请成员关注
471 | */
472 | const WECHAT_USER_BATCH_INVITE_PREFIX = '/cgi-bin/batch/inviteuser';
473 | /**
474 | * 邀请成员关注
475 | * @param array $data
476 | * @return bool
477 | * @throws \yii\web\HttpException
478 | */
479 | public function batchInviteUser(array $data)
480 | {
481 | $result = $this->httpRaw(self::WECHAT_TAG_USERS_DELETE_PREFIX, $data, [
482 | 'access_token' => $this->getAccessToken()
483 | ]);
484 | return isset($result['errmsg']) && $result['errmsg'] == 'ok' ? $result['jobid'] : false;
485 | }
486 |
487 | /**
488 | * 增量更新成员
489 | */
490 | const WECHAT_USER_BATCH_SYNC_PREFIX = '/cgi-bin/batch/syncuser';
491 | /**
492 | * 增量更新成员
493 | * @param array $data
494 | * @return bool
495 | * @throws \yii\web\HttpException
496 | */
497 | public function batchSyncUser(array $data)
498 | {
499 | $result = $this->httpRaw(self::WECHAT_USER_BATCH_SYNC_PREFIX, $data, [
500 | 'access_token' => $this->getAccessToken()
501 | ]);
502 | return isset($result['errmsg']) && $result['errmsg'] == 'ok' ? $result['jobid'] : false;
503 | }
504 |
505 | /**
506 | * 全量覆盖成员
507 | */
508 | const WECHAT_USER_BATCH_REPLACE_PREFIX = '/cgi-bin/batch/replaceuser';
509 | /**
510 | * 全量覆盖成员
511 | * @param array $data
512 | * @return bool
513 | * @throws \yii\web\HttpException
514 | */
515 | public function batchReplaceUser(array $data)
516 | {
517 | $result = $this->httpRaw(self::WECHAT_USER_BATCH_REPLACE_PREFIX, $data, [
518 | 'access_token' => $this->getAccessToken(),
519 | ]);
520 | return isset($result['errmsg']) && $result['errmsg'] == 'ok' ? $result['jobid'] : false;
521 | }
522 |
523 | /**
524 | * 全量覆盖部门
525 | */
526 | const WECHAT_PARTY_BATCH_REPLACE_PREFIX = '/cgi-bin/batch/replaceparty';
527 | /**
528 | * 全量覆盖部门
529 | * @param array $data
530 | * @return bool
531 | * @throws \yii\web\HttpException
532 | */
533 | public function batchReplaceParty(array $data)
534 | {
535 | $result = $this->httpRaw(self::WECHAT_PARTY_BATCH_REPLACE_PREFIX, $data, [
536 | 'access_token' => $this->getAccessToken()
537 | ]);
538 | return isset($result['errmsg']) && $result['errmsg'] == 'ok' ? $result['jobid'] : false;
539 | }
540 |
541 | /**
542 | * 获取异步任务结果
543 | */
544 | const WECHAT_BATCH_RESULT_GET_PREFIX = '/cgi-bin/batch/getresult';
545 | /**
546 | * 获取异步任务结果
547 | * @param $jobId
548 | * @return bool|mixed
549 | * @throws \yii\web\HttpException
550 | */
551 | public function getBatchResult($jobId)
552 | {
553 | $result = $this->httpGet(self::WECHAT_BATCH_RESULT_GET_PREFIX, [
554 | 'access_token' => $this->getAccessToken(),
555 | 'jobid' => $jobId
556 | ]);
557 | return isset($result['errmsg']) && $result['errmsg'] == 'ok' ? $result : false;
558 | }
559 |
560 | /* =================== 管理多媒体文件 =================== */
561 |
562 | /**
563 | * 上传媒体文件
564 | */
565 | const WECHAT_MEDIA_UPLOAD_PREFIX = '/cgi-bin/media/upload';
566 | /**
567 | * 上传媒体文件
568 | * @param $mediaPath
569 | * @param $type
570 | * @return bool|mixed
571 | * @throws \yii\web\HttpException
572 | */
573 | public function updateMedia($mediaPath, $type)
574 | {
575 | $result = $this->httpPost(self::WECHAT_MEDIA_UPLOAD_PREFIX, [
576 | 'media' => $this->uploadFile($mediaPath)
577 | ], [
578 | 'access_token' => $this->getAccessToken(),
579 | 'type' => $type
580 | ]);
581 | return isset($result['media_id']) ? $result : false;
582 | }
583 |
584 | /**
585 | * 获取媒体文件
586 | */
587 | const WECHAT_MEDIA_GET_PREFIX = '/cgi-bin/media/get';
588 | /**
589 | * 获取媒体文件
590 | * @param $mediaId
591 | * @return bool|string
592 | * @throws \yii\web\HttpException
593 | */
594 | public function getMedia($mediaId)
595 | {
596 | $result = $this->httpGet(self::WECHAT_MEDIA_GET_PREFIX, [
597 | 'access_token' => $this->getAccessToken(),
598 | 'media_id' => $mediaId
599 | ]);
600 | return !isset($result['errcode']) ? $result : false;
601 | }
602 |
603 | /* =================== 管理企业号应用 =================== */
604 |
605 | /**
606 | * 获取企业号应用
607 | */
608 | const WECHAT_AGENT_GET_PREFIX = '/cgi-bin/agent/get';
609 | /**
610 | * 获取企业号应用
611 | * @param $agentId
612 | * @return bool|mixed
613 | * @throws \yii\web\HttpException
614 | */
615 | public function getAgent($agentId)
616 | {
617 | $result = $this->httpGet(self::WECHAT_AGENT_GET_PREFIX, [
618 | 'access_token' => $this->getAccessToken(),
619 | 'agent_id' => $agentId
620 | ]);
621 | return isset($result['errmsg']) && $result['errmsg'] == 'ok' ? $result : false;
622 | }
623 |
624 | /**
625 | * 设置企业号应用
626 | */
627 | const WECHAT_AGENT_SET_PREFIX = '/cgi-bin/agent/set';
628 | /**
629 | * 设置企业号应用
630 | * @param array $data
631 | * @return bool
632 | * @throws \yii\web\HttpException
633 | */
634 | public function setAgent(array $data)
635 | {
636 | $result = $this->httpRaw(self::WECHAT_AGENT_SET_PREFIX, $data, [
637 | 'access_token' => $this->getAccessToken()
638 | ]);
639 | return isset($result['errmsg']) && $result['errmsg'] == 'ok';
640 | }
641 |
642 | /**
643 | * 获取应用概况列表
644 | */
645 | const WECHAT_AGENT_LIST_GET_PREFIX = '/cgi-bin/agent/list';
646 | /**
647 | * 获取应用概况列表
648 | * @return bool
649 | * @throws \yii\web\HttpException
650 | */
651 | public function getAgentList()
652 | {
653 | $result = $this->httpGet(self::WECHAT_AGENT_SET_PREFIX, [
654 | 'access_token' => $this->getAccessToken()
655 | ]);
656 | return isset($result['errmsg']) && $result['errmsg'] == 'ok' ? $result['agentlist'] : false;
657 | }
658 |
659 | /* =================== 发送消息 =================== */
660 |
661 | /**
662 | * 发送消息
663 | */
664 | const WECHAT_MESSAGE_SEND_PREFIX = '/cgi-bin/message/send';
665 | /**
666 | * 发送消息
667 | * @param array $data
668 | * @return bool
669 | * @throws \yii\web\HttpException
670 | */
671 | public function sendMessage(array $data)
672 | {
673 | $result = $this->httpRaw(self::WECHAT_CUSTOM_MESSAGE_SEND_PREFIX, $data, [
674 | 'access_token' => $this->getAccessToken()
675 | ]);
676 | return isset($result['errmsg']) && $result['errmsg'] == 'ok' ? $result : false;
677 | }
678 |
679 | /* =================== 自定义菜单 =================== */
680 |
681 | /**
682 | * 创建应用菜单
683 | */
684 | const WECHAT_MENU_CREATE_PREFIX = '/cgi-bin/menu/create';
685 | /**
686 | * 创建应用菜单
687 | * @param $agentId
688 | * @param array $data
689 | * @return bool
690 | * @throws \yii\web\HttpException
691 | */
692 | public function createMenu($agentId, array $data)
693 | {
694 | $result = $this->httpRaw(self::WECHAT_MENU_CREATE_PREFIX, $data, [
695 | 'access_token' => $this->getAccessToken(),
696 | 'agentid' => $agentId
697 | ]);
698 | return isset($result['errmsg']) && $result['errmsg'] == 'ok';
699 | }
700 |
701 | /**
702 | * 删除菜单
703 | */
704 | const WECHAT_MENU_DELETE_PREFIX = '/cgi-bin/menu/delete';
705 | /**
706 | * 删除菜单
707 | * @param $agentId
708 | * @return bool
709 | * @throws \yii\web\HttpException
710 | */
711 | public function deleteMenu($agentId)
712 | {
713 | $result = $this->httpGet(self::WECHAT_MENU_DELETE_PREFIX, [
714 | 'access_token' => $this->getAccessToken(),
715 | 'agentid' => $agentId
716 | ]);
717 | return isset($result['errmsg']) && $result['errmsg'] == 'ok';
718 | }
719 |
720 | /**
721 | * 获取菜单列表
722 | */
723 | const WECHAT_MENU_GET_PREFIX = '/cgi-bin/menu/get';
724 | /**
725 | * 获取菜单列表
726 | * @param $agentId
727 | * @return bool
728 | * @throws \yii\web\HttpException
729 | */
730 | public function getMenu($agentId)
731 | {
732 | $result = $this->httpGet(self::WECHAT_MENU_GET_PREFIX, [
733 | 'access_token' => $this->getAccessToken(),
734 | 'agentid' => $agentId
735 | ]);
736 | return isset($result['menu']['button']) ? $result['menu']['button'] : false;
737 | }
738 |
739 | /* =================== OAuth2验证接口 =================== */
740 |
741 | /**
742 | * 企业获取code
743 | */
744 | const WECHAT_OAUTH2_AUTHORIZE_URL = 'https://open.weixin.qq.com/connect/oauth2/authorize';
745 | /**
746 | * 企业获取code:第
747 | * 通过此函数生成授权url
748 | * @param $redirectUrl 授权后重定向的回调链接地址,请使用urlencode对链接进行处理
749 | * @param string $state 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值
750 | * @param string $scope 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),
751 | * snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息)
752 | * @return string
753 | */
754 | public function getOauth2AuthorizeUrl($redirectUrl, $state = 'authorize', $scope = 'snsapi_base')
755 | {
756 | return $this->httpBuildQuery(self::WECHAT_OAUTH2_AUTHORIZE_URL, [
757 | 'appid' => $this->corpId,
758 | 'redirect_uri' => $redirectUrl,
759 | 'response_type' => 'code',
760 | 'scope' => $scope,
761 | 'state' => $state,
762 | ]) . '#wechat_redirect';
763 | }
764 |
765 | /**
766 | * 根据code获取成员信息
767 | */
768 | const WECHAT_USER_IFNO_GET_PREFIX = '/cgi-bin/user/getuserinfo';
769 | /**
770 | * 根据code获取成员信息
771 | * @param $agentId
772 | * @param $code
773 | * @return bool|mixed
774 | * @throws \yii\web\HttpException
775 | */
776 | public function getUserInfo($agentId, $code)
777 | {
778 | $result = $this->httpGet(self::WECHAT_USER_IFNO_GET_PREFIX, [
779 | 'access_token' => $this->getAccessToken(),
780 | 'code' => $code,
781 | 'agentid' => $agentId
782 | ]);
783 | return !isset($result['errcode']) ? $result : false;
784 | }
785 |
786 | /* =================== 微信JS接口 =================== */
787 |
788 | /**
789 | * js api ticket 获取
790 | */
791 | const WECHAT_JS_API_TICKET_PREFIX = '/cgi-bin/get_jsapi_ticket';
792 | /**
793 | * 请求服务器jsapi_ticket
794 | * @return array
795 | */
796 | protected function requestJsApiTicket()
797 | {
798 | return $this->httpGet(self::WECHAT_JS_API_TICKET_PREFIX, [
799 | 'access_token' => $this->getAccessToken(),
800 | ]);
801 | }
802 |
803 | /**
804 | * 生成js 必需的config
805 | * 只需在视图文件输出JS代码:
806 | * wx.config(= json_encode($wehcat->jsApiConfig()) ?>); // 默认全权限
807 | * wx.config(= json_encode($wehcat->jsApiConfig([ // 只允许使用分享到朋友圈功能
808 | * 'jsApiList' => [
809 | * 'onMenuShareTimeline'
810 | * ]
811 | * ])) ?>);
812 | * @param array $config
813 | * @return array
814 | * @throws HttpException
815 | */
816 | public function jsApiConfig(array $config = [])
817 | {
818 | $data = [
819 | 'jsapi_ticket' => $this->getJsApiTicket(),
820 | 'noncestr' => Yii::$app->security->generateRandomString(16),
821 | 'timestamp' => $_SERVER['REQUEST_TIME'],
822 | 'url' => explode('#', Yii::$app->request->getAbsoluteUrl())[0]
823 | ];
824 | return array_merge([
825 | 'debug' => YII_DEBUG,
826 | 'appId' => $this->corpId,
827 | 'timestamp' => $data['timestamp'],
828 | 'nonceStr' => $data['noncestr'],
829 | 'signature' => sha1(urldecode(http_build_query($data))),
830 | 'jsApiList' => [
831 | 'onMenuShareTimeline',
832 | 'onMenuShareAppMessage',
833 | 'onMenuShareQQ',
834 | 'onMenuShareWeibo',
835 | 'startRecord',
836 | 'stopRecord',
837 | 'onVoiceRecordEnd',
838 | 'playVoice',
839 | 'pauseVoice',
840 | 'stopVoice',
841 | 'onVoicePlayEnd',
842 | 'uploadVoice',
843 | 'downloadVoice',
844 | 'chooseImage',
845 | 'previewImage',
846 | 'uploadImage',
847 | 'downloadImage',
848 | 'translateVoice',
849 | 'getNetworkType',
850 | 'openLocation',
851 | 'getLocation',
852 | 'hideOptionMenu',
853 | 'showOptionMenu',
854 | 'hideMenuItems',
855 | 'showMenuItems',
856 | 'hideAllNonBaseMenuItem',
857 | 'showAllNonBaseMenuItem',
858 | 'closeWindow',
859 | 'scanQRCode'
860 | ]
861 | ], $config);
862 | }
863 |
864 | /* =================== 第三方应用授权 =================== */
865 |
866 |
867 |
868 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | yii2-wechat-sdk
2 | ===============
3 |
4 | 感谢选择 yii2-wechat-sdk 扩展, 该扩展是基于[Yii2](https://github.com/yiisoft/yii2)框架基础开发,借助Yii2的强劲特性可以定制开发属于您自己的微信公众号
5 |
6 | [](https://packagist.org/packages/callmez/yii2-wechat-sdk) [](https://packagist.org/packages/callmez/yii2-wechat-sdk) [](https://packagist.org/packages/callmez/yii2-wechat-sdk) [](https://packagist.org/packages/callmez/yii2-wechat-sdk)
7 |
8 | 注意
9 | ---
10 | ** 新版本正在重构中, 直到1.0正式版发布前.你依然可以继续使用功能 **
11 |
12 | 目前有3个主要文件可以使用
13 | - `Wechat.php` 旧版微信公众号操作类(在新版[1.0]发布后会删除)
14 | - `MpWechat.php` 新版微信公众号操作类(更标准,更完善), 如果您是新使用该库请按照文档说明替换旧版`Wechat.php`使用
15 | - `QyWechat.php` 新版微信企业号操作类(为了更加全面的微信功能操作, 将在[1.1版本中完善发布]), 强势集成企业号功能
16 |
17 | 环境条件
18 | --------
19 | - >= php5.4
20 | - Yii2
21 |
22 | 安装
23 | ----
24 |
25 | 您可以使用composer来安装, 添加下列代码在您的``composer.json``文件中并执行``composer update``操作
26 |
27 | ```json
28 | {
29 | "require": {
30 | "callmez/yii2-wechat-sdk": "dev-master"
31 | }
32 | }
33 | ```
34 |
35 | 使用示例
36 | --------
37 | 在使用前,请先参考微信公众平台的[开发文档](http://mp.weixin.qq.com/wiki/index.php?title=%E9%A6%96%E9%A1%B5)
38 |
39 | Wechat定义方式
40 | ```php
41 | //在config/web.php配置文件中定义component配置信息
42 | 'components' => [
43 | .....
44 | 'wechat' => [
45 | 'class' => 'callmez\wechat\sdk\Wechat',
46 | 'appId' => '微信公众平台中的appid',
47 | 'appSecret' => '微信公众平台中的secret',
48 | 'token' => '微信服务器对接您的服务器验证token'
49 | ]
50 | ....
51 | ]
52 | // 全局公众号sdk使用
53 | $wechat = Yii::$app->wechat;
54 |
55 |
56 | //多公众号使用方式
57 | $wechat = Yii::createObject([
58 | 'class' => 'callmez\wechat\sdk\Wechat',
59 | 'appId' => '微信公众平台中的appid',
60 | 'appSecret' => '微信公众平台中的secret',
61 | 'token' => '微信服务器对接您的服务器验证token'
62 | ]);
63 | ```
64 |
65 | Wechat方法使用(部分示例)
66 | ```php
67 | //获取access_token
68 | var_dump($wechat->accessToken);
69 |
70 | //获取二维码ticket
71 | $qrcode = $wechat->createQrCode([
72 | 'expire_seconds' => 604800,
73 | 'action_name' => 'QR_SCENE',
74 | 'action_info' => ['scene' => ['scene_id' => rand(1, 999999999)]]
75 | ]);
76 | var_dump($qrcode);
77 |
78 | //获取二维码
79 | $imgRawData = $wechat->getQrCodeUrl($qrcode['ticket']);
80 |
81 | //获取群组列表
82 | var_dump($wechat->getGroups());
83 |
84 |
85 | //创建分组
86 | $group = $wechat->createGroup('测试分组');
87 | echo $group ? '测试分组创建成功' : '测试分组创建失败';
88 |
89 | //修改分组
90 | echo $wechat->updateGroupName($group['id'], '修改测试分组') ? '修改测试分组成功' : '测试分组创建失败';
91 |
92 |
93 | //根据关注者ID获取关注者所在分组ID
94 | $openID = 'oiNHQjh-8k4DrQgY5H7xofx_ayfQ'; //此处应填写公众号关注者的唯一openId
95 |
96 | //修改关注者所在分组
97 | echo $wechat->updateMemberGroup($openID, 1) ? '修改关注者分组成功' : '修改关注者分组失败';
98 |
99 | //获取关注者所在分组
100 | echo $wechat->getGroupId($openID);
101 |
102 | //修改关注者备注
103 | echo $wechat->updateMemberRemark($openID, '测试更改备注') ? '关注者备注修改成功' : '关注者备注修改失败';
104 |
105 | //获取关注者基本信息
106 | var_dump($wechat->getMemberInfo($openID));
107 |
108 | //获取关注者列表
109 | var_dump($wechat->getMemberList());
110 |
111 | //获取关注者的客服聊天记录,
112 | var_dump($wechat->getCustomerServiceRecords($openID, mktime(0, 0, 0, 1, 1, date('Y')), time())); //获取今年的聊天数据(可能获取不到数据)
113 |
114 | //上传媒体文件
115 | $filePath = '图片绝对路径'; //目前微信只开发jpg上传
116 | var_dump($media = $wechat->uploadMedia(realpath($filePath), 'image'));
117 |
118 | //下载媒体文件
119 | echo $wechat->getMedia($media['media_id']) ? 'media下载成功' : 'media下载失败';
120 |
121 | ```
122 |
123 | 反馈或贡献代码
124 | --------------
125 | 您可以在[这里](https://github.com/callmez/yii2-wechat-sdk/issues)给我提出在使用中碰到的问题或Bug.
126 | 我会在第一时间回复您并修复.
127 |
128 | 您也可以 发送邮件callme-z@qq.com给我并且说明您的问题.
129 |
130 | 如果你有更好代码实现,请fork项目并发起您的pull request.我会及时处理. 感谢!
131 |
--------------------------------------------------------------------------------
/components/BaseWechat.php:
--------------------------------------------------------------------------------
1 | 'xxx',
57 | * 'expirs_in' => 7200
58 | * ]
59 | * @return array|bool
60 | */
61 | abstract protected function requestAccessToken();
62 |
63 | /**
64 | * 获取AccessToken
65 | * 超时后会自动重新获取AccessToken并触发self::EVENT_AFTER_ACCESS_TOKEN_UPDATE事件
66 | * @param bool $force 是否强制获取
67 | * @return mixed
68 | * @throws HttpException
69 | */
70 | public function getAccessToken($force = false)
71 | {
72 | $time = time(); // 为了更精确控制.取当前时间计算
73 | if ($this->_accessToken === null || $this->_accessToken['expire'] < $time || $force) {
74 | $result = $this->_accessToken === null && !$force ? $this->getCache('access_token', false) : false;
75 | if ($result === false) {
76 | if (!($result = $this->requestAccessToken())) {
77 | throw new HttpException(500, 'Fail to get access_token from wechat server.');
78 | }
79 | $result['expire'] = $time + $result['expires_in'];
80 | $this->trigger(self::EVENT_AFTER_ACCESS_TOKEN_UPDATE, new Event(['data' => $result]));
81 | $this->setCache('access_token', $result, $result['expires_in']);
82 | }
83 | $this->setAccessToken($result);
84 | }
85 | return $this->_accessToken['access_token'];
86 | }
87 |
88 | /**
89 | * 设置AccessToken
90 | * @param array $accessToken
91 | * @throws InvalidParamException
92 | */
93 | public function setAccessToken(array $accessToken)
94 | {
95 | if (!isset($accessToken['access_token'])) {
96 | throw new InvalidParamException('The wechat access_token must be set.');
97 | } elseif(!isset($accessToken['expire'])) {
98 | throw new InvalidParamException('Wechat access_token expire time must be set.');
99 | }
100 | $this->_accessToken = $accessToken;
101 | }
102 |
103 | /**
104 | * 请求微信服务器获取JsApiTicket
105 | * 必须返回以下格式内容
106 | * [
107 | * 'ticket => 'xxx',
108 | * 'expirs_in' => 7200
109 | * ]
110 | * @return array|bool
111 | */
112 | abstract protected function requestJsApiTicket();
113 |
114 | /**
115 | * 生成js 必要的config
116 | */
117 | abstract public function jsApiConfig(array $config = []);
118 |
119 | /**
120 | * 获取js api ticket
121 | * 超时后会自动重新获取JsApiTicket并触发self::EVENT_AFTER_JS_API_TICKET_UPDATE事件
122 | * @param bool $force 是否强制获取
123 | * @return mixed
124 | * @throws HttpException
125 | */
126 | public function getJsApiTicket($force = false)
127 | {
128 | $time = time(); // 为了更精确控制.取当前时间计算
129 | if ($this->_jsApiTicket === null || $this->_jsApiTicket['expire'] < $time || $force) {
130 | $result = $this->_jsApiTicket === null && !$force ? $this->getCache('js_api_ticket', false) : false;
131 | if ($result === false) {
132 | if (!($result = $this->requestJsApiTicket())) {
133 | throw new HttpException(500, 'Fail to get jsapi_ticket from wechat server.');
134 | }
135 | $result['expire'] = $time + $result['expires_in'];
136 | $this->trigger(self::EVENT_AFTER_JS_API_TICKET_UPDATE, new Event(['data' => $result]));
137 | $this->setCache('js_api_ticket', $result, $result['expires_in']);
138 | }
139 | $this->setJsApiTicket($result);
140 | }
141 | return $this->_jsApiTicket['ticket'];
142 | }
143 |
144 | /**
145 | * 设置JsApiTicket
146 | * @param array $jsApiTicket
147 | */
148 | public function setJsApiTicket(array $jsApiTicket)
149 | {
150 | $this->_jsApiTicket = $jsApiTicket;
151 | }
152 |
153 | /**
154 | * 创建消息加密类
155 | * @return mixed
156 | */
157 | abstract protected function createMessageCrypt();
158 |
159 | /**
160 | * 设置消息加密处理类
161 | * @return MessageCrypt
162 | */
163 | public function getMessageCrypt()
164 | {
165 | if ($this->_messageCrypt === null) {
166 | $this->setMessageCrypt($this->createMessageCrypt());
167 | }
168 | return $this->_messageCrypt;
169 | }
170 |
171 | /**
172 | * 设置消息加密处理类
173 | * @param MessageCrypt $messageCrypt
174 | */
175 | public function setMessageCrypt(MessageCrypt $messageCrypt)
176 | {
177 | $this->_messageCrypt = $messageCrypt;
178 | }
179 |
180 | /**
181 | * 加密XML数据
182 | * @param string $xml 加密的XML
183 | * @param string $timestamp 加密时间戳
184 | * @param string $nonce 加密随机串
185 | * @return string|bool
186 | */
187 | public function encryptXml($xml, $timestamp , $nonce)
188 | {
189 | $errorCode = $this->getMessageCrypt()->encryptMsg($xml, $timestamp, $nonce, $xml);
190 | if ($errorCode) {
191 | $this->lastError = [
192 | 'errcode' => $errorCode,
193 | 'errmsg' => 'XML数据加密失败!'
194 | ];
195 | return false;
196 | }
197 | return $xml;
198 | }
199 |
200 | /**
201 | * 解密XML数据
202 | * @param string $xml 解密的XML
203 | * @param string $messageSignature 加密签名
204 | * @param string $timestamp 加密时间戳
205 | * @param string $nonce 加密随机串
206 | * @return string|bool
207 | */
208 | public function decryptXml($xml, $messageSignature, $timestamp , $nonce)
209 | {
210 | $errorCode = $this->getMessageCrypt()->decryptMsg($messageSignature, $timestamp, $nonce, $xml, $xml);
211 | if ($errorCode) {
212 | $this->lastError = [
213 | 'errcode' => $errorCode,
214 | 'errmsg' => 'XML数据解密失败!'
215 | ];
216 | return false;
217 | }
218 | return $xml;
219 | }
220 |
221 | /**
222 | * 创建微信格式的XML
223 | * @param array $data
224 | * @param null $charset
225 | * @return string
226 | */
227 | public function xml(array $data, $charset = null)
228 | {
229 | $dom = new DOMDocument('1.0', $charset === null ? Yii::$app->charset : $charset);
230 | $root = new DOMElement('xml');
231 | $dom->appendChild($root);
232 | $this->buildXml($root, $data);
233 | $xml = $dom->saveXML();
234 | return trim(substr($xml, strpos($xml, '?>') + 2));
235 | }
236 |
237 | /**
238 | * @var string the name of the elements that represent the array elements with numeric keys.
239 | */
240 | public $itemTag = 'item';
241 |
242 | /**
243 | * @see yii\web\XmlResponseFormatter::buildXml()
244 | */
245 | protected function buildXml($element, $data)
246 | {
247 | if (is_object($data)) {
248 | $child = new DOMElement(StringHelper::basename(get_class($data)));
249 | $element->appendChild($child);
250 | if ($data instanceof Arrayable) {
251 | $this->buildXml($child, $data->toArray());
252 | } else {
253 | $array = [];
254 | foreach ($data as $name => $value) {
255 | $array[$name] = $value;
256 | }
257 | $this->buildXml($child, $array);
258 | }
259 | } elseif (is_array($data)) {
260 | foreach ($data as $name => $value) {
261 | if (is_int($name) && is_object($value)) {
262 | $this->buildXml($element, $value);
263 | } elseif (is_array($value) || is_object($value)) {
264 | $child = new DOMElement(is_int($name) ? $this->itemTag : $name);
265 | $element->appendChild($child);
266 | $this->buildXml($child, $value);
267 | } else {
268 | $child = new DOMElement(is_int($name) ? $this->itemTag : $name);
269 | $element->appendChild($child);
270 | $child->appendChild(new DOMText((string) $value));
271 | }
272 | }
273 | } else {
274 | $element->appendChild(new DOMText((string) $data));
275 | }
276 | }
277 |
278 | /**
279 | * 微信数据缓存基本键值
280 | * @param $name
281 | * @return string
282 | */
283 | abstract protected function getCacheKey($name);
284 |
285 | /**
286 | * 缓存微信数据
287 | * @param $name
288 | * @param $value
289 | * @param null $duration
290 | * @return bool
291 | */
292 | protected function setCache($name, $value, $duration = null)
293 | {
294 | $duration === null && $duration = $this->cacheTime;
295 | return Yii::$app->cache->set($this->getCacheKey($name), $value, $duration);
296 | }
297 |
298 | /**
299 | * 获取微信缓存数据
300 | * @param $name
301 | * @return mixed
302 | */
303 | protected function getCache($name)
304 | {
305 | return Yii::$app->cache->get($this->getCacheKey($name));
306 | }
307 |
308 | /**
309 | * Api url 组装
310 | * @param $url
311 | * @param array $options
312 | * @return string
313 | */
314 | protected function httpBuildQuery($url, array $options)
315 | {
316 | if (!empty($options)) {
317 | $url .= (stripos($url, '?') === null ? '&' : '?') . http_build_query($options);
318 | }
319 | return $url;
320 | }
321 |
322 | /**
323 | * Http Get 请求
324 | * @param $url
325 | * @param array $options
326 | * @return mixed
327 | */
328 | public function httpGet($url, array $options = [])
329 | {
330 | Yii::info([
331 | 'url' => $url,
332 | 'options' => $options
333 | ], __METHOD__);
334 | return $this->parseHttpRequest(function($url) {
335 | return $this->http($url);
336 | }, $this->httpBuildQuery($url, $options));
337 | }
338 |
339 | /**
340 | * Http Post 请求
341 | * @param $url
342 | * @param array $postOptions
343 | * @param array $options
344 | * @return mixed
345 | */
346 | public function httpPost($url, array $postOptions, array $options = [])
347 | {
348 | Yii::info([
349 | 'url' => $url,
350 | 'postOptions' => $postOptions,
351 | 'options' => $options
352 | ], __METHOD__);
353 | return $this->parseHttpRequest(function($url, $postOptions) {
354 | return $this->http($url, [
355 | CURLOPT_POST => true,
356 | CURLOPT_POSTFIELDS => $postOptions
357 | ]);
358 | }, $this->httpBuildQuery($url, $options), $postOptions);
359 | }
360 |
361 | /**
362 | * Http Raw数据 Post 请求
363 | * @param $url
364 | * @param $postOptions
365 | * @param array $options
366 | * @return mixed
367 | */
368 | public function httpRaw($url, $postOptions, array $options = [])
369 | {
370 | Yii::info([
371 | 'url' => $url,
372 | 'postOptions' => $postOptions,
373 | 'options' => $options
374 | ], __METHOD__);
375 | return $this->parseHttpRequest(function($url, $postOptions) {
376 | return $this->http($url, [
377 | CURLOPT_POST => true,
378 | CURLOPT_POSTFIELDS => is_array($postOptions) ? json_encode($postOptions, JSON_UNESCAPED_UNICODE) : $postOptions
379 | ]);
380 | }, $this->httpBuildQuery($url, $options), $postOptions);
381 | }
382 |
383 | /**
384 | * 解析微信请求响应内容
385 | * @param callable $callable Http请求主体函数
386 | * @param string $url Api地址
387 | * @param array|string|null $postOptions Api地址一般所需要的post参数
388 | * @return array|bool
389 | */
390 | abstract public function parseHttpRequest(callable $callable, $url, $postOptions = null);
391 |
392 | /**
393 | * Http基础库 使用该库请求微信服务器
394 | * @param $url
395 | * @param array $options
396 | * @return bool|mixed
397 | */
398 | protected function http($url, $options = [])
399 | {
400 | $options = [
401 | CURLOPT_URL => $url,
402 | CURLOPT_TIMEOUT => 30,
403 | CURLOPT_CONNECTTIMEOUT => 30,
404 | CURLOPT_RETURNTRANSFER => true,
405 | ] + (stripos($url, "https://") !== false ? [
406 | CURLOPT_SSL_VERIFYPEER => false,
407 | CURLOPT_SSL_VERIFYHOST => false,
408 | CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1 // 微信官方屏蔽了ssl2和ssl3, 启用更高级的ssl
409 | ] : []) + $options;
410 |
411 | $curl = curl_init();
412 | curl_setopt_array($curl, $options);
413 | $content = curl_exec($curl);
414 | $status = curl_getinfo($curl);
415 | curl_close($curl);
416 | if (isset($status['http_code']) && $status['http_code'] == 200) {
417 | return json_decode($content, true) ?: false; // 正常加载应该是只返回json字符串
418 | }
419 | Yii::error([
420 | 'result' => $content,
421 | 'status' => $status
422 | ], __METHOD__);
423 | return false;
424 | }
425 |
426 | /**
427 | * 上传文件请使用该类来解决curl版本兼容问题
428 | * @param $filePath
429 | * @return \CURLFile|string
430 | */
431 | protected function uploadFile($filePath)
432 | {
433 | // php 5.5将抛弃@写法,引用CURLFile类来实现 @see http://segmentfault.com/a/1190000000725185
434 | return class_exists('\CURLFile') ? new \CURLFile($filePath) : '@' . $filePath;
435 | }
436 | }
437 |
--------------------------------------------------------------------------------
/components/MessageCrypt.php:
--------------------------------------------------------------------------------
1 | wechat = $wechat;
24 | parent::__construct($config);
25 | }
26 | }
--------------------------------------------------------------------------------
/components/messageCrypt/ReadMe.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/callmez/yii2-wechat-sdk/6c3c9fbeecb5ad67741238ed67e80fd1016c18c7/components/messageCrypt/ReadMe.txt
--------------------------------------------------------------------------------
/components/messageCrypt/demo.php:
--------------------------------------------------------------------------------
1 |