微信公众平台 自动回复消息

含有4个类

Wechat 消息处理Encrypt 消息加解密Encodexml xml处理WeixinEvent 返回数据处理

  1 namespace app\common\logic\Wechat;
  2
  3 /**
  4  * Class Weixin  获取服务器发送的消息并返回消息
  5  *
  6  * 包含 Encrypt AES加解密类
  7  * 包含 Encodexml xml格式化类
  8  */
  9
 10 class Wechat
 11 {
 12     // 消息加解密类
 13     protected $encrypt;
 14
 15     //  公众号 原始ID
 16     protected $id = ‘‘;
 17     //  令牌(Token)
 18     protected $token = ‘‘;
 19     // 开发者ID(AppID)
 20     protected $appId = ‘‘;
 21     // 开发者密码(AppSecret)
 22     protected $appSecret = ‘‘;
 23     // 消息加解密密钥(EncodingAESKey)
 24     protected $encodingAESKey = ‘‘;
 25
 26     // 强制验证消息
 27     protected $authMsg = true;
 28
 29     // 消息数据
 30     protected $signature = ‘‘;
 31     protected $msgSignature = ‘‘;
 32     protected $echoStr = ‘‘;
 33     protected $timeStamp = 0;
 34     protected $nonce = ‘‘;
 35     protected $encryptType = null;
 36
 37     // 请求服务器
 38     protected static $server = ‘https://api.weixin.qq.com‘;
 39     // 请求路径
 40     protected static $serverpath = [
 41         ‘getAccessToken‘ => ‘/cgi-bin/token‘
 42     ];
 43     // 令牌缓存时间
 44     protected static $expiresIn = 7200;
 45
 46     // 错误代码
 47     protected static $errorCode = [
 48         -99999 => ‘未知错误‘,
 49
 50         0 => ‘成功‘,
 51         -40001 => ‘签名验证错误‘,
 52         -40002 => ‘xml解析失败‘,
 53         -40003 => ‘sha加密生成签名失败‘,
 54         -40004 => ‘encodingAesKey 非法‘,
 55         -40005 => ‘appid 校验错误‘,
 56         -40006 => ‘aes 加密失败‘,
 57         -40007 => ‘aes 解密失败‘,
 58         -40008 => ‘解密后得到的buffer非法‘,
 59         -40009 => ‘base64加密失败‘,
 60         -40010 => ‘base64解密失败‘,
 61         -40011 => ‘生成xml失败‘,
 62
 63         -50001 => ‘消息验证数据不完整‘,
 64         -50002 => ‘接口请求出错 返回无令牌数据‘,
 65         -50003 => ‘连接到远程服务器错误‘,
 66         -50004 => ‘获取 access_token(权限令牌) json解析错误‘,
 67         -50005 => ‘待解密消息不完整‘,
 68         -50006 => ‘数据验证签名错误‘,
 69         -50007 => ‘xml 解析加密数据失败‘,
 70         -50008 => ‘从 xml 获取信息出错‘,
 71
 72         -51001 => ‘微信返回消息 事件 错误‘,
 73         -51002 => ‘微信返回消息 数据 错误‘,
 74
 75         -1 => ‘系统繁忙,此时请开发者稍候再试‘,
 76         40001 => ‘获取 access_token 时 AppSecret 错误,或者 access_token 无效。请开发者认真比对 AppSecret 的正确性,或查看是否正在为恰当的公众号调用接口‘,
 77         40002 => ‘不合法的凭证类型‘,
 78         40003 => ‘不合法的 OpenID ,请开发者确认 OpenID (该用户)是否已关注公众号,或是否是其他公众号的 OpenID‘,
 79         40004 => ‘不合法的媒体文件类型‘,
 80         40005 => ‘不合法的文件类型‘,
 81         40006 => ‘不合法的文件大小‘,
 82         40007 => ‘不合法的媒体文件 id‘,
 83         40008 => ‘不合法的消息类型‘,
 84         40009 => ‘不合法的图片文件大小‘,
 85         40010 => ‘不合法的语音文件大小‘,
 86         40011 => ‘不合法的视频文件大小‘,
 87         40012 => ‘不合法的缩略图文件大小‘,
 88         40013 => ‘不合法的 AppID ,请开发者检查 AppID 的正确性,避免异常字符,注意大小写‘,
 89         40014 => ‘不合法的 access_token ,请开发者认真比对 access_token 的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口‘,
 90         40015 => ‘不合法的菜单类型‘,
 91         40016 => ‘不合法的按钮个数‘,
 92         40017 => ‘不合法的按钮个数‘,
 93         40018 => ‘不合法的按钮名字长度‘,
 94         40019 => ‘不合法的按钮 KEY 长度‘,
 95         40020 => ‘不合法的按钮 URL 长度‘,
 96         40021 => ‘不合法的菜单版本号‘,
 97         40022 => ‘不合法的子菜单级数‘,
 98         40023 => ‘不合法的子菜单按钮个数‘,
 99         40024 => ‘不合法的子菜单按钮类型‘,
100         40025 => ‘不合法的子菜单按钮名字长度‘,
101         40026 => ‘不合法的子菜单按钮 KEY 长度‘,
102         40027 => ‘不合法的子菜单按钮 URL 长度‘,
103         40028 => ‘不合法的自定义菜单使用用户‘,
104         40029 => ‘不合法的 oauth_code‘,
105         40030 => ‘不合法的 refresh_token‘,
106         40031 => ‘不合法的 openid 列表‘,
107         40032 => ‘不合法的 openid 列表长度‘,
108         40033 => ‘不合法的请求字符,不能包含 \uxxxx 格式的字符‘,
109         40035 => ‘不合法的参数‘,
110         40038 => ‘不合法的请求格式‘,
111         40039 => ‘不合法的 URL 长度‘,
112         40050 => ‘不合法的分组 id‘,
113         40051 => ‘分组名字不合法‘,
114         40060 => ‘删除单篇图文时,指定的 article_idx 不合法‘,
115         40117 => ‘分组名字不合法‘,
116         40118 => ‘media_id 大小不合法‘,
117         40119 => ‘button 类型错误‘,
118         40120 => ‘button 类型错误‘,
119         40121 => ‘不合法的 media_id 类型‘,
120         40132 => ‘微信号不合法‘,
121         40137 => ‘不支持的图片格式‘,
122         40155 => ‘请勿添加其他公众号的主页链接‘,
123         41001 => ‘缺少 access_token 参数‘,
124         41002 => ‘缺少 appid 参数‘,
125         41003 => ‘缺少 refresh_token 参数‘,
126         41004 => ‘缺少 secret 参数‘,
127         41005 => ‘缺少多媒体文件数据‘,
128         41006 => ‘缺少 media_id 参数‘,
129         41007 => ‘缺少子菜单数据‘,
130         41008 => ‘缺少 oauth code‘,
131         41009 => ‘缺少 openid‘,
132         42001 => ‘access_token 超时,请检查 access_token 的有效期,请参考基础支持 - 获取 access_token 中,对 access_token 的详细机制说明‘,
133         42002 => ‘refresh_token 超时‘,
134         42003 => ‘oauth_code 超时‘,
135         42007 => ‘用户修改微信密码, accesstoken 和 refreshtoken 失效,需要重新授权‘,
136         43001 => ‘需要 GET 请求‘,
137         43002 => ‘需要 POST 请求‘,
138         43003 => ‘需要 HTTPS 请求‘,
139         43004 => ‘需要接收者关注‘,
140         43005 => ‘需要好友关系‘,
141         43019 => ‘需要将接收者从黑名单中移除‘,
142         44001 => ‘多媒体文件为空‘,
143         44002 => ‘POST 的数据包为空‘,
144         44003 => ‘图文消息内容为空‘,
145         44004 => ‘文本消息内容为空‘,
146         45001 => ‘多媒体文件大小超过限制‘,
147         45002 => ‘消息内容超过限制‘,
148         45003 => ‘标题字段超过限制‘,
149         45004 => ‘描述字段超过限制‘,
150         45005 => ‘链接字段超过限制‘,
151         45006 => ‘图片链接字段超过限制‘,
152         45007 => ‘语音播放时间超过限制‘,
153         45008 => ‘图文消息超过限制‘,
154         45009 => ‘接口调用超过限制‘,
155         45010 => ‘创建菜单个数超过限制‘,
156         45011 => ‘API 调用太频繁,请稍候再试‘,
157         45015 => ‘回复时间超过限制‘,
158         45016 => ‘系统分组,不允许修改‘,
159         45017 => ‘分组名字过长‘,
160         45018 => ‘分组数量超过上限‘,
161         45047 => ‘客服接口下行条数超过上限‘,
162         46001 => ‘不存在媒体数据‘,
163         46002 => ‘不存在的菜单版本‘,
164         46003 => ‘不存在的菜单数据‘,
165         46004 => ‘不存在的用户‘,
166         47001 => ‘解析 JSON/XML 内容错误‘,
167         48001 => ‘api 功能未授权,请确认公众号已获得该接口,可以在公众平台官网 - 开发者中心页中查看接口权限‘,
168         48002 => ‘粉丝拒收消息(粉丝在公众号选项中,关闭了 “ 接收消息 ” )‘,
169         48004 => ‘api 接口被封禁,请登录 mp.weixin.qq.com 查看详情‘,
170         48005 => ‘api 禁止删除被自动回复和自定义菜单引用的素材‘,
171         48006 => ‘api 禁止清零调用次数,因为清零次数达到上限‘,
172         48008 => ‘没有该类型消息的发送权限‘,
173         50001 => ‘用户未授权该 api‘,
174         50002 => ‘用户受限,可能是违规后接口被封禁‘,
175         61451 => ‘参数错误 (invalid parameter)‘,
176         61452 => ‘无效客服账号 (invalid kf_account)‘,
177         61453 => ‘客服帐号已存在 (kf_account exsited)‘,
178         61454 => ‘客服帐号名长度超过限制 ( 仅允许 10 个英文字符,不包括 @ 及 @ 后的公众号的微信号 )(invalid kf_acount length)‘,
179         61455 => ‘客服帐号名包含非法字符 ( 仅允许英文 + 数字 )(illegal character in kf_account)‘,
180         61456 => ‘客服帐号个数超过限制 (10 个客服账号 )(kf_account count exceeded)‘,
181         61457 => ‘无效头像文件类型 (invalid file type)‘,
182         61450 => ‘系统错误 (system error)‘,
183         61500 => ‘日期格式错误‘,
184         65301 => ‘不存在此 menuid 对应的个性化菜单‘,
185         65302 => ‘没有相应的用户‘,
186         65303 => ‘没有默认菜单,不能创建个性化菜单‘,
187         65304 => ‘MatchRule 信息为空‘,
188         65305 => ‘个性化菜单数量受限‘,
189         65306 => ‘不支持个性化菜单的帐号‘,
190         65307 => ‘个性化菜单信息为空‘,
191         65308 => ‘包含没有响应类型的 button‘,
192         65309 => ‘个性化菜单开关处于关闭状态‘,
193         65310 => ‘填写了省份或城市信息,国家信息不能为空‘,
194         65311 => ‘填写了城市信息,省份信息不能为空‘,
195         65312 => ‘不合法的国家信息‘,
196         65313 => ‘不合法的省份信息‘,
197         65314 => ‘不合法的城市信息‘,
198         65316 => ‘该公众号的菜单设置了过多的域名外跳(最多跳转到 3 个域名的链接)‘,
199         65317 => ‘不合法的 URL‘,
200         9001001 => ‘POST 数据参数不合法‘,
201         9001002 => ‘远端服务不可用‘,
202         9001003 => ‘Ticket 不合法‘,
203         9001004 => ‘获取摇周边用户信息失败‘,
204         9001005 => ‘获取商户信息失败‘,
205         9001006 => ‘获取 OpenID 失败‘,
206         9001007 => ‘上传文件缺失‘,
207         9001008 => ‘上传素材的文件类型不合法‘,
208         9001009 => ‘上传素材的文件尺寸不合法‘,
209         9001010 => ‘上传失败‘,
210         9001020 => ‘帐号不合法‘,
211         9001021 => ‘已有设备激活率低于 50% ,不能新增设备‘,
212         9001022 => ‘设备申请数不合法,必须为大于 0 的数字‘,
213         9001023 => ‘已存在审核中的设备 ID 申请‘,
214         9001024 => ‘一次查询设备 ID 数量不能超过 50‘,
215         9001025 => ‘设备 ID 不合法‘,
216         9001026 => ‘页面 ID 不合法‘,
217         9001027 => ‘页面参数不合法‘,
218         9001028 => ‘一次删除页面 ID 数量不能超过 10‘,
219         9001029 => ‘页面已应用在设备中,请先解除应用关系再删除‘,
220         9001030 => ‘一次查询页面 ID 数量不能超过 50‘,
221         9001031 => ‘时间区间不合法‘,
222         9001032 => ‘保存设备与页面的绑定关系参数错误‘,
223         9001033 => ‘门店 ID 不合法‘,
224         9001034 => ‘设备备注信息过长‘,
225         9001035 => ‘设备申请参数不合法‘,
226         9001036 => ‘查询起始值 begin 不合法‘
227     ];
228
229     public $url;
230     // 收到的 array数据
231     public $receiveArray = [];
232     // 微信发送消息的时间
233     public $receiveTime = 0;
234     // 收到的xml数据
235     public $receiveMsg = null;
236     // 收到的解密xml数据
237     public $receiveEncryptMsg = null;
238     // 发送的未加密xml数据
239     public $sendEncryptMsg = null;
240     // 发送的xml数据
241     public $sendMsg = null;
242
243     /**
244      * 初始化类
245      * @param array $user 公众号数据
246      * id 原始ID,
247      * token 令牌(Token),
248      * appId 开发者ID(AppID),
249      * appSecret 开发者密码(AppSecret),
250      * encodingAESKey 消息加解密密钥(EncodingAESKey)
251      * authMsg 验证消息
252      */
253     public function __construct ($user = [])
254     {
255         // 参数赋值
256         isset($user[‘id‘]) && $this->id = $user[‘id‘];
257         isset($user[‘token‘]) && $this->token = $user[‘token‘];
258         isset($user[‘appId‘]) && $this->appId = $user[‘appId‘];
259         isset($user[‘appSecret‘]) && $this->appSecret = $user[‘appSecret‘];
260         isset($user[‘encodingAESKey‘]) && strlen($user[‘encodingAESKey‘]) == 43 && $this->encodingAESKey = $user[‘encodingAESKey‘];
261         isset($user[‘authMsg‘]) && $this->authMsg = $user[‘authMsg‘];
262         $this->timeStamp = time();
263
264         // 处理类赋值
265         // 加解密消息类
266         $this->encrypt = new Encrypt($this->encodingAESKey);
267         $this->encodeXml = new Encodexml();
268     }
269
270     /**
271      * 获取信息
272      * @param array $getData $_GET 数据
273      * @param string $postData file_get_contents(‘php://input‘) 数据
274      * @param \Closure $response 回调函数 生成返回消息 xml ,参数 (string $msgType 消息类型, array $msg 消息数据) 返回 Encodexml
275      * @return mixed 错误代码string 和 消息array
276      */
277     public function getBackMsg ($getData, $postData, $response=null)
278     {
279         // 验证消息赋值
280         isset($getData[‘signature‘]) && $this->signature = $getData[‘signature‘];
281         isset($getData[‘msg_signature‘]) && $this->msgSignature = $getData[‘msg_signature‘];
282         isset($getData[‘echostr‘]) && $this->echoStr = $getData[‘echostr‘];
283         isset($getData[‘timestamp‘]) && $this->timeStamp = $getData[‘timestamp‘];
284         isset($getData[‘nonce‘]) && $this->nonce = $getData[‘nonce‘];
285         isset($getData[‘encrypt_type‘]) && $this->encryptType = $getData[‘encrypt_type‘];
286         // 保存接收到的xml数据
287         $this->receiveMsg = $postData;
288         // 验证 get 消息
289         $result = $this->checkSignature($this->signature);
290         // 验证 get 消息失败
291         if ($result[0] != 0) {
292             return $result[0];
293         }
294         // 验证 post 消息 无post消息返回验证字符串
295         if (!$postData) {
296             return $result[1];
297         }
298         // 获取信息数据
299         $result = $this->responseMsg($postData);
300         // 返回错误信息
301         if ($result[0]!==0) {
302             return $result[0];
303         }
304         // 获取信息类型
305         $msgtype = $this->getMsgType();
306         // 生成返回 xml
307         if ($response instanceof \Closure) {
308             // 使用回调函数返回数据
309             $result = $response($msgtype,$result[1]);
310         } else {
311             // 默认返回 success
312             $result = $this->encodeXml->text(‘success‘);
313         }
314         // 返回错误信息
315         if (!($result instanceof Encodexml)) {
316             return $result[0];
317         }
318         // 加密 xml
319         $result = $this->replyMsg($result);
320         // 返回错误信息
321         if ($result[0] !== 0) {
322             return $result[0];
323         }
324         return $result[1];
325     }
326
327     /**
328      * 获取 令牌
329      * @return array 错误代码 和 令牌
330      */
331     public function getAccessToken ()
332     {
333         // 获取令牌缓存 可以使用 文件 或 数据库 来存取
334         $result = cache(‘Weixin.access_token‘);
335         if ($result) {
336             return [0, $result];
337         }
338
339         // 获取请求 url
340         $this->url = self::$server . self::$serverpath[‘getAccessToken‘];
341         $this->url .= "?grant_type=client_credential&appid={$this->appId}&secret={$this->appSecret}";
342
343         // 发送 Get 请求
344         $result = $this->send_data($this->url);
345         // 排除连接错误
346         if ($result[0] !== 0) {
347             return [$result[0], null];
348         }
349
350         try {
351             // json 数据转数组
352             $result = json_decode($result, true);
353         } catch (\Exception $e) {
354             // 权限令牌json解析失败
355             return [-50004, null];
356         }
357
358         if (isset($result[‘access_token‘])) {
359             // 缓存令牌数据
360             cache(‘Weixin.access_token‘, $result[‘access_token‘], self::$expiresIn);
361             return [0, $result[‘access_token‘]];
362         } else {
363             // 接口请求出错 返回无令牌数据
364             return [-50002, null];
365         }
366     }
367
368     /**
369      * 通过错误代码 获取错误信息
370      * @param mixed $code 错误代码
371      * @return string 返回错误代码 和 错误信息
372      */
373     public function getError ($code)
374     {
375         if (is_numeric($code) && isset(self::$errorCode[$code])) {
376             return $code . ‘ [‘ . self::$errorCode[$code] . ‘]‘;
377         } else {
378             return "0 [成功]";
379         }
380     }
381
382     /**
383      * 回复微信消息
384      * @param Encodexml $class 处理消息类
385      * @return array 错误代码 和 返回信息
386      */
387     protected function replyMsg (Encodexml $class)
388     {
389         try {
390             $xml = $class->buildXml($this->receiveArray[‘FromUserName‘], $this->receiveArray[‘ToUserName‘]);
391         } catch (\Exception $e) {
392             return [-50008, null];
393         }
394         if (!$this->encryptType) {
395             // 待发送的数据
396             $this->sendMsg = $xml;
397             return [0, $xml];
398         }
399         // 待发送的未加密的 xml
400         $this->sendEncryptMsg = $xml;
401         // 加密xml
402         $result = $this->encrypt->encrypt($xml, $this->appId);
403
404         // 加密失败
405         if ($result[0]!== 0) {
406             return $result;
407         }
408
409         // 生成签名信息
410         $nonce = $this->encrypt->getRandomStr();
411         $timeStamp = time();
412         $msgSignature = $this->getSha($timeStamp, $nonce, $result[1]);
413
414         // 签名失败
415         if ($msgSignature[0]!== 0) {
416             return $msgSignature;
417         }
418
419         // 生成加密信息
420         $xml = "<xml><Encrypt><![CDATA[";
421         $xml .= $result[1];
422         $xml .= "]]></Encrypt><MsgSignature>{$msgSignature[1]}</MsgSignature><TimeStamp>{$timeStamp}</TimeStamp><Nonce>{$nonce}</Nonce></xml>";
423         // 待发送的 加密的 xml
424         $this->sendMsg = $xml;
425         return [0, $xml];
426     }
427
428     /**
429      * 回复微信消息
430      * @return mixed 获取消息类型
431      */
432     protected function getMsgType ()
433     {
434         if (!isset($this->receiveArray[‘MsgType‘])) {
435             return false;
436         }
437
438         $MsgType = $this->receiveArray[‘MsgType‘];
439
440         if ($MsgType != ‘event‘) {
441             return $MsgType;
442         }
443
444         if (!isset($this->receiveArray[‘Event‘])) {
445             return false;
446         }
447
448         return "{$MsgType}_{$this->receiveArray[‘Event‘]}";
449     }
450
451     /**
452      * 验证消息安全性
453      * @param string $encryptMsg 加密数据
454      * @return array 错误代码 和 返回消息
455      */
456     protected function checkSignature ($signature, $encryptMsg = ‘‘)
457     {
458         // 不验证消息
459         if (!$this->encryptType && $this->authMsg === false) {
460             return [0, $this->echoStr];
461         }
462         // 验证消息完整信
463         if (!($this->signature && $this->timeStamp && $this->nonce)) {
464             // 消息数据不完整
465             return [-50001, null];
466         }
467         // 获取签名结果
468         $result = $this->getSha($this->timeStamp, $this->nonce, $encryptMsg);
469         if ($result[0] != 0) {
470             return $result;
471         }
472         // 对比签名结果
473         if ($result[1] == $signature) {
474             return [0, $this->echoStr];
475         } else {
476             if ($encryptMsg) {
477                 // 数据签名验证错误
478                 return [-50006, null];
479             } else {
480                 // 消息签名验证错误
481                 return [-40001, null];
482             }
483         }
484     }
485
486     /**
487      * 获取服务器发送的消息
488      * @param string $postData 加密数据
489      * @return array 错误代码 和 返回消息数组
490      */
491     protected function responseMsg ($postData)
492     {
493         // libxml_disable_entity_loader(true); // xml 安全认证
494         // xml解析消息
495         try {
496             $xml = simplexml_load_string($postData, ‘SimpleXMLElement‘, LIBXML_NOCDATA);
497         } catch (\Exception $e) {
498             // xml 解析失败
499             return [-40002, null];
500         }
501         // json 数据 转数组
502         $msgArray = json_decode(json_encode($xml), true);
503         // 不是加密消息 直接返回数据
504         if (!$this->encryptType) {
505             $this->receiveArray = $msgArray;
506             return [0, $msgArray];
507         }
508         // 验证 待解密 数据
509         try {
510             $userName = $msgArray[‘ToUserName‘];
511             $encrypt = $msgArray[‘Encrypt‘];
512         } catch (\Exception $e) {
513             // 待解密消息不完整
514             return [-50005, null];
515         }
516         // 安全签名验证
517         $sha1 = $this->checkSignature($this->msgSignature,$encrypt);
518         if ($sha1[0]!==0) {
519             return $sha1;
520         }
521         // 解密数据
522         $decryptMsg = $this->encrypt->decrypt($encrypt, $this->appId);
523         // 解密消息失败
524         if ($decryptMsg[0] !== 0) {
525             return [$decryptMsg[0], null];
526         }
527         // 保存解密后的xml数据
528         $this->receiveEncryptMsg = $decryptMsg[1];
529         // xml解析 加密消息
530         try {
531             $xml = simplexml_load_string($decryptMsg[1], ‘SimpleXMLElement‘, LIBXML_NOCDATA);
532         } catch (\Exception $e) {
533             // xml 解析加密数据失败
534             return [-50007, null];
535         }
536         // json 数据 转数组
537         $msgArray = json_decode(json_encode($xml), true);
538         $msgArray[‘ToUserName‘] = $userName;
539         $this->receiveTime = isset($msgArray[‘TimeStamp‘]) ? $msgArray[‘TimeStamp‘] : time();
540         $this->receiveArray = $msgArray;
541         return [0, $msgArray];
542     }
543
544     /**
545      * 发送数据请求
546      * @param string $url 微信服务器地址
547      * @param string $type 发送方式 get post
548      * @param string $data 发送数据
549      * @return array 得到返回数据 错误代码和数据
550      */
551     protected function send_data ($url, $type = "get", $data = null)
552     {
553         //$header[] = "Content-type: text/xml"; //定义content-type为xml
554         $header[] = "Accept-Charset: utf-8";
555         // 初始化 Curl
556         $ch = curl_init();
557         // 判断服务器地址 是否 ssl
558         $ssl = substr($url, 0, 8) == "https://" ? true : false;
559         curl_setopt($ch, CURLOPT_URL, $url);
560         // 修改 header
561         curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
562         curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
563         // 是否返回数据到变量
564         curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
565         If ($type == "post") {                                                        // post 模式
566             curl_setopt($ch, CURLOPT_POST, true);
567             curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
568         }
569         if ($ssl) {                                                                  // ssl 模式
570             curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
571             curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
572         }
573         // 执行
574         $returndata = curl_exec($ch);
575         if (curl_errno($ch)) {
576             curl_close($ch);
577             // 连接到远程服务器错误
578             return [-50003, null];
579         } else {
580             curl_close($ch);
581             return [0, $returndata];
582         }
583     }
584
585     /**
586      * 用SHA1算法生成安全签名
587      * @param string $token 票据
588      * @param string $timestamp 时间戳
589      * @param string $nonce 随机字符串
590      * @param string $encrypt 密文消息
591      * @return array 返回 安全签名结果 和 数据
592      */
593     protected function getSha ($timestamp, $nonce, $msg)
594     {
595         try {
596             $array = [$this->token, $timestamp, $nonce, $msg];
597             // 排序
598             sort($array, SORT_STRING);
599             $str = implode($array);
600             // 返回签名结果
601             return [0, sha1($str)];
602         } catch (\Exception $e) {
603             // sha加密生成签名失败
604             return [-40003, null];
605         }
606     }
607
608
609 }

