You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

888 lines
33 KiB

7 years ago
6 years ago
7 years ago
6 years ago
7 years ago
6 years ago
7 years ago
6 years ago
7 years ago
6 years ago
7 years ago
  1. <?php
  2. namespace App\Common\Wechat;
  3. if (!class_exists("WXBizMsgCrypt"))
  4. {
  5. include_once "aes/wxBizMsgCrypt.php";
  6. }
  7. /**
  8. * 微信第三方平台代小程序或者公众号
  9. */
  10. class WxComponent
  11. {
  12. const API_URL_PREFIX = 'https://api.weixin.qq.com/cgi-bin/component';
  13. const GET_ACCESS_TOKEN_URL = '/api_component_token';
  14. const GET_PREAUTHCODE_URL = '/api_create_preauthcode?component_access_token=';
  15. const GET_WX_AUTH_INFO_URL = '/api_query_auth?component_access_token=';
  16. const GET_WX_ACCESS_TOKEN_URL = '/api_authorizer_token?component_access_token=';
  17. const GET_WX_ACCOUNT_INFO_URL = '/api_get_authorizer_info?component_access_token=';
  18. const GET_WX_OPTION_INFO_URL = '/api_get_authorizer_option?component_access_token=';
  19. const SET_WX_OPTION_INFO_URL = '/api_set_authorizer_option?component_access_token=';
  20. const WX_AUTH_CB_URL = 'https://mp.weixin.qq.com/cgi-bin/componentloginpage?';
  21. // 代公众号发起网页授权相关
  22. // 在{网页开发域名}或者下级域名 $APPID$.{网页开发域名} 的形式,可以代公众号发起网页授权。
  23. const OAUTH_PREFIX = 'https://open.weixin.qq.com/connect/oauth2';
  24. const OAUTH_AUTHORIZE_URL = '/authorize?';
  25. const API_BASE_URL_PREFIX = 'https://api.weixin.qq.com'; //以下API接口URL需要使用此前缀
  26. const OAUTH_TOKEN_URL = '/sns/oauth2/component/access_token?';
  27. const OAUTH_REFRESH_URL = '/sns/oauth2/component/refresh_token?';
  28. const OAUTH_USERINFO_URL = '/sns/userinfo?';
  29. const OAUTH_AUTH_URL = '/sns/auth?';
  30. // 代实现小程序
  31. const API_URL_PREFIX_MINI_PROGRAM = 'https://api.weixin.qq.com'; // 小程序
  32. const SET_DOMAIN = '/wxa/modify_domain';
  33. const BIND_TEST_USER = '/wxa/bind_tester'; // 绑定小程序体验者
  34. const UNBIND_TEST_USER = '/wxa/unbind_tester';
  35. const GET_DRAFT_TEMPLATE = '/wxa/gettemplatedraftlist';
  36. const AUDIT_DRAFT_TEMPLATE = '/wxa/addtotemplate';
  37. const TEMPLATE_LIST = '/wxa/gettemplatelist';
  38. const DELETE_TEMPLATE = '/wxa/deletetemplate';
  39. const UPLOAD_TEMPLATE = '/wxa/commit';
  40. const TEST_QR_CODE = '/wxa/get_qrcode';
  41. const GET_CATEGORY = '/wxa/get_category';
  42. const GET_PAGES = '/wxa/get_page';
  43. const AUDIT_TEMPLATE = '/wxa/submit_audit';
  44. const PUBLISH_TEMPLATE = '/wxa/release';
  45. const AUDIT_STATUS = '/wxa/get_auditstatus';
  46. public $component_appid;
  47. public $component_appsecret;
  48. public $component_verify_ticket;
  49. public $encodingAesKey = "";
  50. public $token = "";
  51. public $debug = false;
  52. public $errCode = 40001;
  53. public $errMsg = "no access";
  54. private $_logcallback;
  55. /**
  56. * 构造函数,填入配置信息
  57. * @param string $component_appid 平台appId
  58. * @param string $component_appsecret 平台appsecret
  59. * @param string $component_verify_ticket 平台票据,微信服务器定时推送过来
  60. * @param string $encodingAesKey 公众号消息加解密Key,公众平台上,开发者设置的EncodingAESKey
  61. * @param string $token 公众号消息校验Token,公众平台上,开发者设置的token
  62. */
  63. public function __construct($component_appid, $component_appsecret, $component_verify_ticket, $encodingAesKey, $token)
  64. {
  65. $this->component_appid = $component_appid;
  66. $this->component_appsecret = $component_appsecret;
  67. $this->component_verify_ticket = $component_verify_ticket;
  68. $this->encodingAesKey = $encodingAesKey;
  69. $this->token = $token;
  70. }
  71. /**
  72. * @return mixed
  73. */
  74. public function getLogcallback()
  75. {
  76. return $this->_logcallback;
  77. }
  78. /**
  79. * @param callable $logcallback
  80. */
  81. public function setLogcallback($logcallback)
  82. {
  83. $this->_logcallback = $logcallback;
  84. return $this;
  85. }
  86. /**
  87. * 设置新的票据
  88. * @param $component_verify_ticket
  89. */
  90. public function setComponentVerifyTicket($component_verify_ticket)
  91. {
  92. $this->component_verify_ticket = $component_verify_ticket;
  93. }
  94. /**
  95. * 得到公众号服务授权的URL
  96. * @param string $pre_auth_code
  97. * @param string $redirect_uri
  98. * @return string
  99. */
  100. public function getAuthCbUrl($pre_auth_code, $redirect_uri)
  101. {
  102. return self::WX_AUTH_CB_URL . "component_appid=" . urlencode($this->component_appid)
  103. . "&pre_auth_code=" . urlencode($pre_auth_code) . "&redirect_uri=" . urlencode($redirect_uri);
  104. }
  105. /**
  106. * 获得服务访问授权key
  107. * @return bool|mixed {
  108. * "component_access_token":"61W3mEpU66027wgNZ_MhGHNQDHnFATkDa9-2llqrMBjUwxRSNPbVsMmyD-yq8wZETSoE5NQgecigDrSHkPtIYA",
  109. * "expires_in":7200
  110. * }
  111. */
  112. public function getAccessToken()
  113. {
  114. $arr = array('component_appid' => $this->component_appid,
  115. 'component_appsecret' => $this->component_appsecret,
  116. 'component_verify_ticket' => $this->component_verify_ticket,
  117. );
  118. $result = $this->httpPost(self::API_URL_PREFIX . self::GET_ACCESS_TOKEN_URL, json_encode($arr));
  119. if ($result) {
  120. $json = json_decode($result, true);
  121. if (!$json || !empty($json['errcode'])) {
  122. $this->errCode = $json['errcode'];
  123. $this->errMsg = $json['errmsg'];
  124. return false;
  125. }
  126. return $json;
  127. }
  128. return false;
  129. }
  130. /**
  131. * 获得预授权码
  132. * @param $component_access_token
  133. * @return bool|mixed{
  134. * "pre_auth_code":"Cx_Dk6qiBE0Dmx4EmlT3oRfArPvwSQ-oa3NL_fwHM7VI08r52wazoZX2Rhpz1dEw",
  135. * "expires_in":600
  136. * }
  137. */
  138. public function getPreauthCode($component_access_token)
  139. {
  140. $arr = array('component_appid' => $this->component_appid);
  141. $result = $this->httpPost(self::API_URL_PREFIX . self::GET_PREAUTHCODE_URL . $component_access_token, json_encode($arr));
  142. if ($result) {
  143. $json = json_decode($result, true);
  144. if (!$json || !empty($json['errcode'])) {
  145. $this->errCode = $json['errcode'];
  146. $this->errMsg = $json['errmsg'];
  147. return false;
  148. }
  149. return $json;
  150. }
  151. return false;
  152. }
  153. /**
  154. * 使用授权码换取公众号的授权信息
  155. * @param $access_token
  156. * @param $auth_code
  157. * @return bool|mixed{ "authorization_info": {
  158. * "authorizer_appid": "wxf8b4f85f3a794e77",
  159. * "authorizer_access_token": "QXjUqNqfYVH0yBE1iI_7vuN_9gQbpjfK7hYwJ3P7xOa88a89-Aga5x1NMYJyB8G2yKt1KCl0nPC3W9GJzw0Zzq_dBxc8pxIGUNi_bFes0qM",
  160. * "expires_in": 7200,
  161. * "authorizer_refresh_token": "dTo-YCXPL4llX-u1W1pPpnp8Hgm4wpJtlR6iV0doKdY",
  162. * "func_info": [{ "funcscope_category": { "id": 1 } },
  163. * {"funcscope_category": {"id": 2 }},
  164. * {"funcscope_category": {"id": 3}}]
  165. * }
  166. */
  167. public function getWxAuthInfo($access_token, $auth_code)
  168. {
  169. $arr = array('component_appid' => $this->component_appid, 'authorization_code' => $auth_code);
  170. $result = $this->httpPost(self::API_URL_PREFIX . self::GET_WX_AUTH_INFO_URL . $access_token, json_encode($arr));
  171. if ($result) {
  172. $json = json_decode($result, true);
  173. if (!$json || !empty($json['errcode'])) {
  174. $this->log('test--------------' . $result);
  175. $this->errCode = $json['errcode'];
  176. $this->errMsg = $json['errmsg'];
  177. return false;
  178. }
  179. return $json;
  180. }
  181. return false;
  182. }
  183. /**
  184. * 获取(刷新)授权公众号的令牌
  185. * @param $access_token
  186. * @param $authorizer_appid
  187. * @param $authorizer_refresh_token
  188. * @return bool|mixed {
  189. * "authorizer_access_token": "aaUl5s6kAByLwgV0BhXNuIFFUqfrR8vTATsoSHukcIGqJgrc4KmMJ-JlKoC_-NKCLBvuU1cWPv4vDcLN8Z0pn5I45mpATruU0b51hzeT1f8",
  190. * "expires_in": 7200,
  191. * "authorizer_refresh_token": "BstnRqgTJBXb9N2aJq6L5hzfJwP406tpfahQeLNxX0w"
  192. * }
  193. */
  194. public function getWxAccessToken($access_token, $authorizer_appid, $authorizer_refresh_token)
  195. {
  196. $arr = array('component_appid' => $this->component_appid,
  197. 'authorizer_appid' => $authorizer_appid,
  198. 'authorizer_refresh_token' => $authorizer_refresh_token);
  199. $result = $this->httpPost(self::API_URL_PREFIX . self::GET_WX_ACCESS_TOKEN_URL . $access_token, json_encode($arr));
  200. if ($result) {
  201. $json = json_decode($result, true);
  202. if (!$json || !empty($json['errcode'])) {
  203. $this->errCode = $json['errcode'];
  204. $this->errMsg = $json['errmsg'];
  205. return false;
  206. }
  207. return $json;
  208. }
  209. return false;
  210. }
  211. /**
  212. * 获取授权方的账户信息
  213. * @param $access_token
  214. * @param $authorizer_appid
  215. * @return bool|mixed {"authorizer_info": {
  216. * "nick_name": "微信SDK Demo Special",
  217. * "head_img": "http://wx.qlogo.cn/mmopen/GPyw0pGicibl5Eda4GmSSbTguhjg9LZjumHmVjybjiaQXnE9XrXEts6ny9Uv4Fk6hOScWRDibq1fI0WOkSaAjaecNTict3n6EjJaC/0",
  218. * "service_type_info": { "id": 2 },
  219. * "verify_type_info": { "id": 0 },
  220. * "user_name":"gh_eb5e3a772040",
  221. * "alias":"paytest01"
  222. * },
  223. * "authorization_info": {
  224. * "appid": "wxf8b4f85f3a794e77",
  225. * "func_info": [ { "funcscope_category": { "id": 1 } }, { "funcscope_category": { "id": 2 } }, { "funcscope_category": { "id": 3 } }]
  226. * }}
  227. */
  228. public function getWxAccountInfo($access_token, $authorizer_appid)
  229. {
  230. $arr = array('component_appid' => $this->component_appid,
  231. 'authorizer_appid' => $authorizer_appid);
  232. $result = $this->httpPost(self::API_URL_PREFIX . self::GET_WX_ACCOUNT_INFO_URL . $access_token, json_encode($arr));
  233. if ($result) {
  234. $json = json_decode($result, true);
  235. if (!$json || !empty($json['errcode'])) {
  236. $this->log('test###--------------' . $result);
  237. $this->errCode = $json['errcode'];
  238. $this->errMsg = $json['errmsg'];
  239. return false;
  240. }
  241. return $json;
  242. }
  243. return false;
  244. }
  245. /**
  246. * 获取授权方的选项信息
  247. * @param $access_token
  248. * @param $authorizer_appid
  249. * @param $option_name
  250. * @return bool|mixed { "authorizer_appid":"wx7bc5ba58cabd00f4",
  251. * "option_name":"voice_recognize",
  252. * "option_value":"1" }
  253. */
  254. public function getWxOptionInfo($access_token, $authorizer_appid, $option_name)
  255. {
  256. $arr = array('component_appid' => $this->component_appid,
  257. 'authorizer_appid' => $authorizer_appid,
  258. 'option_name' => $option_name);
  259. $result = $this->httpPost(self::API_URL_PREFIX . self::GET_WX_OPTION_INFO_URL . $access_token, json_encode($arr));
  260. if ($result) {
  261. $json = json_decode($result, true);
  262. if (!$json || !empty($json['errcode'])) {
  263. $this->errCode = $json['errcode'];
  264. $this->errMsg = $json['errmsg'];
  265. return false;
  266. }
  267. return $json;
  268. }
  269. return false;
  270. }
  271. /**
  272. * 设置授权方的选项信息
  273. * @param $access_token
  274. * @param $authorizer_appid
  275. * @param $option_name
  276. * @param $option_value
  277. * @return bool|mixed { "errcode":0, "errmsg":"ok" }
  278. */
  279. public function setWxOptionInfo($access_token, $authorizer_appid, $option_name, $option_value)
  280. {
  281. $arr = array('component_appid' => $this->component_appid,
  282. 'authorizer_appid' => $authorizer_appid,
  283. 'option_name' => $option_name,
  284. 'option_value' => $option_value);
  285. $result = $this->httpPost(self::API_URL_PREFIX . self::SET_WX_OPTION_INFO_URL . $access_token, json_encode($arr));
  286. if ($result) {
  287. $json = json_decode($result, true);
  288. if (!$json || $json['errcode'] > 0) {
  289. $this->errCode = $json['errcode'];
  290. $this->errMsg = $json['errmsg'];
  291. return false;
  292. }
  293. return $json;
  294. }
  295. return false;
  296. }
  297. /**
  298. * 处理component_verify_ticket
  299. *
  300. */
  301. /**
  302. * @return array|bool
  303. * <xml>
  304. * <AppId> </AppId>
  305. * <CreateTime>1413192605 </CreateTime>
  306. * <InfoType> </InfoType>
  307. * <ComponentVerifyTicket> </ComponentVerifyTicket>
  308. * </xml>
  309. */
  310. public function processEventNotify($raw = '')
  311. {
  312. if ($_SERVER['REQUEST_METHOD'] == "POST") {
  313. $dec_msg = "";
  314. $postStr = $raw ?? file_get_contents("php://input");
  315. if (!$postStr) {
  316. $postStr = $GLOBALS['HTTP_RAW_POST_DATA'];
  317. }
  318. if (!$postStr) {
  319. return false;
  320. }
  321. $pc = new \WXBizMsgCrypt($this->token, $this->encodingAesKey, $this->component_appid);
  322. $ret = $pc->decryptMsg($_GET['msg_signature'], $_GET['timestamp'], $_GET['nonce'], $postStr, $dec_msg);
  323. if ($ret === 0) {
  324. $arr = (array) simplexml_load_string($dec_msg, 'SimpleXMLElement', LIBXML_NOCDATA);
  325. return $arr;
  326. } else {
  327. return false;
  328. }
  329. } else {
  330. return false;
  331. }
  332. }
  333. public function responseEvent()
  334. {
  335. die("success");
  336. }
  337. /**
  338. * 代公众号发起网页授权 oauth 授权跳转接口
  339. * @param string $appid 公众号appId
  340. * @param string $callback 跳转URL
  341. * @param string $state 状态信息,最多128字节
  342. * @param string $scope 授权作用域 snsapi_base或者snsapi_userinfo 或者 snsapi_base,snsapi_userinfo
  343. * @return string
  344. */
  345. public function getOauthRedirect($appid, $callback, $state = '', $scope = 'snsapi_base')
  346. {
  347. return self::OAUTH_PREFIX . self::OAUTH_AUTHORIZE_URL . 'appid=' . $appid . '&redirect_uri=' . urlencode($callback) .
  348. '&response_type=code&scope=' . $scope . '&state=' . $state . '&component_appid=' . urlencode($this->component_appid)
  349. . '#wechat_redirect';
  350. }
  351. /**
  352. * 代公众号发起网页授权 回调URL时,通过code获取Access Token
  353. * @return array|boolean {access_token,expires_in,refresh_token,openid,scope}
  354. */
  355. public function getOauthAccessToken($appid, $component_access_token)
  356. {
  357. $code = isset($_GET['code']) ? $_GET['code'] : '';
  358. if (!$code) {
  359. return false;
  360. }
  361. $result = $this->httpPost(self::API_BASE_URL_PREFIX . self::OAUTH_TOKEN_URL . 'appid=' . $appid
  362. . '&code=' . $code . '&grant_type=authorization_code'
  363. . '&component_appid=' . urlencode($this->component_appid)
  364. . '&component_access_token=' . $component_access_token);
  365. if ($result) {
  366. $json = json_decode($result, true);
  367. if (!$json || !empty($json['errcode'])) {
  368. $this->errCode = $json['errcode'];
  369. $this->errMsg = $json['errmsg'];
  370. return false;
  371. }
  372. return $json;
  373. }
  374. return false;
  375. }
  376. /**
  377. * 代公众号发起网页授权 刷新access token并续期
  378. * @param string $refresh_token
  379. * @return boolean|mixed
  380. */
  381. public function getOauthRefreshToken($appId, $refresh_token, $component_access_token)
  382. {
  383. $result = $this->httpPost(self::API_BASE_URL_PREFIX . self::OAUTH_REFRESH_URL
  384. . 'appid=' . $appId . '&grant_type=refresh_token&refresh_token=' . $refresh_token
  385. . '&component_appid=' . urlencode($this->component_appid)
  386. . '&component_access_token=' . $component_access_token
  387. );
  388. if ($result) {
  389. $json = json_decode($result, true);
  390. if (!$json || !empty($json['errcode'])) {
  391. $this->errCode = $json['errcode'];
  392. $this->errMsg = $json['errmsg'];
  393. return false;
  394. }
  395. return $json;
  396. }
  397. return false;
  398. }
  399. /**
  400. * 获取授权后的用户资料
  401. * @param string $access_token
  402. * @param string $openid
  403. * @return array|boolean {openid,nickname,sex,province,city,country,headimgurl,privilege,[unionid]}
  404. * 注意:unionid字段 只有在用户将公众号绑定到微信开放平台账号后,才会出现。建议调用前用isset()检测一下
  405. */
  406. public function getOauthUserinfo($access_token, $openid)
  407. {
  408. $result = $this->httpPost(self::API_BASE_URL_PREFIX . self::OAUTH_USERINFO_URL . 'access_token=' . $access_token . '&openid=' . $openid);
  409. if ($result) {
  410. $json = json_decode($result, true);
  411. if (!$json || !empty($json['errcode'])) {
  412. $this->errCode = $json['errcode'];
  413. $this->errMsg = $json['errmsg'];
  414. return false;
  415. }
  416. return $json;
  417. }
  418. return false;
  419. }
  420. /**
  421. * 检验授权凭证是否有效
  422. * @param string $access_token
  423. * @param string $openid
  424. * @return boolean 是否有效
  425. */
  426. public function getOauthAuth($access_token, $openid)
  427. {
  428. $result = $this->httpPost(self::API_BASE_URL_PREFIX . self::OAUTH_AUTH_URL . 'access_token=' . $access_token . '&openid=' . $openid);
  429. if ($result) {
  430. $json = json_decode($result, true);
  431. if (!$json || !empty($json['errcode'])) {
  432. $this->errCode = $json['errcode'];
  433. $this->errMsg = $json['errmsg'];
  434. return false;
  435. } else
  436. if ($json['errcode'] == 0) {
  437. return true;
  438. }
  439. }
  440. return false;
  441. }
  442. public function setMiniProgramDomain($appID, $params, $accessToken)
  443. {
  444. $result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::SET_DOMAIN . '?access_token=' . $accessToken, json_encode($params));
  445. if ($result) {
  446. $json = json_decode($result, true);
  447. if (!$json || !empty($json['errcode'])) {
  448. $this->errCode = $json['errcode'];
  449. $this->errMsg = $json['errmsg'];
  450. return false;
  451. } else {
  452. if ($json['errcode'] == 0) {
  453. return true;
  454. }
  455. }
  456. }
  457. return false;
  458. }
  459. public function uploadTemplate($params, $accessToken)
  460. {
  461. $result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::UPLOAD_TEMPLATE . '?access_token=' . $accessToken, json_encode($params, JSON_UNESCAPED_UNICODE));
  462. if ($result) {
  463. $json = json_decode($result, true);
  464. if (!$json || !empty($json['errcode'])) {
  465. $this->errCode = $json['errcode'];
  466. $this->errMsg = $json['errmsg'];
  467. return false;
  468. } else {
  469. if ($json['errcode'] == 0) {
  470. return true;
  471. }
  472. }
  473. }
  474. return false;
  475. }
  476. public function getDraftTemplateList($accessToken)
  477. {
  478. $result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::GET_DRAFT_TEMPLATE . '?access_token=' . $accessToken, '');
  479. if ($result) {
  480. $json = json_decode($result, true);
  481. return $json;
  482. }
  483. return false;
  484. }
  485. public function getTemplateList($accessToken)
  486. {
  487. $result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::TEMPLATE_LIST . '?access_token=' . $accessToken, '');
  488. if ($result) {
  489. $json = json_decode($result, true);
  490. return $json;
  491. }
  492. return false;
  493. }
  494. public function auditDraftTemplate($accessToken, $draftID)
  495. {
  496. $result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::AUDIT_DRAFT_TEMPLATE . '?access_token=' . $accessToken, json_encode(['draft_id' => $draftID]));
  497. if ($result) {
  498. $json = json_decode($result, true);
  499. if (!$json || !empty($json['errcode'])) {
  500. $this->errCode = $json['errcode'];
  501. $this->errMsg = $json['errmsg'];
  502. return false;
  503. } else {
  504. if ($json['errcode'] == 0) {
  505. return true;
  506. }
  507. }
  508. }
  509. return false;
  510. }
  511. public function deleteTemplate($accessToken, $templateID)
  512. {
  513. $result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::DELETE_TEMPLATE . '?access_token=' . $accessToken, json_encode(['template_id' => $templateID]));
  514. if ($result) {
  515. $json = json_decode($result, true);
  516. if (!$json || !empty($json['errcode'])) {
  517. $this->errCode = $json['errcode'];
  518. $this->errMsg = $json['errmsg'];
  519. return false;
  520. } else {
  521. if ($json['errcode'] == 0) {
  522. return true;
  523. }
  524. }
  525. }
  526. return false;
  527. }
  528. public function getQrCode($accessToken)
  529. {
  530. $result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::TEST_QR_CODE . '?access_token=' . $accessToken, '');
  531. if ($result) {
  532. return $result; // 图片二进制流
  533. }
  534. return false;
  535. }
  536. public function getCategory($accessToken)
  537. {
  538. $result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::GET_CATEGORY . '?access_token=' . $accessToken, '');
  539. if ($result) {
  540. $json = json_decode($result, true);
  541. return $json;
  542. }
  543. return false;
  544. }
  545. public function getPages($accessToken)
  546. {
  547. $result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::GET_PAGES . '?access_token=' . $accessToken, '');
  548. if ($result) {
  549. $json = json_decode($result, true);
  550. return $json;
  551. }
  552. return false;
  553. }
  554. public function auditTemplate($params, $accessToken)
  555. {
  556. $result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::AUDIT_TEMPLATE . '?access_token=' . $accessToken, json_encode($params, JSON_UNESCAPED_UNICODE));
  557. if ($result) {
  558. $json = json_decode($result, true);
  559. if (!$json || !empty($json['errcode'])) {
  560. $this->errCode = $json['errcode'];
  561. $this->errMsg = $json['errmsg'];
  562. return false;
  563. } else {
  564. if ($json['errcode'] == 0) {
  565. return $json;
  566. }
  567. }
  568. }
  569. return false;
  570. }
  571. public function bindTestUser($params, $accessToken)
  572. {
  573. $result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::BIND_TEST_USER . '?access_token=' . $accessToken, json_encode($params));
  574. if ($result) {
  575. $json = json_decode($result, true);
  576. if (!$json || !empty($json['errcode'])) {
  577. $this->errCode = $json['errcode'];
  578. $this->errMsg = $json['errmsg'];
  579. return false;
  580. } else {
  581. if ($json['errcode'] == 0) {
  582. return true;
  583. }
  584. }
  585. }
  586. return false;
  587. }
  588. public function unbindTestUser($params, $accessToken)
  589. {
  590. $result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::UNBIND_TEST_USER . '?access_token=' . $accessToken, json_encode($params));
  591. if ($result) {
  592. $json = json_decode($result, true);
  593. if (!$json || !empty($json['errcode'])) {
  594. $this->errCode = $json['errcode'];
  595. $this->errMsg = $json['errmsg'];
  596. return false;
  597. } else {
  598. if ($json['errcode'] == 0) {
  599. return true;
  600. }
  601. }
  602. }
  603. return false;
  604. }
  605. public function publishTemplate($accessToken)
  606. {
  607. $result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::PUBLISH_TEMPLATE . '?access_token=' . $accessToken, json_encode(new stdClass()));
  608. if ($result) {
  609. $json = json_decode($result, true);
  610. if (!$json || !empty($json['errcode'])) {
  611. $this->errCode = $json['errcode'];
  612. $this->errMsg = $json['errmsg'];
  613. return false;
  614. } else {
  615. if ($json['errcode'] == 0) {
  616. return true;
  617. }
  618. }
  619. }
  620. return false;
  621. }
  622. public function getAuditStatus($auditid, $accessToken)
  623. {
  624. $result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::AUDIT_STATUS . '?access_token=' . $accessToken, json_encode(['auditid' => $auditid]));
  625. if ($result) {
  626. $json = json_decode($result, true);
  627. if (!$json || !empty($json['errcode'])) {
  628. $this->errCode = $json['errcode'];
  629. $this->errMsg = $json['errmsg'];
  630. return false;
  631. } else {
  632. if ($json['errcode'] == 0) {
  633. return $json;
  634. }
  635. }
  636. }
  637. return false;
  638. }
  639. protected function log($log)
  640. {
  641. if ($this->debug && is_callable($this->_logcallback)) {
  642. if (is_array($log)) {
  643. $log = print_r($log, true);
  644. }
  645. return call_user_func($this->_logcallback, $log);
  646. }
  647. return true;
  648. }
  649. /**
  650. * POST 请求
  651. * @param string $url
  652. * @param string|array $param
  653. * @param boolean $post_file 是否文件上传
  654. * @return string content
  655. */
  656. private function httpPost($url, $param = "", $post_file = false)
  657. {
  658. $oCurl = curl_init();
  659. if (stripos($url, "https://") !== false) {
  660. curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, false);
  661. curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);
  662. curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
  663. }
  664. if (is_string($param) || $post_file) {
  665. $strPOST = $param;
  666. } else {
  667. $aPOST = array();
  668. foreach ($param as $key => $val) {
  669. if (is_array($val)) {
  670. foreach ($val as $_k => $_v) {
  671. $aPOST[] = $key . "[]=" . urlencode($_v);
  672. }
  673. } else {
  674. $aPOST[] = $key . "=" . urlencode($val);
  675. }
  676. }
  677. $strPOST = join("&", $aPOST);
  678. }
  679. curl_setopt($oCurl, CURLOPT_URL, $url);
  680. curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1);
  681. if ($strPOST != "") {
  682. curl_setopt($oCurl, CURLOPT_POST, true);
  683. curl_setopt($oCurl, CURLOPT_POSTFIELDS, $strPOST);
  684. }
  685. $sContent = curl_exec($oCurl);
  686. $aStatus = curl_getinfo($oCurl);
  687. curl_close($oCurl);
  688. if (intval($aStatus["http_code"]) == 200) {
  689. $this->log("wxcomponent httpPost: {$strPOST} recv:" . $sContent);
  690. return $sContent;
  691. } else {
  692. $this->log("wxcomponent httpPost: {$strPOST} recv error {$url}, param:{$param} aStatus:" . print_r($aStatus, true));
  693. return false;
  694. }
  695. }
  696. /**
  697. * 微信api不支持中文转义的json结构
  698. * @param array $arr
  699. */
  700. public static function jsonEncode($arr)
  701. {
  702. $parts = array();
  703. $is_list = false;
  704. //Find out if the given array is a numerical array
  705. $keys = array_keys($arr);
  706. $max_length = count($arr) - 1;
  707. if (($keys[0] === 0) && ($keys[$max_length] === $max_length)) { //See if the first key is 0 and last key is length - 1
  708. $is_list = true;
  709. for ($i = 0; $i < count($keys); $i++) { //See if each key correspondes to its position
  710. if ($i != $keys[$i]) { //A key fails at position check.
  711. $is_list = false; //It is an associative array.
  712. break;
  713. }
  714. }
  715. }
  716. foreach ($arr as $key => $value) {
  717. if (is_array($value)) { //Custom handling for arrays
  718. if ($is_list) {
  719. $parts[] = self::jsonEncode($value);
  720. }
  721. /* :RECURSION: */
  722. else {
  723. $parts[] = '"' . $key . '":' . self::jsonEncode($value);
  724. }
  725. /* :RECURSION: */
  726. } else {
  727. $str = '';
  728. if (!$is_list) {
  729. $str = '"' . $key . '":';
  730. }
  731. //Custom handling for multiple data types
  732. if (!is_string($value) && is_numeric($value) && $value < 2000000000) {
  733. $str .= $value;
  734. }
  735. //Numbers
  736. elseif ($value === false) {
  737. $str .= 'false';
  738. }
  739. //The booleans
  740. elseif ($value === true) {
  741. $str .= 'true';
  742. } else {
  743. $str .= '"' . addslashes($value) . '"';
  744. }
  745. //All other things
  746. // :TODO: Is there any more datatype we should be in the lookout for? (Object?)
  747. $parts[] = $str;
  748. }
  749. }
  750. $json = implode(',', $parts);
  751. if ($is_list) {
  752. return '[' . $json . ']';
  753. }
  754. //Return numerical JSON
  755. return '{' . $json . '}'; //Return associative JSON
  756. }
  757. /**
  758. * 获取微信授权链接
  759. *
  760. * @param string $redirect_uri 回调地址,授权后重定向的回调链接地址,请使用urlEncode对链接进行处理
  761. * @param mixed $state 可以为空,重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
  762. */
  763. public function get_authorize_url($redirect_uri = '', $state = '')
  764. {
  765. return "https://open.weixin.qq.com/connect/oauth2/authorize?appid=".$this->app_id."&redirect_uri=".urlencode($redirect_uri)."&response_type=code&scope=snsapi_userinfo&state=".$state."#wechat_redirect";
  766. }
  767. /**
  768. * 微信PC扫码授权登录链接
  769. *
  770. * @param string $redirect_uri 回调地址,授权后重定向的回调链接地址,请使用urlEncode对链接进行处理
  771. * @param mixed $state 可以为空,重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
  772. */
  773. public function get_qrconnect_url($redirect_uri = '', $state = '')
  774. {
  775. return "https://open.weixin.qq.com/connect/qrconnect?appid".$this->app_id."&redirect_uri=".urlencode($redirect_uri)."&response_type=code&scope=snsapi_login&state=".$state."#wechat_redirect";
  776. }
  777. /**
  778. * 获取授权token
  779. *
  780. * @param string $code 通过get_authorize_url获取到的code
  781. */
  782. public function get_access_token($code = '')
  783. {
  784. $token_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->app_id}&secret={$this->app_secret}&code={$code}&grant_type=authorization_code";
  785. $token_data = $this->http($token_url);
  786. return json_decode($token_data, true);
  787. }
  788. /**
  789. * 获取授权后的微信用户信息
  790. *
  791. * @param string $access_token
  792. * @param string $open_id
  793. */
  794. public function get_user_info($access_token = '', $open_id = '')
  795. {
  796. $info_url = "https://api.weixin.qq.com/sns/userinfo?access_token={$access_token}&openid={$open_id}&lang=zh_CN";
  797. $info_data = $this->http($info_url);
  798. return json_decode($info_data, true);
  799. }
  800. /**
  801. * 获取用户基本信息(包括UnionID机制)
  802. *
  803. * @param string $access_token
  804. * @param string $open_id
  805. */
  806. public function get_user_unionid($access_token = '', $open_id = '')
  807. {
  808. $info_url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token={$access_token}&openid={$open_id}&lang=zh_CN";
  809. $info_data = $this->http($info_url);
  810. return json_decode($info_data, true);
  811. }
  812. // cURL函数简单封装
  813. public function http($url, $data = null)
  814. {
  815. $curl = curl_init();
  816. curl_setopt($curl, CURLOPT_URL, $url);
  817. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
  818. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
  819. if (!empty($data))
  820. {
  821. curl_setopt($curl, CURLOPT_POST, 1);
  822. curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
  823. }
  824. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  825. $output = curl_exec($curl);
  826. curl_close($curl);
  827. return $output;
  828. }
  829. }