* @version : HuoMp 1.0 */ namespace huoMpay; use huolib\constant\CacheConst; use huolib\status\OrderStatus; use think\Cache; use think\Controller; use think\Log; class MpaySign extends Controller { private static $url = "https://api.weixin.qq.com"; private static $method = "POST"; /** * @param string $uri * @param array $params * @param string $app_secret 小游戏AppSecret * @param string $midas_app_key 米大师AppKey * @param string $session_key * * @return int|mixed */ public static function apiPay($uri = '', $params = [], $app_secret, $midas_app_key, $session_key) { $_method = strtoupper(self::$method); if (empty($params['openid']) || empty($params['appid']) || empty($params['offer_id']) || empty($params['ts']) || empty($params['zone_id']) || empty($params['pf'])) { Log::write( "func=".__FUNCTION__."&class=".__CLASS__."&step=1&method=$_method&uri=$uri¶ms=".json_encode($params) ."&app_secret=$app_secret"."&secret=$midas_app_key", Log::ERROR ); $_rdata = [ 'code' => OrderStatus::INVALID_PARAMS, 'msg' => OrderStatus::getMsg(OrderStatus::INVALID_PARAMS) ]; return $_rdata; } $_access_token = self::getAccessToken($params['appid'], $app_secret); if (false == $_access_token) { $_rdata = [ 'code' => OrderStatus::ACCESS_TOKEN_GET_ERROR, 'msg' => OrderStatus::getMsg(OrderStatus::ACCESS_TOKEN_GET_ERROR) ]; return $_rdata; } $_params = $params; $_params['sig'] = self::makeSign($_method, $uri, $_params, $midas_app_key); $_params['access_token'] = $_access_token; $_params['mp_sig'] = self::makeMpSign($_method, $uri, $_params, $session_key); $_url = self::$url.$uri.'?access_token='.$_access_token; unset($_params['access_token']); $_query_param_json = json_encode($_params); Log::write("==========Request Info==========\n url:$_url,query_string:$_query_param_json", Log::WECHAT); $header = ['Content-Type: application/octet-stream; charset=utf-8']; $_ret = self::curl($_url, $_query_param_json, 'POST', $header); Log::write('The resp is '.$_ret, Log::WECHAT); $_ret_arr = json_decode($_ret, true); // 远程返回的不是 json 格式, 说明返回包有问题 if (is_null($_ret_arr)) { $_rdata = array( 'code' => OPENAPI_ERROR_RESPONSE_DATA_INVALID, 'msg' => $_ret ); return $_rdata; } if ('0' != $_ret_arr['errcode']) { $_rdata = [ 'code' => $_ret_arr['errcode'], 'msg' => 'midas error!'.$_ret_arr['errmsg'] ]; return $_rdata; } $_rdata = [ 'code' => OrderStatus::NO_ERROR, 'msg' => $_ret_arr['errmsg'], 'data' => $_ret_arr, ]; return $_rdata; } /** * 获取 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_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; } /** * 米大师支付签名 sig签名 * * @param string $method HTTP请求方式 * @param string $uri 请求的URI * @param array $params 参数 * @param string $secret 秘钥 * * @return string */ public static function makeSign($method, $uri = '', $params = array(), $secret = '') { /* 1. 参与米大师签名请求参数 */ $_sign_data = $params; /* 2. 对参与米$_sign_data大师签名的参数按照key=value的格式,并按照参数名ASCII字典序升序排序如下: */ ksort($_sign_data); $_sign_data_str = self::createRawLinkString($_sign_data); /* 3. 拼接uri、method和米大师密钥: */ $_sign_str = $_sign_data_str."&org_loc=".$uri."&method=".strtoupper($method)."&secret=".$secret; /* 4. 把米大师密钥作为key,使用HMAC-SHA256得到签名。 */ $_sig = hash_hmac('sha256', $_sign_str, $secret); return $_sig; } /** * 米大师支付开平签名 mp_sig签名 * * @param string $method HTTP请求方式 * @param string $uri 请求的URI * @param array $params 参数 * @param string $session_key 秘钥 * * @return string */ public static function makeMpSign($method, $uri = '', $params = [], $session_key = '') { /* 1. 参与开平签名请求参数 */ if (empty($params['openid']) || empty($params['appid']) || empty($params['offer_id']) || empty($params['ts']) || empty($params['zone_id']) || empty($params['sig']) || empty($params['access_token']) || empty($params['pf'])) { Log::write( "func=".__FUNCTION__."&class=".__CLASS__."&step=1&method=$method&uri=$uri¶ms=".json_encode($params) ."&session_key=$session_key", Log::ERROR ); return false; } $_sign_data = $params; /* 2. 对参与开平签名的参数按照key=value的格式,并按照参数名ASCII字典序升序排序如下: */ ksort($_sign_data); $_sign_data_str = self::createRawLinkString($_sign_data); /* 3. 拼接uri、method和米大师密钥: */ $_sign_str = $_sign_data_str."&org_loc=".$uri."&method=".strtoupper($method)."&session_key=".$session_key; /* 4. 把米大师密钥作为key,使用HMAC-SHA256得到签名。 */ $_mp_sig = hash_hmac('sha256', $_sign_str, $session_key); return $_mp_sig; } }