app = $this->getApp(); } /** * 获取应用实例 * * @return \EasyWeChat\OpenPlatform\Application * */ public function getApp() { // 返回应用实例 $app = Factory::openPlatform(config('wechat.openplat', [])); $appEnv = config('app.env'); // 如果非正式服 if ($appEnv != 'production') { // 获取线上的代码 $result = Curl::to(config('wechat.openplat.release_host_url','').'openplat/install/get_component_access_token')->asJsonResponse(true)->get(); // 设置 if( !empty($result['data']['component_access_token']) ) $app->access_token->setToken($result['data']['component_access_token'],3600); } // 返回结果 return $app; } /** * 获取授权账号刷新令牌 * * @param string $appId appid * * @return string * */ public function getRefreshToken($appId) { // 获取授权账号刷新令牌 $refreshToken = (new Component())->getOne($appId,'refresh_token'); // $refreshToken = (new Component())->where([['authorizer_appid','=',$appId]])->value('authorizer_refresh_token'); // 返回令牌 return (string) $refreshToken; } /** * 获取授权公众号实例 * * @param string $appId appid * * @return \EasyWeChat\OpenPlatform\Authorizer\OfficialAccount\Application * */ public function getOfficial(string $appId) { $refreshToken = $this->getRefreshToken($appId); // 返回应用实例 $official = $this->app->officialAccount($appId, $refreshToken); $appEnv = config('app.env'); if ($appEnv != 'production') { $response = Http::post(config('wechat.openplat.release_host_url','').'openplat/install/get_official_access_token',[ 'app_id' => $appId, ]); $body = json_decode($response->body(),true); $data = $body['data']; $official->access_token->setToken($data['authorizer_access_token'],7200); } // 返回应用实例 return $official; } /** * 获取授权小程序实例 * * @param string $appId appid * * @return \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application * */ public function getMini(string $appId) { $appEnv = config('app.env'); $refreshToken = $this->getRefreshToken($appId); // 返回应用实例 $mini = $this->app->miniProgram($appId, $refreshToken); if ($appEnv != 'production') { $response = Http::post(config('wechat.openplat.release_host_url','').'openplat/install/get_access_token',[ 'app_id' => $appId, ]); $body = json_decode($response->body(),true); $data = $body['data']; $setResult = $mini->access_token->setToken($data['authorizer_access_token'],7200); } // 返回应用实例 return $mini; } /** * 手机号授权 * @param string $code 授权码 * */ public function getUserPhone($code,$appId){ $Mini = $this->getMini($appId); // 获取手机号 $result = $Mini->phone_number->getUserPhoneNumber($code); // 判断结果 if( !empty($result['errcode']) ) return ['error'=>$result['errcode'].'=>'.$result['errmsg']]; // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号) return $result['phone_info']; } /** * 获取用户openid * @param string $code 授权码 * */ public function jscode2session($code,$appId){ $token = $this->app->access_token->getToken(); $params = [ 'appid' => $appId, 'component_access_token'=>$token['component_access_token'], 'grant_type' => 'authorization_code', 'component_appid' => config('wechat.openplat.app_id',''), 'js_code' => $code, ]; $params = http_build_query($params); $result = Curl::to('https://api.weixin.qq.com/sns/component/jscode2session?'.$params)->asJsonResponse(true)->get(); // 判断结果 if( !empty($result['errcode']) ) return ['error'=>$result['errcode'].'=>'.$result['errmsg']]; // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号) return $result; } /** * 提交小程序体验版 * @param string $appId app_id * */ public function commit($appId,$templateId, $extJson, $version, $description){ $Mini = $this->getMini($appId); // 获取手机号 $result = $Mini->code->commit($templateId, $extJson, $version, $description); // 判断结果 if( !empty($result['errcode']) ) return ['error'=>$result['errcode'].'=>'.$result['errmsg']]; // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号) return $result; } /** * 生产小程序体验版二维码 * @param string $code 授权码 * */ public function getTrialQRCode($appId,$fileName,$path=null){ $Mini = $this->getMini($appId); // 获取手机号 $result = $Mini->code->getQrCode($path); file_put_contents($fileName, $result); // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号) return $result; } /** * 获取user 公众号 * @param string $code 通过 前端授权code获取用户openid * * */ public function userFromCode($code,$appId){ $Mini = $this->getOfficial($appId); // 获取手机号 $result = $Mini->oauth->userFromCode($code); // 判断结果 if( !empty($result['errcode']) ) return ['error'=>$result['errcode'].'=>'.$result['errmsg']]; // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号) return $result; } /** * 配置小程序服务器域名 * @param array $params 配置数据 * @param string $appId app_id */ public function modify($appId,$params){ $Mini = $this->getMini($appId); // 配置小程序服务器域名 $result = $Mini->domain->modify($params); // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号) return $result; } /** * 获取UrlLink * @param string $path 通过 URL Link 进入的小程序页面路径,必须是已经发布的小程序存在的页面,不可携带 query 。path 为空时会跳转小程序主页 * @param string $query 通过 URL Link 进入小程序时的query,最大1024个字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~% * */ public function getUrlLink($appId,$path,$query=''){ $Mini = $this->getMini($appId); // 组合参数 $params['path'] = $path; $params['query'] = $query; $params['expire_type'] = 1; // 默认值0.小程序 URL Link 失效类型,失效时间:0,失效间隔天数:1 $params['expire_interval'] = 30; // 到期失效的URL Link的失效间隔天数 $params['env_version'] = config('app.env','production') == 'production' ? 'release' : 'trial'; // 获取结果 $result = $Mini->url_link->generate($params); // 成功返回 if( $result['errcode'] == 0 ) return $result['url_link']; // 失败 return ''; } /** * 获取小程序 scheme 链接 * @author 唐远望 * @version 1.0 * @date 2025-08-28 * */ public function getScheme($appId,$path, $query = '') { $Mini = $this->getMini($appId); $token = $Mini->access_token->getToken(); $params = [ 'jump_wxa' => [ 'path' => $path, 'query' => $query, ], 'expire_time' => time() + 86400 * 30 //最长有效期为 30 天 ]; // 直接发送 JSON 数据 $result = Curl::to('https://api.weixin.qq.com/wxa/generatescheme?access_token=' . $token['authorizer_access_token']) ->withContentType('application/json') // 设置 Content-Type ->withData(json_encode($params)) // 将数组转换为 JSON 字符串 ->asJsonResponse(true) ->post(); // 判断结果 if (!empty($result['errcode'])) { //写入日志 Log::error('getScheme','获取小程序 scheme 码失败',['errcode' => $result['errcode'], 'errmsg' => $result['errmsg']]); return ''; } // 返回结果 return $result['openlink']; } /** * 获取小程序码 * * @param string $scene 小程序码的场景值 * @param array $optional 其他参数 * page 小程序码的页面路径 * width 小程序码的宽度 * */ public function getUnlimit($appId,string $scene, array $optional = []){ $Mini = $this->getMini($appId); // 小程序版本,体验还是正式版 $optional['env_version'] = config('app.env') == 'production' ? 'release' : 'trial'; // 执行生成 $result = $Mini->app_code->getUnlimit($scene, $optional); // 判断结果 if( $result instanceof \EasyWeChat\Kernel\Http\StreamResponse ) return $result->getBody()->getContents(); // 错误 return ['error'=>$result['errcode'].'=>'.$result['errmsg']]; } /** * 发布最后一个审核通过的小程序代码版本 * * @param string $appId app_id */ public function release($appId){ $Mini = $this->getMini($appId); // 配置小程序服务器域名 $result = $Mini->code->release(); // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号) return $result; } /** * 发布最后一个审核通过的小程序代码版本 * * @param string $appId app_id */ public function submitAudit($appId,$data){ $Mini = $this->getMini($appId); // 配置小程序服务器域名 $result = $Mini->code->submitAudit($data); // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号) return $result; } /** * 获取类目名称信息 * * @param string $appId app_id */ public function getAllCategoryName($appId){ $Mini = $this->getMini($appId); // 配置小程序服务器域名 $result = $Mini->code->getCategory(); // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号) return $result; } /** * 获取永久素材 * * @param string $appId app_id * @param int $mediaId 素材id */ public function getMaterial($appId,$mediaId){ $Mini = $this->getMini($appId); // 配置小程序服务器域名 $result = $Mini->material->get($mediaId); // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号) return $result; } /** * 设置小程序用户隐私保护指引 * * @param string $appId app_id * @param array $params params */ public function setPrivacySetting($appId,$params){ $Mini = $this->getMini($appId); // 设置小程序用户隐私保护指引 $result = $Mini->privacy->set($params); // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号) return $result; } /** * 获取小程序信息 * * @param string $appId app_id */ public function getAccountBasicInfo($appId){ $Mini = $this->getMini($appId); // 设置小程序用户隐私保护指引 $result = $Mini->account->getBasicInfo(); // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号) return $result; } /** * 获取小程序版本信息 * * @param string $appId app_id */ public function getVersionInfo($appId){ $Mini = $this->getMini($appId); // 设置小程序用户隐私保护指引 $result = $Mini->base->getVersionInfo(); // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号) return $result; } /** * 配置小程序业务域名 * @param array $params 配置数据 * @param string $appId app_id */ public function modifyJumpDomain($appId,$params){ $Mini = $this->getMini($appId); // 配置小程序服务器域名 $result = $Mini->domain->setWebviewDomain($params['setWebviewDomain'],$params['action']); // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号) return $result; } /** * 查询最后一个审核结果 * * @param string $appId app_id */ public function getLatestAuditStatus($appId){ $Mini = $this->getMini($appId); // 配置小程序服务器域名 $result = $Mini->code->getLatestAuditStatus(); // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号) return $result; } /** * 小程序获取缓存的AccessToken * */ public function getAccessToken($appId){ // 返回应用实例 $Mini = $this->getMini($appId); // 获取access_token $accessToken = $Mini->access_token->getToken(); return $accessToken; } /** * 公众号获取缓存的AccessToken * */ public function getOfficialAccessToken($appId){ // 返回应用实例 $official = $this->getOfficial($appId); // 获取access_token $accessToken = $official->access_token->getToken(); return $accessToken; } /** * 代小程序上传发货物流信息 * */ public function deliver($appId,$data=[]){ $Mini = $this->getMini($appId); // 获取access_token $accessToken = $Mini->access_token->getToken(); $params = [ 'order_key' =>[ "order_number_type" => $data['order_number_type'], "mchid" => $data['mchid'], "transaction_id" => $data['transaction_id'] ?? null, ], 'delivery_mode' => $data['delivery_mode'], 'logistics_type' => $data['logistics_type'], 'shipping_list' =>[ [ "tracking_no" => $data['tracking_no'], "express_company" => $data['express_company'], "item_desc" => $data['item_desc'], 'contact' => [ 'consignor_contact' => $data['consignor_contact'] ?? null, 'receiver_contact' => $data['receiver_contact'] ?? null, ], ] ], 'upload_time' => date('Y-m-d\TH:i:s.vP',$data['upload_time']), 'payer' => [ 'openid' => $data['openid'], ], ]; // 上传物流信息 //$result = $Mini->shipping->uploadShippingInfo($params); $token = [ 'access_token' => $accessToken['authorizer_access_token'] ]; try { $token = http_build_query($token); $params = json_encode($params, JSON_UNESCAPED_UNICODE); $result = Curl::to('https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info?'.$token)->withData($params)->asJsonResponse(true)->post(); // 判断结果 if( !empty($result['errcode']) ){ Log::error('wechat/deliver','发货失败,出现错误'.json_encode($result).'参数'.json_encode($params)); return ['code'=>'error','msg'=>'发货失败','data'=>$result]; } } catch (\Exception $e) { if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) { $r = $e->getResponse(); // 记录错误信息 Log::error('wechat/deliver','发货失败,出现错误'.$r->getBody().'参数'.json_encode($params)); } return ['code'=>'error','msg'=>'发货失败','data'=>['msg'=>$e->getMessage(),'params'=>$params]]; } // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号) return ['code'=>'success','msg'=>'发货成功']; } /** * 申请物流信息 * */ public function applyMsgPlugin($appId,$data=[]){ // 返回应用实例 $Mini = $this->getMini($appId); // 获取access_token $accessToken = $Mini->access_token->getToken(); $params = [ 'access_token' => $accessToken['authorizer_access_token'], ]; $params = http_build_query($params); $result = Curl::to('https://api.weixin.qq.com/cgi-bin/express/delivery/open_msg/open_openmsg?'.$params)->asJsonResponse(true)->get(); // 判断结果 if( !empty($result['errcode']) ) return ['code'=>'error','msg'=>$result['errcode'].'=>'.$result['errmsg']]; // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号) return ['code'=>'success','msg'=>'申请成功']; } /** * 获取快递公司列表 * */ public function getExpressCompanyList($appId){ // 返回应用实例 $Mini = $this->getMini($appId); // 获取access_token $accessToken = $Mini->access_token->getToken(); $params = [ 'access_token' => $accessToken['authorizer_access_token'], ]; $data = $params; $params = http_build_query($params); $data = json_encode($data, JSON_UNESCAPED_UNICODE); $result = Curl::to('https://api.weixin.qq.com/cgi-bin/express/delivery/open_msg/get_delivery_list?'.$params)->withData($data)->asJsonResponse(true)->post(); // 判断结果 if( !empty($result['errcode']) ) return ['code'=>'error','msg'=>$result['errcode'].'=>'.$result['errmsg']]; // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号) return ['code'=>'success','msg'=>'成功','data'=>$result['delivery_list']]; } }