WechatApi.php 35 KB

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