Offiaccount.Class.php 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. <?php
  2. /**
  3. * 微信公众号相关接口类
  4. * Created by PhpStorm.
  5. * User: phperstar
  6. * Date: 2019/10/26
  7. * Time: 11:44 AM
  8. */
  9. namespace Util\WeiXin;
  10. use Mall\Framework\Core\ResultWrapper;
  11. use Mall\Framework\Core\ErrorCode;
  12. use Mall\Framework\Factory;
  13. class Offiaccount
  14. {
  15. /**
  16. * 公众号 appId
  17. * @var string $appid
  18. */
  19. private $appid;
  20. /**
  21. * 公众号 appSecret
  22. * @var string $appSecret
  23. */
  24. private $appSecret;
  25. /**
  26. * 促销卡
  27. * @var string $cardid
  28. */
  29. private $cardid;
  30. // access_token 公众号的全局唯一接口调用凭据
  31. private $access_token = 'accessToken';
  32. // jsapi_ticket 公众号用于调用微信JS接口的临时票据
  33. private $jsapi_ticket = 'jsapiTicket';
  34. /**
  35. * 微信公众号接口主域名
  36. * @var string $apiUrl
  37. */
  38. private $apiUrl = 'https://api.weixin.qq.com/cgi-bin/';
  39. // 小程序
  40. private $wxUrl = 'https://api.weixin.qq.com/wxa/';
  41. /**
  42. * 基础接口地址
  43. * @var string $baseUrl
  44. */
  45. private $baseUrl = 'https://api.weixin.qq.com/sns/';
  46. /**
  47. * Offiaccount constructor.
  48. * @param string $appid
  49. * @param string $appsecret
  50. * @param string $cardid
  51. */
  52. public function __construct($appid='', $appsecret='', $cardid='')
  53. {
  54. $this->appid = $appid;
  55. $this->appSecret = $appsecret;
  56. $this->cardid = $cardid;
  57. }
  58. /**
  59. * 获取基础access_token
  60. * 官方文档对应地址: https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
  61. * @return ResultWrapper
  62. */
  63. public function token()
  64. {
  65. $access_token = Factory::cache('default')->get($this->access_token.':'.$this->appid);
  66. if(!empty($access_token)){
  67. return ResultWrapper::success($access_token);
  68. }
  69. $apiUrl = $this->apiUrl.'token?grant_type=client_credential&appid='.$this->appid.'&secret='.$this->appSecret;
  70. $reponse = request($apiUrl);
  71. if($reponse['httpcode'] != 200){
  72. return ResultWrapper::fail('请求外部系统接口报错', ErrorCode::$apiNotResult);
  73. }else{
  74. $reponseData = json_decode($reponse['content'], true);
  75. if( isset($reponseData['errcode']) ){
  76. return ResultWrapper::fail($reponseData['errmsg'], $reponseData['errcode']);
  77. }
  78. Factory::cache('default')->set($this->access_token.':'.$this->appid, $reponseData['access_token'], 7000);
  79. return ResultWrapper::success($reponseData['access_token']);
  80. }
  81. }
  82. /**
  83. * 文本安全内容检测
  84. */
  85. public function msg_sec_check($content)
  86. {
  87. $result = self::token();
  88. if (!$result->isSuccess()) {
  89. return ResultWrapper::fail($result->getData(), $result->getErrorCode());
  90. }
  91. $access_token = $result->getData();
  92. $postData = [
  93. 'content' => $content,
  94. ];
  95. $url = $this->wxUrl . 'msg_sec_check?access_token=' . $access_token;
  96. $response = request($url, json_encode($postData, JSON_UNESCAPED_UNICODE), 10);
  97. $result = self::commonResponse($response);
  98. if (!$result->isSuccess()) {
  99. return ResultWrapper::fail('内容含有违法违规内容', $result->getErrorCode());
  100. }
  101. return ResultWrapper::success($result->getData());
  102. }
  103. /**
  104. * 图片安全内容检测
  105. */
  106. public function img_sec_check($media)
  107. {
  108. $result = self::token();
  109. if (!$result->isSuccess()) {
  110. return ResultWrapper::fail($result->getData(), $result->getErrorCode());
  111. }
  112. $authorizer_access_token = $result->getData();
  113. $media = file_get_contents($media);
  114. if (empty($media)) {
  115. return ResultWrapper::fail('图片内容为空', ErrorCode::$contentNotExists);
  116. }
  117. $boundary="----".md5(date);
  118. $formdata = "--" . $boundary ."\r\n";
  119. $formdata .= "Content-Disposition: form-data; name=\"upload\"; filename=\"upload.jpg\"\r\n";
  120. $formdata .= "Content-type: application/octet-stream\r\n\r\n";
  121. $formdata .= $media."\r\n";
  122. $formdata .= "--" . $boundary . "\r\n"."--\r\n\r\n";
  123. $curl_option = [
  124. CURLOPT_HTTPHEADER => [
  125. 'Content-Type: multipart/form-data; boundary='.$boundary,
  126. 'Content-Length: '.strlen($formdata),
  127. ],
  128. ];
  129. $url = $this->wxUrl . 'img_sec_check?access_token=' . $authorizer_access_token;
  130. $response = request($url, $formdata, 10, true, $curl_option);
  131. $result = self::commonResponse($response);
  132. if (!$result->isSuccess()) {
  133. return ResultWrapper::fail('图片含有违法违规内容', $result->getErrorCode());
  134. }
  135. return ResultWrapper::success($result->getData());
  136. }
  137. /**
  138. * 获取jsapi_ticket
  139. */
  140. public function jsapiTicket($access_token)
  141. {
  142. $jsapi_ticket = Factory::cache('default')->get($this->jsapi_ticket.':'.$this->appid);
  143. if(!empty($jsapi_ticket)){
  144. return ResultWrapper::success($jsapi_ticket);
  145. }
  146. $apiUrl = $this->apiUrl.'ticket/getticket?type=jsapi&access_token='.$access_token;
  147. $reponse = request($apiUrl);
  148. if($reponse['httpcode'] != 200){
  149. return ResultWrapper::fail('请求外部系统接口报错', ErrorCode::$apiNotResult);
  150. }else{
  151. $reponseData = json_decode($reponse['content'], true);
  152. if( isset($reponseData['errcode']) && !empty($reponseData['errcode'])){
  153. return ResultWrapper::fail($reponseData['errmsg'], $reponseData['errcode']);
  154. }
  155. Factory::cache('default')->set($this->jsapi_ticket.':'.$this->appid, $reponseData['ticket'], 7000);
  156. return ResultWrapper::success($reponseData['ticket']);
  157. }
  158. }
  159. /**
  160. * 获取getJsSdkConfig
  161. * 官方文档地址: https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62
  162. */
  163. public function getJsSdkConfig()
  164. {
  165. $result = self::token();
  166. if(!$result->isSuccess()){
  167. return ResultWrapper::fail($result->getData(),$result->getErrorCode());
  168. }
  169. $access_token = $result->getData();
  170. unset($result);
  171. $result = self::jsapiTicket($access_token);
  172. if(!$result->isSuccess()){
  173. return ResultWrapper::fail($result->getData(),$result->getErrorCode());
  174. }
  175. $jsapi_ticket = $result->getData();
  176. unset($result);
  177. $nonceStr = self::createNonceStr();
  178. $timestamp = time();
  179. // 注意 URL 一定要动态获取,不能 hardcode.
  180. $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
  181. //$url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
  182. $url = isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:'';
  183. $string = "jsapi_ticket={$jsapi_ticket}&noncestr=$nonceStr&timestamp=$timestamp&url=$url";
  184. $signature = sha1($string);
  185. $signPackage = [
  186. "appId" => $this->appid,
  187. "nonceStr" => $nonceStr,
  188. "timestamp" => $timestamp,
  189. "url" => $url,
  190. "signature" => $signature,
  191. "rawString" => $string,
  192. "accessToken" => $access_token
  193. ];
  194. return ResultWrapper::success($signPackage);
  195. }
  196. /**
  197. * 获取网页access_token
  198. * 官方文档对应地址: https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html#1
  199. * @return ResultWrapper
  200. */
  201. public function access_token($code)
  202. {
  203. $apiUrl = $this->baseUrl.'oauth2/access_token?appid='.$this->appid.'&secret='.$this->appSecret.'&code='.$code.'&grant_type=authorization_code';
  204. $reponse = request($apiUrl);
  205. if($reponse['httpcode'] != 200){
  206. return ResultWrapper::fail('请求外部系统接口报错', ErrorCode::$apiNotResult);
  207. }else{
  208. $reponseData = json_decode($reponse['content'], true);
  209. if( isset($reponseData['errcode']) ){
  210. return ResultWrapper::fail($reponseData['errmsg'], $reponseData['errcode']);
  211. }
  212. return ResultWrapper::success($reponseData);
  213. }
  214. }
  215. // 创建随机字符函数
  216. private function createNonceStr($length = 16)
  217. {
  218. $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  219. $str = "";
  220. for ($i = 0; $i < $length; $i++) {
  221. $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
  222. }
  223. return $str;
  224. }
  225. /**
  226. * 用于获取用户个人信息
  227. */
  228. public function userinfo($access_token, $openid)
  229. {
  230. $apiUrl = $this->baseUrl.'userinfo?access_token='.$access_token.'&openid='.$openid;
  231. $reponse = request($apiUrl);
  232. if($reponse['httpcode'] != 200){
  233. return ResultWrapper::fail('请求外部系统接口报错', ErrorCode::$apiNotResult);
  234. }else{
  235. $reponseData = json_decode($reponse['content'], true);
  236. if( isset($reponseData['errcode']) ){
  237. return ResultWrapper::fail($reponseData['errmsg'], $reponseData['errcode']);
  238. }
  239. return ResultWrapper::success($reponseData);
  240. }
  241. }
  242. /**
  243. * 公共处理返回结果函数
  244. */
  245. public function commonResponse($response, $isBinary = false)
  246. {
  247. if ($response['httpcode'] != 200) {
  248. return ResultWrapper::fail('请求外部系统接口报错', ErrorCode::$apiNotResult);
  249. }
  250. if (!is_object($response['content']) && $isBinary) {
  251. return ResultWrapper::success($response['content']);
  252. }
  253. $responseData = json_decode($response['content'], true);
  254. if (isset($responseData['errcode']) && $responseData['errcode'] != 0) {
  255. return ResultWrapper::fail($responseData['errmsg'], $responseData['errcode']);
  256. }
  257. return ResultWrapper::success($responseData);
  258. }
  259. }