* @version : HuoMp 1.0 */ namespace huoMpQr; use Exception; use FileSystem\FileSystem; use huolib\constant\CacheConst; use think\Cache; use think\Log; class QrCode { private $url = "https://api.weixin.qq.com"; const FILE_PATH = 'upload/qrcode/'; /** * https://developers.weixin.qq.com/miniprogram/dev/api/qrcode.html * 获取小程序码 * 接口A: 适用于需要的码数量较少的业务场景 接口地址: * https://api.weixin.qq.com/wxa/getwxacode?access_token=ACCESS_TOKEN * 通过该接口生成的小程序码,永久有效,数量限制见文末说明,请谨慎使用。用户扫描该码进入小程序后,将直接进入 path 对应的页面。 * * @param string $appid 小游戏AppId * @param string $app_secret 小游戏AppSecret * @param String $path 不能为空,最大长度 128 字节 * @param int $width 二维码的宽度 * @param bool $auto_color 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 * @param string $line_color auth_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"},十进制表示 * @param bool $is_hyaline 是否需要透明底色, is_hyaline 为true时,生成透明底色的小程序码 * * @return int|mixed */ public function getWxACode( $appid, $app_secret, $path, $width = 430, $auto_color = false, $line_color = '{"r":"0","g":"0","b":"0"}', $is_hyaline = false ) { $_uri = '/wxa/getwxacode'; $_param['path'] = $path; $_param['width'] = $width; $_param['auto_color'] = $auto_color; $_param['line_color'] = $line_color; $_param['is_hyaline'] = $is_hyaline; return $this->getImage($appid, $app_secret, $_uri, $_param); } /** * 获取小程序码 * 接口B:适用于需要的码数量极多的业务场景 * https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN * 注意:通过该接口生成的小程序码,永久有效,数量暂无限制。用户扫描该码进入小程序后,开发者需在对应页面获取的码中scene 字段的值,再做处理逻辑。 * 使用如下代码可以获取到二维码中的 scene字段的值。 * 调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟,开发工具模拟时的 scene 的参数值需要进行 urlencode * * @param string $appid 小游戏AppId * @param string $app_secret 小游戏AppSecret * @param String $scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 * urlencode 处理,请使用其他编码方式) * @param String $page 必须是已经发布的小程序存在的页面(否则报错),例如 "pages/index/index" * ,根路径前不要填加'/',不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面 * @param int $width 二维码的宽度 * @param bool $auto_color 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 * @param string $line_color auth_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"},十进制表示 * @param bool $is_hyaline 是否需要透明底色, is_hyaline 为true时,生成透明底色的小程序码 * * @return int|mixed */ public function getWxACodeUnLimit( $appid, $app_secret, $page = '', $scene = '', $width = 430, $auto_color = false, $line_color = '{"r":"0","g":"0","b":"0"}', $is_hyaline = false ) { $_uri = '/wxa/getwxacodeunlimit'; $_param['scene'] = $scene; $_param['page'] = $page; // $_param['page'] = ''; // $_param['width'] = $width; // $_param['auto_color'] = $auto_color; // $_param['line_color'] = $line_color; // $_param['is_hyaline'] = $is_hyaline; $_rs = $this->getImage($appid, $app_secret, $_uri, $_param); return $_rs; } /** * 获取小程序码 * 接口C:适用于需要的码数量较少的业务场景 * * https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=ACCESS_TOKEN * 通过该接口生成的小程序二维码,永久有效,数量限制见文末说明,请谨慎使用。用户扫描该码进入小程序后,将直接进入 path 对应的页面。 * tip:通过该接口,仅能生成已发布的小程序的二维码。 * tip:可以在开发者工具预览时生成开发版的带参二维码。 * tip:接口A加上接口C,总共生成的码数量限制为100,000,请谨慎调用。 * tip: POST 参数需要转成 json 字符串,不支持 form 表单提交。 * tip: auto_color line_color 参数仅对小程序码生效。 * * @param string $appid 小游戏AppId * @param string $app_secret 小游戏AppSecret * @param String $path 不能为空,最大长度 128 字节 * @param int $width 二维码的宽度 * * @return int|mixed */ public function createWAQrCode($appid, $app_secret, $path, $width = 430) { $_uri = '/cgi-bin/wxaapp/createwxaqrcode'; $_param['path'] = $path; $_param['width'] = $width; return $this->getImage($appid, $app_secret, $_uri, $_param); } /** * 获取图片信息 * * @param string $appid 小游戏AppId * @param string $app_secret 小游戏AppSecret * @param string $uri 请求URI * @param array $param 参数 * * @return int|mixed */ public function getImage($appid, $app_secret, $uri, $param) { $_file_name = md5(json_encode(array($appid, $param))).'.png'; $_target_file = CMF_ROOT.'public/'.self::FILE_PATH.$_file_name; if (file_exists($_target_file)) { return STATICSITE.DS.self::FILE_PATH.$_file_name; } /* Modified by luowei BEGIN 2019/8/9 ISSUES: 使用组件上传,不用ossfs */ /*try { $_filesystem = FileSystem::create(); } catch (Exception $_exception) { return []; } $_path = DS.self::FILE_PATH.$_file_name; if ($_filesystem->has($_path)) { return STATICSITE.DS.self::FILE_PATH.$_file_name; }*/ /* END 2019/8/9 ISSUES: */ $_access_token = self::getAccessToken($appid, $app_secret); $_url = $this->url.$uri.'?access_token='.$_access_token; $_query_param_json = json_encode($param); Log::write( "==========Request Info==========\n url:$_url,$appid,query_string:$_query_param_json", Log::WECHAT, true ); $header = ['Content-Type: application/json; charset=utf-8']; $_ret = self::curl($_url, $_query_param_json, 'POST', $header); if ($this->isJson($_ret)) { Log::write('The resp is '.$_ret, Log::ERROR); return json_decode($_ret, true); } $_rs = self::save($_ret, $_file_name); /* Modified by luowei BEGIN 2019/8/9 ISSUES: 使用组件上传,不用ossfs */ //$_rs = $_filesystem->write($_path, $_ret); /* END 2019/8/9 ISSUES: */ if (true == $_rs) { return STATICSITE.DS.self::FILE_PATH.$_file_name; } return []; } /** * 判断是否json * * @param $string * * @return bool */ public function isJson($string) { json_decode($string); return (json_last_error() == JSON_ERROR_NONE); } /** * @param $content * @param $filename * * @return bool */ public static function save($content, $filename) { $_target_dir = CMF_ROOT.'public/'.self::FILE_PATH; if (!file_exists($_target_dir)) { mkdir($_target_dir, 0777, true); } try { $_handle = fopen($_target_dir.$filename, "wb"); fwrite($_handle, $content); fclose($_handle); return true; } catch (Exception $e) { $_msg = 'Exception reçue : '.$e->getMessage().'\n'; Log::write("func=".__FUNCTION__."&class=".__CLASS__."&code=".$e->getCode()."&msg=".$_msg, Log::ERROR); return false; } } /** * 获取 access_token * https://developers.weixin.qq.com/miniprogram/dev/api/token.html * * @param string $appid 第三方用户唯一凭证 * @param string $app_secret 第三方用户唯一凭证密钥,即appsecret * * @return string|bool */ public static function getAccessToken($appid, $app_secret) { $_cache_key = CacheConst::CACHE_ACCESS_TOKEN_PREFIX.$appid; $_data = Cache::get($_cache_key); if (!empty($_data)) { $_data = json_decode($_data, true); $_now_time = time(); if (isset($_data['expires_time']) && $_now_time < $_data['expires_time']) { return $_data['access_token']; } } $_url = 'https://api.weixin.qq.com/cgi-bin/token'; $_param['grant_type'] = 'client_credential'; $_param['appid'] = $appid; $_param['secret'] = $app_secret; $_rdata = self::curl($_url, http_build_query($_param)); $_rdata = json_decode($_rdata, true); if (!isset($_rdata['access_token'])) { Log::write( "func=".__FUNCTION__."&class=".__CLASS__."&step=1&appid=$appid&app_secret=$app_secret". json_encode($_rdata), Log::ERROR ); return false; } $_data['access_token'] = $_rdata['access_token']; $_data['expires_in'] = $_rdata['expires_in']; $_data['expires_time'] = time() + $_rdata['expires_in']; $_rs = Cache::set($_cache_key, json_encode($_data)); if (false == $_rs) { return false; } return $_data['access_token']; } /** * CURL请求 * * @param string $url 请求的URL * @param string $params 请求的参数 * @param string $is_get 是否是get请求 * @param array $header 头 * * @return int|mixed */ public static function curl($url, $params, $is_get = "GET", $header = []) { $_header = $header; $ch = curl_init(); if ("GET" == $is_get) { $url = $url.'?'.$params; curl_setopt($ch, CURLOPT_HEADER, 0); // 过滤HTTP头 } else { curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $params); } curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);//设置等待时间 curl_setopt($ch, CURLOPT_TIMEOUT, 3); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);//要求结果为字符串且输出到屏幕上 /* https 请求 */ if (strlen($url) > 5 && strtolower(substr($url, 0, 5)) == "https") { curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); } if (!empty($_header)) { array_push($_header, 'Content-Length: '.strlen($params)); curl_setopt($ch, CURLOPT_HTTPHEADER, $_header); } $_rs = curl_exec($ch); if (false === $_rs) { $_rs = curl_errno($ch); } curl_close($ch); return $_rs; } /** * 获取原始拼接的链接 * * @param array $params * * @return bool|string */ public static function createRawLinkString($params = []) { $_arg = ""; foreach ($params as $_k => $_v) { $_arg .= $_k."=".$_v."&"; } // 去掉最后一个&字符 $_arg = substr($_arg, 0, strlen($_arg) - 1); return $_arg; } public function getPosterImg($qrcode) { $_bg = '/bg.png'; $_qrcode = str_replace(STATICSITE, '', $qrcode); $_qrcode .= '?x-oss-process=image/resize,P_80'; $_img_str = $this->urlsafe_b64encode($_qrcode); return STATICSITE.$_bg.'?x-oss-process=image/watermark,image_'.$_img_str.',x_54,y_254'; } public function urlsafe_b64encode($string) { $data = base64_encode($string); $data = str_replace(array('+', '/', '='), array('-', '_', ''), $data); return $data; } }