weixin.account.class.php 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540
  1. <?php
  2. /**
  3. * [WeEngine System] Copyright (c) 2014 WE7.CC
  4. * WeEngine is NOT a free software, it under the license terms, visited http://www.we7.cc/ for more details.
  5. */
  6. defined('IN_IA') or exit('Access Denied');
  7. class WeiXinAccount extends WeAccount {
  8. public $tablename = 'account_wechats';
  9. public $apis = array();
  10. public $types = array(
  11. 'view', 'click', 'scancode_push',
  12. 'scancode_waitmsg', 'pic_sysphoto', 'pic_photo_or_album',
  13. 'pic_weixin', 'location_select', 'media_id', 'view_limited'
  14. );
  15. public function __construct($account = array()) {
  16. $this->menuFrame = 'account';
  17. $this->type = ACCOUNT_TYPE_OFFCIAL_NORMAL;
  18. $this->typeName = '公众号';
  19. }
  20. public function accountDisplayUrl() {
  21. return url('account/display');
  22. }
  23. public function fetchAccountInfo() {
  24. $account_table = table('account');
  25. $account = $account_table->getWechatappAccount($this->uniaccount['acid']);
  26. $account['encrypt_key'] = $account['key'];
  27. return $account;
  28. }
  29. public function checkSign() {
  30. $token = $this->account['token'];
  31. $signkey = array($token, $_GET['timestamp'], $_GET['nonce']);
  32. sort($signkey, SORT_STRING);
  33. $signString = implode($signkey);
  34. $signString = sha1($signString);
  35. return $signString == $_GET['signature'];
  36. }
  37. public function checkSignature($encrypt_msg) {
  38. $str = $this->buildSignature($encrypt_msg);
  39. return $str == $_GET['msg_signature'];
  40. }
  41. public function checkIntoManage() {
  42. if (empty($this->uniaccount) || (!empty($this->uniaccount['account']) && !in_array($this->uniaccount['type'], array(ACCOUNT_TYPE_OFFCIAL_NORMAL, ACCOUNT_TYPE_OFFCIAL_AUTH)) && !defined('IN_MODULE'))) {
  43. return false;
  44. }
  45. return true;
  46. }
  47. public function local_checkSignature($packet) {
  48. $token = $this->account['token'];
  49. $array = array($packet['Encrypt'], $token, $packet['TimeStamp'], $packet['Nonce']);
  50. sort($array, SORT_STRING);
  51. $str = implode($array);
  52. $str = sha1($str);
  53. return $str == $packet['MsgSignature'];
  54. }
  55. public function local_decryptMsg($postData) {
  56. $token = $this->account['token'];
  57. $encodingaeskey = $this->account['encodingaeskey'];
  58. $appid = $this->account['encrypt_key'];
  59. if(strlen($encodingaeskey) != 43) {
  60. return error(-1, "微信公众平台返回接口错误. \n错误代码为: 40004 \n,错误描述为: " . $this->encryptErrorCode('40004'));
  61. }
  62. $key = base64_decode($encodingaeskey . '=');
  63. $packet = $this->local_xmlExtract($postData);
  64. if(is_error($packet)) {
  65. return error(-1, $packet['message']);
  66. }
  67. $istrue = $this->local_checkSignature($packet);
  68. if(!$istrue) {
  69. return error(-1, "微信公众平台返回接口错误. \n错误代码为: 40001 \n,错误描述为: " . $this->encryptErrorCode('40001'));
  70. }
  71. $ciphertext_dec = base64_decode($packet['Encrypt']);
  72. $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
  73. $iv = substr($key, 0, 16);
  74. mcrypt_generic_init($module, $key, $iv);
  75. $decrypted = mdecrypt_generic($module, $ciphertext_dec);
  76. mcrypt_generic_deinit($module);
  77. mcrypt_module_close($module);
  78. $block_size = 32;
  79. $pad = ord(substr($decrypted, -1));
  80. if ($pad < 1 || $pad > 32) {
  81. $pad = 0;
  82. }
  83. $result = substr($decrypted, 0, (strlen($decrypted) - $pad));
  84. if (strlen($result) < 16) {
  85. return '';
  86. }
  87. $content = substr($result, 16, strlen($result));
  88. $len_list = unpack("N", substr($content, 0, 4));
  89. $xml_len = $len_list[1];
  90. $xml_content = substr($content, 4, $xml_len);
  91. $from_appid = substr($content, $xml_len + 4);
  92. if ($from_appid != $appid) {
  93. return error(-1, "微信公众平台返回接口错误. \n错误代码为: 40005 \n,错误描述为: " . $this->encryptErrorCode('40005'));
  94. }
  95. return $xml_content;
  96. }
  97. public function buildSignature($encrypt_msg) {
  98. $token = $this->account['token'];
  99. $array = array($encrypt_msg, $token, $_GET['timestamp'], $_GET['nonce']);
  100. sort($array, SORT_STRING);
  101. $str = implode($array);
  102. $str = sha1($str);
  103. return $str;
  104. }
  105. public function encryptMsg($text) {
  106. $token = $this->account['token'];
  107. $encodingaeskey = $this->account['encodingaeskey'];
  108. $appid = $this->account['encrypt_key'];
  109. $key = base64_decode($encodingaeskey . '=');
  110. $text = random(16) . pack("N", strlen($text)) . $text . $appid;
  111. $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
  112. $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
  113. $iv = substr($key, 0, 16);
  114. $block_size = 32;
  115. $text_length = strlen($text);
  116. $amount_to_pad = $block_size - ($text_length % $block_size);
  117. if ($amount_to_pad == 0) {
  118. $amount_to_pad = $block_size;
  119. }
  120. $pad_chr = chr($amount_to_pad);
  121. $tmp = '';
  122. for ($index = 0; $index < $amount_to_pad; $index++) {
  123. $tmp .= $pad_chr;
  124. }
  125. $text = $text . $tmp;
  126. mcrypt_generic_init($module, $key, $iv);
  127. $encrypted = mcrypt_generic($module, $text);
  128. mcrypt_generic_deinit($module);
  129. mcrypt_module_close($module);
  130. $encrypt_msg = base64_encode($encrypted);
  131. $signature = $this->buildSignature($encrypt_msg);
  132. return array($signature, $encrypt_msg);
  133. }
  134. public function decryptMsg($postData) {
  135. $token = $this->account['token'];
  136. $encodingaeskey = $this->account['encodingaeskey'];
  137. $appid = $this->account['encrypt_key'];
  138. $key = base64_decode($encodingaeskey . '=');
  139. if(strlen($encodingaeskey) != 43) {
  140. return error(-1, "微信公众平台返回接口错误. \n错误代码为: 40004 \n,错误描述为: " . $this->encryptErrorCode('40004'));
  141. }
  142. $packet = $this->xmlExtract($postData);
  143. if(is_error($packet)) {
  144. return error(-1, $packet['message']);
  145. }
  146. $istrue = $this->checkSignature($packet['encrypt']);
  147. if(!$istrue) {
  148. return error(-1, "微信公众平台返回接口错误. \n错误代码为: 40001 \n,错误描述为: " . $this->encryptErrorCode('40001'));
  149. }
  150. $ciphertext_dec = base64_decode($packet['encrypt']);
  151. $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
  152. $iv = substr($key, 0, 16);
  153. mcrypt_generic_init($module, $key, $iv);
  154. $decrypted = mdecrypt_generic($module, $ciphertext_dec);
  155. mcrypt_generic_deinit($module);
  156. mcrypt_module_close($module);
  157. $block_size = 32;
  158. $pad = ord(substr($decrypted, -1));
  159. if ($pad < 1 || $pad > 32) {
  160. $pad = 0;
  161. }
  162. $result = substr($decrypted, 0, (strlen($decrypted) - $pad));
  163. if (strlen($result) < 16) {
  164. return '';
  165. }
  166. $content = substr($result, 16, strlen($result));
  167. $len_list = unpack("N", substr($content, 0, 4));
  168. $xml_len = $len_list[1];
  169. $xml_content = substr($content, 4, $xml_len);
  170. $from_appid = substr($content, $xml_len + 4);
  171. if ($from_appid != $appid) {
  172. return error(-1, "微信公众平台返回接口错误. \n错误代码为: 40005 \n,错误描述为: " . $this->encryptErrorCode('40005'));
  173. }
  174. return $xml_content;
  175. }
  176. function xmlDetract($data) {
  177. $xml['Encrypt'] = $data[1];
  178. $xml['MsgSignature'] = $data[0];
  179. $xml['TimeStamp'] = $_GET['timestamp'];
  180. $xml['Nonce'] = $_GET['nonce'];
  181. return array2xml($xml);
  182. }
  183. public function xmlExtract($message) {
  184. $packet = array();
  185. if (!empty($message)){
  186. $obj = isimplexml_load_string($message, 'SimpleXMLElement', LIBXML_NOCDATA);
  187. if($obj instanceof SimpleXMLElement) {
  188. $packet['encrypt'] = strval($obj->Encrypt);
  189. $packet['to'] = strval($obj->ToUserName);
  190. }
  191. }
  192. if(!empty($packet['encrypt'])) {
  193. return $packet;
  194. } else {
  195. return error(-1, "微信公众平台返回接口错误. \n错误代码为: 40002 \n,错误描述为: " . $this->encryptErrorCode('40002'));
  196. }
  197. }
  198. public function local_xmlExtract($message) {
  199. $packet = array();
  200. if (!empty($message)){
  201. $obj = isimplexml_load_string($message, 'SimpleXMLElement', LIBXML_NOCDATA);
  202. if($obj instanceof SimpleXMLElement) {
  203. $packet['Encrypt'] = strval($obj->Encrypt);
  204. $packet['MsgSignature'] = strval($obj->MsgSignature);
  205. $packet['TimeStamp'] = strval($obj->TimeStamp);
  206. $packet['Nonce'] = strval($obj->Nonce);
  207. }
  208. }
  209. if(!empty($packet)) {
  210. return $packet;
  211. } else {
  212. return error(-1, "微信公众平台返回接口错误. \n错误代码为: 40002 \n,错误描述为: " . $this->encryptErrorCode('40002'));
  213. }
  214. }
  215. public function queryAvailableMessages() {
  216. $messages = array('text', 'image', 'voice', 'video', 'location', 'link', 'subscribe', 'unsubscribe');
  217. if(!empty($this->account['key']) && !empty($this->account['secret'])) {
  218. $level = intval($this->account['level']);
  219. if($level > 1){
  220. $messages[] = 'click';
  221. $messages[] = 'view';
  222. }
  223. if ($level > 2) {
  224. $messages[] = 'qr';
  225. $messages[] = 'trace';
  226. }
  227. }
  228. return $messages;
  229. }
  230. public function queryAvailablePackets() {
  231. $packets = array('text', 'music', 'news');
  232. if(!empty($this->account['key']) && !empty($this->account['secret'])) {
  233. if (intval($this->account['level']) > 1) {
  234. $packets[] = 'image';
  235. $packets[] = 'voice';
  236. $packets[] = 'video';
  237. }
  238. }
  239. return $packets;
  240. }
  241. public function isMenuSupported() {
  242. return !empty($this->account['key']) &&
  243. !empty($this->account['secret']) &&
  244. (intval($this->account['level']) > 1);
  245. }
  246. public function menuCreate($menu) {
  247. global $_W;
  248. $token = $this->getAccessToken();
  249. if(is_error($token)){
  250. return $token;
  251. }
  252. $url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token={$token}";
  253. if(!empty($menu['matchrule'])) {
  254. $url = "https://api.weixin.qq.com/cgi-bin/menu/addconditional?access_token={$token}";
  255. }
  256. $data = urldecode(json_encode($menu));
  257. $response = ihttp_post($url, $data);
  258. if(is_error($response)) {
  259. return error(-1, "访问公众平台接口失败, 错误: {$response['message']}");
  260. }
  261. $result = @json_decode($response['content'], true);
  262. if(!empty($result['errcode'])) {
  263. return error(-1, "访问微信接口错误, 错误代码: {$result['errcode']}, 错误信息: {$result['errmsg']},错误详情:{$this->errorCode($result['errcode'])}");
  264. }
  265. return $result['menuid'];
  266. }
  267. public function menuDelete($menuid = 0) {
  268. $token = $this->getAccessToken();
  269. if(is_error($token)){
  270. return $token;
  271. }
  272. if($menuid > 0) {
  273. $url = "https://api.weixin.qq.com/cgi-bin/menu/delconditional?access_token={$token}";
  274. $data = array(
  275. 'menuid' => $menuid
  276. );
  277. $response = ihttp_post($url, json_encode($data));
  278. } else {
  279. $url = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token={$token}";
  280. $response = ihttp_get($url);
  281. }
  282. if(is_error($response)) {
  283. return error(-1, "访问公众平台接口失败, 错误: {$response['message']}");
  284. }
  285. $result = @json_decode($response['content'], true);
  286. if(!empty($result['errcode'])) {
  287. return error(-1, "访问微信接口错误, 错误代码: {$result['errcode']}, 错误信息: {$result['errmsg']},错误详情:{$this->errorCode($result['errcode'])}");
  288. }
  289. return true;
  290. }
  291. public function menuModify($menu) {
  292. return $this->menuCreate($menu);
  293. }
  294. public function menuCurrentQuery() {
  295. $token = $this->getAccessToken();
  296. if(is_error($token)){
  297. return $token;
  298. }
  299. $url = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token={$token}";
  300. $result = $this->requestApi($url);
  301. return $result;
  302. }
  303. public function menuQuery() {
  304. $token = $this->getAccessToken();
  305. if(is_error($token)){
  306. return $token;
  307. }
  308. $url = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token={$token}";
  309. $response = ihttp_get($url);
  310. if(is_error($response)) {
  311. return error(-1, "访问公众平台接口失败, 错误: {$response['message']}");
  312. }
  313. $result = @json_decode($response['content'], true);
  314. if(!empty($result['errcode']) && $result['errcode'] != '46003') {
  315. return error(-1, "访问微信接口错误, 错误代码: {$result['errcode']}, 错误信息: {$result['errmsg']},错误详情:{$this->errorCode($result['errcode'])}");
  316. }
  317. return $result;
  318. }
  319. public function fansQueryInfo($uniid, $isOpen = true) {
  320. if($isOpen) {
  321. $openid = $uniid;
  322. } else {
  323. exit('error');
  324. }
  325. $token = $this->getAccessToken();
  326. if(is_error($token)){
  327. return $token;
  328. }
  329. $url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token={$token}&openid={$openid}&lang=zh_CN";
  330. $response = ihttp_get($url);
  331. if(is_error($response)) {
  332. return error(-1, "访问公众平台接口失败, 错误: {$response['message']}");
  333. }
  334. preg_match('/city":"(.*)","province":"(.*)","country":"(.*)"/U', $response['content'], $reg_arr);
  335. $city = htmlentities(bin2hex($reg_arr[1]));
  336. $province = htmlentities(bin2hex($reg_arr[2]));
  337. $country = htmlentities(bin2hex($reg_arr[3]));
  338. $response['content'] = str_replace('"city":"'.$reg_arr[1].'","province":"'.$reg_arr[2].'","country":"'.$reg_arr[3].'"', '"city":"'.$city.'","province":"'.$province.'","country":"'.$country.'"', $response['content']);
  339. $result = @json_decode($response['content'], true);
  340. $result['city'] = hex2bin(html_entity_decode($result['city']));
  341. $result['province'] = hex2bin(html_entity_decode($result['province']));
  342. $result['country'] = hex2bin(html_entity_decode($result['country']));
  343. if(empty($result)) {
  344. return error(-1, "接口调用失败, 元数据: {$response['meta']}");
  345. } elseif(!empty($result['errcode'])) {
  346. return error(-1, "访问微信接口错误, 错误代码: {$result['errcode']}, 错误信息: {$result['errmsg']},错误详情:{$this->errorCode($result['errcode'])}");
  347. }
  348. return $result;
  349. }
  350. public function fansBatchQueryInfo($data) {
  351. if(empty($data)) {
  352. return error(-1, '粉丝openid错误');
  353. }
  354. foreach($data as $da) {
  355. $post[] = array(
  356. 'openid' => trim($da),
  357. 'lang' => 'zh-CN'
  358. );
  359. }
  360. $data = array();
  361. $data['user_list'] = $post;
  362. $token = $this->getAccessToken();
  363. if(is_error($token)){
  364. return $token;
  365. }
  366. $url = "https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token={$token}";
  367. $response = ihttp_post($url, json_encode($data));
  368. if(is_error($response)) {
  369. return error(-1, "访问公众平台接口失败, 错误: {$response['message']}");
  370. }
  371. $result = @json_decode($response['content'], true);
  372. if(empty($result)) {
  373. return error(-1, "接口调用失败, 元数据: {$response['meta']}");
  374. } elseif(!empty($result['errcode'])) {
  375. return error(-1, "访问微信接口错误, 错误代码: {$result['errcode']}, 错误信息: {$result['errmsg']},错误详情:{$this->errorCode($result['errcode'])}");
  376. }
  377. return $result['user_info_list'];
  378. }
  379. public function fansAll($startopenid = '') {
  380. global $_GPC;
  381. $token = $this->getAccessToken();
  382. if(is_error($token)){
  383. return $token;
  384. }
  385. $url = 'https://api.weixin.qq.com/cgi-bin/user/get?access_token=' . $token;
  386. if(!empty($_GPC['next_openid'])) {
  387. $startopenid = $_GPC['next_openid'];
  388. }
  389. if (!empty($startopenid)) {
  390. $url .= '&next_openid=' . $startopenid;
  391. }
  392. $response = ihttp_get($url);
  393. if(is_error($response)) {
  394. return error(-1, "访问公众平台接口失败, 错误: {$response['message']}");
  395. }
  396. $result = @json_decode($response['content'], true);
  397. if(empty($result)) {
  398. return error(-1, "接口调用失败, 元数据: {$response['meta']}");
  399. } elseif(!empty($result['errcode'])) {
  400. return error(-1, "访问公众平台接口失败, 错误: {$result['errmsg']},错误详情:{$this->errorCode($result['errcode'])}");
  401. }
  402. $return = array();
  403. $return['total'] = $result['total'];
  404. $return['fans'] = $result['data']['openid'];
  405. $return['next'] = $result['next_openid'];
  406. return $return;
  407. }
  408. public function queryBarCodeActions() {
  409. return array('barCodeCreateDisposable', 'barCodeCreateFixed');
  410. }
  411. public function barCodeCreateDisposable($barcode) {
  412. $barcode['expire_seconds'] = empty($barcode['expire_seconds']) ? 2592000 : $barcode['expire_seconds'];
  413. if (empty($barcode['action_info']['scene']['scene_id']) || empty($barcode['action_name'])) {
  414. return error('1', 'Invalid params');
  415. }
  416. $token = $this->getAccessToken();
  417. $response = ihttp_request("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=".$token, json_encode($barcode));
  418. if (is_error($response)) {
  419. return $response;
  420. }
  421. $content = @json_decode($response['content'], true);
  422. if(empty($content)) {
  423. return error(-1, "接口调用失败, 元数据: {$response['meta']}");
  424. }
  425. if (!empty($content['errcode'])) {
  426. return error(-1, "访问微信接口错误, 错误代码: {$content['errcode']}, 错误信息: {$content['errmsg']},错误详情:{$this->errorCode($content['errcode'])}");
  427. }
  428. return $content;
  429. }
  430. public function barCodeCreateFixed($barcode) {
  431. if($barcode['action_name'] == 'QR_LIMIT_SCENE' && empty($barcode['action_info']['scene']['scene_id'])) {
  432. return error('1', '场景值错误');
  433. }
  434. if($barcode['action_name'] == 'QR_LIMIT_STR_SCENE' && empty($barcode['action_info']['scene']['scene_str'])) {
  435. return error('1', '场景字符串错误');
  436. }
  437. $token = $this->getAccessToken();
  438. $response = ihttp_request("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=".$token, json_encode($barcode));
  439. if (is_error($response)) {
  440. return $response;
  441. }
  442. $content = @json_decode($response['content'], true);
  443. if(empty($content)) {
  444. return error(-1, "接口调用失败, 元数据: {$response['meta']}");
  445. }
  446. if(!empty($content['errcode'])) {
  447. return error(-1, "访问微信接口错误, 错误代码: {$content['errcode']}, 错误信息: {$content['errmsg']},错误详情:{$this->errorCode($content['errcode'])}");
  448. }
  449. return $content;
  450. }
  451. private function encryptErrorCode($code) {
  452. $errors = array(
  453. '40001' => '签名验证错误',
  454. '40002' => 'xml解析失败',
  455. '40003' => 'sha加密生成签名失败',
  456. '40004' => 'encodingAesKey 非法',
  457. '40005' => 'appid 校验错误',
  458. '40006' => 'aes 加密失败',
  459. '40007' => 'aes 解密失败',
  460. '40008' => '解密后得到的buffer非法',
  461. '40009' => 'base64加密失败',
  462. '40010' => 'base64解密失败',
  463. '40011' => '生成xml失败',
  464. );
  465. if($errors[$code]) {
  466. return $errors[$code];
  467. } else {
  468. return '未知错误';
  469. }
  470. }
  471. public function changeSend($send) {
  472. if (empty($send)) {
  473. return error(-1, 'Invalid params');
  474. }
  475. $token = $this->getAccessToken();
  476. if(is_error($token)){
  477. return $token;
  478. }
  479. $sendapi = 'https://api.weixin.qq.com/pay/delivernotify?access_token='.$token;
  480. $response = ihttp_request($sendapi, json_encode($send));
  481. $response = json_decode($response['content'], true);
  482. if (empty($response)) {
  483. return error(-1, '发货失败,请检查您的公众号权限或是公众号AppId和公众号AppSecret!');
  484. }
  485. if (!empty($response['errcode'])) {
  486. return error(-1, $response['errmsg']);
  487. }
  488. return true;
  489. }
  490. public function getAccessToken() {
  491. $cachekey = "accesstoken:{$this->account['acid']}";
  492. $cache = cache_load($cachekey);
  493. if (!empty($cache) && !empty($cache['token']) && $cache['expire'] > TIMESTAMP) {
  494. $this->account['access_token'] = $cache;
  495. return $cache['token'];
  496. }
  497. if (empty($this->account['key']) || empty($this->account['secret'])) {
  498. return error('-1', '未填写公众号的 appid 或 appsecret!');
  499. }
  500. $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->account['key']}&secret={$this->account['secret']}";
  501. $content = ihttp_get($url);
  502. if(is_error($content)) {
  503. return error('-1', '获取微信公众号授权失败, 请稍后重试!错误详情: ' . $content['message']);
  504. }
  505. if (empty($content['content'])) {
  506. return error('-1', 'AccessToken获取失败,请检查appid和appsecret的值是否与微信公众平台一致!');
  507. }
  508. $token = @json_decode($content['content'], true);
  509. if(empty($token) || !is_array($token) || empty($token['access_token']) || empty($token['expires_in'])) {
  510. $errorinfo = substr($content['meta'], strpos($content['meta'], '{'));
  511. $errorinfo = @json_decode($errorinfo, true);
  512. return error('-1', '获取微信公众号授权失败, 请稍后重试! 公众平台返回原始数据为: 错误代码-' . $errorinfo['errcode'] . ',错误信息-' . $errorinfo['errmsg']);
  513. }
  514. $record = array();
  515. $record['token'] = $token['access_token'];
  516. $record['expire'] = TIMESTAMP + $token['expires_in'] - 200;
  517. $this->account['access_token'] = $record;
  518. cache_write($cachekey, $record);
  519. return $record['token'];
  520. }
  521. public function getVailableAccessToken() {
  522. $accounts = pdo_fetchall("SELECT `key`, `secret`, `acid` FROM ".tablename('account_wechats')." WHERE uniacid = :uniacid ORDER BY `level` DESC ", array(':uniacid' => $GLOBALS['_W']['uniacid']));
  523. if (empty($accounts)) {
  524. return error(-1, 'no permission');
  525. }
  526. foreach ($accounts as $account) {
  527. if (empty($account['key']) || empty($account['secret'])) {
  528. continue;
  529. }
  530. $acid = $account['acid'];
  531. break;
  532. }
  533. $account = WeAccount::create($acid);
  534. return $account->getAccessToken();
  535. }
  536. public function fetch_token() {
  537. return $this->getAccessToken();
  538. }
  539. public function fetch_available_token() {
  540. return $this->getVailableAccessToken();
  541. }
  542. public function clearAccessToken() {
  543. $access_token = $this->getAccessToken();
  544. if(is_error($access_token)){
  545. return $access_token;
  546. }
  547. $url = 'https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token=' . $access_token;
  548. $response = $this->requestApi($url);
  549. if (is_error($response) && $response['errno'] == '40001') {
  550. $cachekey = "accesstoken:{$this->account['acid']}";
  551. cache_delete($cachekey);
  552. }
  553. return true;
  554. }
  555. public function getJsApiTicket(){
  556. $cachekey = "jsticket:{$this->account['acid']}";
  557. $cache = cache_load($cachekey);
  558. if(!empty($cache) && !empty($cache['ticket']) && $cache['expire'] > TIMESTAMP) {
  559. return $cache['ticket'];
  560. }
  561. $access_token = $this->getAccessToken();
  562. if(is_error($access_token)){
  563. return $access_token;
  564. }
  565. $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={$access_token}&type=jsapi";
  566. $content = ihttp_get($url);
  567. if(is_error($content)) {
  568. return error(-1, '调用接口获取微信公众号 jsapi_ticket 失败, 错误信息: ' . $content['message']);
  569. }
  570. $result = @json_decode($content['content'], true);
  571. if(empty($result) || intval(($result['errcode'])) != 0 || $result['errmsg'] != 'ok') {
  572. return error(-1, '获取微信公众号 jsapi_ticket 结果错误, 错误信息: ' . $result['errmsg']);
  573. }
  574. $record = array();
  575. $record['ticket'] = $result['ticket'];
  576. $record['expire'] = TIMESTAMP + $result['expires_in'] - 200;
  577. $this->account['jsapi_ticket'] = $record;
  578. cache_write($cachekey, $record);
  579. return $record['ticket'];
  580. }
  581. public function getJssdkConfig($url = ''){
  582. global $_W;
  583. $jsapiTicket = $this->getJsApiTicket();
  584. if(is_error($jsapiTicket)){
  585. $jsapiTicket = $jsapiTicket['message'];
  586. }
  587. $nonceStr = random(16);
  588. $timestamp = TIMESTAMP;
  589. $url = empty($url) ? $_W['siteurl'] : $url;
  590. $string1 = "jsapi_ticket={$jsapiTicket}&noncestr={$nonceStr}&timestamp={$timestamp}&url={$url}";
  591. $signature = sha1($string1);
  592. $config = array(
  593. "appId" => $this->account['key'],
  594. "nonceStr" => $nonceStr,
  595. "timestamp" => "$timestamp",
  596. "signature" => $signature,
  597. );
  598. if(DEVELOPMENT) {
  599. $config['url'] = $url;
  600. $config['string1'] = $string1;
  601. $config['name'] = $this->account['name'];
  602. }
  603. return $config;
  604. }
  605. public function long2short($longurl) {
  606. $token = $this->getAccessToken();
  607. if(is_error($token)){
  608. return $token;
  609. }
  610. $url = "https://api.weixin.qq.com/cgi-bin/shorturl?access_token={$token}";
  611. $send = array();
  612. $send['action'] = 'long2short';
  613. $send['long_url'] = $longurl;
  614. $response = ihttp_request($url, json_encode($send));
  615. if(is_error($response)) {
  616. return error(-1, "访问公众平台接口失败, 错误: {$response['message']}");
  617. }
  618. $result = @json_decode($response['content'], true);
  619. if(empty($result)) {
  620. return error(-1, "接口调用失败, 元数据: {$response['meta']}");
  621. } elseif(!empty($result['errcode'])) {
  622. return error(-1, "访问微信接口错误, 错误代码: {$result['errcode']}, 错误信息: {$result['errmsg']},错误详情:{$this->errorCode($result['errcode'])}");
  623. }
  624. return $result;
  625. }
  626. public function fetchChatLog($params = array()) {
  627. if(empty($params['starttime']) || empty($params['endtime'])) {
  628. return error(-1, '没有要查询的时间段');
  629. }
  630. $starttmp = date('Y-m-d', $params['starttime']);
  631. $endtmp = date('Y-m-d', $params['endtime']);
  632. if($starttmp != $endtmp) {
  633. return error(-1, '时间范围有误,微信公众平台不支持跨日查询');
  634. }
  635. if(empty($params['openid'])) {
  636. return error(-1, '没有要查询的openid');
  637. }
  638. if(empty($params['pagesize'])) {
  639. $params['pagesize'] = 50;
  640. }
  641. if(empty($params['pageindex'])) {
  642. $params['pageindex'] = 1;
  643. }
  644. $token = $this->getAccessToken();
  645. if(is_error($token)){
  646. return $token;
  647. }
  648. $url = "https://api.weixin.qq.com/customservice/msgrecord/getrecord?access_token={$token}";
  649. $response = ihttp_request($url, json_encode($params));
  650. if(is_error($response)) {
  651. return error(-1, "访问公众平台接口失败, 错误: {$response['message']}");
  652. }
  653. $result = @json_decode($response['content'], true);
  654. if(empty($result)) {
  655. return error(-1, "接口调用失败, 元数据: {$response['meta']}");
  656. } elseif(!empty($result['errcode'])) {
  657. return error(-1, "访问微信接口错误, 错误代码: {$result['errcode']}, 错误信息: {$result['errmsg']},错误详情:{$this->errorCode($result['errcode'])}");
  658. }
  659. return $result;
  660. }
  661. public function isTagSupported() {
  662. return (!empty($this->account['key']) &&
  663. !empty($this->account['secret']) || $this->account['type'] == ACCOUNT_OAUTH_LOGIN) &&
  664. (intval($this->account['level']) > ACCOUNT_SERVICE);
  665. }
  666. public function fansTagAdd($tagname) {
  667. if(empty($tagname)) {
  668. return error(-1, '请填写标签名称');
  669. }
  670. $token = $this->getAccessToken();
  671. if(is_error($token)){
  672. return $token;
  673. }
  674. $url = "https://api.weixin.qq.com/cgi-bin/tags/create?access_token={$token}";
  675. $data = stripslashes(ijson_encode(array('tag' => array('name' => $tagname)), JSON_UNESCAPED_UNICODE));
  676. $result = $this->requestApi($url, $data);
  677. return $result;
  678. }
  679. public function fansTagFetchAll() {
  680. $token = $this->getAccessToken();
  681. if(is_error($token)){
  682. return $token;
  683. }
  684. $url = "https://api.weixin.qq.com/cgi-bin/tags/get?access_token={$token}";
  685. $result = $this->requestApi($url);
  686. return $result;
  687. }
  688. public function fansTagEdit($tagid, $tagname) {
  689. if(empty($tagid) || empty($tagname)) {
  690. return error(-1, '标签信息错误');
  691. }
  692. if(in_array($tagid, array(1, 2))) {
  693. return error(-1, '微信平台默认标签,不能修改');
  694. }
  695. $token = $this->getAccessToken();
  696. if(is_error($token)){
  697. return $token;
  698. }
  699. $url = "https://api.weixin.qq.com/cgi-bin/tags/update?access_token={$token}";
  700. $data = stripslashes(ijson_encode(array('tag' => array('id' => $tagid, 'name' => $tagname)), JSON_UNESCAPED_UNICODE));
  701. $result = $this->requestApi($url, $data);
  702. if (is_error($result)) {
  703. return $result;
  704. }
  705. return true;
  706. }
  707. public function fansTagDelete($tagid) {
  708. $tagid = intval($tagid);
  709. if(empty($tagid)) {
  710. return error(-1, '标签id错误');
  711. }
  712. $token = $this->getAccessToken();
  713. if(is_error($token)){
  714. return $token;
  715. }
  716. $url = "https://api.weixin.qq.com/cgi-bin/tags/delete?access_token={$token}";
  717. $data = json_encode(array('tag' => array('id' => $tagid)));
  718. $result = $this->requestApi($url, $data);
  719. if (is_error($result)) {
  720. return $result;
  721. }
  722. return true;
  723. }
  724. public function fansTagGetUserlist($tagid, $next_openid = '') {
  725. $tagid = intval($tagid);
  726. $next_openid = (string) $next_openid;
  727. if(empty($tagid)) {
  728. return error(-1, '标签id错误');
  729. }
  730. $token = $this->getAccessToken();
  731. if(is_error($token)){
  732. return $token;
  733. }
  734. $url = 'https://api.weixin.qq.com/cgi-bin/tag/get?access_token=' . $token;
  735. $data = array(
  736. 'tagid' => $tagid
  737. );
  738. if( ! empty($next_openid)){
  739. $data['next_openid'] = $next_openid;
  740. }
  741. $data = json_encode($data);
  742. $result = $this->requestApi($url, $data);
  743. return $result;
  744. }
  745. public function fansTagTagging($openid, $tagids) {
  746. $openid = (string) $openid;
  747. $tagids = (array) $tagids;
  748. if(empty($openid)){
  749. return error(-1, '没有填写用户openid');
  750. }
  751. if(empty($tagids)) {
  752. return error(-1, '没有填写标签');
  753. }
  754. if(count($tagids) > 3) {
  755. return error(-1, '最多3个标签');
  756. }
  757. $token = $this->getAccessToken();
  758. if (is_error($token)) {
  759. return $token;
  760. }
  761. $fetch_result = $this->fansTagFetchOwnTags($openid);
  762. if(is_error($fetch_result)) {
  763. return $fetch_result;
  764. }
  765. foreach($fetch_result['tagid_list'] as $del_tagid) {
  766. $this->fansTagBatchUntagging($openid, $del_tagid);
  767. }
  768. $url = "https://api.weixin.qq.com/cgi-bin/tags/members/batchtagging?access_token={$token}";
  769. foreach($tagids as $tagid) {
  770. $data = array(
  771. 'openid_list' => $openid,
  772. 'tagid' => $tagid
  773. );
  774. $data = json_encode($data);
  775. $result = $this->requestApi($url, $data);
  776. if(is_error($result)) {
  777. return $result;
  778. }
  779. }
  780. return true;
  781. }
  782. public function fansTagBatchTagging($openid_list, $tagid) {
  783. $openid_list = (array) $openid_list;
  784. $tagid = (int) $tagid;
  785. if(empty($openid_list)){
  786. return error(-1, '没有填写用户openid列表');
  787. }
  788. if(empty($tagid)) {
  789. return error(-1, '没有填写tagid');
  790. }
  791. $token = $this->getAccessToken();
  792. if(is_error($token)){
  793. return $token;
  794. }
  795. $url = "https://api.weixin.qq.com/cgi-bin/tags/members/batchtagging?access_token={$token}";
  796. $data = array(
  797. 'openid_list' => $openid_list,
  798. 'tagid' => $tagid
  799. );
  800. $data = json_encode($data);
  801. $result = $this->requestApi($url, $data);
  802. if(is_error($result)) {
  803. return $result;
  804. }
  805. return true;
  806. }
  807. public function fansTagBatchUntagging($openid_list, $tagid) {
  808. $openid_list = (array) $openid_list;
  809. $tagid = (int) $tagid;
  810. if(empty($openid_list)){
  811. return error(-1, '没有填写用户openid列表');
  812. }
  813. if(empty($tagid)) {
  814. return error(-1, '没有填写tagid');
  815. }
  816. $token = $this->getAccessToken();
  817. if(is_error($token)){
  818. return $token;
  819. }
  820. $url = "https://api.weixin.qq.com/cgi-bin/tags/members/batchuntagging?access_token={$token}";
  821. $data = array(
  822. 'openid_list' => $openid_list,
  823. 'tagid' => $tagid
  824. );
  825. $data = json_encode($data);
  826. $result = $this->requestApi($url, $data);
  827. if(is_error($result)) {
  828. return $result;
  829. }
  830. return true;
  831. }
  832. public function fansTagFetchOwnTags($openid) {
  833. $openid = (string) $openid;
  834. if(empty($openid)){
  835. return error(-1, '没有填写用户openid');
  836. }
  837. $token = $this->getAccessToken();
  838. if(is_error($token)){
  839. return $token;
  840. }
  841. $url = "https://api.weixin.qq.com/cgi-bin/tags/getidlist?access_token={$token}";
  842. $data = json_encode(array('openid' => $openid));
  843. $result = $this->requestApi($url, $data);
  844. return $result;
  845. }
  846. public function fansSendAll($group, $msgtype, $media_id) {
  847. $types = array('text' => 'text', 'image' => 'image', 'news' => 'mpnews', 'voice' => 'voice', 'video' => 'mpvideo', 'wxcard' => 'wxcard');
  848. if(empty($types[$msgtype])) {
  849. return error(-1, '消息类型不合法');
  850. }
  851. $is_to_all = false;
  852. if($group == - 1) {
  853. $is_to_all = true;
  854. }
  855. $data = array(
  856. 'filter' => array(
  857. 'is_to_all' => $is_to_all,
  858. 'group_id' => $group
  859. ),
  860. 'msgtype' => $types[$msgtype],
  861. $types[$msgtype] => array(
  862. 'media_id' => $media_id
  863. )
  864. );
  865. if($msgtype == 'wxcard') {
  866. unset($data['wxcard']['media_id']);
  867. $data['wxcard']['card_id'] = $media_id;
  868. }
  869. $token = $this->getAccessToken();
  870. if(is_error($token)){
  871. return $token;
  872. }
  873. $url = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token={$token}";
  874. $data = urldecode(json_encode($data));
  875. $response = ihttp_request($url, $data);
  876. if(is_error($response)) {
  877. return error(-1, "访问公众平台接口失败, 错误: {$response['message']}");
  878. }
  879. $result = @json_decode($response['content'], true);
  880. if(empty($result)) {
  881. return error(-1, "接口调用失败, 元数据: {$response['meta']}");
  882. } elseif(!empty($result['errcode'])) {
  883. return error(-1, "访问微信接口错误, 错误代码: {$result['errcode']}, 错误信息: {$result['errmsg']},错误详情:{$this->errorCode($result['errcode'])}");
  884. }
  885. return $result;
  886. }
  887. public function fansSendPreview($wxname, $content, $msgtype) {
  888. $types = array('text' => 'text', 'image' => 'image', 'news' => 'mpnews', 'voice' => 'voice', 'video' => 'mpvideo', 'wxcard' => 'wxcard');
  889. if(empty($types[$msgtype])) {
  890. return error(-1, '群发类型不合法');
  891. }
  892. $msgtype = $types[$msgtype];
  893. $token = $this->getAccessToken();
  894. if(is_error($token)){
  895. return $token;
  896. }
  897. $url = 'https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token=' . $token;
  898. $send = array(
  899. 'towxname' => $wxname,
  900. 'msgtype' => $msgtype,
  901. );
  902. if($msgtype == 'text') {
  903. $send[$msgtype] = array(
  904. 'content' => $content
  905. );
  906. } elseif($msgtype == 'wxcard') {
  907. $send[$msgtype] = array(
  908. 'card_id' => $content
  909. );
  910. } else {
  911. $send[$msgtype] = array(
  912. 'media_id' => $content
  913. );
  914. }
  915. $response = ihttp_request($url, json_encode($send));
  916. if(is_error($response)) {
  917. return error(-1, "访问公众平台接口失败, 错误: {$response['message']}");
  918. }
  919. $result = @json_decode($response['content'], true);
  920. if(empty($result)) {
  921. } elseif(!empty($result['errcode'])) {
  922. return error(-1, "访问公众平台接口失败, 错误: {$result['errmsg']},错误详情:{$this->errorCode($result['errcode'])}");
  923. }
  924. return $result;
  925. }
  926. public function sendCustomNotice($data) {
  927. if(empty($data)) {
  928. return error(-1, '参数错误');
  929. }
  930. $token = $this->getAccessToken();
  931. if(is_error($token)){
  932. return $token;
  933. }
  934. $url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token={$token}";
  935. $response = ihttp_request($url, urldecode(json_encode($data)));
  936. if(is_error($response)) {
  937. return error(-1, "访问公众平台接口失败, 错误: {$response['message']}");
  938. }
  939. $result = @json_decode($response['content'], true);
  940. if(empty($result)) {
  941. return error(-1, "接口调用失败, 元数据: {$response['meta']}");
  942. } elseif(!empty($result['errcode'])) {
  943. return error(-1, "访问微信接口错误, 错误代码: {$result['errcode']}, 错误信息: {$result['errmsg']},错误详情:{$this->errorCode($result['errcode'])}");
  944. }
  945. return true;
  946. }
  947. public function sendTplNotice($touser, $template_id, $postdata, $url = '', $topcolor = '#FF683F') {
  948. if(empty($this->account['key']) || $this->account['level'] != ACCOUNT_SERVICE_VERIFY) {
  949. return error(-1, '你的公众号没有发送模板消息的权限');
  950. }
  951. if(empty($touser)) {
  952. return error(-1, '参数错误,粉丝openid不能为空');
  953. }
  954. if(empty($template_id)) {
  955. return error(-1, '参数错误,模板标示不能为空');
  956. }
  957. if(empty($postdata) || !is_array($postdata)) {
  958. return error(-1, '参数错误,请根据模板规则完善消息内容');
  959. }
  960. $token = $this->getAccessToken();
  961. if (is_error($token)) {
  962. return $token;
  963. }
  964. $data = array();
  965. $data['touser'] = $touser;
  966. $data['template_id'] = trim($template_id);
  967. $data['url'] = trim($url);
  968. $data['topcolor'] = trim($topcolor);
  969. $data['data'] = $postdata;
  970. $data = json_encode($data);
  971. $post_url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={$token}";
  972. $response = ihttp_request($post_url, $data);
  973. if(is_error($response)) {
  974. return error(-1, "访问公众平台接口失败, 错误: {$response['message']}");
  975. }
  976. $result = @json_decode($response['content'], true);
  977. if(empty($result)) {
  978. return error(-1, "接口调用失败, 元数据: {$response['meta']}");
  979. } elseif(!empty($result['errcode'])) {
  980. return error(-1, "访问微信接口错误, 错误代码: {$result['errcode']}, 错误信息: {$result['errmsg']},信息详情:{$this->errorCode($result['errcode'])}");
  981. }
  982. return true;
  983. }
  984. public function uploadMedia($path, $type = 'image') {
  985. if(empty($path)) {
  986. return error(-1, '参数错误');
  987. }
  988. if (in_array(substr(ltrim($path, '/'), 0, 6), array('images', 'videos', 'audios', 'thumb'))) {
  989. $path = ATTACHMENT_ROOT . ltrim($path, '/');
  990. }
  991. $token = $this->getAccessToken();
  992. if(is_error($token)){
  993. return $token;
  994. }
  995. $url = "https://api.weixin.qq.com/cgi-bin/media/upload?access_token={$token}&type={$type}";
  996. $data = array(
  997. 'media' => '@' . $path,
  998. );
  999. return $this->requestApi($url, $data);
  1000. }
  1001. public function uploadMediaFixed($path, $type = 'images') {
  1002. if(empty($path)) {
  1003. return error(-1, '参数错误');
  1004. }
  1005. if (in_array(substr(ltrim($path, '/'), 0, 6), array('images', 'videos', 'audios', 'thumb'))) {
  1006. $path = ATTACHMENT_ROOT . ltrim($path, '/');
  1007. }
  1008. $token = $this->getAccessToken();
  1009. if(is_error($token)){
  1010. return $token;
  1011. }
  1012. $url = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token={$token}&type={$type}";
  1013. $data = array(
  1014. 'media' => '@' . $path
  1015. );
  1016. $filename = pathinfo($path, PATHINFO_FILENAME);
  1017. $description = array(
  1018. 'title' => $filename,
  1019. 'introduction' => $filename,
  1020. );
  1021. $data['description'] = urldecode(json_encode($description));
  1022. return $this->requestApi($url, $data);
  1023. }
  1024. public function editMaterialNews($data) {
  1025. $token = $this->getAccessToken();
  1026. if(is_error($token)){
  1027. return $token;
  1028. }
  1029. $url = "https://api.weixin.qq.com/cgi-bin/material/update_news?access_token={$token}";
  1030. $response = $this->requestApi($url, stripslashes(ijson_encode($data, JSON_UNESCAPED_UNICODE)));
  1031. if (is_error($response)) {
  1032. return $response;
  1033. }
  1034. return true;
  1035. }
  1036. public function uploadNewsThumb($thumb) {
  1037. $token = $this->getAccessToken();
  1038. if(is_error($token)){
  1039. return $token;
  1040. }
  1041. $data = array(
  1042. 'media' => '@'. $thumb,
  1043. );
  1044. $url = "https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token={$token}";
  1045. $response = $this->requestApi($url, $data);
  1046. if (is_error($response)) {
  1047. return $response;
  1048. } else {
  1049. return $response['url'];
  1050. }
  1051. }
  1052. public function uploadVideoFixed($title, $description, $path) {
  1053. if(empty($path) || empty($title) || empty($description)) {
  1054. return error(-1, '参数错误');
  1055. }
  1056. if (in_array(substr(ltrim($path, '/'), 0, 6), array('images', 'videos', 'audios'))) {
  1057. $path = ATTACHMENT_ROOT . ltrim($path, '/');
  1058. }
  1059. $token = $this->getAccessToken();
  1060. if(is_error($token)){
  1061. return $token;
  1062. }
  1063. $url = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token={$token}&type=videos";
  1064. $data = array(
  1065. 'media' => '@' . $path,
  1066. 'description' => stripslashes(ijson_encode(array('title' => $title, 'introduction' => $description), JSON_UNESCAPED_UNICODE)),
  1067. );
  1068. $response = $this->requestApi($url, $data);
  1069. return $response;
  1070. }
  1071. public function uploadVideo($data) {
  1072. if(empty($data)) {
  1073. return error(-1, '参数错误');
  1074. }
  1075. $token = $this->getAccessToken();
  1076. if(is_error($token)){
  1077. return $token;
  1078. }
  1079. $url = "https://file.api.weixin.qq.com/cgi-bin/media/uploadvideo?access_token={$token}";
  1080. $response = ihttp_request($url, urldecode(json_encode($data)));
  1081. if(is_error($response)) {
  1082. return error(-1, "访问公众平台接口失败, 错误: {$response['message']}");
  1083. }
  1084. $result = @json_decode($response['content'], true);
  1085. if(empty($result)) {
  1086. return error(-1, "接口调用失败, 元数据: {$response['meta']}");
  1087. } elseif(!empty($result['errcode'])) {
  1088. return error(-1, "访问微信接口错误, 错误代码: {$result['errcode']}, 错误信息: {$result['errmsg']}, 错误详情:{$this->errorCode($result['errcode'])}");
  1089. }
  1090. return $result;
  1091. }
  1092. public function uploadNews($data) {
  1093. if(empty($data)) {
  1094. return error(-1, '参数错误');
  1095. }
  1096. $token = $this->getAccessToken();
  1097. if(is_error($token)){
  1098. return $token;
  1099. }
  1100. $url = "https://api.weixin.qq.com/cgi-bin/media/uploadnews?access_token={$token}";
  1101. $response = ihttp_request($url, urldecode(json_encode($data)));
  1102. if(is_error($response)) {
  1103. return error(-1, "访问公众平台接口失败, 错误: {$response['message']}");
  1104. }
  1105. $result = @json_decode($response['content'], true);
  1106. if(empty($result)) {
  1107. return error(-1, "接口调用失败, 元数据: {$response['meta']}");
  1108. } elseif(!empty($result['errcode'])) {
  1109. return error(-1, "访问微信接口错误, 错误代码: {$result['errcode']}, 错误信息: {$result['errmsg']},错误详情:{$this->errorCode($result['errcode'])}");
  1110. }
  1111. return $result;
  1112. }
  1113. public function addMatrialNews($data) {
  1114. $token = $this->getAccessToken();
  1115. if(is_error($token)){
  1116. return $token;
  1117. }
  1118. $url = "https://api.weixin.qq.com/cgi-bin/material/add_news?access_token={$token}";
  1119. $data = stripslashes(urldecode(ijson_encode($data, JSON_UNESCAPED_UNICODE)));
  1120. $response = $this->requestApi($url, $data);
  1121. if (is_error($response)) {
  1122. return $response;
  1123. }
  1124. return $response['media_id'];
  1125. }
  1126. public function batchGetMaterial($type = 'news', $offset = 0, $count = 20) {
  1127. global $_W;
  1128. $token = $this->getAccessToken();
  1129. if(is_error($token)){
  1130. return $token;
  1131. }
  1132. $url = 'https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token=' . $token;
  1133. $data = array(
  1134. 'type' => $type,
  1135. 'offset' => intval($offset),
  1136. 'count' => $count,
  1137. );
  1138. $response = $this->requestApi($url, json_encode($data));
  1139. return $response;
  1140. }
  1141. public function getMaterial($media_id, $savefile = true) {
  1142. $token = $this->getAccessToken();
  1143. if(is_error($token)){
  1144. return $token;
  1145. }
  1146. $url = 'https://api.weixin.qq.com/cgi-bin/material/get_material?access_token=' . $token;
  1147. $data = array(
  1148. 'media_id' => trim($media_id),
  1149. );
  1150. $response = ihttp_request($url, json_encode($data));
  1151. if(is_error($response)) {
  1152. return error(-1, "访问公平台接口失败, 错误: {$response['message']}");
  1153. }
  1154. $result = @json_decode($response['content'], true);
  1155. if(!empty($result['errcode'])) {
  1156. return error(-1, "访问公众平台接口失败, 错误: {$result['errmsg']},错误详情:{$this->errorCode($result['errcode'])}");
  1157. }
  1158. if (empty($response['headers']['Content-disposition'])) {
  1159. $response = json_decode($response['content'], true);
  1160. if (!empty($response['down_url'])) {
  1161. if (empty($savefile)) {
  1162. return $response;
  1163. }
  1164. $response = ihttp_get($response['down_url']);
  1165. $response['headers']['Content-disposition'] = $response['headers']['Content-Disposition'];
  1166. } elseif (!empty($response['news_item'])) {
  1167. return $response;
  1168. }
  1169. }
  1170. if($savefile && !empty($response['headers']['Content-disposition']) && strexists($response['headers']['Content-disposition'], 'filename=')){
  1171. global $_W;
  1172. preg_match('/filename=\"?([^"]*)/', $response['headers']['Content-disposition'], $match);
  1173. $pathinfo = pathinfo($match[1]);
  1174. $filename = $_W['uniacid'].'/'.date('Y/m/');
  1175. if (in_array(strtolower($pathinfo['extension']), array('mp4'))) {
  1176. $filename = 'videos/' . $filename;
  1177. } elseif (in_array(strtolower($pathinfo['extension']), array('amr', 'mp3', 'wma', 'wmv'))) {
  1178. $filename = 'audios/' . $filename;
  1179. } else {
  1180. $filename = 'images/' . $filename;
  1181. }
  1182. $filename .= file_random_name($filename, $pathinfo['extension']);
  1183. load()->func('file');
  1184. file_write($filename, $response['content']);
  1185. file_remote_upload($filename);
  1186. return $filename;
  1187. } else {
  1188. return $response['content'];
  1189. }
  1190. return $result;
  1191. }
  1192. public function downloadMedia($media_id, $savefile = true) {
  1193. $mediatypes = array('image', 'voice', 'thumb');
  1194. $media_id = is_array($media_id) ? $media_id['media_id'] : $media_id;
  1195. if (empty($media_id)) {
  1196. return error(-1, '微信下载媒体资源参数错误');
  1197. }
  1198. $token = $this->getAccessToken();
  1199. if(is_error($token)){
  1200. return $token;
  1201. }
  1202. $url = "https://api.weixin.qq.com/cgi-bin/media/get?access_token={$token}&media_id={$media_id}";
  1203. $response = ihttp_get($url);
  1204. if (empty($response['headers']['Content-disposition'])) {
  1205. $response = json_decode($response['content'], true);
  1206. if (!empty($response['video_url'])) {
  1207. $response = ihttp_get($response['video_url']);
  1208. $response['headers']['Content-disposition'] = $response['headers']['Content-Disposition'];
  1209. }
  1210. }
  1211. if($savefile && !empty($response['headers']['Content-disposition']) && strexists($response['headers']['Content-disposition'], 'filename=')){
  1212. global $_W;
  1213. preg_match('/filename=\"?([^"]*)/', $response['headers']['Content-disposition'], $match);
  1214. $filename = $_W['uniacid'].'/'.date('Y/m/') . $match[1];
  1215. $pathinfo = pathinfo($filename);
  1216. if (in_array(strtolower($pathinfo['extension']), array('mp4'))) {
  1217. $filename = 'videos/' . $filename;
  1218. } elseif (in_array(strtolower($pathinfo['extension']), array('amr', 'mp3', 'wma', 'wmv'))) {
  1219. $filename = 'audios/' . $filename;
  1220. } else {
  1221. $filename = 'images/' . $filename;
  1222. }
  1223. load()->func('file');
  1224. file_write($filename, $response['content']);
  1225. file_remote_upload($filename);
  1226. return $filename;
  1227. } else {
  1228. return $response['content'];
  1229. }
  1230. }
  1231. public function getMaterialCount() {
  1232. $token = $this->getAccessToken();
  1233. if(is_error($token)){
  1234. return $token;
  1235. }
  1236. $url = 'https://api.weixin.qq.com/cgi-bin/material/get_materialcount?access_token=' . $token;
  1237. $response = $this->requestApi($url);
  1238. return $response;
  1239. }
  1240. public function delMaterial($media_id) {
  1241. $media_id = trim($media_id);
  1242. if(empty($media_id)) {
  1243. return error(-1, '素材media_id错误');
  1244. }
  1245. $token = $this->getAccessToken();
  1246. if(is_error($token)){
  1247. return $token;
  1248. }
  1249. $url = 'https://api.weixin.qq.com/cgi-bin/material/del_material?access_token=' . $token;
  1250. $data = array(
  1251. 'media_id' => trim($media_id),
  1252. );
  1253. $response = $this->requestApi($url, json_encode($data));
  1254. if (is_error($response)) {
  1255. return $response;
  1256. }
  1257. return true;
  1258. }
  1259. public function changeOrderStatus($send) {
  1260. if (empty($send)) {
  1261. return error(-1, '参数错误');
  1262. }
  1263. $token = $this->getAccessToken();
  1264. if(is_error($token)){
  1265. return $token;
  1266. }
  1267. $sendapi = 'https://api.weixin.qq.com/pay/delivernotify?access_token=' . $token;
  1268. $response = ihttp_request($sendapi, json_encode($send));
  1269. $response = json_decode($response['content'], true);
  1270. if (empty($response)) {
  1271. return error(-1, '发货失败,请检查您的公众号权限或是公众号AppId和公众号AppSecret!');
  1272. }
  1273. if (!empty($response['errcode'])) {
  1274. return error(-1, $response['errmsg']);
  1275. }
  1276. return $response;
  1277. }
  1278. public function getOauthUserInfo($accesstoken, $openid) {
  1279. $apiurl = "https://api.weixin.qq.com/sns/userinfo?access_token={$accesstoken}&openid={$openid}&lang=zh_CN";
  1280. $response = $this->requestApi($apiurl);
  1281. return $response;
  1282. }
  1283. public function getOauthInfo($code = '') {
  1284. global $_W, $_GPC;
  1285. if (!empty($_GPC['code'])) {
  1286. $code = $_GPC['code'];
  1287. }
  1288. if (empty($code)) {
  1289. $sitepath = substr($_SERVER['PHP_SELF'], 0, strrpos($_SERVER['PHP_SELF'], '/'));
  1290. $unisetting = uni_setting_load();
  1291. $global_unisetting = uni_account_global_oauth();
  1292. $unisetting['oauth']['host'] = !empty($unisetting['oauth']['host']) ? $unisetting['oauth']['host'] : $global_unisetting['oauth']['host'];
  1293. $url = (!empty($unisetting['oauth']['host']) ? ($unisetting['oauth']['host'] . $sitepath . '/') : $_W['siteroot'] . 'app/') . "index.php?{$_SERVER['QUERY_STRING']}";
  1294. $forward = $this->getOauthCodeUrl(urlencode($url));
  1295. header('Location: ' . $forward);
  1296. exit;
  1297. }
  1298. $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->account['key']}&secret={$this->account['secret']}&code={$code}&grant_type=authorization_code";
  1299. $response = $this->requestApi($url);
  1300. return $response;
  1301. }
  1302. public function getOauthAccessToken() {
  1303. $cachekey = "oauthaccesstoken:{$this->account['acid']}";
  1304. $cache = cache_load($cachekey);
  1305. if (!empty($cache) && !empty($cache['token']) && $cache['expire'] > TIMESTAMP) {
  1306. return $cache['token'];
  1307. }
  1308. $token = $this->getOauthInfo();
  1309. if (is_error($token)) {
  1310. return error(1);
  1311. }
  1312. $record = array();
  1313. $record['token'] = $token['access_token'];
  1314. $record['expire'] = TIMESTAMP + $token['expires_in'] - 200;
  1315. cache_write($cachekey, $record);
  1316. return $token['access_token'];
  1317. }
  1318. public function getShareAddressConfig() {
  1319. global $_W;
  1320. static $current_url;
  1321. if (empty($current_url)) {
  1322. $current_url = $_W['siteurl'];
  1323. }
  1324. $token = $this->getOauthAccessToken();
  1325. if (is_error($token)) {
  1326. return false;
  1327. }
  1328. $package = array(
  1329. 'appid' => $this->account['key'],
  1330. 'url' => $current_url,
  1331. 'timestamp' => strval(TIMESTAMP),
  1332. 'noncestr' => strval(random(8, true)),
  1333. 'accesstoken' => $token
  1334. );
  1335. ksort($package, SORT_STRING);
  1336. $signstring = array();
  1337. foreach ($package as $k => $v) {
  1338. $signstring[] = "{$k}={$v}";
  1339. }
  1340. $signstring = strtolower(sha1(trim(implode('&', $signstring))));
  1341. $shareaddress_config = array(
  1342. 'appId' => $this->account['key'],
  1343. 'scope' => 'jsapi_address',
  1344. 'signType' => 'sha1',
  1345. 'addrSign' => $signstring,
  1346. 'timeStamp' => $package['timestamp'],
  1347. 'nonceStr' => $package['noncestr']
  1348. );
  1349. return $shareaddress_config;
  1350. }
  1351. public function getOauthCodeUrl($callback, $state = '') {
  1352. return "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->account['key']}&redirect_uri={$callback}&response_type=code&scope=snsapi_base&state={$state}#wechat_redirect";
  1353. }
  1354. public function getOauthUserInfoUrl($callback, $state = '') {
  1355. return "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->account['key']}&redirect_uri={$callback}&response_type=code&scope=snsapi_userinfo&state={$state}#wechat_redirect";
  1356. }
  1357. public function getFansStat() {
  1358. global $_W;
  1359. $token = $this->getAccessToken();
  1360. if (is_error($token)) {
  1361. return $token;
  1362. }
  1363. $url = "https://api.weixin.qq.com/datacube/getusersummary?access_token={$token}";
  1364. $data = array(
  1365. 'begin_date' => date('Y-m-d', strtotime('-7 days')),
  1366. 'end_date' => date('Y-m-d', strtotime('-1 days'))
  1367. );
  1368. $summary_response = $this->requestApi($url, json_encode($data));
  1369. if (is_error($summary_response)) {
  1370. return $summary_response;
  1371. }
  1372. $url = "https://api.weixin.qq.com/datacube/getusercumulate?access_token={$token}";
  1373. $cumulate_response = $this->requestApi($url, json_encode($data));
  1374. if(is_error($cumulate_response)) {
  1375. return $cumulate_response;
  1376. }
  1377. $result = array();
  1378. if (!empty($summary_response['list'])) {
  1379. foreach ($summary_response['list'] as $row) {
  1380. $key = str_replace('-', '', $row['ref_date']);
  1381. $result[$key]['new'] = intval($result[$key]['new']) + $row['new_user'];
  1382. $result[$key]['cancel'] = intval($result[$key]['cancel']) + $row['cancel_user'];
  1383. }
  1384. }
  1385. if (!empty($cumulate_response['list'])) {
  1386. foreach ($cumulate_response['list'] as $row) {
  1387. $key = str_replace('-', '', $row['ref_date']);
  1388. $result[$key]['cumulate'] = $row['cumulate_user'];
  1389. }
  1390. }
  1391. return $result;
  1392. }
  1393. protected function requestApi($url, $post = '') {
  1394. $response = ihttp_request($url, $post);
  1395. $result = @json_decode($response['content'], true);
  1396. if(is_error($response)) {
  1397. return error($result['errcode'], "访问公众平台接口失败, 错误详情: {$this->errorCode($result['errcode'])}");
  1398. }
  1399. if(empty($result)) {
  1400. return error(-1, "接口调用失败, 元数据: {$response['meta']}");
  1401. } elseif(!empty($result['errcode'])) {
  1402. return error($result['errcode'], "访问公众平台接口失败, 错误: {$result['errmsg']},错误详情:{$this->errorCode($result['errcode'])}");
  1403. }
  1404. return $result;
  1405. }
  1406. }