Wechat

  1 namespace app\common\logic\Wechat;
  2
  3 /**
  4  * Prpcrypt class
  5  *
  6  * 提供接收和推送给公众平台消息的加解密接口.
  7  */
  8 class Encrypt
  9 {
 10     public $key;
 11     public static $block_size = 32;
 12
 13    public function __construct($k)
 14     {
 15         $this->key = base64_decode($k . "=");
 16     }
 17
 18     /**
 19      * 对需要加密的明文进行填充补位
 20      * @param string $text 需要进行填充补位操作的明文
 21      * @return string 补齐明文字符串
 22      */
 23     protected function encode($text)
 24     {
 25         $block_size = self::$block_size;
 26         $text_length = strlen($text);
 27         //计算需要填充的位数
 28         $amount_to_pad = self::$block_size - ($text_length % self::$block_size);
 29         if ($amount_to_pad == 0) {
 30             $amount_to_pad = self::$block_size;
 31         }
 32         //获得补位所用的字符
 33         $pad_chr = chr($amount_to_pad);
 34         $tmp = "";
 35         for ($index = 0; $index < $amount_to_pad; $index++) {
 36             $tmp .= $pad_chr;
 37         }
 38         return $text . $tmp;
 39     }
 40
 41     /**
 42      * 对解密后的明文进行补位删除
 43      * @param string $text 解密后的明文
 44      * @return string 删除填充补位后的明文
 45      */
 46     protected function decode($text)
 47     {
 48
 49         $pad = ord(substr($text, -1));
 50         if ($pad < 1 || $pad > 32) {
 51             $pad = 0;
 52         }
 53         return substr($text, 0, (strlen($text) - $pad));
 54     }
 55
 56     /**
 57      * 对明文进行加密
 58      * @param string $text 需要加密的明文
 59      * @return array 加密后的密文
 60      */
 61     public function encrypt($text, $appid)
 62     {
 63
 64         try {
 65             //获得16位随机字符串,填充到明文之前
 66             $random = $this->getRandomStr();
 67             $text = $random . pack("N", strlen($text)) . $text . $appid;
 68             // 网络字节序
 69             $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
 70             $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, ‘‘, MCRYPT_MODE_CBC, ‘‘);
 71             $iv = substr($this->key, 0, 16);
 72             //使用自定义的填充方式对明文进行补位填充
 73             $text = $this->encode($text);
 74             mcrypt_generic_init($module, $this->key, $iv);
 75             //加密
 76             $encrypted = mcrypt_generic($module, $text);
 77             mcrypt_generic_deinit($module);
 78             mcrypt_module_close($module);
 79
 80             //print(base64_encode($encrypted));
 81             //使用BASE64对加密后的字符串进行编码
 82             return [0, base64_encode($encrypted)];
 83         } catch (\Exception $e) {
 84             // aes 加密失败
 85             return [-40006, null];
 86         }
 87     }
 88
 89     /**
 90      * 对密文进行解密
 91      * @param string $encrypted 需要解密的密文
 92      * @param string $appid appID
 93      * @return array 解密得到的明文
 94      */
 95     public function decrypt($encrypted, $appid)
 96     {
 97
 98         try {
 99             //使用BASE64对需要解密的字符串进行解码
100             $ciphertext_dec = base64_decode($encrypted);
101             $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, ‘‘, MCRYPT_MODE_CBC, ‘‘);
102             $iv = substr($this->key, 0, 16);
103             mcrypt_generic_init($module, $this->key, $iv);
104
105             //解密
106             $decrypted = mdecrypt_generic($module, $ciphertext_dec);
107             mcrypt_generic_deinit($module);
108             mcrypt_module_close($module);
109         } catch (\Exception $e) {
110             // aes 解密失败
111             return [-40007, null];
112         }
113
114
115         try {
116             //去除补位字符
117             $result = $this->decode($decrypted);
118             //去除16位随机字符串,网络字节序和AppId
119             if (strlen($result) < 16) {
120                 // 解密后得到的buffer非法
121                 return [-40008, null];
122             }
123             $content = substr($result, 16, strlen($result));
124             $len_list = unpack("N", substr($content, 0, 4));
125             $xml_len = $len_list[1];
126             $xml_content = substr($content, 4, $xml_len);
127             $from_appid = substr($content, $xml_len + 4);
128         } catch (\Exception $e) {
129             // 解密后得到的buffer非法
130             return [-40008, null];
131         }
132         if ($from_appid != $appid) {
133             // appid 校验错误
134             return [-40005, null];
135         }
136         return [0, $xml_content];
137
138     }
139
140
141     /**
142      * 随机生成16位字符串
143      * @return string 生成的字符串
144      */
145     public function getRandomStr()
146     {
147
148         $str = "";
149         $str_pol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
150         $max = strlen($str_pol) - 1;
151         for ($i = 0; $i < 16; $i++) {
152             $str .= $str_pol[mt_rand(0, $max)];
153         }
154         return $str;
155     }
156
157 }

