"小程序", ); //小程序认证类型 public $verify_type = array( -1 => "未认证", 0 => "微信认证", ); //business_info 说明 public $business_type = array( 'open_store' => "是否开通微信门店功能", 'open_scan' => "是否开通微信扫商品功能", 'open_pay' => "是否开通微信支付功能", 'open_card' => "是否开通微信卡券功能", 'open_shake' => "是否开通微信摇一摇功能", ); // 站点ID private $site_id; private $app_id; public $openConfig; private $access_token; public function __construct($site_id = 0) { $this->site_id = $site_id; //微信小程序配置 $weapp_config_model = new WeappConfigModel(); $weapp_config = $weapp_config_model->getWeappConfig($site_id); $weapp_config = $weapp_config["data"]["value"]; if (isset($weapp_config['is_authopen'])&&$weapp_config['is_authopen']&& addon_is_exit('wxoplatform')) { $platform=new OpenPlatform($site_id); $this->platform=$platform->openPlatform(); $this->app = $this->platform->app('weapp'); // $this->access_token = $this->app->componentAccessToken; // var_dump($this->access_token); } else { $config = [ 'app_id' => $weapp_config["appid"] ?? '', 'secret' => $weapp_config["appsecret"] ?? '', 'response_type' => 'array', 'log' => [ 'level' => 'debug', 'permission' => 0777, 'file' => 'runtime/log/wechat/easywechat.logs', ], ]; $this->app = Factory::miniProgram($config); $this->app->rebind('request', request()); $this->app->rebind('cache', Cache::store('redis_public')); $accessToken = $this->app->access_token; $accessTokenInfo = $accessToken->getToken(); $this->access_token = $accessTokenInfo['access_token']; } $this->app_id = $weapp_config["appid"] ?? ''; $this->app->rebind('getClient', new Client($this->app)); } public function app() { return $this->app; } public function SetVerifyTicket() { $verify_ticket = new VerifyTicket($this->platform); try { $verify_ticket->getTicket(); } catch (\Exception $e) { $where = [['site_id', '=', 0], ['app_module', '=', 'admin'], ['config_key', '=', 'OPLATFORM_CONFIG']]; $info = model('config')->getValue($where, 'value'); if ($info) { $v = json_decode($info, true); $verify_ticket->setTicket($v['ComponentVerifyTicket'] ?? ''); } } } public function httpPostJson($url, array $data = [], array $query = []) { return $this->app->getClient->httpPostJson($url, $data, $query); } public function httpPost($url, array $data = []) { return $this->app->getClient->httpPost($url, $data); } public function httpGet($url, array $data = []) { return $this->app->getClient->httpGet($url, $data); } /** * 获取快递公司列表 * @return array * @throws \GuzzleHttp\Exception\GuzzleException */ public function getCompanyList() { $cache = Cache::store('redis_public')->get('weixinCompanyListDeliveryList'); if ($cache) return $cache; try { $result = $this->httpPostJson('cgi-bin/express/delivery/open_msg/get_delivery_list'); if (isset($result['errcode']) && $result['errcode'] == 0) { $data = $this->success($result['delivery_list']); Cache::store('redis_public')->set('weixinCompanyListDeliveryList', $data); return $data; } else { return $this->error('', $result['errmsg']); } } catch (\Exception $e) { return $this->error('', $e->getMessage()); } } public function isTradeManaged() { try { $cache = Cache::store('redis_public')->get('isTradeManaged'); if ($cache) return $cache; $result = $this->app->getClient->isTradeManaged($this->app_id); if (isset($result['errcode']) && $result['errcode'] == 0) { $data = $result['is_trade_managed']; if ($data) { Cache::store('redis_public')->set('isTradeManaged', $data); } return $data; } else { return false; } } catch (Exception $e) { return false; } } /** * TODO * 根据 jsCode 获取用户 session 信息 * @param $param * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException */ public function authCodeToOpenid($param) { try { $result = $this->app->auth->session($param['code']); if (isset($result['errcode']) && $result['errcode'] != 0) { return $this->handleError($result['errcode'], $result['errmsg']); } else { Cache::store('redis_public')->set('weapp_' . $result['openid'], $result); unset($result['session_key']); return $this->success($result); } } catch (\Exception $e) { if (property_exists($e, 'formattedResponse')) { return $this->handleError($e->formattedResponse['errcode'], $e->formattedResponse['errmsg']); } return $this->error('', $e->getMessage()); } } /** * 生成二维码 * @param unknown $param */ public function createQrcode($param) { try { $checkpath_result = $this->checkPath($param['qrcode_path']); if ($checkpath_result["code"] != 0) return $checkpath_result; // scene:场景值最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~ $scene = ''; if (!empty($param['data'])) { foreach ($param['data'] as $key => $value) { //防止参数过长,source_member用m代替 if ($key == 'source_member') { $key = 'm'; } if ($scene == '') $scene .= $key . '-' . $value; else $scene .= '&' . $key . '-' . $value; } } $response = $this->app->app_code->getUnlimit($scene, [ 'page' => substr($param['page'], 1), 'width' => isset($param['width']) ? $param['width'] : 120 ]); if ($response instanceof \EasyWeChat\Kernel\Http\StreamResponse) { $filename = $param['qrcode_path'] . '/'; $filename .= $response->saveAs($param['qrcode_path'], $param['qrcode_name'] . '_' . $param['app_type'] . '.png'); return $this->success(['path' => $filename]); } else { return $this->handleError($response['errcode'], $response['errmsg']); } } catch (\Exception $e) { if (property_exists($e, 'formattedResponse')) { return $this->handleError($e->formattedResponse['errcode'], $e->formattedResponse['errmsg']); } return $this->error('', $e->getMessage()); } } /** * 校验目录是否可写 * @param unknown $path * @return multitype:number unknown |multitype:unknown */ private function checkPath($path) { if (is_dir($path) || mkdir($path, intval('0755', 8), true)) { return $this->success(); } return $this->error('', "directory {$path} creation failed"); } /************************************************************* 数据统计与分析 start **************************************************************/ /** * 访问日趋势 * @param $from 格式 20170313 * @param $to 格式 20170313 */ public function dailyVisitTrend($from, $to) { try { $result = $this->app->data_cube->dailyVisitTrend($from, $to); if (isset($result['errcode']) && $result['errcode'] != 0) { return $this->error([], $result["errmsg"]); } return $this->success($result["list"]); } catch (\Exception $e) { return $this->error([], $e->getMessage()); } } /** * 访问周趋势 * @param $from * @param $to * @return array|\multitype */ public function weeklyVisitTrend($from, $to) { try { $result = $this->app->data_cube->weeklyVisitTrend($from, $to); if (isset($result['errcode']) && $result['errcode'] != 0) { return $this->error([], $result["errmsg"]); } return $this->success($result["list"]); } catch (\Exception $e) { return $this->error([], $e->getMessage()); } } /** * 访问月趋势 * @param $from * @param $to * @return array|\multitype */ public function monthlyVisitTrend($from, $to) { try { $result = $this->app->data_cube->monthlyVisitTrend($from, $to); if (isset($result['errcode']) && $result['errcode'] != 0) { return $this->error([], $result["errmsg"]); } return $this->success($result["list"]); } catch (\Exception $e) { return $this->error([], $e->getMessage()); } } /** * 访问分布 * @param $from * @param $to */ public function visitDistribution($from, $to) { try { $result = $this->app->data_cube->visitDistribution($from, $to); if (isset($result['errcode']) && $result['errcode'] != 0) { return $this->error($result, $result["errmsg"]); } return $this->success($result["list"]); } catch (\Exception $e) { return $this->error([], $e->getMessage()); } } /** * 访问页面 * @param $from * @param $to */ public function visitPage($from, $to) { try { $result = $this->app->data_cube->visitPage($from, $to); if (isset($result['errcode']) && $result['errcode'] != 0) { return $this->error([], $result["errmsg"]); } return $this->success($result["list"]); } catch (\Exception $e) { return $this->error([], $e->getMessage()); } } /************************************************************* 数据统计与分析 end **************************************************************/ /** * 下载小程序代码包 * @param $site_id */ public function download($site_id) { $source_file_path = $this->createTempPackage($site_id, 'public/weapp'); $file_arr = getFileMap($source_file_path); if (!empty($file_arr)) { $zipname = 'upload/weapp_' . $site_id . '_' . date('Ymd') . '.zip'; $zip = new \ZipArchive(); $res = $zip->open($zipname, \ZipArchive::CREATE); if ($res === TRUE) { foreach ($file_arr as $file_path => $file_name) { if (is_dir($file_path)) { $file_path = str_replace($source_file_path . '/', '', $file_path); $zip->addEmptyDir($file_path); } else { $zip_path = str_replace($source_file_path . '/', '', $file_path); $zip->addFile($file_path, $zip_path); } } $zip->close(); header("Content-Type: application/zip"); header("Content-Transfer-Encoding: Binary"); header("Content-Length: " . filesize($zipname)); header("Content-Disposition: attachment; filename=\"" . basename($zipname) . "\""); readfile($zipname); @unlink($zipname); deleteDir($source_file_path); } } } /** * 创建临时包 * @param $site_id * @param $package_path * @param string $to_path * @return array */ private function createTempPackage($site_id, $package_path, $to_path = '') { if (is_dir($package_path)) { $package = scandir($package_path); if (empty($to_path)) { $to_path = 'upload/temp/' . $site_id . '/'; dir_mkdir($to_path); } foreach ($package as $path) { $temp_path = $package_path . '/' . $path; if (is_dir($temp_path)) { if ($path == '.' || $path == '..') {//判断是否为系统隐藏的文件.和.. 如果是则跳过否则就继续往下走,防止无限循环再这里。 continue; } dir_mkdir($to_path . $path); $this->createTempPackage($site_id, $temp_path, $to_path . $path . '/'); } else { if (file_exists($temp_path)) { copy($temp_path, $to_path . $path); if (stristr($temp_path, 'common/vendor.js')) { $content = file_get_contents($to_path . $path); $content = $this->paramReplace($site_id, $content); file_put_contents($to_path . $path, $content); } } } } return $to_path; } } /** * 参数替换 * @param $site_id * @param $string * @return null|string|string[] */ private function paramReplace($site_id, $string) { $api_model = new Api(); $api_config = $api_model->getApiConfig(); $api_config = $api_config['data']; $web_config_model = new WebConfig(); $web_config = $web_config_model->getMapConfig(); $web_config = $web_config['data']['value']; $socket_url = (strstr(ROOT_URL, 'https://') === false ? str_replace('http', 'ws', ROOT_URL) : str_replace('https', 'wss', ROOT_URL)) . '/wss'; $patterns = [ '/\{\{\$baseUrl\}\}/', '/\{\{\$imgDomain\}\}/', '/\{\{\$h5Domain\}\}/', '/\{\{\$mpKey\}\}/', '/\{\{\$apiSecurity\}\}/', '/\{\{\$publicKey\}\}/', '/\{\{\$webSocket\}\}/' ]; $replacements = [ ROOT_URL, ROOT_URL, ROOT_URL . '/h5', $web_config['tencent_map_key'] ?? '', $api_config['is_use'] ?? 0, $api_config['value']['public_key'] ?? '', $socket_url ]; $string = preg_replace($patterns, $replacements, $string); return $string; } /** * 消息解密 * @param array $param */ public function decryptData($param = []) { try { $cache = Cache::store('redis_public')->get('weapp_' . $param['weapp_openid']); $session_key = $cache['session_key'] ?? ''; $result = $this->app->encryptor->decryptData($session_key, $param['iv'], $param['encryptedData']); if (isset($result['errcode']) && $result['errcode'] != 0) { return $this->handleError($result['errcode'], $result['errmsg']); } return $this->success($result); } catch (\Exception $e) { if (property_exists($e, 'formattedResponse')) { return $this->handleError($e->formattedResponse['errcode'], $e->formattedResponse['errmsg']); } return $this->error([], $e->getMessage()); } } /** * 获取用户手机号 * @param $code * @return array * @throws \GuzzleHttp\Exception\GuzzleException */ public function getUserPhoneNumber($code) { try { $result = $this->app->getClient->phoneNumber($code); if (isset($result['errcode']) && $result['errcode'] != 0) { return $this->handleError($result['errcode'], $result['errmsg']); } return $this->success($result['phone_info']); } catch (\Exception $e) { if (property_exists($e, 'formattedResponse')) { return $this->handleError($e->formattedResponse['errcode'], $e->formattedResponse['errmsg']); } return $this->error([], $e->getMessage()); } } /** * 获取订阅消息template_id * @param array $param */ public function getTemplateId(array $param) { try { $result = $this->app->subscribe_message->addTemplate($param['tid'], $param['kidList'], $param['sceneDesc']); return $result; } catch (\Exception $e) { return ['errcode' => -1, 'errmsg' => $e->getMessage()]; } } /** * 发送订阅消息 * @param array $param * @return array */ public function sendTemplateMessage(array $param) { $result = $this->app->subscribe_message->send([ 'template_id' => $param['template_id'],// 模板id 'touser' => $param['openid'], // openid 'page' => $param['page'], // 点击模板卡片后的跳转页面 支持带参数 'data' => $param['data'] // 模板变量 ]); if (isset($result['errcode']) && $result['errcode'] != 0) { return $this->error($result, $result["errmsg"]); } return $this->success($result); } /** * 消息推送 */ public function relateWeixin() { $server = $this->app->server; $message = $server->getMessage(); if (isset($message['MsgType'])) { switch ($message['MsgType']) { case 'event': $this->app->server->push(function ($res) { switch ($res['Event']) { case 'open_product_spu_audit' && addon_is_exit('shopcomponent', $this->site_id): model('shopcompoent_goods')->update([ 'edit_status' => $res['OpenProductSpuAudit']['status'], 'reject_reason' => !empty($res['OpenProductSpuAudit']['reject_reason']) ?: '', 'audit_time' => time() ], [ ['out_product_id', '=', $res['OpenProductSpuAudit']['out_product_id']] ]); break; case 'open_product_category_audit' && addon_is_exit('shopcomponent', $this->site_id): model('shopcompoent_category_audit')->update([ 'status' => $res['QualificationAuditResult']['status'], 'reject_reason' => !empty($res['QualificationAuditResult']['reject_reason']) ?: '', 'audit_time' => time() ], [ ['audit_id', '=', $res['QualificationAuditResult']['audit_id']] ]); break; case 'open_product_order_pay' && addon_is_exit('shopcomponent', $this->site_id): event("shopcomponentNotify", $res); break; case 'weapp_audit_fail': case 'weapp_audit_success'://小程序审核 event('WeappAuditEvent', $res); break; case 'weapp_audit_delay': case 'wxa_nickname_audit': case 'wxa_privacy_apply': } }); break; } } $response = $this->app->server->serve(); return $response->send(); } /** * 检查场景值是否在支付校验范围内 * @param $scene * @return array * @throws \GuzzleHttp\Exception\GuzzleException */ public function sceneCheck($scene) { try { $result = $this->app->mini_store->checkScene($scene); if (isset($result['errcode']) && $result['errcode'] == 0) { return $this->success($result['is_matched']); } else { return $this->error('', $result['errmsg']); } } catch (\Exception $e) { return $this->error([], $e->getMessage()); } } public function createOrder($order_info) { try { $result = $this->app->mini_store->addOrder($order_info); if (isset($result['errcode']) && $result['errcode'] == 0) { return $this->success($result['is_matched']); } else { return $this->error('', $result['errmsg']); } } catch (\Exception $e) { return $this->error([], $e->getMessage()); } } /** * 处理错误信息 * @param $errcode * @param string $message * @return array */ public function handleError($errcode, $message = '') { $error = require 'addon/weapp/config/weapp_error.php'; return $this->error([], $error[$errcode] ?? $message); } /** * Common: 自动发货 * Author: wu-hui * Time: 2024/07/17 11:23 * @param $params */ public function authSendGoods($params){ // 创建一个DateTime对象,代表当前日期和时间 $now = new DateTime(); $timeZone = new DateTimeZone('Asia/Shanghai'); $now->setTimezone($timeZone); // 用户信息 $memberInfo = model('member')->getInfo(['member_id' => $params['member_id']], 'weapp_openid,wx_openid'); $openId = $memberInfo['weapp_openid'] ?? $memberInfo['wx_openid']; // 交易单号 $tradeNo = model('pay')->getValue([['out_trade_no', '=', $params['out_trade_no']]],'trade_no'); // 配置信息 $config_model = new ConfigModel(); $payConfig = $config_model->getPayConfig($params['site_id'])['data']['value']; // 参数配置 $data = [ // 'access_token' => $this->access_token, 'order_key' => [ 'order_number_type' => 2,// 订单单号类型,用于确认需要上传详情的订单。枚举值1,使用下单商户号和商户侧单号;枚举值2,使用微信支付单号。 'transaction_id' => $tradeNo,//原支付交易对应的微信订单号 'mchid' => $payConfig['mch_id'], 'out_trade_no' => $params['out_trade_no'],//商户系统内部订单号,只能是数字、大小写字母`_-*`且在同一个商户号下唯一 ], 'logistics_type' => 3,//物流模式,发货方式枚举值:1、实体物流配送采用快递公司进行实体物流配送形式 2、同城配送 3、虚拟商品 4、用户自提 'delivery_mode' => 1,// 发货模式,发货模式枚举值:1、UNIFIED_DELIVERY(统一发货)2、SPLIT_DELIVERY(分拆发货) 示例值: UNIFIED_DELIVERY 'shipping_list' => [ [ 'item_desc' => $params['order_name'] ?? '收银台订单',// 商品信息,限120个字以内 ] ], 'upload_time' => $now->format(DateTime::RFC3339_EXTENDED),// 上传时间,用于标识请求的先后顺序 示例值: `2022-12-15T13:29:35.120+08:00` 'payer' => [ 'openid' => $openId, ], ]; // trace($data, '处理订单自动发货 - 请求信息'); $result = $this->app->httpPostJson('wxa/sec/order/upload_shipping_info',$data , ['access_token' => $this->access_token]); if($result['errcode'] != 0){ trace([ '请求数据' => $data, '请求结果' => $result ], '处理订单自动发货 - 请求结果错误'); } } // 获取错误详细信息 public function getRidInfo(){ // 参数配置 $data = [ 'access_token' => $this->access_token, 'rid' => '66972cfa-62e04390-78e7cb02' ]; // trace($data, '处理订单自动发货 - 请求信息'); $result = $this->httpPostJson('/cgi-bin/openapi/rid/get', $data); debug([ '请求数据' => $data, '请求结果' => $result ]); } }