'https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=xydc#besi_redirect', // 第三方 'qrConnect' => 'https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=SCOPE&state=STATE', // 永久ACCESS_TOKEN 'accessToken' => 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s', // 临时ACCESS_TOKEN 'tempToken' => 'https://api.weixin.qq.com/sns/oauth2/access_token?code=%s&appid=%s&secret=%s&grant_type=authorization_code', // 微信用户信息 'wxInfo' => 'https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN', // 获取userInfo 'userInfo' => 'https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=zh_CN', // 统一下单V3 'unifiedorderV3' => 'https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi', // 统一下单V2 'unifiedorder' => 'https://api.mch.weixin.qq.com/pay/unifiedorder', // 原路退款接口 'refundOrder' => 'https://api.mch.weixin.qq.com/pay/unifiedorder', // 查询订单 'queryOrder' => 'https://api.mch.weixin.qq.com/pay/orderquery', // 企业付款到零钱 'transfers' => 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers', // 查询企业付款订单 'queryTransfer' => 'https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo', // 生成二维码 'makeQrcode' => 'https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=%s', // 换取二维码 'getQrcodeByTicket' => 'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=%s', // 创建公众号菜单 'createMenu' => 'https://api.weixin.qq.com/cgi-bin/menu/create?access_token=%s', // 获取公众号菜单 'getMenu' => 'https://api.weixin.qq.com/cgi-bin/menu/get?access_token=%s', // 删除公众号菜单 'delMenu' => 'https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=%s', // 发送客服消息 'customMessage' => 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=%s', // 发送模板消息 'tplMessage' => 'https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s', // 获取消息模板列表 'templateList' => 'https://api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token=%s', // 添加媒体素材 'uploadMedia' => 'https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=%s&type=%s', // 生成短连接 'shortUrl' => 'https://api.weixin.qq.com/cgi-bin/shorturl?access_token=%s', // 清除接口限制 'clearTokenQuota'=> 'https://api.weixin.qq.com/cgi-bin/clear_quota?access_token=%s', // 获取证书列表 'getCerts'=> 'https://api.mch.weixin.qq.com/v3/certificates', ]; private static $jsApiUrl = [ // jssdk 验证参数 'ticket' => 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=%s', // 永久TOKENresponseText 'token' => 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s', ]; // 支付证书 protected static $certPaths = [ 'cert_path'=> WECHAT_PAY_CERT_PATH.'/apiclient_cert.pem', 'key_path'=> WECHAT_PAY_CERT_PATH.'/apiclient_key.pem', ]; /** * 授权地址校验 */ public static function valid() { echo request()->get('echostr'); exit; } /** * 微信注册初始化和授权登录 */ public static function auth() { $wxInfo = WechatService::getWechatInfo('', true); $openid = isset($wxInfo['openid'])? $wxInfo['openid'] : ''; //$cacheKey = 'caches:weixin:init:'.get_client_ip().'_'.$openid; if (empty($wxInfo) || empty($openid)) { return false; } // 验证微信信息是否存在 $wxData = [ 'openid' => $openid, 'nickname' => isset($wxInfo['nickname']) ? $wxInfo['nickname'] : '', 'headimgurl' => isset($wxInfo['headimgurl']) ? $wxInfo['headimgurl'] : '', 'sex' => isset($wxInfo['sex']) ? $wxInfo['sex'] : 0, 'country' => isset($wxInfo['country']) ? $wxInfo['country'] : '', 'province' => isset($wxInfo['province']) ? $wxInfo['province'] : '', 'city' => isset($wxInfo['city']) ? $wxInfo['city'] : '', ]; // 用户信息 $inviteId = request()->get('sid'); $inviteId = $inviteId? $inviteId : session('sid'); $userData = [ 'gender' => $wxData['sex'], 'openid' => $wxData['openid'], 'nickname' => $wxData['nickname'], 'login_time' => time(), 'login_ip' => get_client_ip(), 'avatar' => $wxData['headimgurl'], 'status'=> 1, ]; // 微信用户不存在 $userInfo = MemberModel::where(['openid' => $openid]) ->where('status','>', 0) ->select(['id','openid','avatar']) ->first(); $userInfo = $userInfo? $userInfo->toArray() : []; if (empty($userInfo)) { $inviteId = MemberModel::where(['id' => $inviteId])->value('id'); $userData['member_level'] = 1; $userData['password'] = get_password('123456'); $userData['invite_id'] = $inviteId? intval($inviteId) : 0; $userData['code'] = makeUniqueCode($openid, 8); $userData['create_time'] = time(); RedisService::set("caches:member:register_".$openid, ['data'=> request()->all(),'info'=> $userData], 600); $userId = MemberModel::insertGetId($userData); // 邀请奖励 if($userId && $inviteId){ MemberService::make()->inviteAward($userId, $inviteId, $userData['nickname']); } } else{ $updateData = [ 'is_follow'=> isset($wxInfo['subscribe']) ? intval($wxInfo['subscribe']) : 0, 'login_time'=> time(), 'login_ip'=> get_client_ip(), 'update_time'=> time() ]; MemberModel::where(['openid' => $openid]) ->where('status','>=', 0) ->update($updateData); } // 记录OPENID $memberService = new MemberService(); $field = ['m.id','m.openid','m.mobile','m.realname','m.nickname','m.avatar','m.login_time','m.status']; $userInfo = $memberService->getUserInfo(['m.openid'=> $openid], $field); return $userInfo; } /** * 跳转授权 * @param string $url 回跳地址 * @return mixed */ public static function makeRedirectUrl($url) { $appid = WechatService::getConfigs('wx_appid'); return sprintf(self::$apiUrl['auth'], $appid, urlencode($url), 'snsapi_userinfo'); } /** * 获取配置 * @param string $key 键名 * @return array|mixed|string */ public static function getConfigs($key = '') { $configService = new ConfigService(); $defConfig = config('weixin.*'); $notifyConfig = config('weixin.notify'); $config = $configService->getConfigByGroup(7); $config = $config ? $config : $defConfig; $config['notify'] = $notifyConfig? $notifyConfig : []; if ($key && $key != 'notify') { return isset($config[$key]['value']) ? $config[$key]['value'] : ''; } else if ($key == 'notify'){ return isset($config['notify'])? $config['notify'] : []; } else { return $config ? $config : []; } } /** * 获取ACCESS_TOKEN * @return bool|string */ public static function getTempAccessToken($key = '', $refresh = false) { $code= request()->get('code',''); $appid = WechatService::getConfigs('wx_appid'); $appsecret = WechatService::getConfigs('wx_appsecret'); $cacheKey = 'caches:tokens:access_temp:' . $code; $tokenData = RedisService::get($cacheKey); if (empty($tokenData) || $refresh) { $url = sprintf(self::$apiUrl['tempToken'], $code, $appid, $appsecret); $tokenData = httpRequest($url); RedisService::set("caches:tokens:result:temp_{$code}", $tokenData, 3600); $code = isset($tokenData['errcode']) ? $tokenData['errcode'] : ''; if ($code || empty($tokenData)) { return $tokenData; } $token = isset($tokenData['access_token']) ? $tokenData['access_token'] : ''; $openid = isset($tokenData['openid']) ? $tokenData['openid'] : ''; $tokenData = [ 'token' => $token, 'openid' => $openid, 'data' => $tokenData, 'date' => date('Y-m-d H:i:s'), 'expire' => time() + 7000, ]; RedisService::set($cacheKey, $tokenData, 7200); } $expire = isset($tokenData['expire']) ? intval($tokenData['expire']) : 0; $token = isset($tokenData['token']) ? trim($tokenData['token']) : ''; if (($expire && $expire < time()) || empty($token)) { $tokenData = WechatService::getTempAccessToken($key, true); } if ($key) { $tokenData = isset($tokenData[$key]) ? trim($tokenData[$key]) : ''; } return $tokenData; } /** * 获取ACCESS_TOKEN * @return bool|string */ public static function getAccessToken($key = '', $refresh = false) { $code= request()->get('code',''); $code = $code? $code : session('code'); session('code', $code); $appid = WechatService::getConfigs('wx_appid'); $appsecret = WechatService::getConfigs('wx_appsecret'); $cacheKey = 'caches:tokens:access_' . $appid . '_' . $appsecret; $tokenData = RedisService::get($cacheKey); if (empty($tokenData) || $refresh) { $url = sprintf(self::$apiUrl['accessToken'], $appid, $appsecret); $tokenData = httpRequest($url); RedisService::set("caches:tokens:result:{$code}", $tokenData, 3600); $code = isset($tokenData['errcode']) ? $tokenData['errcode'] : ''; if ($code || empty($tokenData)) { return $tokenData; } $token = isset($tokenData['access_token']) ? $tokenData['access_token'] : ''; $openid = isset($tokenData['openid']) ? $tokenData['openid'] : ''; $tokenData = [ 'token' => $token, 'openid' => $openid, 'data' => $tokenData, 'date' => date('Y-m-d H:i:s'), 'expire' => time() + 7000, ]; RedisService::set($cacheKey, $tokenData, 7200); } $expire = isset($tokenData['expire']) ? intval($tokenData['expire']) : 0; $token = isset($tokenData['token']) ? trim($tokenData['token']) : ''; if (($expire && $expire < time()) || empty($token)) { $tokenData = WechatService::getAccessToken($key, true); } if ($key) { $tokenData = isset($tokenData[$key]) ? trim($tokenData[$key]) : ''; } return $tokenData; } /** * 获取微信UserInfo用户信息 * @param string $openid 获取的用户OPENID,默认当前用户 * @return mixed */ public static function getWechatInfo($curOpenid = '', $saveData = false) { $code= request()->get('code',''); $tokenData = WechatService::getTempAccessToken(); $token = isset($tokenData['token']) ? trim($tokenData['token']) : ''; $openid = isset($tokenData['openid']) ? trim($tokenData['openid']) : ''; if (empty($token)) { return false; } $openid = $curOpenid ? $curOpenid : $openid; $url = sprintf(self::$apiUrl['wxInfo'], $token, $openid); RedisService::set("caches:userInfo:request_{$openid}",['token'=> $tokenData,'url'=> $url], 600); $result = httpRequest($url); $errcode = isset($result['errcode']) ? $result['errcode'] : ''; RedisService::set('caches:weixin:userInfo:result_'.$openid, $result, 600); if (empty($result) || $errcode) { RedisService::keyDel('caches:tokens:access_temp:' . $code); return false; } if ($saveData) { $wxData = [ 'openid' => $openid, 'nickname' => isset($result['nickname']) ? $result['nickname'] : '', 'headimgurl' => isset($result['headimgurl']) ? $result['headimgurl'] : '', 'sex' => isset($result['sex']) ? $result['sex'] : 0, 'country' => isset($result['country']) ? $result['country'] : '', 'province' => isset($result['province']) ? $result['province'] : '', 'city' => isset($result['city']) ? $result['city'] : '', ]; if (empty(FansModel::where(['openid' => $openid])->value('id'))) { FansModel::insertGetId($wxData); } else { FansModel::where(['openid' => $openid])->update($wxData); } } return $result; } /** * 获取JSSDK ticket参数 * @author wesmiler */ private static function getTicket($refresh = false, $refreshToken = false) { $appid = WechatService::getConfigs('wx_appid'); $appsecret = WechatService::getConfigs('wx_appsecret'); $cacheKey = 'caches:tokens:jsapiTicket:' . $appid . '_' . $appsecret; $ticketData = RedisService::get($cacheKey); $ticket = isset($ticketData['ticket']) ? $ticketData['ticket'] : ''; if (empty($ticket) || $refresh) { $tokenData = WechatService::getAccessToken('', $refreshToken); $code = isset($tokenData['errcode']) ? $tokenData['errcode'] : ''; if ($code) { return $tokenData; } $token = isset($tokenData['token']) ? trim($tokenData['token']) : ''; $url = sprintf(self::$jsApiUrl['ticket'], $token); $result = httpRequest($url); if (empty($result)) { RedisService::set('caches:tokens:jsapiTicket_error', $result, 3600); return false; } $ticket = isset($result['ticket']) ? $result['ticket'] : ''; $ticketData = [ 'ticket' => $ticket, 'expire' => time() + 6000, ]; RedisService::set($cacheKey, $ticketData, 7200); } $expire = isset($ticketData['expire']) ? intval($ticketData['expire']) : 0; if (empty($expire) || $expire < time()) { $ticket = WechatService::getTicket(true); } if (empty($ticket)) { $ticket = WechatService::getTicket(true, true); } return $ticket; } /** * 获取JSSDK签名参数 * @param string $url 请求地址 * @return array */ public static function getJssdkParams($url = '') { // token请求次数超出警告范围 $countKey = "caches:tokens:count"; $requestCount = RedisService::get($countKey); if($requestCount>=5000){ return ['error'=> 'token请求失败次数已超出警告值5000']; } $countKey = "caches:tokens:ticketCount:".get_client_ip(); $requestCount = RedisService::get($countKey); if($requestCount>=100){ return ['error'=> '分享参数请求次数过多请稍后重试']; } $result = WechatService::getTicket(); $url = $url ? $url : Input::url(); $code = isset($result['errcode']) ? $result['errcode'] : ''; if ($code) { return $result; } $params = [ 'jsapi_ticket' => $result, 'noncestr' => uniqid('J'), 'timestamp' => time(), 'url' => $url, ]; RedisService::set($countKey, $requestCount+1, 30); $signature = WechatService::getJssdkSign($params); return [ 'appId' => WechatService::getConfigs('wx_appid'), 'timestamp' => $params['timestamp'], 'nonceStr' => $params['noncestr'], 'signature' => $signature, 'url' => $url, ]; } /** * 获取JSSDK 签名 * @param $params 签名参数 * @return string */ private static function getJssdkSign($params) { $str = []; ksort($params); foreach ($params as $k => $val) { $str[] = $k . '=' . $val; } $str = implode('&', $str); return sha1($str); } /** * jsapi统一下单V3 * @param $order 订单参数 * @author wesmiler * @return array */ public static function jsapiUnifiedorder($order, $scene = 'jsapiPay') { $appId = WechatService::getConfigs('wx_appid'); $spAppId = WechatService::getConfigs('wx_sp_appid'); $spMchId = WechatService::getConfigs('wx_sp_mchid'); $mchId = WechatService::getConfigs('wx_mch_id'); $serial = WechatService::getConfigs('wx_mch_cert_no'); $notifyUrls = WechatService::getConfigs('notify'); $notifyUrl = isset($notifyUrls[$scene]) ? url()->formatRoot('http://').$notifyUrls[$scene] : url()->formatRoot('http://').'/api/notify/pay/index'; $openid = isset($order['openid']) ? trim($order['openid']) : ''; $orderNo = isset($order['orderNo']) ? trim($order['orderNo']) : ''; $totalFee = isset($order['amount']) ? moneyFormat($order['amount']) : 0.00; // 测试支付金额 $payDebug = config('weixin.payDebug'); if ($payDebug) { $totalFee = 0.01; } if (empty($openid) || empty($orderNo) || empty($totalFee)) { return ['code' => 'error', 'message' => '参数错误']; } $unified = array( 'sp_appid' => $spAppId, 'sub_appid' => $appId, 'attach' => 'pay', //商家数据包,原样返回,如果填写中文,请注意转换为utf-8 'description' => isset($order['body']) ? trim($order['body']) : '订单支付', 'sp_mchid' => $spMchId, 'sub_mchid' => $mchId, 'notify_url' => $notifyUrl, 'payer'=> [ 'sub_openid' => $openid, //子商户此参数必传 ], 'out_trade_no' => $orderNo, 'amount' => [ 'total'=> intval($totalFee * 100), 'currency'=> 'CNY' ], //单位 转为分 'scene_info' => [ 'payer_client_ip'=> get_client_ip() ], ); $body = json_encode($unified); $url = !empty(self::$apiUrl['unifiedorderV3']) ? trim(self::$apiUrl['unifiedorderV3']) : 'https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi'; $token = WechatService::getSignToken(['url'=> $url, 'method'=> 'POST','mchid'=> $spMchId, 'body'=> $body]); RedisService::set('caches:orders:'.$scene.':'.$openid.':unifiedSign', ['data'=>$unified,'token'=> $token], 600); $headers = ["Authorization: {$token}","Content-Type: application/json","Accept: application/json","User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"]; RedisService::set('caches:orders:'.$scene.':'.$openid.':unifiedRequest', ['data'=> $unified,'headers'=> $headers], 600); $response = WechatService::curlPost($url, $body,[],[],$headers); $response = $response? json_decode($response, true) : $response; //禁止引用外部xml实体 RedisService::set('caches:orders:'.$scene.':'.$openid.':unifiedResult', ['data'=> $response], 600); $prepayId = isset($response['prepay_id'])? $response['prepay_id'] : ''; $code = isset($response['code'])? $response['code'] : ''; if (!$prepayId) { $message = isset($response['message'])? $response['message'] : 'prepayid get fail'; return ['code' => 'error', 'message' => $message,'result'=> $response]; } // 返回支付参数 return WechatService::getJsapiPareamsV3($response, $unified); } /** * jsapi统一下单V2 * @param $order 订单参数 * @author wesmiler * @return array */ public static function jsapiUnifiedorderV2($order, $scene = 'jsapiPay') { $appId = WechatService::getConfigs('wx_appid'); $mchId = WechatService::getConfigs('wx_mch_id'); $notifyUrls = WechatService::getConfigs('notify'); $notifyUrl = isset($notifyUrls[$scene]) ? url()->formatRoot('http://').$notifyUrls[$scene] : url()->formatRoot('http://').'/api/notify/pay/index'; $openid = isset($order['openid']) ? trim($order['openid']) : ''; $orderNo = isset($order['orderNo']) ? trim($order['orderNo']) : ''; $totalFee = isset($order['amount']) ? moneyFormat($order['amount']) : 0.00; // 测试支付金额 $payDebug = config('weixin.payDebug'); if ($payDebug) { $totalFee = 0.01; } if (empty($openid) || empty($orderNo) || empty($totalFee)) { return ['code' => 'error', 'message' => '参数错误']; } $unified = array( 'appid' => $appId, 'attach' => 'pay', //商家数据包,原样返回,如果填写中文,请注意转换为utf-8 'body' => isset($order['body']) ? trim($order['body']) : '订单支付', 'mch_id' => $mchId, 'nonce_str' => WechatService::createNonceStr(), 'notify_url' => $notifyUrl, 'openid'=> $openid, 'out_trade_no' => $orderNo, 'spbill_create_ip' => get_client_ip(), 'total_fee' => intval($totalFee * 100), //单位 转为分 'trade_type' => 'JSAPI', ); RedisService::set('caches:orders:'.$scene.':'.$openid.':unified', $unified, 600); $unified['sign'] = WechatService::getPaySign($unified); RedisService::set('caches:orders:'.$scene.':'.$openid.':unifiedSign', $unified, 600); $url = !empty(self::$apiUrl['unifiedorder']) ? trim(self::$apiUrl['unifiedorder']) : 'https://api.mch.weixin.qq.com/pay/unifiedorder'; $data = WechatService::arrayToXml($unified); RedisService::set('caches:orders:'.$scene.':'.$openid.':unifiedXml', ['data'=> $unified,'result'=> $data], 600); $responseXml = WechatService::curlPost($url, $data); //禁止引用外部xml实体 libxml_disable_entity_loader(true); $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA); $unifiedOrder = (array)$unifiedOrder; RedisService::set('caches:orders:'.$scene.':'.$openid.':unifiedResult', ['data'=> $unifiedOrder,'result'=> $data], 600); if ($unifiedOrder === false) { return ['code' => 'exception', 'message' => 'parase xml error']; } if (isset($unifiedOrder['return_code']) && $unifiedOrder['return_code'] != 'SUCCESS') { return ['code' => 'error', 'message' => $unifiedOrder['return_msg']]; } if (isset($unifiedOrder['result_code']) && $unifiedOrder['result_code'] != 'SUCCESS') { return ['code' => 'error', 'message' => $unifiedOrder['err_code']]; } // 返回支付参数 return WechatService::getJsapiPareams($unifiedOrder, $unified); } /** * 提现打款 * @param $order * @param string $scene * @return array|string[] */ public static function transferOrder($order, $scene='withdraw'){ $appId = WechatService::getConfigs('wx_appid'); $mchId = WechatService::getConfigs('wx_mch_id'); $openid = isset($order['openid']) ? trim($order['openid']) : ''; $orderNo = isset($order['orderNo']) ? trim($order['orderNo']) : ''; $totalFee = isset($order['amount']) ? moneyFormat($order['amount']) : 0.00; // 测试支付金额 $payDebug = config('weixin.payDebug'); if ($payDebug) { $totalFee = 0.3; } if (empty($openid) || empty($orderNo) || empty($totalFee)) { return ['code' => 'error', 'message' => '参数错误']; } $unified = array( 'mch_appid' => $appId, 'mchid' => trim($mchId), 'device_info' => uniqid(), 'nonce_str' => WechatService::createNonceStr(), 'partner_trade_no' => $orderNo, 'openid' => $openid, 'check_name' => isset($order['check_name']) && $order['check_name']? trim($order['check_name']) : 'NO_CHECK', // 是否校验真实姓名 'amount' => intval($totalFee * 100), //单位 转为分 'desc' => isset($order['body']) ? trim($order['body']) : '余额提现', 'spbill_create_ip' => get_client_ip(), ); // 是否校验真实姓名 if($unified['check_name'] == 'FORCE_CHECK'){ $unified['re_user_name'] = isset($order['real_name']) ? trim($order['real_name']) : ''; } RedisService::set('caches:orders:'.$scene.':'.$openid.':unified', $unified, 600); $unified['sign'] = WechatService::getPaySign($unified); RedisService::set('caches:orders:'.$scene.':'.$openid.':unifiedSign', $unified, 600); $url = !empty(self::$apiUrl['transfers']) ? trim(self::$apiUrl['transfers']) : 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers'; $data = WechatService::arrayToXml($unified); RedisService::set('caches:orders:'.$scene.':'.$openid.':unifiedXml', ['data'=> $unified,'result'=> $data], 600); $responseXml = WechatService::curlPost($url, $data, [], self::$certPaths); //禁止引用外部xml实体 libxml_disable_entity_loader(true); $result = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA); $result = (array)$result; RedisService::set('caches:orders:'.$scene.':'.$openid.':unifiedResult', ['data'=> $result,'result'=> $data], 600); if ($result === false) { return ['code' => 'exception', 'message' => 'parase xml error']; } if (isset($result['return_code']) && $result['return_code'] != 'SUCCESS') { return ['code' => 'error', 'message' => WechatService::getError($result['return_msg']),'type'=>'return_code']; } if (isset($result['result_code']) && $result['result_code'] != 'SUCCESS') { return ['code' => 'error', 'message' => $result['err_code_des'],'error_code'=> $result['err_code'],'type'=>'result_code']; } return $result; } /** * 查询企业付款订单 * @param $trane_order_no 订单号 * @return string[] */ public static function queryTransferOrder($trane_order_no){ $appId = WechatService::getConfigs('wx_appid'); $mchId = WechatService::getConfigs('wx_mch_id'); if (empty($trane_order_no)) { return ['code' => 'error', 'message' => '参数错误']; } $unified = array( 'wx_appid' => $appId, 'wx_mch_id' => trim($mchId), 'nonce_str' => WechatService::createNonceStr(), 'partner_trade_no' => $trane_order_no, ); RedisService::set('orders:transfer:'.$trane_order_no.':unified', $unified, 600); $unified['sign'] = WechatService::getPaySign($unified); RedisService::set('orders:transfer:'.$trane_order_no.':unifiedSign', $unified, 600); $url = !empty(self::$apiUrl['queryTransfer']) ? trim(self::$apiUrl['queryTransfer']) : 'https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo'; $data = WechatService::arrayToXml($unified); RedisService::set('orders:transfer:'.$trane_order_no.':unifiedXml', ['data'=> $unified,'result'=> $data], 600); $responseXml = WechatService::curlPost($url, $data, [], self::$certPaths); //禁止引用外部xml实体 libxml_disable_entity_loader(true); $result = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA); $result = (array)$result; RedisService::set('orders:transfer:'.$trane_order_no.':unifiedResult', ['data'=> $result,'result'=> $data], 600); if ($result === false) { return ['code' => 'exception', 'message' => 'parase xml error']; } if (isset($result['return_code']) && $result['return_code'] != 'SUCCESS') { return ['code' => 'error', 'message' => WechatService::getError($result['return_msg']),'type'=>'return_code']; } if (isset($result['result_code']) && $result['result_code'] != 'SUCCESS') { return ['code' => 'error', 'message' => $result['err_code_des'],'error_code'=> $result['err_code'],'type'=>'result_code']; } return $result; } /** * 获取JSAPI支付签名参数 * @param $unifiedOrder 统一下单结果 * @param array $unified 提交统一下单参数 * @return array */ public static function getJsapiPareams($unifiedOrder, $unified = []) { $time = time(); $prepayId = isset($unifiedOrder['prepay_id']) ? $unifiedOrder['prepay_id'] : ''; $params = array( "appId" => WechatService::getConfigs('wx_appid'), "timeStamp" => "$time", //这里是字符串的时间戳,不是int,所以需加引号 "nonceStr" => isset($unified['nonce_str']) ? trim($unified['nonce_str']) : WechatService::createNonceStr(), "package" => "prepay_id=" . $prepayId, "signType" => 'MD5', ); // 重签名 $params['paySign'] = WechatService::getPaySign($params); $params['prepay_id'] = $prepayId; return $params; } /** * 获取JSAPI支付签名参数 * @param $unifiedOrder 统一下单结果 * @param array $unified 提交统一下单参数 * @return array */ public static function getJsapiPareamsV3($unifiedOrder, $unified = []) { $time = time(); $prepayId = isset($unifiedOrder['prepay_id']) ? $unifiedOrder['prepay_id'] : ''; $params = array( "appId" => WechatService::getConfigs('wx_appid'), "timeStamp" => "$time", //这里是字符串的时间戳,不是int,所以需加引号 "nonceStr" => isset($unified['nonce_str']) ? trim($unified['nonce_str']) : WechatService::createNonceStr(32), "package" => "prepay_id=" . $prepayId, ); // 重签名 $params['paySign'] = WechatService::getPaySignV3($params); $params['signType'] = 'RSA'; $params['prepay_id'] = $prepayId; return $params; } /** * 查询订单 * @param $outTradeNo 单号 * @return bool|\SimpleXMLElement */ public static function queryOrder($outTradeNo) { $params['wx_appid'] = WechatService::getConfigs('wx_appid'); $params['wx_mch_id'] = WechatService::getConfigs('wx_mch_id'); $params['nonce_str'] = WechatService::createNonceStr(); $params['out_trade_no'] = $outTradeNo; //获取签名数据 $params['sign'] = WechatService::getPaySign($params); $responseXml = WechatService::curlPost(self::$apiUrl['queryOrder'], WechatService::arrayToXml($params)); $result = WechatService::xmlToArray($responseXml); $returnCode = isset($result['return_code']) ? $result['return_code'] : ''; $tradState = isset($result['trade_state']) ? $result['trade_state'] : ''; $resultCode = isset($result['result_code']) ? $result['result_code'] : ''; if ($resultCode && $returnCode && $tradState) { return $result; } else { return false; } } /** * XML转数组 * @param $xml * @return bool|\SimpleXMLElement */ private static function xmlToArray($xml) { if (empty($xml)) return false; libxml_disable_entity_loader(true); return simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA); } /** * 生成随机字符串 * @param int $length 长度 * @return string */ public static function createNonceStr($length = 16) { $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $str = ''; for ($i = 0; $i < $length; $i++) { $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); } return $str; } /** * @param $params * @param $key * @return string */ public static function getPaySign($params, $key = '') { ksort($params, SORT_STRING); $key = $key ? $key : WechatService::getConfigs('wx_pay_key'); $unSignParaString = WechatService::formatParams($params, false); //echo $unSignParaString.'++'.$key; $signStr = strtoupper(md5($unSignParaString . "&key=" . $key)); return $signStr; } /** * 获取V3签名参数 * @param $params * @param $key * @return string */ public static function getPaySignV3($params, $key = '') { $appId = isset($params['appId'])? $params['appId'] : ''; $timeStamp = isset($params['timeStamp'])? $params['timeStamp'] : ''; $nonceStr = isset($params['nonceStr'])? $params['nonceStr'] : ''; unset($params['signType']); $signStr = implode("\n", array_values($params))."\n"; //echo $signStr; $mch_private_key = openssl_get_privatekey(file_get_contents(self::$certPaths['key_path'])); openssl_sign($signStr, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption'); $sign = base64_encode($raw_sign); return $sign; } /** * 回调数据解密 * @param $notifyData 回调数据对象 * @return false|string * @throws \SodiumException */ public static function decryptNotifyData($notifyData){ $md5Key = WechatService::getConfigs('wx_pay_key'); $ciphertext = isset($notifyData['ciphertext'])? $notifyData['ciphertext'] : ''; $associatedData = isset($notifyData['associated_data'])? $notifyData['associated_data'] : ''; $nonce = isset($notifyData['nonce'])? $notifyData['nonce'] : ''; $ciphertext = base64_decode($ciphertext); return sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonce, $md5Key); } /** * 验证JSAPI回调V3 * @param $notifyData * @return array|bool */ public static function checkJsapiNotifyV3($signStr, $sign, $notifyData=[]) { $tradeState = isset($notifyData['trade_state'])? $notifyData['trade_state'] : false; if($notifyData && $tradeState != 'SUCCESS'){ return false; } $transaction_id = isset($notifyData['transaction_id'])? $notifyData['transaction_id'] : ''; if($notifyData && empty($transaction_id)){ return false; } // 证书序列号 $serial = WechatService::getConfigs('wx_mch_cert_no'); $checkSerial = isset($notifyData['serial'])? $notifyData['serial'] : ''; if($serial != $checkSerial){ //return false; } return true; /*$sign = stripslashes($sign); $signature = base64_decode($sign); $publicKey = openssl_pkey_get_public(file_get_contents(self::$certPaths['cert_path'])); return openssl_verify($signStr, $signature, $publicKey, 'sha256WithRSAEncryption');*/ } /** * 验证JSAPI回调 * @param $notifyData * @return array|bool */ public static function checkJsapiNotify($notifyData) { if (empty($notifyData)) { return ['code' => 'error', 'message' => 'parse xml error']; } $returnCode = isset($notifyData['return_code']) ? trim($notifyData['return_code']) : ''; $resultCode = isset($notifyData['result_code']) ? trim($notifyData['result_code']) : ''; $nofitySign = isset($notifyData['sign']) ? trim($notifyData['sign']) : ''; if ($returnCode != 'SUCCESS') { $error = isset($notifyData['return_msg']) ? $notifyData['return_msg'] : ''; return ['code' => 'error', 'message' => $error]; } if ($resultCode != 'SUCCESS') { $error = isset($notifyData['err_code']) ? $notifyData['err_code'] : ''; return ['code' => 'error', 'message' => $error]; } // 验证签名 unset($notifyData['sign']); $sign = WechatService::getPaySign($notifyData); if ($nofitySign == $sign) { echo ''; return true; } return false; } /** * 请求接口 * @param string $url 地址 * @param string $postData xml参数 * @param array $options * @return mixed */ public static function curlPost($url = '', $postData = '', $options = array(), $cert=[], $headers=[]) { if (is_array($postData)) { $postData = http_build_query($postData); } $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数 if (!empty($options)) { curl_setopt_array($ch, $options); } if($headers){ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); } if($cert){ curl_setopt($ch,CURLOPT_HEADER,FALSE); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE); if(isset($cert['cert_path']) && $cert['cert_path']){ curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); curl_setopt($ch,CURLOPT_SSLCERT, $cert['cert_path']); } if(isset($cert['key_path']) && $cert['key_path']) { curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM'); curl_setopt($ch, CURLOPT_SSLKEY, $cert['key_path']); } } //https请求 不验证证书和host curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $data = curl_exec($ch); curl_close($ch); return $data; } /** * 数组转XML * @param $arr 数组数据 * @return string */ public static function arrayToXml($arr) { $xml = ""; foreach ($arr as $key => $val) { if ($key == 'detail') { $xml .= "<" . $key . ">"; } else { $xml .= "<" . $key . ">" . $val . ""; } } $xml .= ""; return $xml; } /** * 签名参数格式化 * @param $paraMap 参数 * @param bool $urlEncode 是否编码 * @return bool|string */ protected static function formatParams($paraMap, $urlEncode = false) { $buff = ""; ksort($paraMap); foreach ($paraMap as $k => $v) { if (null != $v && "null" != $v) { if ($urlEncode) { $v = urlencode($v); } $buff .= $k . "=" . $v . "&"; } } $reqPar = ''; if (strlen($buff) > 0) { $reqPar = substr($buff, 0, strlen($buff) - 1); } return $reqPar; } /** * 生成微信二维码 * @param int $sourceId 来源ID * @param $sceneStr 场景参数字符串或ID * @param $scene 场景标识:qrcode-用户二维码 * @param string $qrType 二维码生成类型:QR_SCENE, QR_STR_SCENE, QR_LIMIT_SCENE, QR_LIMIT_STR_SCENE * @param int $expire 有效期,配合场景类型使用,临时二维码最长30天有效期,0-永久 * @return array|bool * @throws \think\Exception * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException * @throws \think\exception\PDOException */ public static function makeQrcode($sourceId = 0, $sceneStr = '', $scene = 'qrcode', $qrType = 'QR_STR_SCENE', $expire = -1) { $expire = $expire>=0 ? $expire : 24 * 3600 * 20; $tokenData = WechatService::getAccessToken(''); $token = isset($tokenData['token']) ? trim($tokenData['token']) : ''; $qrData = db('qrcode') ->where(['source_id' => $sourceId, 'scene' => $scene, 'status' => 1]) ->field('id,source_id,ticket,url,expire_at') ->find(); $qrcodeId = isset($qrData['id']) ? $qrData['id'] : 0; $qrcodeExpire = isset($qrData['expire_at']) ? strtotime($qrData['expire_at']) : 0; if (($expire==0 && $qrcodeId) || $qrcodeExpire > time() && $qrcodeId) { $ticket = isset($qrData['ticket']) ? $qrData['ticket'] : ''; $qrcode = sprintf(self::$apiUrl['getQrcodeByTicket'], $ticket); $qrData['qrcode'] = WechatService::loadImage($qrcode, $scene); if($qrData['qrcode']){ return $qrData; } } if (empty($token)) { $tokenData = WechatService::getAccessToken('', true); $token = isset($tokenData['token']) ? trim($tokenData['token']) : ''; if (empty($token)) { return 1010; } } if (!in_array($qrType, ['QR_SCENE', 'QR_STR_SCENE', 'QR_LIMIT_SCENE', 'QR_LIMIT_STR_SCENE'])) { return 2111; } $data = [ 'expire_seconds' => $expire, 'action_name' => $qrType, ]; if (in_array($qrType, ['QR_SCENE', 'QR_LIMIT_SCENE'])) { $data['action_info'] = ['scene' => ['scene_id' => $sceneStr]]; } else { $data['action_info'] = ['scene' => ['scene_str' => $scene . '_' . $sceneStr]]; } $url = sprintf(self::$apiUrl['makeQrcode'], $token); $result = httpRequest($url, json_encode($data, 256)); $ticket = isset($result['ticket']) ? $result['ticket'] : ''; RedisService::set('qrcodes:result', ['url'=> $url, 'params'=> $data, 'result'=> $result], 600); if ($result && $ticket) { $qrData = [ 'source_id' => $sourceId, 'scene' => $scene, 'scene_str' => $sceneStr, 'ticket' => $ticket, 'expire_at' => $expire>0? date('Y-m-d H:i:s', time() + $expire) : null, 'url' => isset($result['url']) ? $result['url'] : '', ]; if ($qrcodeId) { $qrData['updated_at'] = date('Y-m-d H:i:s'); //$qrcodeId = db('qrcode')->where(['id' => $qrcodeId])->update($qrData); } else { $qrData['created_at'] = date('Y-m-d H:i:s'); //$qrcodeId = db('qrcode')->insertGetId($qrData); $qrData['id'] = $qrcodeId; } $qrcode = sprintf(self::$apiUrl['getQrcodeByTicket'], $ticket); $qrData['qrcode'] = WechatService::loadImage($qrcode, $scene, true); } return $qrcodeId > 0 ? $qrData : 1009; } /** * 下载图片 * @param $file 远程文件 * @param string $type 类型 * @return bool|string */ public static function loadImage($file, $type='qrcode', $refresh=false){ if(empty($file)){ return false; } $key = "caches:members:{$type}:".md5($file); $qrcode = RedisService::get($key); if(empty($qrcode) || $refresh){ $qrcodeContent = file_get_contents($file); if($qrcodeContent){ if(!is_dir("upload/{$type}/weixin/")){ mkdir("upload/{$type}/weixin/", 0755, true); } $qrcode = "{$type}/weixin/QR_".md5($file).'.jpg'; file_put_contents("upload/".$qrcode, $qrcodeContent); RedisService::set($key, $qrcode, 7 * 24 * 3600); } } if(!file_exists('./upload/'.$qrcode)){ return false; } return get_image_url($qrcode); } /** * 获取微信二维码数据 * @param $where 条件 * @param string $field 返回字段 * @return bool * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public static function getQrcode($where, $field = '') { if (!is_array($where)) { return false; } $where['status'] = 1; $field = $field ? $field : 'id,source_id,scene,ticket,url'; $info = db('qrcode') ->where($where) ->where('expire_at', '>', date('Y-m-d H:i:s')) ->field($field) ->find(); $ticket = isset($info['ticket']) ? $info['ticket'] : ''; if ($info && $ticket) { $info['qrcode'] = sprintf(self::$apiUrl['getQrcodeByTicket'], $ticket); } return $info; } /** * 响应消息 * @param $postObj */ public static function responseText($postObj) { $openid = isset($postObj['FromUserName']) ? $postObj['FromUserName'] : ''; $msgId = isset($postObj['MsgId']) ? $postObj['MsgId'] : ''; $keyword = isset($postObj['Content']) ? trim($postObj['Content']) : ''; $cacheKey = "messages:replys:{$msgId}"; if(RedisService::get($cacheKey)){ return false; } WechatService::rebackOk(); echo ' '; exit; } /** * 响应消息 * @param $fromUsername 发送用户 * @param $toUsername 接收用户 * @param $contentStr 发送内容 * @param string $msgType 消息类型 */ public static function responseTplMsg($fromUsername, $toUsername, $contentStr, $msgType = 'text') { $textTpl = " %s "; $resultStr = sprintf($textTpl, $fromUsername, $toUsername, time(), $msgType, $contentStr); echo $resultStr; exit; } /** * 创建菜单 * @param array $menus 菜单数组数据 * @params $delete 是否删除就菜单 * @return bool */ public static function createMenu($menus = [], $delete = false) { $weixinConfig = config('weixin.'); $menus = $menus ? $menus : (isset($weixinConfig['menus']) ? $weixinConfig['menus'] : []); if (empty($menus)) { return false; } $tokenData = WechatService::getAccessToken('', 'accessToken'); $token = isset($tokenData['token']) ? trim($tokenData['token']) : ''; if (empty($token)) { return false; } // 删除菜单 if ($delete) { WechatService::delMenu(); } $url = sprintf(self::$apiUrl['createMenu'], $token); $result = httpRequest($url, json_encode(['button' => $menus], 256)); $errorCode = isset($result['errcode']) ? $result['errcode'] : true; if ($errorCode != 0) { return false; } return $result; } /** * 删除菜单 * @return bool */ public static function delMenu() { $tokenData = WechatService::getAccessToken('', 'accessToken'); $token = isset($tokenData['token']) ? trim($tokenData['token']) : ''; $url = sprintf(self::$apiUrl['delMenu'], $token); $result = httpRequest($url); $errorCode = isset($result['errcode']) ? $result['errcode'] : true; if ($errorCode == 0) { return false; } return true; } /** * 查询菜单 * @return bool */ public static function getMenu() { $tokenData = WechatService::getAccessToken('', 'accessToken'); $token = isset($tokenData['token']) ? trim($tokenData['token']) : ''; $url = sprintf(self::$apiUrl['getMenu'], $token); $result = httpRequest($url); return $result; } /** * 发送客服消息 * @param $openid 接受用户OPENID * @param $content 消息内容:数组 * @param string $msgType 消息类型 * @return mixed */ public static function sendCustomMsg($openid, $content, $msgType = 'text') { $data = [ 'touser' => $openid, 'msgtype' => $msgType, $msgType => $content ]; $lockKey = 'caches:weixin:custonLock:' . $openid . '_' . md5(json_encode($data)); if (RedisService::get($lockKey)) { return false; } $tokenData = WechatService::getAccessToken(''); $token = isset($tokenData['token']) ? trim($tokenData['token']) : ''; RedisService::set($lockKey, $data, 5); $url = sprintf(self::$apiUrl['customMessage'], $token); $result = httpRequest($url, json_encode($data, 256)); RedisService::set('caches:weixin:customLock:' . $openid . '_' . date('YmdHis'), ['data' => $data, 'tokenData' => $tokenData, 'result' => $result], 3); return $result; } /** * 发送模板消息 * @param $openid OPENID * @param $params 参数:title-标题(必填),type-模板类型标识字符串(必填),keywords-模板字段数据(必填),url-模板跳转链接,remark-模板备注信息 * @return array|int */ public static function sendTplMsg($openid, $params, $formatUrl=true) { $title = isset($params['title']) ? $params['title'] : ''; $remark = isset($params['remark']) ? $params['remark'] : ''; $type = isset($params['type']) ? $params['type'] : 'default'; $keywords = isset($params['keywords']) ? $params['keywords'] : []; $keywords = $keywords ? $keywords : []; if ($title) { $keywords['first'] = ['value' => $title, 'color' => '#173177']; } if ($remark) { $keywords['remark'] = ['value' => $remark, 'color' => '#173177']; } $configService = new ConfigService(); $templates = $configService->getConfigByGroup(10); $templateId = isset($templates[$type]) ? trim($templates[$type]) : ''; if (empty($templateId)) { return 2110; } ksort($keywords); $tplData = [ 'touser' => $openid, 'template_id' => $templateId, 'data' => $keywords, ]; $url = isset($params['url']) ? trim($params['url']) : ''; if ($url) { $tplData['url'] = $formatUrl? WechatService::makeRedirectUrl($url) : $url; } // 删除旧数据,新增消息记录 $tokenData = WechatService::getAccessToken(''); $token = isset($tokenData['token']) ? trim($tokenData['token']) : ''; if (empty($token)) { return 1010; } $url = sprintf(self::$apiUrl['tplMessage'], $token); $result = httpRequest($url, json_encode($tplData, 256)); $code = isset($result['errcode']) ? $result['errcode'] : true; RedisService::set('caches:messages:result:'.$openid.'_'.date('YmdHi'), ['url'=> $url,'params'=> $tplData,'result'=> $result], 600); if ($code == 'ok') { return true; } else { $result = httpRequest($url, json_encode($tplData, 256)); $code = isset($result['errcode']) ? $result['errcode'] : true; RedisService::set('caches:messages:result:'.$openid.'_'.date('YmdHi'), ['url'=> $url,'params'=> $tplData,'result'=> $result], 600); if ($code === 0) { return true; } } return 2113; } /** * 获取消息模板列表 * @return int|mixed */ public static function getTemplateList(){ $tokenData = WechatService::getAccessToken(''); $token = isset($tokenData['token']) ? trim($tokenData['token']) : ''; if (empty($token)) { return 1010; } $url = sprintf(self::$apiUrl['templateList'], $token); $result = httpRequest($url); RedisService::set('caches:messages:templates', $result, 600); return $result; } /** * 获取支付签名token * @return string */ public static function getSignToken($params, $type=1){ $url = $params['url']; $url_parts = parse_url($url); $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : "")); $nonce = WechatService::createNonceStr(32); $timestamp = time(); $body = isset($params['body'])? $params['body'] : ''; $method = isset($params['method'])? $params['method'] : 'POST'; $message = "{$method}\n". $canonical_url."\n". $timestamp."\n". $nonce."\n"; if($body || $type == 2){ $message .= $body."\n"; } $mch_private_key = openssl_get_privatekey(file_get_contents(self::$certPaths['key_path'])); openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption'); $sign = base64_encode($raw_sign); $schema = 'WECHATPAY2-SHA256-RSA2048'; $serial_no = WechatService::getConfigs('wx_mch_cert_no'); $mchId = isset($params['mchid'])? $params['mchid'] : WechatService::getConfigs('wx_mchid'); $token = sprintf('%s mchid="%s",nonce_str="%s",timestamp="%d",signature="%s",serial_no="%s"', $schema, $mchId, $nonce, $timestamp, $sign, $serial_no); return $token; } /** * 获取支付证书列表 * @return array|mixed */ public static function getCerts(){ $url = self::$apiUrl['getCerts']; $mchId = WechatService::getConfigs('wx_mch_id'); $cacheKey = "caches:certs:{$mchId}:list"; $certs = RedisService::get($cacheKey); if($certs){ return $certs; } $params = ['url'=> $url, 'method'=> 'GET','mchid'=> $mchId, 'body'=> '']; $token = WechatService::getSignToken($params, 2); RedisService::set('caches:certs:'.$mchId.':token', ['params'=>$params,'token'=> $token], 600); $headers = ["Authorization: {$token}","Content-Type: application/json","Accept: application/json","User-Agent: https://zh.wikipedia.org/wiki/User_agent"]; RedisService::set('caches:certs:'.$mchId.':request', ['params'=>$params,'token'=> $token,'headers'=> $headers], 600); $response = WechatService::curlPost($url, '',[],[],$headers); $response = $response? json_decode($response, true) : $response; RedisService::set('caches:certs:'.$mchId.':result', ['params'=>$params,'result'=> $response], 600); if($response){ $certs = []; foreach($response as $item){ $certs[$item['serial_no']] = $item; } if($certs){ RedisService::set($cacheKey, $certs, 300); } } return $certs; } /** * Read certificate from file * * @param string $filepath PEM encoded X.509 certificate file path * * @return resource|bool X.509 certificate resource identifier on success or FALSE on failure */ public static function getCertificate() { return openssl_x509_read(file_get_contents(self::$certPaths['cert_path']));} /** * 生成普通参数二维码 * @param $str 参数 * @param bool $refresh 是否重新生成 * @return bool */ public static function makeNormalQrcode($str, $refresh = false, $size = 3, $margin=0, $level=1) { $qrFile = '/img/qrcode/'; if (!is_dir('/uploads'.$qrFile)) { @mkdir('./uploads' . $qrFile, 0755, true); } $qrFile = $qrFile . 'U_' . strtoupper(md5($str . '_' . $size.$margin.$level)) . '.png'; $cacheKey = "caches:qrcodes:member_".md5($str); if(RedisService::get($cacheKey) && is_file('/uploads'.$qrFile) && !$refresh){ return $qrFile; } QRcode::png($str, './uploads' . $qrFile, $level, $size, $margin); if(!file_exists('./uploads'.$qrFile)){ return false; } RedisService::set($cacheKey, ['str'=> $str, 'qrcode'=> $qrFile,'date'=> date('Y-m-d H:i:s')], 7 * 24 * 3600); return $qrFile; } /** * 返回给微信 */ public static function rebackOk(){ echo ''; exit; } } ?>