app = $app; $this->request = app('request'); $this->initialize($escape); } } public function initialize($escape = false) { if (!($this->request->action() == 'wxTicketCallback') && !($this->request->action() == 'wxCallback') && !$escape) { parent::initialize(); } $this->encodingAesKey = config('third.encodingAesKey'); $this->appId = config('third.appId'); $this->appSecret = config('third.appSecret'); $this->token = config('third.token'); } /** * 票据获取接口 * @param Request $request * @throws InvalidArgumentException */ public function wxTicketCallback(Request $request) { $msg = ''; $timeStamp = $request->param('timestamp', ''); $nonce = $request->param('nonce', ''); $msg_sign = $request->param('msg_signature', ''); $encryptMsg = file_get_contents('php://input'); $pc = new WXBizMsgCrypt($this->token, $this->encodingAesKey, $this->appId); $errCode = $pc->decryptMsg($msg_sign, $timeStamp, $nonce, $encryptMsg, $msg); if ($errCode == 0) { $data = $this->_xmlToArr($msg); switch ($data['InfoType']) { case'component_verify_ticket': //接收验证票据 CacheService::redisHandler()->set('component_verify_ticket', $data['ComponentVerifyTicket']); break; case 'unauthorized'; //todo 发送授权取消通知 break; case 'updateauthorized': //todo 发送授权更新通知 break; case 'authorized': //todo 发送授权成功通知 break; default: break; } echo 'success'; } else { echo '解密失败' . $errCode; } } /** * 消息与事件接受接口 * @param $appid * @param Request $request */ public function wxCallback($appid, Request $request) { $msg = ''; $timeStamp = $request->param('timestamp', ''); $nonce = $request->param('nonce', ''); $msg_sign = $request->param('msg_signature', ''); $encryptMsg = file_get_contents('php://input'); $pc = new WXBizMsgCrypt($this->token, $this->encodingAesKey, $this->appId); Log::error('77777777777'); Log::error($msg_sign); Log::error($timeStamp); Log::error($nonce); Log::error($encryptMsg); Log::error($msg); $errCode = $pc->decryptMsg($msg_sign, $timeStamp, $nonce, $encryptMsg, $msg); Log::error($errCode); Log::error($this->_xmlToArr($msg)); @file_put_contents('call_back_log.txt', '[' . date('Y-m-d H:i:s') . ']' . json_encode($data) . PHP_EOL, FILE_APPEND); if ($errCode == 0) { $data = $this->_xmlToArr($msg); @file_put_contents('call_back_log.txt', '[' . date('Y-m-d H:i:s') . ']' . json_encode($data) . PHP_EOL, FILE_APPEND); $mer_id = MerchantMiniprogram::where(['appid' => $appid])->value('mer_id'); BaseModel::beginTrans(); try { switch ($data['Event']) { case'weapp_audit_success': $info = MerchantCodeAudit::where('mer_id', $mer_id)->where('status', 2)->order('commit_time', 'desc')->find(); $info->save(['success_time' => $data['SuccTime'], 'status' => 0]); //todo 某小程序代码审核通过 break; case 'weapp_audit_fail'; $info = MerchantCodeAudit::where('mer_id', $mer_id)->where('status', 2)->order('commit_time', 'desc')->find(); $info->save(['fail_time' => $data['FailTime'], 'status' => 1, 'reason' => $data['Reason'], 'screenshot' => $data['ScreenShot']]); //todo 某小程序代码审核不通过 break; case 'weapp_audit_delay': $info = MerchantCodeAudit::where('mer_id', $mer_id)->where('status', 2)->order('commit_time', 'desc')->find(); $info->save(['delay_time' => $data['DelayTime'], 'status' => 4, 'reason' => $data['Reason']]); //todo 某小程序代码核延后 break; default: break; } BaseModel::commitTrans(); echo 'success'; } catch (Exception $e) { BaseModel::rollbackTrans(); echo $e->getMessage(); } catch (DbException $e) { BaseModel::rollbackTrans(); echo $e->getMessage(); } } else { BaseModel::rollbackTrans(); echo '解密失败' . $errCode; } } /** * @param $xml * @return mixed|SimpleXMLElement */ protected function _xmlToArr($xml) { $res = @simplexml_load_string($xml, NULL, LIBXML_NOCDATA); $res = json_decode(json_encode($res), true); return $res; } /** * 获取令牌 * @return mixed * @throws \Exception * @throws InvalidArgumentException */ public function getComponentAccessToken() { try { if (CacheService::redisHandler()->get('component_access_token')) { return CacheService::redisHandler()->get('component_access_token'); } $url = 'https://api.weixin.qq.com/cgi-bin/component/api_component_token'; $data = [ 'component_appid' => $this->appId, 'component_appsecret' => $this->appSecret, 'component_verify_ticket' => CacheService::redisHandler()->get('component_verify_ticket'), ]; // $data['component_verify_ticket'] = 'ticket@@@iWDHjOTCGw8wIGeopp1Nux6gbi7KYD-MtFroWBw6WkEReGbCQWBesNMd-c3LlaR07J1cx4AQF48LYDMmcmdqZQ'; $res = json_decode(doRequest($url, $data, null, true, true), true); if (isset($res['component_access_token'])) { CacheService::redisHandler()->set('component_access_token', $res['component_access_token'], $res['expires_in']); return $res['component_access_token']; } else { throw exception($res['errmsg'] ?? $res['msg'] ?? '请求错误'); } } catch (Exception $e) { throw exception($e->getMessage()); } } /** * 获取预授权码 * @return mixed * @throws \Exception * @throws InvalidArgumentException */ protected function getPreAuthCode() { try { if (CacheService::redisHandler()->get('pre_auth_code')) { return CacheService::redisHandler()->get('pre_auth_code'); } $component_access_token = $this->getComponentAccessToken(); $url = 'https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=' . $component_access_token; $data = [ 'component_appid' => $this->appId, ]; $res = json_decode(doRequest($url, $data, null, true, true), true); if (isset($res['pre_auth_code'])) { CacheService::redisHandler()->set('pre_auth_code', $res['pre_auth_code'], $res['expires_in']); return $res['pre_auth_code']; } else { throw exception($res['errmsg'] ?? $res['msg'] ?? '请求错误'); } } catch (Exception $e) { throw exception($e->getMessage()); } } /** * 生成授权地址 * @param string $re_url 回调地址 * @param int $type 授权地址类型:1桌面端,其他移动端 * @return mixed * @throws InvalidArgumentException */ public function createAuthUrl($type = 1) { try { $re_url = $this->request->get('re_url'); if (!$re_url) { return app('json')->fail('请传入回调地址'); } $url_base = $type == 1 ? 'https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=' : 'https://mp.weixin.qq.com/safe/bindcomponent?action=bindcomponent&auth_type=3&no_scan=1&component_appid='; $pre_auth_code = $this->getPreAuthCode(); $url = $url_base . $this->appId . '&pre_auth_code=' . $pre_auth_code . '&redirect_uri=' . $re_url;//todo 改成前端的接收页面 if ($type != 1) { $url .= '#wechat_redirect'; } return app('json')->success('ok', ['url' => $url]); } catch (Exception $e) { return app('json')->fail($e->getMessage(), ['file' => $e->getFile(), 'line' => $e->getLine()]); } } /** * 接收授权码 * @param $mer_id * @param Request $request * @return mixed * @throws InvalidArgumentException */ public function wxAuthRedirect(Request $request, $mer_id) { $auth_code = $request->post('authorization_code'); $expires_in = $request->post('expires_in'); if ($auth_code && $expires_in) { try { CacheService::redisHandler()->set($mer_id . '_authorization_code', $auth_code, $expires_in); //return $this->getAuthInfo(['mer_id' => $mer_id]); return app('json')->success('获取授权码成功'); } catch (Exception $e) { return app('json')->fail($e->getMessage(), ['file' => $e->getFile(), 'line' => $e->getLine()]); } } else { return app('json')->fail('获取授权码失败'); } } /** *使用授权码获取授权信息 * @param array $param * @return mixed * @throws \Exception * @throws InvalidArgumentException */ protected function getAuthInfo($param = ['mer_id' => '']) { BaseModel::beginTrans(); try { $mer_id = $param['mer_id'] ?? ''; if (!$mer_id) { BaseModel::rollbackTrans(); throw exception('未找到商家'); } $mer_info = Merchant::get($mer_id); if (!$mer_info) { BaseModel::rollbackTrans(); throw exception('未找到商家'); } $authorization_code = CacheService::redisHandler()->get($param['mer_id'] . '_authorization_code'); if (!$authorization_code) { BaseModel::rollbackTrans(); throw exception('请先获取授权码'); } $component_access_token = $this->getComponentAccessToken(); $url = 'https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=' . $component_access_token; $data = [ 'component_appid' => $this->appId, 'authorization_code' => $authorization_code, ]; $res = json_decode(doRequest($url, $data, null, true, true), true); if (isset($res['authorization_info'])) { //保存/更新内容到商户小程序表 if (MerchantMiniprogram::vaildWhere()->where(['mer_id' => $mer_id])->find()) { MerchantMiniprogram::vaildWhere()->where('mer_id', $mer_id)->update([ 'appid' => $res['authorization_info']['authorizer_appid'], 'authorizer_refresh_token' => $res['authorization_info']['authorizer_refresh_token'], 'func_info' => json_encode($res['authorization_info']), 'update' => time(), ]); BaseModel::commitTrans(); } else { $app_secret = self::createAppSecret(); MerchantMiniprogram::create([ 'mer_id' => $mer_id, 'appid' => $res['authorization_info']['authorizer_appid'], 'authorizer_refresh_token' => $res['authorization_info']['authorizer_refresh_token'], 'func_info' => json_encode($res['authorization_info']), 'add_time' => time(), 'update' => time(), 'boofly_app_secret' => $app_secret, ]); BaseModel::commitTrans(); } //保存authorizer_access_token到redis,过期时间为expires_in CacheService::redisHandler()->set($mer_id . '_authorizer_access_token', $res['authorization_info']['authorizer_access_token'], $res['authorization_info']['expires_in']); return array_merge($res['authorization_info'], ['boofly_app_secret' => $app_secret ?? '']); } else { BaseModel::rollbackTrans(); throw exception($res['errmsg'] ?? $res['msg'] ?? '请求错误'); } } catch (Exception $e) { BaseModel::rollbackTrans(); throw exception($e->getMessage()); } catch (DbException $e) { BaseModel::rollbackTrans(); throw exception($e->getMessage()); } } /** * 创建app_secret * @return string * @throws \Exception */ private static function createAppSecret() { return md5(bin2hex(random_bytes(8))); } /** *获取/刷新商户接口调用令牌 * @param array $param * @return mixed * @throws InvalidArgumentException * @throws \Exception */ public function getAuthorizerAccessToken($param = ['mer_id' => '', 'get_token' => 1, 'return_array' => false]) { BaseModel::beginTrans(); try { $mer_id = $param['mer_id'] ?? ''; $get_token = isset($param['get_token']) ? $param['get_token'] : 1; $return_array = isset($param['return_array']) ? $param['return_array'] : false; if (!$mer_id) { throw exception('未找到商家'); } if (CacheService::redisHandler()->get($mer_id . '_authorizer_access_token') && $get_token == 1) { BaseModel::commitTrans(); return CacheService::redisHandler()->get($mer_id . '_authorizer_access_token'); } // 根据商户id从商户表获取authorizer_appid和authorizer_refresh_token $authorizer_info = MerchantMiniprogram::vaildWhere()->where(['mer_id' => $mer_id])->find(); if (!$authorizer_info) { throw exception('未找到商家'); } $component_access_token = $this->getComponentAccessToken(); $url = 'https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=' . $component_access_token; $data = [ 'component_appid' => $this->appId, 'authorizer_appid' => $authorizer_info['appid'], 'authorizer_refresh_token' => $authorizer_info['authorizer_refresh_token'], ]; $res = json_decode(doRequest($url, $data, null, true, true), true); if (isset($res['authorizer_access_token'])) { MerchantMiniprogram::vaildWhere()->where('mer_id', $mer_id)->update([ 'authorizer_refresh_token' => $res['authorizer_refresh_token'], 'update' => time(), ]); BaseModel::commitTrans(); //保存新的authorizer_access_token到redis,过期时间为expires_in CacheService::redisHandler()->set($mer_id . '_authorizer_access_token', $res['authorizer_access_token'], $res['expires_in']); return $return_array ? ['access_token' => $res['authorizer_access_token'], 'expires_in' => $res['expires_in']] : $res['authorizer_access_token']; } else { BaseModel::rollbackTrans(); throw exception($res['errmsg'] ?? $res['msg'] ?? '请求错误'); } } catch (Exception $e) { BaseModel::rollbackTrans(); throw exception($e->getMessage()); } catch (DbException $e) { BaseModel::rollbackTrans(); throw exception($e->getMessage()); } } /** * 获取授权方的帐号基本信息 * 包括头像、昵称、帐号类型、认证类型、微信号、原始ID和二维码图片URL * 具体参数详见https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/api_get_authorizer_info.html#%E5%85%AC%E4%BC%97%E5%8F%B7%E5%B8%90%E5%8F%B7%E4%BF%A1%E6%81%AF * @param array $param * @return mixed * @throws InvalidArgumentException * @throws \Exception */ protected function getAuthorizerInfo($param = ['mer_id' => '']) { BaseModel::beginTrans(); try { $mer_id = $param['mer_id'] ?? ''; if (!$mer_id) { BaseModel::rollbackTrans(); throw exception('未找到商家'); } //根据商户id从商户表获取authorizer_appid和authorizer_refresh_token $authorizer_info = MerchantMiniprogram::vaildWhere()->where(['mer_id' => $mer_id])->find(); if (!$authorizer_info) { BaseModel::rollbackTrans(); throw exception('未找到商家'); } $component_access_token = $this->getComponentAccessToken(); $url = 'https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token=' . $component_access_token; $data = [ 'component_appid' => $this->appId, 'authorizer_appid' => $authorizer_info['appid'], ]; $res = json_decode(doRequest($url, $data, null, true, true), true); if (isset($res['authorizer_info'])) { //更新商户信息到商户表 MerchantMiniprogram::vaildWhere()->where('mer_id', $mer_id)->update([ 'nick_name' => $res['authorizer_info']['nick_name'], 'head_img' => $res['authorizer_info']['head_img'], 'signature' => $res['authorizer_info']['signature'], 'principal_name' => $res['authorizer_info']['principal_name'], 'categories' => $res['authorizer_info']['nick_name'], 'business_info' => json_encode($res['authorizer_info']['business_info']), 'service_type_info' => $res['authorizer_info']['service_type_info']['id'], 'user_name' => $res['authorizer_info']['user_name'], 'qrcode_url' => $res['authorizer_info']['qrcode_url'], 'update' => time(), ]); Merchant::where('id', $mer_id)->update(['name' =>$res['authorizer_info']['nick_name'], 'logo' => $res['authorizer_info']['head_img']]); BaseModel::commitTrans(); // CacheService::redisHandler()->set($mer_id . '_authorizer_access_token', $res['authorizer_access_token'], $res['expires_in']); return $res; } else { BaseModel::rollbackTrans(); throw exception($res['errmsg'] ?? $res['msg'] ?? '请求错误'); } } catch (Exception $e) { BaseModel::rollbackTrans(); throw exception($e->getMessage()); } catch (DbException $e) { BaseModel::rollbackTrans(); throw exception($e->getMessage()); } } /** *获取授权方选项信息(可能不常用) * 如:地理位置上报,语音识别开关,多客服开关 * @param array $param * @return mixed * @throws InvalidArgumentException * @throws \Exception */ protected function getAuthorizerOption($param = ['mer_id' => '', 'option_name' => '']) { try { $mer_id = $param['mer_id'] ?? ''; $option_name = $param['option_name'] ?? ''; if (!$mer_id) { throw exception('未找到商家'); } //根据商户id从商户表获取authorizer_appid和authorizer_refresh_token $authorizer_info = MerchantMiniprogram::vaildWhere()->where(['mer_id' => $mer_id])->find(); if (!$authorizer_info) { throw exception('未找到商家'); } $component_access_token = $this->getComponentAccessToken(); $url = 'https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_option?component_access_token=' . $component_access_token; $data = [ 'component_appid' => $this->appId, 'authorizer_appid' => $authorizer_info['appid'], 'option_name' => $option_name, ]; $res = json_decode(doRequest($url, $data, null, true, true), true); if (isset($res['option_value'])) { return $res['option_value']; } else { throw exception($res['errmsg'] ?? $res['msg'] ?? '请求错误'); } } catch (Exception $e) { throw exception($e->getMessage()); } } /** * 设置授权方选项信息 * @param $mer_id * @param $option_name * @param $option_value * @return mixed * @throws InvalidArgumentException * @throws \Exception */ public function setAuthorizerOption($mer_id, $option_name, $option_value) { try { if (!$mer_id) { return app('json')->fail('未找到商家'); } //根据商户id从商户表获取authorizer_appid和authorizer_refresh_token $authorizer_info = MerchantMiniprogram::vaildWhere()->where(['mer_id' => $mer_id])->find(); if (!$authorizer_info) { throw exception('未找到商家'); } $component_access_token = $this->getComponentAccessToken(); $url = 'https://api.weixin.qq.com/cgi-bin/component/api_set_authorizer_option?component_access_token=' . $component_access_token; $data = [ 'component_appid' => $this->appId, 'authorizer_appid' => $authorizer_info['appid'], 'option_name' => $option_name, 'option_value' => $option_value, ]; $res = json_decode(doRequest($url, $data, null, true, true), true); if (isset($res['errcode'])) { if ($res['errcode'] == 0) { return app('json')->success('设置成功'); } else { return app('json')->fail($res['errmsg']); } } else { return app('json')->fail($res['errmsg'] ?? $res['msg'] ?? '请求错误'); } } catch (Exception $e) { return app('json')->fail($e->getMessage(), ['file' => $e->getFile(), 'line' => $e->getLine()]); } } /** * 拉取所有已授权的帐号信息 * @param array $page * @return mixed * @throws InvalidArgumentException * @throws \Exception */ protected function getAuthorizerList($page = ['offset' => 1, 'count' => 10]) { $offset = $page['offset'] ?? 1; $count = $page['count'] ?? 10; BaseModel::beginTrans(); try { $component_access_token = $this->getComponentAccessToken(); $url = 'https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_list?component_access_token=' . $component_access_token; $data = [ 'component_appid' => $this->appId, 'offset' => $offset, 'count' => $count, ]; $res = json_decode(doRequest($url, $data, null, true, true), true); if (isset($res['total_count'])) { foreach ($res['list'] as $v) { MerchantMiniprogram::vaildWhere()->where('appid', $v['authorizer_appid'])->update(['authorizer_refresh_token' => $v['refresh_token'], 'update' => time()]); } BaseModel::commitTrans(); return $res; } else { BaseModel::rollbackTrans(); throw exception($res['errmsg'] ?? '请求错误'); } } catch (Exception $e) { BaseModel::rollbackTrans(); throw exception($e->getMessage()); } catch (DbException $e) { BaseModel::rollbackTrans(); throw exception($e->getMessage()); } } /** * 前端获取某些信息 * @param Request $request * @param string $info_type 获取的信息类型 * @return mixed */ public function getInfoForFront(Request $request, $info_type = 'AuthorizerAccessToken') { try { $function = 'get' . $info_type; if (method_exists($this, $function)) { if (!$request->param()) { $res = $this->$function(); } else { $res = $this->$function($request->param()); } return app('json')->success('ok', ['res' => $res]); } else { return app('json')->fail('获取失败'); } } catch (Exception $e) { return app('json')->fail($e->getMessage(), ['file' => $e->getFile(), 'line' => $e->getLine()]); } } }