Encrypt

 1 namespace app\common\logic\Wechat;
 2
 3 class Encodexml {
 4
 5     protected $xml=‘‘;
 6     protected $articleCount = 0;
 7     protected $articles;
 8
 9     protected static $shareFormat = "<ToUserName><![CDATA[%1\$s]]></ToUserName><FromUserName><![CDATA[%2\$s]]></FromUserName><CreateTime>%3\$d</CreateTime>";
10     protected static $format = [
11         ‘text‘ => "<MsgType><![CDATA[%1\$s]]></MsgType><Content><![CDATA[%2\$s]]></Content>",
12         ‘image‘ => "<MsgType><![CDATA[%1\$s]]></MsgType><Image><MediaId><![CDATA[%2\$s]]></MediaId></Image>",
13         ‘voice‘ => "<MsgType><![CDATA[%1\$s]]></MsgType><Voice><MediaId><![CDATA[%2\$s]]></MediaId></Voice>",
14         ‘video‘ => "<MsgType><![CDATA[%1\$s]]></MsgType><Video><MediaId><![CDATA[%2\$s]]></MediaId><Title><![CDATA[%3\$s]]></Title><Description><![CDATA[%4\$s]]></Description></Video>",
15         ‘music‘ => "<MsgType><![CDATA[%1\$s]]></MsgType><Music><Title><![CDATA[%2\$s]]></Title><Description><![CDATA[%3\$s]]></Description><MusicUrl><![CDATA[%4\$s]]></MusicUrl><HQMusicUrl><![CDATA[%5\$s]]></HQMusicUrl><ThumbMediaId><![CDATA[%6\$s]]></ThumbMediaId></Music>",
16         ‘news‘ => "<MsgType><![CDATA[%1\$s]]></MsgType><ArticleCount>%2\$d</ArticleCount><Articles>%3\$s</Articles>",
17         ‘article‘ => "<item><Title><![CDATA[%1\$s]]></Title> <Description><![CDATA[%2\$s]]></Description><PicUrl><![CDATA[%3\$s]]></PicUrl><Url><![CDATA[%4\$s]]></Url></item>"
18     ];
19
20     public function text ($content) {
21         $this->xml = sprintf(self::$format[‘text‘], ‘text‘, $content);
22         return $this;
23     }
24
25     public function image ($MediaId) {
26         $this->xml = sprintf(self::$format[‘image‘], ‘image‘, $MediaId);
27         return $this;
28     }
29
30     public function voice ($MediaId) {
31         $this->xml = sprintf(self::$format[‘voice‘], ‘voice‘, $MediaId);
32         return $this;
33     }
34
35     public function video ($MediaId, $Title, $Description) {
36         $this->xml = sprintf(self::$format[‘video‘], ‘video‘, $MediaId, $Title, $Description);
37         return $this;
38     }
39
40     public function music ($ThumbMediaId, $Title=‘‘, $Description=‘‘, $MusicUrl=‘‘, $HQMusicUrl=‘‘) {
41         $this->xml = sprintf(self::$format[‘music‘], ‘music‘, $Title, $Description, $MusicUrl, $HQMusicUrl, $ThumbMediaId);
42         return $this;
43     }
44
45     public function articleAdd ($Title, $Description, $PicUrl, $Url)
46     {
47         $this->articleCount +=1;
48         $this->articles .= sprintf(self::$format[‘article‘], $Title, $Description, $PicUrl, $Url);
49
50
51         $this->xml = sprintf(self::$format[‘news‘], ‘news‘, $this->articleCount, $this->articles);
52         return $this;
53     }
54
55     public function buildXml($ToUserName, $FromUserName) {
56         if (!$this->xml) {
57             return "success";
58         }
59         $xml = $this->xml;
60         $this->xml = ‘‘;
61         $this->articleCount = 0;
62         $this->articles = ‘‘;
63         $share = sprintf(self::$shareFormat, $ToUserName, $FromUserName, time());
64         return "<xml>{$share}{$xml}</xml>";
65     }
66 }

