WechatApi.php 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113
  1. <?php
  2. /**
  3. * 公众号管理
  4. */
  5. namespace app\api\controller;
  6. use think\facade\Log;
  7. final class WechatApi
  8. {
  9. const MSGTYPE_TEXT = 'text';
  10. const MSGTYPE_IMAGE = 'image';
  11. const MSGTYPE_LOCATION = 'location';
  12. const MSGTYPE_LINK = 'link';
  13. const MSGTYPE_EVENT = 'event';
  14. const MSGTYPE_MUSIC = 'music';
  15. const MSGTYPE_NEWS = 'news';
  16. const MSGTYPE_VOICE = 'voice';
  17. const MSGTYPE_VIDEO = 'video';
  18. const API_URL_PREFIX = 'https://api.weixin.qq.com/cgi-bin';
  19. const AUTH_URL = '/token?grant_type=client_credential&';
  20. const MENU_CREATE_URL = '/menu/create?';
  21. const MENU_GET_URL = '/menu/get?';
  22. const MENU_DELETE_URL = '/menu/delete?';
  23. const MEDIA_GET_URL = '/media/get?';
  24. const QRCODE_CREATE_URL = '/qrcode/create?';
  25. const QR_SCENE = 0;
  26. const QR_LIMIT_SCENE = 1;
  27. const QRCODE_IMG_URL = 'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=';
  28. const USER_GET_URL = '/user/get?';
  29. const USER_INFO_URL = '/user/info?';
  30. const GROUP_GET_URL = '/groups/get?';
  31. const GROUP_CREATE_URL = '/groups/create?';
  32. const GROUP_UPDATE_URL = '/groups/update?';
  33. const GROUP_MEMBER_UPDATE_URL = '/groups/members/update?';
  34. const CUSTOM_SEND_URL = '/message/custom/send?';
  35. const OAUTH_PREFIX = 'https://open.weixin.qq.com/connect/oauth2';
  36. const OAUTH_AUTHORIZE_URL = '/authorize?';
  37. const OAUTH_TOKEN_PREFIX = 'https://api.weixin.qq.com/sns/oauth2';
  38. const OAUTH_TOKEN_URL = '/access_token?';
  39. const OAUTH_REFRESH_URL = '/refresh_token?';
  40. const OAUTH_USERINFO_URL = 'https://api.weixin.qq.com/sns/userinfo?';
  41. const MASS_SEND_URL = '/message/mass/send?';
  42. private $token;
  43. private $appid;
  44. private $appsecret;
  45. private $access_token;
  46. private $user_token;
  47. private $_msg;
  48. private $_funcflag = false;
  49. private $_receive;
  50. public $debug = false;
  51. public $errCode = 40001;
  52. public $errMsg = "no access";
  53. private $_logcallback;
  54. private $_charset = 'utf-8';
  55. public function __construct($options) {
  56. $this->token = isset($options['token']) ? $options['token'] : '';
  57. $this->appid = isset($options['appid']) ? $options['appid'] : '';
  58. $this->appsecret = isset($options['appsecret']) ? $options['appsecret'] : '';
  59. $this->debug = isset($options['debug']) ? $options['debug'] : false;
  60. $this->_logcallback = isset($options['logcallback']) ? $options['logcallback'] : false;
  61. }
  62. /**
  63. * For weixin server validation
  64. */
  65. private function checkSignature() {
  66. $signature = isset($_GET["signature"]) ? $_GET["signature"] : '';
  67. $timestamp = isset($_GET["timestamp"]) ? $_GET["timestamp"] : '';
  68. $nonce = isset($_GET["nonce"]) ? $_GET["nonce"] : '';
  69. $token = $this->token;
  70. $tmpArr = array($token, $timestamp, $nonce);
  71. sort($tmpArr, SORT_STRING);
  72. $tmpStr = implode($tmpArr);
  73. $tmpStr = sha1($tmpStr);
  74. if ($tmpStr == $signature) {
  75. return true;
  76. } else {
  77. return false;
  78. }
  79. }
  80. /**
  81. * For weixin server validation
  82. * @param bool $return 是否返回
  83. */
  84. public function valid($return = false) {
  85. $echoStr = isset($_GET["echostr"]) ? $_GET["echostr"] : '';
  86. if ($return) {
  87. if ($echoStr) {
  88. if ($this->checkSignature())
  89. return $echoStr;
  90. else
  91. return false;
  92. }
  93. else
  94. return $this->checkSignature();
  95. } else {
  96. if ($echoStr) {
  97. if ($this->checkSignature())
  98. die($echoStr);
  99. else
  100. die('no access');
  101. } else {
  102. if ($this->checkSignature())
  103. return true;
  104. else
  105. die('no access');
  106. }
  107. }
  108. return false;
  109. }
  110. /**
  111. * 设置发送消息
  112. * @param array $msg 消息数组
  113. * @param bool $append 是否在原消息数组追加
  114. */
  115. public function Message($msg = '', $append = false) {
  116. if (is_null($msg)) {
  117. $this->_msg = array();
  118. } elseif (is_array($msg)) {
  119. if ($append)
  120. $this->_msg = array_merge($this->_msg, $msg);
  121. else
  122. $this->_msg = $msg;
  123. //return $this->_msg;
  124. } else {
  125. //return $this->_msg;
  126. }
  127. return $this->iconvUtf($this->_msg);
  128. }
  129. public function setFuncFlag($flag) {
  130. $this->_funcflag = $flag;
  131. return $this;
  132. }
  133. private function log($log) {
  134. if ($this->debug && function_exists($this->_logcallback)) {
  135. if (is_array($log))
  136. $log = print_r($log, true);
  137. return call_user_func($this->_logcallback, $log);
  138. }
  139. }
  140. /**
  141. * 获取微信服务器发来的信息
  142. */
  143. public function getRev() {
  144. if ($this->_receive)
  145. return $this;
  146. //$postStr = $GLOBALS["HTTP_RAW_POST_DATA"];//
  147. $postStr = file_get_contents("php://input");
  148. $this->log($postStr);
  149. if (!empty($postStr)) {
  150. $this->_receive = (array) simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
  151. $this->_receive = $this->iconvUtf($this->_receive);
  152. }
  153. return $this;
  154. }
  155. /**
  156. * 获取微信服务器发来的信息
  157. */
  158. public function getRevData() {
  159. return $this->iconvUtf($this->_receive);
  160. }
  161. /**
  162. * 获取消息发送者
  163. */
  164. public function getRevFrom() {
  165. if (isset($this->_receive['FromUserName']))
  166. return $this->_receive['FromUserName'];
  167. else
  168. return false;
  169. }
  170. /**
  171. * 获取消息接受者
  172. */
  173. public function getRevTo() {
  174. if (isset($this->_receive['ToUserName']))
  175. return $this->_receive['ToUserName'];
  176. else
  177. return false;
  178. }
  179. /**
  180. * 获取接收消息的类型
  181. */
  182. public function getRevType() {
  183. if (isset($this->_receive['MsgType']))
  184. return $this->_receive['MsgType'];
  185. else
  186. return false;
  187. }
  188. /**
  189. * 获取消息ID
  190. */
  191. public function getRevID() {
  192. if (isset($this->_receive['MsgId']))
  193. return $this->_receive['MsgId'];
  194. else
  195. return false;
  196. }
  197. /**
  198. * 获取消息发送时间
  199. */
  200. public function getRevCtime() {
  201. if (isset($this->_receive['CreateTime']))
  202. return $this->_receive['CreateTime'];
  203. else
  204. return false;
  205. }
  206. /**
  207. * 获取接收消息内容正文
  208. */
  209. public function getRevContent() {
  210. if (isset($this->_receive['Content']))
  211. return $this->_receive['Content'];
  212. else if (isset($this->_receive['Recognition'])) //获取语音识别文字内容,需申请开通
  213. return $this->_receive['Recognition'];
  214. else
  215. return false;
  216. }
  217. /**
  218. * 获取接收消息图片
  219. */
  220. public function getRevPic() {
  221. if (isset($this->_receive['PicUrl']))
  222. return $this->_receive['PicUrl'];
  223. else
  224. return false;
  225. }
  226. /**
  227. * 获取接收消息链接
  228. */
  229. public function getRevLink() {
  230. if (isset($this->_receive['Url'])) {
  231. return array(
  232. 'url' => $this->_receive['Url'],
  233. 'title' => $this->_receive['Title'],
  234. 'description' => $this->_receive['Description']
  235. );
  236. }
  237. else
  238. return false;
  239. }
  240. /**
  241. * 获取接收地理位置
  242. */
  243. public function getRevGeo() {
  244. if (isset($this->_receive['Location_X'])) {
  245. return array(
  246. 'x' => $this->_receive['Location_X'],
  247. 'y' => $this->_receive['Location_Y'],
  248. 'scale' => $this->_receive['Scale'],
  249. 'label' => $this->_receive['Label']
  250. );
  251. }
  252. else
  253. return false;
  254. }
  255. /**
  256. * 获取接收事件推送
  257. */
  258. public function getRevEvent() {
  259. if (isset($this->_receive['Event'])) {
  260. return array(
  261. 'event' => $this->_receive['Event'],
  262. 'key' => isset($this->_receive['EventKey'])?$this->_receive['EventKey']:'',
  263. 'Latitude' => isset($this->_receive['Latitude'])?$this->_receive['Latitude']:'',
  264. 'Longitude' => isset($this->_receive['Longitude'])?$this->_receive['Longitude']:'',
  265. 'Precision' => isset($this->_receive['Precision'])?$this->_receive['Precision']:'',
  266. );
  267. }
  268. else
  269. return false;
  270. }
  271. /**
  272. * 获取接收语言推送
  273. */
  274. public function getRevVoice() {
  275. if (isset($this->_receive['MediaId'])) {
  276. return array(
  277. 'mediaid' => $this->_receive['MediaId'],
  278. 'format' => $this->_receive['Format'],
  279. );
  280. }
  281. else
  282. return false;
  283. }
  284. /**
  285. * 获取接收视频推送
  286. */
  287. public function getRevVideo() {
  288. if (isset($this->_receive['MediaId'])) {
  289. return array(
  290. 'mediaid' => $this->_receive['MediaId'],
  291. 'thumbmediaid' => $this->_receive['ThumbMediaId']
  292. );
  293. }
  294. else
  295. return false;
  296. }
  297. /**
  298. * 获取接收TICKET
  299. */
  300. public function getRevTicket() {
  301. if (isset($this->_receive['Ticket'])) {
  302. return $this->_receive['Ticket'];
  303. }
  304. else
  305. return false;
  306. }
  307. public static function xmlSafeStr($str) {
  308. return '<![CDATA[' . preg_replace("/[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]/", '', $str) . ']]>';
  309. }
  310. /**
  311. * 数据XML编码
  312. * @param mixed $data 数据
  313. * @return string
  314. */
  315. public static function data_to_xml($data) {
  316. $xml = '';
  317. foreach ($data as $key => $val) {
  318. is_numeric($key) && $key = "item id=\"$key\"";
  319. $xml .= "<$key>";
  320. $xml .= ( is_array($val) || is_object($val)) ? self::data_to_xml($val) : self::xmlSafeStr($val);
  321. list($key, ) = explode(' ', $key);
  322. $xml .= "</$key>";
  323. }
  324. return $xml;
  325. }
  326. /**
  327. * XML编码
  328. * @param mixed $data 数据
  329. * @param string $root 根节点名
  330. * @param string $item 数字索引的子节点名
  331. * @param string $attr 根节点属性
  332. * @param string $id 数字索引子节点key转换的属性名
  333. * @param string $encoding 数据编码
  334. * @return string
  335. */
  336. public function xml_encode($data, $root = 'xml', $item = 'item', $attr = '', $id = 'id', $encoding = 'utf-8') {
  337. if (is_array($attr)) {
  338. $_attr = array();
  339. foreach ($attr as $key => $value) {
  340. $_attr[] = "{$key}=\"{$value}\"";
  341. }
  342. $attr = implode(' ', $_attr);
  343. }
  344. $attr = trim($attr);
  345. $attr = empty($attr) ? '' : " {$attr}";
  346. $xml = "<{$root}{$attr}>";
  347. $xml .= self::data_to_xml($data, $item, $id);
  348. $xml .= "</{$root}>";
  349. return $xml;
  350. }
  351. /**
  352. * 设置回复消息
  353. * Examle: $obj->text('hello')->reply();
  354. * @param string $text
  355. */
  356. public function text($text = '') {
  357. $FuncFlag = $this->_funcflag ? 1 : 0;
  358. $msg = array(
  359. 'ToUserName' => $this->getRevFrom(),
  360. 'FromUserName' => $this->getRevTo(),
  361. 'CreateTime' => TIMESTAMP,
  362. 'MsgType' => self::MSGTYPE_TEXT,
  363. 'Content' => $text,
  364. 'FuncFlag' => $FuncFlag
  365. );
  366. $this->Message($msg);
  367. return $this;
  368. }
  369. /**
  370. * 设置回复消息
  371. * Examle: $obj->text('hello')->reply();
  372. * @param string $text
  373. *
  374. */
  375. public function image($media_id = '') {
  376. $msg = array(
  377. 'touser' => $this->getRevFrom(),
  378. 'msgtype' => self::MSGTYPE_IMAGE,
  379. 'image' => array(
  380. 'media_id' => $media_id,
  381. ),
  382. );
  383. $this->sendCustomMessage($msg);
  384. // return $this;
  385. }
  386. /**
  387. * 设置回复音乐
  388. * @param string $title
  389. * @param string $desc
  390. * @param string $musicurl
  391. * @param string $hgmusicurl
  392. */
  393. public function music($title, $desc, $musicurl, $hgmusicurl = '') {
  394. $FuncFlag = $this->_funcflag ? 1 : 0;
  395. $msg = array(
  396. 'ToUserName' => $this->getRevFrom(),
  397. 'FromUserName' => $this->getRevTo(),
  398. 'CreateTime' => TIMESTAMP,
  399. 'MsgType' => self::MSGTYPE_MUSIC,
  400. 'Music' => array(
  401. 'Title' => $title,
  402. 'Description' => $desc,
  403. 'MusicUrl' => $musicurl,
  404. 'HQMusicUrl' => $hgmusicurl
  405. ),
  406. // "music":
  407. // {
  408. // "title":"MUSIC_TITLE",
  409. // "description":"MUSIC_DESCRIPTION",
  410. // "musicurl":"MUSIC_URL",
  411. // "hqmusicurl":"HQ_MUSIC_URL",
  412. // "thumb_media_id":"THUMB_MEDIA_ID"
  413. // }
  414. 'FuncFlag' => $FuncFlag
  415. );
  416. $this->Message($msg);
  417. return $this;
  418. }
  419. /**
  420. * 设置回复图文
  421. * @param array $newsData
  422. * 数组结构:
  423. * array(
  424. * [0]=>array(
  425. * 'Title'=>'msg title',
  426. * 'Description'=>'summary text',
  427. * 'PicUrl'=>'http://www.domain.com/1.jpg',
  428. * 'Url'=>'http://www.domain.com/1.html'
  429. * ),
  430. * [1]=>....
  431. * )
  432. */
  433. public function news($newsData = array()) {
  434. $FuncFlag = $this->_funcflag ? 1 : 0;
  435. $count = count($newsData);
  436. $msg = array(
  437. 'ToUserName' => $this->getRevFrom(),
  438. 'FromUserName' => $this->getRevTo(),
  439. 'MsgType' => self::MSGTYPE_NEWS,
  440. 'CreateTime' => TIMESTAMP,
  441. 'ArticleCount' => $count,
  442. 'Articles' => $newsData,
  443. 'FuncFlag' => $FuncFlag
  444. );
  445. $this->Message($msg);
  446. return $this;
  447. }
  448. /**
  449. *
  450. * 回复微信服务器, 此函数支持链式操作
  451. * @example $this->text('msg tips')->reply();
  452. * @param string $msg 要发送的信息, 默认取$this->_msg
  453. * @param bool $return 是否返回信息而不抛出到浏览器 默认:否
  454. */
  455. public function reply($msg = array(), $return = false) {
  456. if (empty($msg))
  457. $msg = $this->_msg;
  458. // Log::record(json_encode($msg));
  459. $xmldata = $this->xml_encode($msg);
  460. // $this->log($xmldata);
  461. // Log::save();
  462. if ($return)
  463. return $xmldata;
  464. else
  465. echo $xmldata;
  466. }
  467. /**
  468. * GET 请求
  469. * @param string $url
  470. */
  471. private function http_get($url) {
  472. $oCurl = curl_init();
  473. if (stripos($url, "https://") !== FALSE) {
  474. curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
  475. curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);
  476. }
  477. curl_setopt($oCurl, CURLOPT_URL, $url);
  478. curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1);
  479. $sContent = curl_exec($oCurl);
  480. $aStatus = curl_getinfo($oCurl);
  481. curl_close($oCurl);
  482. if (intval($aStatus["http_code"]) == 200) {
  483. return $sContent;
  484. } else {
  485. return false;
  486. }
  487. }
  488. /**
  489. * POST 请求
  490. * @param string $url
  491. * @param array $param
  492. * @return string content
  493. */
  494. function http_post($url, $param) {
  495. $oCurl = curl_init();
  496. if (stripos($url, "https://") !== FALSE) {
  497. curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
  498. curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);
  499. }
  500. if (is_string($param)) {
  501. $strPOST = $param;
  502. } else {
  503. $aPOST = array();
  504. foreach ($param as $key => $val) {
  505. $aPOST[] = $key . "=" . urlencode($val);
  506. }
  507. $strPOST = join("&", $aPOST);
  508. }
  509. curl_setopt($oCurl, CURLOPT_URL, $url);
  510. curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1);
  511. curl_setopt($oCurl, CURLOPT_POST, true);
  512. curl_setopt($oCurl, CURLOPT_POSTFIELDS, $strPOST);
  513. $sContent = curl_exec($oCurl);
  514. $aStatus = curl_getinfo($oCurl);
  515. curl_close($oCurl);
  516. if (intval($aStatus["http_code"]) == 200) {
  517. return $sContent;
  518. } else {
  519. return false;
  520. }
  521. }
  522. /**
  523. * 通用auth验证方法,暂时仅用于菜单更新操作
  524. * @param string $appid
  525. * @param string $appsecret
  526. */
  527. public function checkAuth($appid = '', $appsecret = '') {
  528. if (!$appid || !$appsecret) {
  529. $appid = $this->appid;
  530. $appsecret = $this->appsecret;
  531. }
  532. //TODO: get the cache access_token
  533. $result = $this->http_get(self::API_URL_PREFIX . self::AUTH_URL . 'appid=' . $appid . '&secret=' . $appsecret);
  534. if ($result) {
  535. $json = json_decode($result, true);
  536. if (!$json || isset($json['errcode'])) {
  537. $this->errCode = $json['errcode'];
  538. $this->errMsg = $json['errmsg'];
  539. return false;
  540. }
  541. $this->access_token = $json['access_token'];
  542. $expire = $json['expires_in'] ? intval($json['expires_in']) - 100 : 3600;
  543. //TODO: cache access_token
  544. return $this->access_token;
  545. }
  546. return false;
  547. }
  548. /**
  549. * 删除验证数据
  550. * @param string $appid
  551. */
  552. public function resetAuth($appid = '') {
  553. $this->access_token = '';
  554. //TODO: remove cache
  555. return true;
  556. }
  557. /**
  558. * 微信api不支持中文转义的json结构
  559. * @param array $arr
  560. */
  561. static function json_encode($arr) {
  562. $parts = array();
  563. $is_list = false;
  564. //Find out if the given array is a numerical array
  565. $keys = array_keys($arr);
  566. $max_length = count($arr) - 1;
  567. if (isset($keys[0])&&($keys[0] === 0) && ($keys[$max_length] === $max_length )) { //See if the first key is 0 and last key is length - 1
  568. $is_list = true;
  569. for ($i = 0; $i < count($keys); $i++) { //See if each key correspondes to its position
  570. if ($i != $keys [$i]) { //A key fails at position check.
  571. $is_list = false; //It is an associative array.
  572. break;
  573. }
  574. }
  575. }
  576. foreach ($arr as $key => $value) {
  577. if (is_array($value)) { //Custom handling for arrays
  578. if ($is_list)
  579. $parts [] = self::json_encode($value); /* :RECURSION: */
  580. else
  581. $parts [] = '"' . $key . '":' . self::json_encode($value); /* :RECURSION: */
  582. } else {
  583. $str = '';
  584. if (!$is_list)
  585. $str = '"' . $key . '":';
  586. //Custom handling for multiple data types
  587. if (is_numeric($value) && $value < 2000000000)
  588. $str .= $value; //Numbers
  589. elseif ($value === false)
  590. $str .= 'false'; //The booleans
  591. elseif ($value === true)
  592. $str .= 'true';
  593. else
  594. $str .= '"' . addslashes($value) . '"'; //All other things
  595. // :TODO: Is there any more datatype we should be in the lookout for? (Object?)
  596. $parts [] = $str;
  597. }
  598. }
  599. $json = implode(',', $parts);
  600. if ($is_list)
  601. return '[' . $json . ']'; //Return numerical JSON
  602. return '{' . $json . '}'; //Return associative JSON
  603. }
  604. /**
  605. * 创建菜单
  606. * @param array $data 菜单数组数据
  607. * example:
  608. {
  609. "button":[
  610. {
  611. "type":"click",
  612. "name":"今日歌曲",
  613. "key":"MENU_KEY_MUSIC"
  614. },
  615. {
  616. "type":"view",
  617. "name":"歌手简介",
  618. "url":"http://www.qq.com/"
  619. },
  620. {
  621. "name":"菜单",
  622. "sub_button":[
  623. {
  624. "type":"click",
  625. "name":"hello word",
  626. "key":"MENU_KEY_MENU"
  627. },
  628. {
  629. "type":"click",
  630. "name":"赞一下我们",
  631. "key":"MENU_KEY_GOOD"
  632. }]
  633. }]
  634. }
  635. */
  636. public function createMenu($data) {
  637. if (!$this->access_token && !$this->checkAuth())
  638. return false;
  639. $result = $this->http_post(self::API_URL_PREFIX . self::MENU_CREATE_URL . 'access_token=' . $this->access_token, self::json_encode($data));
  640. if ($result) {
  641. $json = json_decode($result, true);
  642. if (!$json || $json['errcode']!='0') {
  643. $this->errCode = $json['errcode'];
  644. $this->errMsg = $json['errmsg'];
  645. return false;
  646. }
  647. return true;
  648. }
  649. return false;
  650. }
  651. /**
  652. * 获取菜单
  653. * @return array('menu'=>array(....s))
  654. */
  655. public function getMenu() {
  656. if (!$this->access_token && !$this->checkAuth())
  657. return false;
  658. $result = $this->http_get(self::API_URL_PREFIX . self::MENU_GET_URL . 'access_token=' . $this->access_token);
  659. if ($result) {
  660. $json = json_decode($result, true);
  661. if (!$json || isset($json['errcode'])) {
  662. $this->errCode = $json['errcode'];
  663. $this->errMsg = $json['errmsg'];
  664. return false;
  665. }
  666. return $json;
  667. }
  668. return false;
  669. }
  670. /**
  671. * 删除菜单
  672. * @return boolean
  673. */
  674. public function deleteMenu() {
  675. if (!$this->access_token && !$this->checkAuth())
  676. return false;
  677. $result = $this->http_get(self::API_URL_PREFIX . self::MENU_DELETE_URL . 'access_token=' . $this->access_token);
  678. if ($result) {
  679. $json = json_decode($result, true);
  680. if (!$json || !empty($json['errcode'])) {
  681. $this->errCode = $json['errcode'];
  682. $this->errMsg = $json['errmsg'];
  683. return false;
  684. }
  685. return true;
  686. }
  687. return false;
  688. }
  689. /**
  690. * 根据媒体文件ID获取媒体文件
  691. * @param string $media_id 媒体文件id
  692. * @return raw data
  693. */
  694. public function getMedia($media_id) {
  695. if (!$this->access_token && !$this->checkAuth())
  696. return false;
  697. $result = $this->http_get(self::API_URL_PREFIX . self::MEDIA_GET_URL . 'access_token=' . $this->access_token . '&media_id=' . $media_id);
  698. if ($result) {
  699. $json = json_decode($result, true);
  700. if (isset($json['errcode'])) {
  701. $this->errCode = $json['errcode'];
  702. $this->errMsg = $json['errmsg'];
  703. return false;
  704. }
  705. return $json;
  706. }
  707. return false;
  708. }
  709. // public function addmaterial($type){
  710. // if (!$this->access_token && !$this->checkAuth())
  711. // return false;
  712. // $result = $this->http_get('https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=' . $this->access_token . '&type=' . $type);
  713. // if ($result) {
  714. // $json = json_decode($result, true);
  715. // if (isset($json['errcode'])) {
  716. // $this->errCode = $json['errcode'];
  717. // $this->errMsg = $json['errmsg'];
  718. // return false;
  719. // }
  720. // return $json;
  721. // }
  722. // return false;
  723. // https://api.weixin.qq.com/cgi-bin/material/add_news?access_token=ACCESS_TOKEN
  724. // }
  725. /**
  726. * 创建二维码ticket
  727. * @param int $scene_id 自定义追踪id
  728. * @param int $type 0:临时二维码;1:永久二维码(此时expire参数无效)
  729. * @param int $expire 临时二维码有效期,最大为1800秒
  730. * @return array('ticket'=>'qrcode字串','expire_seconds'=>1800)
  731. */
  732. public function getQRCode($scene_id, $type = 0, $expire = 1800) {
  733. if (!$this->access_token && !$this->checkAuth())
  734. return false;
  735. $data = array(
  736. 'action_name' => $type ? "QR_LIMIT_SCENE" : "QR_SCENE",
  737. 'expire_seconds' => $expire,
  738. 'action_info' => array('scene' => array('scene_id' => $scene_id))
  739. );
  740. if ($type == 1) {
  741. unset($data['expire_seconds']);
  742. }
  743. $result = $this->http_post(self::API_URL_PREFIX . self::QRCODE_CREATE_URL . 'access_token=' . $this->access_token, self::json_encode($data));
  744. if ($result) {
  745. $json = json_decode($result, true);
  746. if (!$json || !empty($json['errcode'])) {
  747. $this->errCode = $json['errcode'];
  748. $this->errMsg = $json['errmsg'];
  749. return false;
  750. }
  751. return $json;
  752. }
  753. return false;
  754. }
  755. /**
  756. * 获取二维码图片
  757. * @param string $ticket 传入由getQRCode方法生成的ticket参数
  758. * @return string url 返回http地址
  759. */
  760. public function getQRUrl($ticket) {
  761. return self::QRCODE_IMG_URL . $ticket;
  762. }
  763. /**
  764. * 批量获取关注用户列表
  765. * @param unknown $next_openid
  766. */
  767. public function getUserList($next_openid = '') {
  768. if (!$this->access_token && !$this->checkAuth())
  769. return false;
  770. $result = $this->http_get(self::API_URL_PREFIX . self::USER_GET_URL . 'access_token=' . $this->access_token . '&next_openid=' . $next_openid);
  771. if ($result) {
  772. $json = json_decode($result, true);
  773. if (isset($json['errcode'])) {
  774. $this->errCode = $json['errcode'];
  775. $this->errMsg = $json['errmsg'];
  776. return false;
  777. }
  778. return $json;
  779. }
  780. return false;
  781. }
  782. /**
  783. * 获取关注者详细信息
  784. * @param string $openid
  785. * @return array
  786. */
  787. public function getwxUserInfo($openid) {
  788. if (!$this->access_token && !$this->checkAuth())
  789. return false;
  790. $result = $this->http_get(self::API_URL_PREFIX . self::USER_INFO_URL . 'access_token=' . $this->access_token . '&openid=' . $openid);
  791. if ($result) {
  792. $json = json_decode($result, true);
  793. if (isset($json['errcode'])) {
  794. $this->errCode = $json['errcode'];
  795. $this->errMsg = $json['errmsg'];
  796. return false;
  797. }
  798. $json['access_token'] = $this->access_token;
  799. return $json;
  800. }
  801. return false;
  802. }
  803. /**
  804. * 获取用户分组列表
  805. * @return boolean|array
  806. */
  807. public function getGroup() {
  808. if (!$this->access_token && !$this->checkAuth())
  809. return false;
  810. $result = $this->http_get(self::API_URL_PREFIX . self::GROUP_GET_URL . 'access_token=' . $this->access_token);
  811. if ($result) {
  812. $json = json_decode($result, true);
  813. if (isset($json['errcode'])) {
  814. $this->errCode = $json['errcode'];
  815. $this->errMsg = $json['errmsg'];
  816. return false;
  817. }
  818. return $json;
  819. }
  820. return false;
  821. }
  822. /**
  823. * 新增自定分组
  824. * @param string $name 分组名称
  825. * @return boolean|array
  826. */
  827. public function createGroup($name) {
  828. if (!$this->access_token && !$this->checkAuth())
  829. return false;
  830. $data = array(
  831. 'group' => array('name' => $name)
  832. );
  833. $result = $this->http_post(self::API_URL_PREFIX . self::GROUP_CREATE_URL . 'access_token=' . $this->access_token, self::json_encode($data));
  834. if ($result) {
  835. $json = json_decode($result, true);
  836. if (!$json || !empty($json['errcode'])) {
  837. $this->errCode = $json['errcode'];
  838. $this->errMsg = $json['errmsg'];
  839. return false;
  840. }
  841. return $json;
  842. }
  843. return false;
  844. }
  845. /**
  846. * 更改分组名称
  847. * @param int $groupid 分组id
  848. * @param string $name 分组名称
  849. * @return boolean|array
  850. */
  851. public function updateGroup($groupid, $name) {
  852. if (!$this->access_token && !$this->checkAuth())
  853. return false;
  854. $data = array(
  855. 'group' => array('id' => $groupid, 'name' => $name)
  856. );
  857. $result = $this->http_post(self::API_URL_PREFIX . self::GROUP_UPDATE_URL . 'access_token=' . $this->access_token, self::json_encode($data));
  858. if ($result) {
  859. $json = json_decode($result, true);
  860. if (!$json || !empty($json['errcode'])) {
  861. $this->errCode = $json['errcode'];
  862. $this->errMsg = $json['errmsg'];
  863. return false;
  864. }
  865. return $json;
  866. }
  867. return false;
  868. }
  869. /**
  870. * 移动用户分组
  871. * @param int $groupid 分组id
  872. * @param string $openid 用户openid
  873. * @return boolean|array
  874. */
  875. public function updateGroupMembers($groupid, $openid) {
  876. if (!$this->access_token && !$this->checkAuth())
  877. return false;
  878. $data = array(
  879. 'openid' => $openid,
  880. 'to_groupid' => $groupid
  881. );
  882. $result = $this->http_post(self::API_URL_PREFIX . self::GROUP_MEMBER_UPDATE_URL . 'access_token=' . $this->access_token, self::json_encode($data));
  883. if ($result) {
  884. $json = json_decode($result, true);
  885. if (!$json || !empty($json['errcode'])) {
  886. $this->errCode = $json['errcode'];
  887. $this->errMsg = $json['errmsg'];
  888. return false;
  889. }
  890. return $json;
  891. }
  892. return false;
  893. }
  894. /**
  895. * 发送客服消息
  896. * @param array $data 消息结构{"touser":"OPENID","msgtype":"news","news":{...}}
  897. * @return boolean|array
  898. */
  899. public function sendCustomMessage($data) {
  900. if(!$this->access_token && !$this->checkAuth())
  901. {
  902. return false;
  903. }
  904. $result = $this->http_post(self::API_URL_PREFIX . self::CUSTOM_SEND_URL . 'access_token=' . $this->access_token, self::json_encode($data));
  905. if ($result) {
  906. $json = json_decode($result, true);
  907. //file_put_contents('send.xml',$result);
  908. if (!$json || $json['errcode']!='0') {
  909. $this->errCode = $json['errcode'];
  910. $this->errMsg = $json['errmsg'];
  911. return false;
  912. }else {
  913. return $json;
  914. }
  915. }
  916. return false;
  917. }
  918. //消息群发
  919. function massSend($data){
  920. if(!$this->access_token && !$this->checkAuth())
  921. {
  922. return false;
  923. }
  924. $result = $this->http_post(self::API_URL_PREFIX . self::MASS_SEND_URL . 'access_token=' . $this->access_token, self::json_encode($data));
  925. if ($result) {
  926. $json = json_decode($result, true);
  927. //file_put_contents('send.xml',$result);
  928. if (!$json || $json['errcode']!='0') {
  929. $this->errCode = $json['errcode'];
  930. $this->errMsg = $json['errmsg'];
  931. return false;
  932. }else {
  933. return $json;
  934. }
  935. }
  936. return false;
  937. }
  938. /**
  939. * oauth 授权跳转接口
  940. * @param string $callback 回调URI
  941. * @return string
  942. */
  943. public function getOauthRedirect($callback, $state = '', $scope = 'snsapi_userinfo') {
  944. return self::OAUTH_PREFIX . self::OAUTH_AUTHORIZE_URL . 'appid=' . $this->appid . '&redirect_uri=' . urlencode($callback) . '&response_type=code&scope=' . $scope . '&state=' . $state . '#wechat_redirect';
  945. }
  946. /*
  947. * 通过code获取Access Token
  948. * @return array {access_token,expires_in,refresh_token,openid,scope}
  949. */
  950. public function getOauthAccessToken() {
  951. $code = isset($_GET['code']) ? $_GET['code'] : '';
  952. if (!$code)
  953. return false;
  954. $result = $this->http_get(self::OAUTH_TOKEN_PREFIX . self::OAUTH_TOKEN_URL . 'appid=' . $this->appid . '&secret=' . $this->appsecret . '&code=' . $code . '&grant_type=authorization_code');
  955. if ($result) {
  956. $json = json_decode($result, true);
  957. if (!$json || !empty($json['errcode'])) {
  958. $this->errCode = $json['errcode'];
  959. $this->errMsg = $json['errmsg'];
  960. return false;
  961. }
  962. $this->user_token = $json['access_token'];
  963. return $json;
  964. }
  965. return false;
  966. }
  967. /**
  968. * 刷新access token并续期
  969. * @param string $refresh_token
  970. * @return boolean|mixed
  971. */
  972. public function getOauthRefreshToken($refresh_token) {
  973. $result = $this->http_get(self::OAUTH_TOKEN_PREFIX . self::OAUTH_REFRESH_URL . 'appid=' . $this->appid . '&grant_type=refresh_token&refresh_token=' . $refresh_token);
  974. if ($result) {
  975. $json = json_decode($result, true);
  976. if (!$json || !empty($json['errcode'])) {
  977. $this->errCode = $json['errcode'];
  978. $this->errMsg = $json['errmsg'];
  979. return false;
  980. }
  981. $this->user_token = $json['access_token'];
  982. return $json;
  983. }
  984. return false;
  985. }
  986. /**
  987. * 获取授权后的用户资料
  988. * @param string $access_token
  989. * @param string $openid
  990. * @return array {openid,nickname,sex,province,city,country,headimgurl,privilege}
  991. */
  992. public function getOauthUserinfo($access_token, $openid) {
  993. $result = $this->http_get(self::OAUTH_USERINFO_URL . 'access_token=' . $access_token . '&openid=' . $openid);
  994. if ($result) {
  995. $json = json_decode($result, true);
  996. if (!$json || !empty($json['errcode'])) {
  997. $this->errCode = $json['errcode'];
  998. $this->errMsg = $json['errmsg'];
  999. return false;
  1000. }
  1001. return $json;
  1002. }
  1003. return false;
  1004. }
  1005. /**
  1006. * 进入客服模式
  1007. * Examle: $obj->kefu()->reply();
  1008. * @param string $text
  1009. */
  1010. public function kefu() {
  1011. $msg = array(
  1012. 'ToUserName' => $this->getRevFrom(),
  1013. 'FromUserName' => $this->getRevTo(),
  1014. 'MsgType' => 'transfer_customer_service',
  1015. 'CreateTime' => TIMESTAMP
  1016. );
  1017. $this->Message($msg);
  1018. return $this;
  1019. }
  1020. //编码转换
  1021. function iconvUtf($content) {
  1022. if ($this->_charset != 'utf-8') {
  1023. $content = serialize($content);
  1024. $content = iconv($this->_charset, 'UTF-8', $content);
  1025. $content = unserialize($content);
  1026. }
  1027. return $content;
  1028. }
  1029. //获取用户上报的地理信息
  1030. function getUserLocation() {
  1031. if (empty($this->_receive['Latitude']))
  1032. return false;
  1033. return array(
  1034. 'FromUserName' => $this->_receive['FromUserName'],
  1035. 'Latitude' => $this->_receive['Latitude'],
  1036. 'Longitude' => $this->_receive['Longitude'],
  1037. 'Precision' => $this->_receive['Precision'],
  1038. );
  1039. }
  1040. }