crypt = new WxBizMsgCrypt(self::TOKEN, self::KEY, self::APPID); } public function checkSignature($signature, $timestamp, $nonce): bool { $token = self::TOKEN; $tmpArr = array($token, $timestamp, $nonce); sort($tmpArr, SORT_STRING); $tmpStr = implode($tmpArr); $tmpStr = sha1($tmpStr); if ($tmpStr == $signature) { return true; } else { return false; } } /** * @notes:保存Ticket * @param string $xml * @return mixed|string * @author: wanghousheng */ public function getVerifyTicket(string $xml) { $ticket = ''; $xmlData = $this->crypt->fromXml($xml); if (!empty($xmlData['Encrypt'])) { //解密 $encryptXml = $this->crypt->decryptMsg($xmlData['Encrypt']); $data = $this->crypt->fromXml($encryptXml); if (!empty($data['ComponentVerifyTicket'])) { $ticket = $data['ComponentVerifyTicket']; Cache::set('wholesaler_component_verify_ticket', $ticket); } } return $ticket; } public function decryptXml($xml) { $data = []; $xmlData = $this->crypt->fromXml($xml); if (!empty($xmlData['Encrypt'])) { $encryptXml = $this->crypt->decryptMsg($xmlData['Encrypt']); $data = $this->crypt->fromXml($encryptXml); } return $data; } public function getStableAccessToken() { $url = 'https://api.weixin.qq.com/cgi-bin/stable_token'; $data['grant_type'] = 'client_credential'; $data['appid'] = self::APPID; $data['secret'] = self::SECRET; $result = $this->curlPost($url, json_encode($data), 10); $result = json_decode($result, true); if (!empty($result['access_token'])) { return $result['access_token']; } } /** * @notes:获取令牌 * @return mixed|string * @author: wanghousheng */ public function getComponentAccessToken() { if (Cache::has('wholesaler_component_access_token')) { return Cache::get('wholesaler_component_access_token'); } $url = 'https://api.weixin.qq.com/cgi-bin/component/api_component_token'; $ticket = Cache::get('wholesaler_component_verify_ticket'); if (!empty($ticket)) { $data = []; $data['component_appid'] = self::APPID; $data['component_appsecret'] = self::SECRET; $data['component_verify_ticket'] = $ticket; $result = $this->curlPost($url, json_encode($data), 10); $result = json_decode($result, true); if (!empty($result['component_access_token'])) { Cache::set('wholesaler_component_access_token', $result['component_access_token'], 6200); return $result['component_access_token']; } } return ''; } /** * @notes:生成H5授权链接 * @param string $redirect_uri //授权后重定向的回调链接地址 * @param string $biz_appid //业务公众号appid * @return string * @author: wanghousheng */ public function jumpH5Url(string $redirect_uri, string $biz_appid = ''): string { $url = ''; $auth_code = $this->getPreAuthCode(); if ($auth_code) { $url = "https://open.weixin.qq.com/wxaopen/safe/bindcomponent?action=bindcomponent&no_scan=1"; $url .= "&component_appid=" . self::APPID . "&pre_auth_code=" . $auth_code . "&redirect_uri=" . $redirect_uri; $url .= '&auth_type=3&biz_appid=' . $biz_appid . "#wechat_redirect"; } return $url; } /** * @notes:获取预授权码 * @return false|mixed * @author: wanghousheng */ public function getPreAuthCode() { $token = $this->getComponentAccessToken(); if ($token) { $url = 'https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=' . $token; $data['component_appid'] = self::APPID; $result = $this->curlPost($url, json_encode($data)); $result = json_decode($result, true); if ($result && !empty($result['pre_auth_code'])) { return $result['pre_auth_code']; } } return false; } private function getRefreshToken($appid) { $model = new WxwholesalerAccount(); return $model->where(['appid' => $appid])->value('refresh_token'); } private function storeId($appid) { $model = new WxwholesalerAccount(); return $model->where(['appid' => $appid])->value('store_id'); } /** * @notes:获取体验码 * @param $appid * @return string * @author: wanghousheng */ public function getQrcode($appid): string { $savePath = root_path() . "public/wxserve/experience"; !is_dir($savePath) && mkdir($savePath, 0755, true); $savePath .= '/' . $appid . '.jpg'; if (file_exists($savePath)) { return '/wxserve/experience/' . $appid . '.jpg'; } $access_token = $this->authorizerAccessToken($appid); if ($access_token) { $url = 'https://api.weixin.qq.com/wxa/get_qrcode?access_token=' . $access_token; $client = new Client(); $response = $client->get($url); $res = $response->body(); if ($res) { $newFile = fopen($savePath, "a"); fwrite($newFile, $res); fclose($newFile); return '/wxserve/experience/' . $appid . '.jpg'; } } return ''; } /** * @notes:发布体验版 * @param $appid * @param $template_id * @return bool * @throws BaseException * @author: wanghousheng */ public function commit($appid, $template_id): bool { $version = ''; $access_token = $this->authorizerAccessToken($appid); //获取模板 $templatelist = $this->getTemplatelist(); if ($templatelist) { foreach ($templatelist as $value) { if (!empty($value['user_version']) && $value['template_id'] == $template_id) { $version = $value['user_version']; break; } } } if ($version == '') { throwError('模板不存在'); } $data['template_id'] = $template_id; $data['user_version'] = $version; $data['user_desc'] = 'updated'; $ext_json['extAppid'] = $appid; $ext_json['ext']['store_id'] = $this->storeId($appid); $ext_json['window'] = []; $data['ext_json'] = json_encode($ext_json); $url = 'https://api.weixin.qq.com/wxa/commit?access_token=' . $access_token; $result = $this->curlPost($url, json_encode($data)); $result = json_decode($result, true); if ($result && !empty($result['errmsg']) && $result['errmsg'] == 'ok') { //体验码 $qr_code = $this->getQrcode($appid); if ($qr_code) { $model = new WxwholesalerAccount(); $model->update(['experience_code' => $qr_code], ['appid' => $appid]); } return true; } return false; } /** * @notes:发布已经审核通过的小程序 * @param $appid * @return bool * @author: wanghousheng */ public function release($appid): bool { $access_token = $this->authorizerAccessToken($appid); if ($access_token) { $url = 'https://api.weixin.qq.com/wxa/release?access_token=' . $access_token; $result = $this->curlPost($url, '{}'); $result = json_decode($result, true); if ($result && !empty($result['errmsg']) && $result['errmsg'] == 'ok') { return true; } } return false; } /** * @notes:获取版本信息 * @param $appid * @return bool * @author: wanghousheng */ public function getVersioninfo($appid): bool { $access_token = $this->authorizerAccessToken($appid); $url = 'https://api.weixin.qq.com/wxa/getversioninfo?access_token=' . $access_token; $result = $this->curlPost($url, '{}'); $result = json_decode($result, true); if ($result && !empty($result['errmsg']) && $result['errmsg'] == 'ok') { $up = []; if (!empty($result['exp_info'])) { $up['exp_info'] = json_encode($result['exp_info']); } if (!empty($result['release_info'])) { $up['release_info'] = json_encode($result['release_info']); } if ($up) { $model = new WxwholesalerAccount(); $model->update($up, ['appid' => $appid]); } return true; } return false; } /** * @notes:获取隐私接口检测结果 * @param $appid * @return mixed|string * @author: wanghousheng */ public function privacyInfo($appid) { $errmsg = ''; $access_token = $this->authorizerAccessToken($appid); $url = 'https://api.weixin.qq.com/wxa/security/get_code_privacy_info?access_token=' . $access_token; $result = $this->curlGet($url); $result = json_decode($result, true); if ($result && !empty($result['errmsg'])) { $errmsg = $result['errmsg']; } return $errmsg; } /** * @notes:获取/刷新接口调用令牌 * @param $appid * @return false|mixed * @author: wanghousheng */ public function authorizerAccessToken($appid) { if (Cache::has($appid . '_wholesaler_authorizer_access_token')) { return Cache::get($appid . '_wholesaler_authorizer_access_token'); } $refresh_token = $this->getRefreshToken($appid); if ($refresh_token) { $token = $this->getComponentAccessToken(); if ($token) { $url = 'https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=' . $token; $data['component_appid'] = self::APPID; $data['authorizer_appid'] = $appid; $data['authorizer_refresh_token'] = $refresh_token; $result = $this->curlPost($url, json_encode($data)); $result = json_decode($result, true); if ($result && !empty($result['authorizer_access_token'])) { Cache::set($appid . '_wholesaler_authorizer_access_token', $result['authorizer_access_token'], 6200); return $result['authorizer_access_token']; } } } return false; } private function getAuditStatusText($status): string { $status = intval($status); switch ($status) { case 0: $text = '未提交审核'; break; case 1: $text = '审核中'; break; case 2: $text = '审核驳回'; break; case 3: $text = '审核通过'; break; case 4: $text = '提审中'; break; case 5: $text = '提审失败'; break; default: $text = '未知'; break; } return $text; } /** * @notes:获取草稿箱列表 * @return array|mixed * @author: wanghousheng */ public function getTemplatedraftlist($type) { if (!$type && Cache::has('wx_wholesaler_drafttemplatelist')) { return Cache::get('wx_wholesaler_drafttemplatelist'); } $token = $this->getComponentAccessToken(); $list = []; if ($token) { $url = 'https://api.weixin.qq.com/wxa/gettemplatedraftlist?access_token=' . $token; $result = $this->curlGet($url); $result = json_decode($result, true); if ($result && !empty($result['draft_list'])) { $draft_list = $result['draft_list']; foreach ($draft_list as $value) { $list[] = [ 'create_time' => date('Y-m-d H:i:s', $value['create_time']), 'user_version' => $value['user_version'], 'user_desc' => $value['user_desc'], 'id' => $value['draft_id'], ]; } Cache::set('wx_wholesaler_drafttemplatelist', $list, 7200); } } return $list; } /** * @notes:添加草稿箱到模板中 * @param $draft_id * @return bool * @author: wanghousheng */ public function addTotemplate($draft_id): bool { $token = $this->getComponentAccessToken(); if ($token) { $url = 'https://api.weixin.qq.com/wxa/addtotemplate?access_token=' . $token; $data['draft_id'] = $draft_id; $data['template_type'] = 1; $res = $this->curlPost($url, json_encode($data)); $res = json_decode($res, true); if ($res && !empty($res['errmsg']) && $res['errmsg'] == 'ok') { Cache::delete('wx_wholesaler_templatelist'); return true; } } return false; } /** * @notes:删除模板 * @param $template_id * @return bool * @author: wanghousheng */ public function deleTetemplate($template_id): bool { $token = $this->getComponentAccessToken(); if ($token) { $url = 'https://api.weixin.qq.com/wxa/deletetemplate?access_token=' . $token; $data['template_id'] = $template_id; $res = $this->curlPost($url, json_encode($data)); $res = json_decode($res, true); if ($res && !empty($res['errmsg']) && $res['errmsg'] == 'ok') { Cache::delete('wx_wholesaler_templatelist'); return true; } } return false; } /** * @notes:代码模板列表 * @param int $type * @return array * @author: wanghousheng */ public function getTemplatelist(int $type = 0): array { if (!$type && Cache::has('wx_wholesaler_templatelist')) { return Cache::get('wx_wholesaler_templatelist'); } $token = $this->getComponentAccessToken(); $list = []; if ($token) { $url = 'https://api.weixin.qq.com/wxa/gettemplatelist?access_token=' . $token . '&template_type=1'; $result = $this->curlGet($url); $result = json_decode($result, true); if ($result && !empty($result['template_list'])) { $template_list = $result['template_list']; foreach ($template_list as $value) { $list[] = [ 'create_time' => date('Y-m-d H:i:s', $value['create_time']), 'user_version' => $value['user_version'], 'user_desc' => $value['user_desc'], 'template_id' => $value['template_id'], 'audit_status' => $value['audit_status'], 'audit_status_text' => $this->getAuditStatusText($value['audit_status']), 'reason' => !empty($value['reason']) ? $value['reason'] : '', 'draft_id' => $value['draft_id'], ]; } Cache::set('wx_wholesaler_templatelist', $list, 7200); } } return $list; } /** * @notes:设置域名 * @param $appid * @param $domain * @return bool * @author: wanghousheng */ public function setDomain($appid, $domain) { $token = $this->authorizerAccessToken($appid); if ($token) { $url = "https://api.weixin.qq.com/wxa/modify_domain_directly?access_token=$token"; $data['action'] = 'set'; $data['requestdomain'] = $domain; $data['uploaddomain'] = $domain; $data['downloaddomain'] = $domain; $result = $this->curlPost($url, json_encode($data)); Db::table('yoshop_wx_server')->insertGetId(['content' => "设置域名信息", 'created_at' => date('Y-m-d H:i:s')]); Db::table('yoshop_wx_server')->insertGetId(['content' => $result, 'created_at' => date('Y-m-d H:i:s')]); $result = json_decode($result, true); if ($result && !empty($result['errmsg']) && $result['errmsg'] == 'ok') { return true; } } return false; } /** * @notes:设置隐私 * @param $appid * @return bool * @author: wanghousheng */ public function getPrivacySetting($appid): bool { $token = $this->authorizerAccessToken($appid); if ($token) { $url = "https://api.weixin.qq.com/cgi-bin/component/setprivacysetting?access_token=$token"; $setting_list = [ ['privacy_key' => 'PhoneNumber', 'privacy_text' => '用于用户一键登录'], ['privacy_key' => 'Location', 'privacy_text' => '用于用户选择自提点'], ['privacy_key' => 'AlbumWriteOnly', 'privacy_text' => '用于用户保存海报'], ['privacy_key' => 'Address', 'privacy_text' => '用于用户快速获取微信地址'], ['privacy_key' => 'Invoice', 'privacy_text' => '用于用户申请开票'], ['privacy_key' => 'Album', 'privacy_text' => '用于用户上传图片'], ['privacy_key' => 'Clipboard', 'privacy_text' => '用于用户复制粘贴'], ]; $data['setting_list'] = $setting_list; $data['owner_setting']['contact_qq'] = '527264601'; $data['owner_setting']['notice_method'] = '公告'; $result = $this->curlPost($url, json_encode($data)); Db::table('yoshop_wx_server')->insertGetId(['content' => "设置隐私信息", 'created_at' => date('Y-m-d H:i:s')]); Db::table('yoshop_wx_server')->insertGetId(['content' => $result, 'created_at' => date('Y-m-d H:i:s')]); $result = json_decode($result, true); if ($result && !empty($result['errmsg']) && $result['errmsg'] == 'ok') { return true; } } return false; } public function apiText($authorization_code, $touser) { $url = 'https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=' . $this->getComponentAccessToken(); $data['component_appid'] = self::APPID; $data['authorization_code'] = $authorization_code; $result = $this->curlPost($url, json_encode($data)); $result = json_decode($result, true); if (!empty($result['authorization_info']['authorizer_access_token'])) { $token = $result['authorization_info']['authorizer_access_token']; $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=' . $token; $data = [ 'touser' => $touser, 'msgtype' => 'text', 'text' => ['content' => $authorization_code . '_from_api'], ]; $this->curlPost($url, json_encode($data)); } } /** * @notes:获取授权信息 * @param $authorization_code * @param string $domain * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException * @author: wanghousheng */ public function authorizationInfo($authorization_code, string $domain = '') { $url = 'https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=' . $this->getComponentAccessToken(); $data = []; $data['component_appid'] = self::APPID; $data['authorization_code'] = $authorization_code; $result = $this->curlPost($url, json_encode($data)); $result = json_decode($result, true); if ($result && !empty($result['authorization_info']) && !empty($result['authorization_info']['authorizer_appid'])) { $url = 'https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token=' . $this->getComponentAccessToken(); $data = []; $data['component_appid'] = self::APPID; $data['authorizer_appid'] = $result['authorization_info']['authorizer_appid']; $res = $this->curlPost($url, json_encode($data)); $res = json_decode($res, true); if ($res && !empty($res['authorizer_info']) && !empty($res['authorization_info'])) { $authorizer_info = $res['authorizer_info']; $authorization_info = $res['authorization_info']; $model = new WxwholesalerAccount(); $appid = $authorization_info['authorizer_appid']; $insert_data['access_token'] = $result['authorization_info']['authorizer_access_token']; $insert_data['refresh_token'] = $authorization_info['authorizer_refresh_token']; $insert_data['nick_name'] = $authorizer_info['nick_name']; $insert_data['head_image'] = $authorizer_info['head_img']; $insert_data['user_name'] = $authorizer_info['user_name']; $insert_data['qrcode_image'] = $authorizer_info['qrcode_url']; $insert_data['principal_name'] = $authorizer_info['principal_name']; $insert_data['signature'] = $authorizer_info['signature']; $insert_data['store_id'] = $this->getStoreId($authorization_info['authorizer_appid']); $insert_data['update_time'] = time(); $exit_id = $model->where(['appid' => $appid])->value('id'); if ($exit_id) { $model->update($insert_data, ['id' => $exit_id]); } else { $insert_data['appid'] = $appid; $insert_data['create_time'] = time(); $model->insertGetId($insert_data); } $this->setOther($appid, $domain); } } } private function setOther($appid, $domain) { $up = []; //设置域名信息 if ($this->setDomain($appid, $domain)) { $up['domain_status'] = 1; } //设置隐私 if ($this->getPrivacySetting($appid)) { $up['privacy_status'] = 1; } //设置业务域名 if ($this->modifyJumpDomain($appid)) { $up['jump_domain_status'] = 1; } if ($up) { $model = new WxwholesalerAccount(); $model->update($up, ['appid' => $appid]); } } private function modifyJumpDomain($appid, $domain): bool { $token = $this->authorizerAccessToken($appid); if ($token) { $url = "https://api.weixin.qq.com/wxa/setwebviewdomain?access_token=$token"; $data['action'] = 'set'; $data['webviewdomain'] = $domain; $result = $this->curlPost($url, json_encode($data)); Db::table('yoshop_wx_server')->insertGetId(['content' => "业务域名", 'created_at' => date('Y-m-d H:i:s')]); Db::table('yoshop_wx_server')->insertGetId(['content' => $result, 'created_at' => date('Y-m-d H:i:s')]); $result = json_decode($result, true); if ($result && !empty($result['errmsg']) && $result['errmsg'] == 'ok') { return true; } } return false; } /** * @notes:获取店铺ID * @param $appid * @return int|mixed * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException * @author: wanghousheng */ public function getStoreId($appid) { $list = Db::table('yoshop_wxapp_setting')->where(['key' => 'basic'])->select(); if (!$list->isEmpty()) { $list = $list->toArray(); foreach ($list as $value) { if ($value['values']) { $item = json_decode($value['values'], true); if (!empty($item['app_id']) && $item['app_id'] == $appid) { $store_id = Db::table('yoshop_store')->where(['store_id' => $value['store_id'], 'is_recycle' => 0])->value('store_id'); if ($store_id) { return $store_id; } } } } } return 0; } public function submitAudit($appid): bool { $access_token = $this->authorizerAccessToken($appid); if ($access_token) { $url = 'https://api.weixin.qq.com/wxa/submit_audit?access_token=' . $access_token; $data['item_list'] = $this->getAllCategory($appid); if (!empty($data['item_list'])) { $data = '{}'; } else { $data = json_encode($data); } $result = $this->curlPost($url, $data); $result = json_decode($result, true); if (!empty($result) && intval($result['errcode']) == 0 && !empty($result['auditid'])) { $model = new WxwholesalerAccount(); $up['audit_status'] = 1; $up['audit_reason'] = ''; $up['audit_time'] = date('Y-m-d H:i:s'); $up['auditid'] = $result['auditid']; $model->update($up, ['appid' => $appid]); return true; } } return false; } /** * @notes:检测审核状态 * @param $appid * @param $auditid * @return array|mixed * @author: wanghousheng */ public function getAuditStatus($appid, $auditid) { $access_token = $this->authorizerAccessToken($appid); if ($access_token && $auditid) { $url = 'https://api.weixin.qq.com/wxa/get_auditstatus?access_token=' . $access_token; $data['auditid'] = $auditid; $result = $this->curlPost($url, json_encode($data)); $result = json_decode($result, true); if (!empty($result) && intval($result['errcode']) == 0 && isset($result['status'])) { $status = intval($result['status']); $up = []; if ($status == 0) { $up['audit_status'] = 2; } elseif ($status == 1) { $up['audit_status'] = 3; } elseif ($status == 2) { $up['audit_status'] = 1; } elseif ($status == 4) { $up['audit_status'] = 4; } if (!empty($result['reason'])) { $up['audit_reason'] = $result['reason']; } if ($up) { $model = new WxwholesalerAccount(); $model->update($up, ['appid' => $appid]); } return $result; } } return []; } public function getAllCategory($appid): array { $data = []; $access_token = $this->authorizerAccessToken($appid); if ($access_token) { $url = 'https://api.weixin.qq.com/wxa/get_category?access_token=' . $access_token; $result = $this->curlGet($url); $result = json_decode($result, true); if ($result && !empty($result['category_list'])) { $category_list = $result['category_list']; foreach ($category_list as $value) { $arr = []; if (!empty($value['first_class'])) { $arr['first_class'] = $value['first_class']; } if (!empty($value['second_class'])) { $arr['second_class'] = $value['second_class']; } if (!empty($value['third_class'])) { $arr['third_class'] = $value['third_class']; } if (!empty($value['first_id'])) { $arr['first_id'] = $value['first_id']; } if (!empty($value['second_id'])) { $arr['second_id'] = $value['second_id']; } if (!empty($value['third_id'])) { $arr['third_id'] = $value['third_id']; } if ($arr) { $data[] = $arr; } } } } return $data; } private function curlPost($url, $post_data, $timeout = 5) { $ch = curl_init(); if (stripos($url, 'https://') !== false) { curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_SSLVERSION, 1); } if (is_string($post_data)) { $strPOST = $post_data; } else { $aPOST = array(); foreach ($post_data as $key => $val) { $aPOST[] = $key . '=' . urlencode($val); } $strPOST = join('&', $aPOST); } curl_setopt($ch, CURLOPT_HTTP_VERSION, 1); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $strPOST); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); $result = curl_exec($ch); $aStatus = curl_getinfo($ch); curl_close($ch); if (intval($aStatus['http_code']) == 200) { return $result; } else { return false; } } protected function curlGet(string $url, array $data = []) { if (!empty($data)) { $url = $url . '?' . http_build_query($data); } $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $result = curl_exec($ch); curl_close($ch); return $result; } }