ChapterShareWechatConfigService.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: tandunzhao
  5. * Date: 2017/12/4
  6. * Time: 上午11:49
  7. */
  8. namespace App\Modules\Book\Services;
  9. use Redis;
  10. use EasyWeChat\Foundation\Application;
  11. class ChapterShareWechatConfigService
  12. {
  13. private $appId;
  14. private $appSecret;
  15. private $accessToken;
  16. private $jsapiTicket;
  17. public function __construct($appId, $appSecret)
  18. {
  19. $this->appId = $appId ? $appId : env('JS_AppID');
  20. $this->appSecret = $appSecret ? $appSecret : env('JS_AppSecret');
  21. //\Log::info('appId:'.($this->appId).'appSecret:'.($this->appSecret));
  22. }
  23. public function getSignPackage($url = '', $is_force = true, $only_cache = false)
  24. {
  25. $jsapiTicket = $this->getJsApiTicket($is_force, $only_cache);
  26. $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
  27. $url = $url ? $url : "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
  28. $timestamp = time();
  29. $nonceStr = $this->createNonceStr();
  30. // 这里参数的顺序要按照 key 值 ASCII 码升序排序
  31. $string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";
  32. \Log::info($string);
  33. $signature = sha1($string);
  34. $signPackage = array(
  35. "appId" => $this->appId,
  36. "nonceStr" => $nonceStr,
  37. "timestamp" => $timestamp,
  38. "url" => $url,
  39. "signature" => $signature,
  40. "rawString" => $string
  41. );
  42. return $signPackage;
  43. }
  44. private function createNonceStr($length = 16)
  45. {
  46. $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  47. $str = "";
  48. for ($i = 0; $i < $length; $i++) {
  49. $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
  50. }
  51. return $str;
  52. }
  53. private function getJsApiTicket($is_force = false, $only_cache = true)
  54. {
  55. // jsapi_ticket 应该全局存储与更新,以下代码以写入到文件中做示例
  56. $data = unserialize(Redis::get(($this->appId) . "jsapi_ticket"));
  57. if (empty($data) || $data['expire_time'] < time()) {
  58. $accessToken = $this->getToken($is_force, $only_cache);
  59. \Log::info(($this->appId) . ':' . $accessToken);
  60. // 如果是企业号用以下 URL 获取 ticket
  61. $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token={$accessToken}";
  62. $res = json_decode($this->httpGet($url));
  63. $ticket = $res->ticket;
  64. if ($ticket) {
  65. $data['expire_time'] = time() + 7000;
  66. $data['jsapi_ticket'] = $ticket;
  67. Redis::setex(($this->appId) . "jsapi_ticket", 7000, serialize($data));
  68. }
  69. } else {
  70. $ticket = $data['jsapi_ticket'];
  71. }
  72. \Log::info(($this->appId) . ':' . 'jsapi_tiket:' . $ticket);
  73. return $ticket;
  74. }
  75. private function httpGet($url)
  76. {
  77. $curl = curl_init();
  78. curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  79. curl_setopt($curl, CURLOPT_TIMEOUT, 3);
  80. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
  81. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
  82. curl_setopt($curl, CURLOPT_URL, $url);
  83. $res = curl_exec($curl);
  84. curl_close($curl);
  85. return $res;
  86. }
  87. /**
  88. * 获取access token
  89. *
  90. * @param string $is_force
  91. * @return unknown|unknown|mixed|object|boolean
  92. */
  93. public function getToken($is_force = true, $only_cache = true)
  94. {
  95. if ($is_force == false) {
  96. $data = unserialize(Redis::get(($this->appId) . "access_token"));
  97. if ($data) {
  98. if (is_array($data) && $data['expire_time'] > time()) {
  99. $access_token = $data['access_token'];
  100. return $access_token;
  101. } else if (is_string($data)) {
  102. $access_token = $data;
  103. return $access_token;
  104. }
  105. }
  106. if ($only_cache) {
  107. // 非强制获取access_token,cache没有就直接返回false,防止获取access_token的接口在高并发下被滥用
  108. return false;
  109. }
  110. }
  111. $appId = $this->appId;
  112. $appSecret = $this->appSecret;
  113. $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' . $appId . '&secret=' . $appSecret;
  114. $rs1 = self::https_request($url);
  115. \Log::info(($this->appId) . 'get_token-' . $rs1);
  116. if ($rs1) {
  117. $rs = json_decode($rs1, true);
  118. if (is_array($rs) && $rs['access_token']) {
  119. $access_token = $rs['access_token'];
  120. $expires_in = intval($rs['expires_in'], 10);
  121. if ($expires_in <= 0) {
  122. $expires_in = 5;
  123. } else if ($expires_in >= 7200) {
  124. $expires_in = 7000;
  125. }
  126. $data = array();
  127. $data['expire_time'] = time() + $expires_in;
  128. $data['access_token'] = $access_token;
  129. Redis::setex(($this->appId) . 'access_token', $expires_in, serialize($data));
  130. return $access_token;
  131. } else {
  132. $content = "get access token error:" . $rs1;
  133. \Log::info(($this->appId) . $content);
  134. }
  135. } else {
  136. $content = "get access token error:" . $rs1;
  137. \Log::info(($this->appId) . $content);
  138. }
  139. return false;
  140. }
  141. /**
  142. * 抓https数据
  143. *
  144. * @param unknown $url
  145. * @param string $data
  146. * @param string $timeout
  147. */
  148. static function https_request($url, $data = null, $timeout = 5000)
  149. {
  150. $curl = curl_init();
  151. curl_setopt($curl, CURLOPT_URL, $url);
  152. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
  153. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
  154. if (!empty($data)) {
  155. curl_setopt($curl, CURLOPT_POST, 1);
  156. curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
  157. }
  158. if ($timeout) {
  159. curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
  160. }
  161. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  162. $output = curl_exec($curl);
  163. if ($output === false) {
  164. \Log::error('Curl error: ' . curl_error($curl) . ". url:" . $url);
  165. }
  166. curl_close($curl);
  167. return $output;
  168. }
  169. public function setAccessToken($token)
  170. {
  171. $this->accessToken = $token;
  172. }
  173. public function requestAccessToken()
  174. {
  175. $data = unserialize(Redis::get(($this->appId) . "access_token"));
  176. if ($data) {
  177. if (is_array($data) && $data['expire_time'] > time()) {
  178. $access_token = $data['access_token'];
  179. $this->setAccessToken($access_token);
  180. return true;
  181. } else if (is_string($data)) {
  182. $access_token = $data;
  183. $this->setAccessToken($access_token);
  184. return true;
  185. }
  186. }
  187. $appId = $this->appId;
  188. $appSecret = $this->appSecret;
  189. $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' . $appId . '&secret=' . $appSecret;
  190. $rs1 = self::https_request($url);
  191. \Log::info(($this->appId) . 'get_token-' . $rs1);
  192. if ($rs1) {
  193. $rs = json_decode($rs1, true);
  194. if (is_array($rs) && $rs['access_token']) {
  195. $access_token = $rs['access_token'];
  196. $expires_in = intval($rs['expires_in'], 10);
  197. if ($expires_in <= 0) {
  198. $expires_in = 5;
  199. } else if ($expires_in >= 7200) {
  200. $expires_in = 7000;
  201. }
  202. $data = array();
  203. $data['expire_time'] = time() + $expires_in;
  204. $data['access_token'] = $access_token;
  205. Redis::setex(($this->appId) . 'access_token', $expires_in, serialize($data));
  206. $this->setAccessToken($access_token);
  207. return true;
  208. } else {
  209. $content = "get access token error:" . $rs1;
  210. \Log::info(($this->appId) . $content);
  211. return false;
  212. }
  213. } else {
  214. $content = "get access token error:" . $rs1;
  215. \Log::info(($this->appId) . $content);
  216. return false;
  217. }
  218. }
  219. public function setJsapiTicket($ticket){
  220. $this->jsapiTicket = $ticket;
  221. }
  222. public function requestJsApiTicket()
  223. {
  224. // jsapi_ticket 应该全局存储与更新,以下代码以写入到文件中做示例
  225. $data = unserialize(Redis::get(($this->appId) . "jsapi_ticket"));
  226. if (empty($data) || $data['expire_time'] < time()) {
  227. $accessToken = $this->accessToken;
  228. \Log::info(($this->appId) . ':' . $accessToken);
  229. // 如果是企业号用以下 URL 获取 ticket
  230. $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token={$accessToken}";
  231. $res = json_decode($this->httpGet($url));
  232. $ticket = $res->ticket;
  233. if ($ticket) {
  234. $data['expire_time'] = time() + 7000;
  235. $data['jsapi_ticket'] = $ticket;
  236. Redis::setex(($this->appId) . "jsapi_ticket", 7000, serialize($data));
  237. $this->setJsapiTicket($ticket);
  238. return true;
  239. }
  240. return false;
  241. } else {
  242. $ticket = $data['jsapi_ticket'];
  243. $this->setJsapiTicket($ticket);
  244. return true;
  245. }
  246. }
  247. public function getJssdkConfig($url)
  248. {
  249. $jsapiTicket = $this->jsapiTicket;
  250. $timestamp = time();
  251. $nonceStr = $this->createNonceStr();
  252. // 这里参数的顺序要按照 key 值 ASCII 码升序排序
  253. $string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";
  254. $signature = sha1($string);
  255. $signPackage = array(
  256. "appId" => $this->appId,
  257. "nonceStr" => $nonceStr,
  258. "timestamp" => $timestamp,
  259. "url" => $url,
  260. "signature" => $signature,
  261. "rawString" => $string
  262. );
  263. return $signPackage;
  264. }
  265. public static function getConfig($url)
  266. {
  267. $options = [
  268. 'debug' => false,
  269. 'app_id' => env('JS_AppId'),
  270. 'secret' => env('JS_AppSecret'),
  271. //'token' => 'easywechat',
  272. // 'aes_key' => null, // 可选
  273. 'log' => [
  274. 'level' => 'debug',
  275. 'file' => storage_path('easywechat.log'), // XXX: 绝对路径!!!!
  276. ],
  277. ];
  278. $app = new Application($options);
  279. $js = $app->js;
  280. $js->setUrl($url);
  281. $config = $js->Config([
  282. 'onMenuShareTimeline',
  283. 'onMenuShareAppMessage',
  284. 'onMenuShareQQ',
  285. 'onMenuShareWeibo',
  286. 'showOptionMenu',
  287. 'hideOptionMenu',
  288. 'hideMenuItems',
  289. 'hideAllNonBaseMenuItem'
  290. ], $debug = false, $beta = false, $json = false);
  291. return $config;
  292. }
  293. }