Encodexml

  1 namespace app\common\logic\Wechat;
  2
  3 class WeixinEvent {
  4
  5     protected $ToUserName;
  6     protected $FromUserName;
  7     protected $CreateTime;
  8
  9     protected $encodexml;
 10
 11     public function __construct ()
 12     {
 13         $this->encodexml = new Encodexml();
 14     }
 15
 16     public function __call ($name, $arguments)
 17     {
 18         if ( isset(array_flip(get_class_methods($this))[$name] )) {
 19             // 处理微信消息事件 后返回
 20             try {
 21                 $this->ToUserName = $arguments[0][‘ToUserName‘];
 22                 $this->FromUserName = $arguments[0][‘FromUserName‘];
 23                 $this->CreateTime = $arguments[0][‘CreateTime‘];
 24             } catch (\Exception $e) {
 25                 return [-51002, null];
 26             }
 27             return call_user_func_array([$this, $name], $arguments);
 28         } else {
 29             return [-51001, null];
 30         }
 31     }
 32
 33     // 处理文字消息
 34     protected function text($param) {
 35         // 文本消息
 36         $Content = $param[‘Content‘];
 37         // 消息ID
 38         $MsgId = $param[‘MsgId‘];
 39
 40
 41         $result = $this->encodexml->text(‘欢迎!‘);
 42         return $result;
 43     }
 44
 45     // 处理图片消息
 46     protected function image($param) {
 47         // 图片链接
 48         $PicUrl = $param[‘PicUrl‘];
 49         // 图片消息媒体id
 50         $MediaId = $param[‘MediaId‘];
 51         // 消息ID
 52         $MsgId = $param[‘MsgId‘];
 53
 54         $result = $this->encodexml->text(‘success‘);
 55         return $result;
 56     }
 57
 58     // 处理语音消息
 59     protected function voice($param) {
 60         // 语音消息媒体id
 61         $MediaId = $param[‘MediaId‘];
 62         // 语音格式
 63         $Format = $param[‘Format‘];
 64         // 消息ID
 65         $MsgId = $param[‘MsgId‘];
 66         // 语音识别结果
 67         $Recognition = isset($param[‘Recognition‘]) ? $param[‘Recognition‘] : null;
 68
 69         $result = $this->encodexml->text(‘success‘);
 70         return $result;
 71     }
 72
 73     // 处理视频消息
 74     protected function video($param) {
 75         // 视频消息媒体id
 76         $MediaId = $param[‘MediaId‘];
 77         // 视频缩略图id
 78         $ThumbMediaId = $param[‘ThumbMediaId‘];
 79         // 消息ID
 80         $MsgId = $param[‘MsgId‘];
 81
 82         $result = $this->encodexml->text(‘success‘);
 83         return $result;
 84     }
 85
 86     // 处理小视频消息
 87     protected function shortvideo($param) {
 88         // 小视频消息媒体id
 89         $MediaId = $param[‘MediaId‘];
 90         // 视频缩略图id
 91         $ThumbMediaId = $param[‘ThumbMediaId‘];
 92         // 消息ID
 93         $MsgId = $param[‘MsgId‘];
 94
 95         $result = $this->encodexml->text(‘success‘);
 96         return $result;
 97     }
 98
 99     // 处理地理位置消息
100     protected function location($param) {
101         // 地理位置维度
102         $Location_X = $param[‘Location_X‘];
103         // 地理位置经度
104         $Location_Y = $param[‘Location_Y‘];
105         // 地图缩放大小
106         $Scale = $param[‘Scale‘];
107         // 地理位置信息
108         $Label = $param[‘Label‘];
109         // 消息ID
110         $MsgId = $param[‘MsgId‘];
111
112         $result = $this->encodexml->text(‘success‘);
113         return $result;
114     }
115
116     // 处理链接消息
117     protected function link($param) {
118         // 消息标题
119         $Title = $param[‘Title‘];
120         // 消息ID
121         $MsgId = $param[‘MsgId‘];
122
123         $result = $this->encodexml->text(‘success‘);
124         return $result;
125     }
126
127     // 处理事件 关注公众号
128     protected function event_subscribe($param) {
129
130         if (isset($param[‘EventKey‘])) {
131             // 扫描带参数二维码事件
132             // 事件KEY值
133             $EventKey = $param[‘EventKey‘];
134             // 二维码的ticket
135             $Ticket = $param[‘Ticket‘];
136
137         } else {
138             // 关注事件
139
140         }
141
142         $result = $this->encodexml->text(‘success‘);
143         return $result;
144     }
145
146     // 处理事件 用户已关注时的事件推送
147     protected function event_SCAN($param) {
148         // 事件KEY值
149         $EventKey = $param[‘EventKey‘];
150         // 二维码的ticket
151         $Ticket = $param[‘Ticket‘];
152
153         $result = $this->encodexml->text(‘success‘);
154         return $result;
155     }
156
157     // 处理事件 上报地理位置事件
158     protected function event_LOCATION($param) {
159         // 地理位置纬度
160         $Latitude = $param[‘Latitude‘];
161         //     地理位置经度
162         $Longitude = $param[‘Longitude‘];
163         // 地理位置精度
164         $Precision = $param[‘Precision‘];
165
166         $result = $this->encodexml->text(‘success‘);
167         return $result;
168     }
169
170     // 处理事件 自定义菜单事件 点击菜单拉取消息时的事件推送
171     protected function event_CLICK($param) {
172         // 事件KEY值
173         $EventKey = $param[‘EventKey‘];
174
175         $result = $this->encodexml->text(‘success‘);
176         return $result;
177     }
178
179     // 处理事件 自定义菜单事件 点击菜单跳转链接时的事件推送
180     protected function event_VIEW($param) {
181         // 事件KEY值
182         $EventKey = $param[‘EventKey‘];
183
184         $result = $this->encodexml->text(‘success‘);
185         return $result;
186     }
187
188 }

WeixinEvent

使用方法

 1 namespace app\wechat\Controller;
 2
 3 use think\Controller;
 4 use app\common\logic\Wechat;
 5
 6 class Index extends Controller
 7 {
 8
 9     public function api ()
10     {
11         config(‘app_trace‘, false);
12
13         $userid = input(‘get.id/s,0‘);
14         // 查询公众号
15         $data = db(‘wechat_accounts‘)->where([‘account_id‘=>$userid])->find();
16         if (!$data) {exit;}
17         $user[‘id‘] = $data[‘account_id‘];
18         $user[‘token‘] = $data[‘token‘];
19         $user[‘appId‘] = $data[‘app_id‘];
20         $user[‘appSecret‘] = $data[‘app_secret‘];
21         $user[‘encodingAESKey‘] = $data[‘aeskey‘];
22         // 不验证消息
23         //$user[‘authMsg‘] = false;
24
25         // 回调函数
26         $response = function ($msgType,$data) {
27             $event = new Wechat\WeixinEvent();
28             return $event->$msgType($data);
29         };
30
31         $wiki = new Wechat\Wechat($user);
32         // 获取信息体
33         $response = $wiki->getBackMsg($_GET, file_get_contents(‘php://input‘),$response);
34
35         // 记录日志
36         RecordLog(
37             ‘WikiMsg‘,
38             [
39                 ‘time‘ => $wiki->receiveTime,
40                 ‘state‘ => $wiki->getError($response),
41                 ‘receiveMsg‘ => $wiki->receiveMsg,
42                 ‘receiveEncryptMsg‘ => $wiki->receiveEncryptMsg,
43                 ‘sendMsg‘ => $wiki->sendMsg,
44                 ‘sendEncryptMsg‘ => $wiki->sendEncryptMsg,
45             ]);
46
47         print $response;
48
49     }
50
51 }


原文地址:https://www.cnblogs.com/everz/p/8572650.html

时间: 2024-10-11 06:06:23

微信公众平台 自动回复消息的相关文章

微信公众号教程(3)微信公众平台群发消息

微信公众平台最常用到的就是群发消息,这种一对多.几乎百分百到达的传播方式,取代了短信群发,并且具有多媒体形态,还可以直接引导转化.因此编辑一条好的内容,挑选恰当的时候发送,既可以给用户带来有价值的信息,又有机会给公众账号带来新的关注和收益.本期将重点为大家讲解素材管理该怎么玩,群发消息需要注意些什么. 第三章 微信公众平台群发消息 一.微信公众平台后台素材管理   点击公众平台后台导航的"素材管理",或者在群发消息或者与用户聊天页面时,选择发送多媒体内容时快捷进入.这个页面主要是用来管

另类保存微信公众平台历史消息的方法 - 星标消息

前面怎样把微信聊天记录导出备份到电脑[微信公众平台技巧]介绍的通过复制源代码来保存微信公众平台历史消息的方法,有网友反映说不会用批量替换.不会批量换行保存,一头雾水.这里我们就说个简单的方法,直接用星标消息保存,打开微信公众号消息管理,鼠标移动到你要保存的消息,右侧会显示几个菜单,其中有个星星形状的,默认是灰色的,如下图红框所示,点击后变成黄色的小星星,这样微信公众号星标信息就完成了,so easy!哪里要存点哪里! 腾讯客服提供的回答"微信公众号星标信息有什么用?" 他们的解释是:

微信公众平台群发消息的方法及注意事项

群发消息的方法 操作方法:登录微信公众平台(https://mp.weixin.qq.com)=>群发消息=>根据需要填写文字/语音/图片/视频/录音等内容后,选择对群发对象.性别.群发地区发送即可. 群发消息内容 目前支持群发的内容:文字.语音.图片.视频.图文消息. 1.群发内容中需添加文字+图片+视频,可先在“素材管理”中设置图文消息,然后群发时选择“图文消息”类型即可. 2.上传至素材管理中的图片.语音可多次群发,没有有效期. 3.群发图文消息的标题上限为64个字节: 4.群发内容字数

微信公众平台-接收消息与发送被动消息

接收消息代码如下(包含回复消息调用): /// <summary> /// 接收用户消息 /// iftrue /// 2014-07-08 /// </summary> public class Receive { public delegate Models.Send_Msg delegate_SendMsg(string msgType); public delegate void delegate_RececiveHandler(Models.Receive_Msg mod

微信公众平台图文消息条数限制在1条以内

从2018年10月12日起,微信公众平台图文消息被限制为1条. 受影响的有 客服接口发送的图文消息,如 { "touser":"OPENID", "msgtype":"news", "news":{ "articles": [ { "title":"Happy Day", "description":"Is Reall

[号外]微信公众平台开发---消息体签名及加解密

离刚开始做微信开发到现在已经两个月了,因为被分配了另外一个任务,所以微信的开发就先放到了一遍. 在小公司便是如此,只有自己一个人做开发,所以哪里需要就要先转到哪一块.其实想想自己也没什么太好的理由说留在这个公司,想想也就是工作比较放松点,老板人还可以,项目上也催的不紧,孩子还小家里有点事了可以随时请假回家,这次也是家里父亲和小孩都住院了请了半个月假刚过来. 闲话不多说,先说下微信开发的博客,微信开发也算完成了一部分,因为没有完成,所以之前的『微信公众平台开发(一)---接口介绍及配置』起了个头便

微信公众平台开发学习系列(二):微信公众平台接收消息与发送消息

本篇主要介绍如何使用senparc来处理微信公众平台的接收消息与发送消息. 首先微信端会将用户的发送的信息以post请求发送到填写的url上,服务端代码如下: 1 [HttpPost] 2 public ActionResult Get(PostModel postModel) 3 { 4 var messageHandler = new CustomMessageHandler(Request.InputStream, postModel); 5 6 messageHandler.Execut

微信公众平台的消息类型与事件类型

消息类型(MsgType):event textvoicevideolocationlink 而消息类型(Event)下的事件类型:subscribeSCANLOCATIONCLICKVIEW 具体内容参考微信公众平台网址: 消息类型: http://mp.weixin.qq.com/wiki/10/79502792eef98d6e0c6e1739da387346.html 事件类型: http://mp.weixin.qq.com/wiki/2/5baf56ce4947d35003b86a98

ASP.NET 微信公众平台模板消息推送功能完整开发

最近公众平台的用户提出了新需求,他们希望当收到新的邮件或者日程的时候,公众平台能主动推送一条提醒给用户.看了看平台提供的接口,似乎只有[模板消息]能尽量满足这一需求,但不得不说微信提供的实例太少,而且只有PHP的,不过摸索了一下还是成功的实现了. 首先是准备工作,需要申请开通[模板消息]功能,网上教程很多.然后申请通过之后,用户可以从现有的模板里选择15个使用,也可以自己添加模板使用.这里吐槽一下:申请添加新的模板时,微信出于保护用户的目的禁止了一些“看起来会很频繁”的消息类型,比如这图里的——