'', 'url' => '', 'local_sym_key' => '',//API对称密钥 'local_sym_sn' => '',//API对称密钥编号 'local_sn' => '',//平台证书编号 'local_key' => '', 'local_certificate' => '',//平台证书 'store_id' => '', 'out_store_id' => '', ]; private $key =[ 'rsa_sn'=>'', 'rsa_private_key'=>'' ]; public function rules() { return [ [['store_id'], 'integer'] ]; } public function getWechatApp($store_id) { $this->store_id = $store_id; $result = $this->initData($store_id); $this->param['local_sym_sn'] = $result['local_sym_sn']; $this->param['local_sym_key'] = $result['local_sym_key']; $this->key['rsa_sn'] = $result['rsa_sn']; $this->key['rsa_private_key'] = $result['rsa_private_key']; if (empty($this->param['local_sym_sn']) || empty($this->param['local_sym_key']) || empty($this->key['rsa_sn']) || empty($this->key['rsa_private_key']) ) { $this->app = null; } else { $this->app = (new WechatMini())::getWechatConfig($this->store_id); } $storeMini = StoreMini::findOne(['is_use' => 1, 'store_id' => $store_id]); if ($storeMini) { $this->param['app_id'] = Option::get("platform_third_appid", 0, 'saas')['value']; } else { if ($this->app) { $this->param['app_id'] = $this->app->getConfig()['app_id']; } } } public function initData($store_id) { $keys = [ 'local_sym_sn', 'local_sym_key', 'rsa_sn', 'rsa_private_key' ]; // if (is_open_platform()) { // $store_id = 0; // } $storeMini = StoreMini::findOne(['is_use' => 1, 'store_id' => $store_id]); if ($storeMini) { $delivery_data = Option::get($keys, 0, 'delivery'); } else { $delivery_data = Option::get($keys, $store_id, 'delivery'); } if ($delivery_data) { $arr = []; foreach ($delivery_data as $value) { $index = array_search($value['name'], $keys); unset($keys[$index]); $arr[$value['name']] = $value['value']; } foreach ($keys as $key) { $arr[$key] = ''; } $delivery_data = $arr; } else { $delivery_data = [ 'local_sym_sn' => '', 'local_sym_key' => '', 'rsa_sn' => '', 'rsa_private_key' => '' ]; } return $delivery_data; } public function getUrlData($store_id, $url_, $data = [], $type = 0) { $this->getWechatApp($store_id); $app = $this->app; try { if (empty($app)) { throw new \Exception(); } $access_token = $app->access_token->getToken(); } catch (\Exception $e) { return [ 'code' => 1, 'msg' => '未获取到配置信息' ]; } $accessToken = $access_token['authorizer_access_token'] ?? $access_token['access_token']; $url_ = $this->base_uri . $url_; $url = $url_ . "?access_token=" . $accessToken; $newRe = $this->getRequestParam($data, $url_, ['app_id' => $this->param['app_id']]); if (!$type) { $signature = $this->getSignature($newRe, $url_, ['app_id' => $this->param['app_id']]); $headerArray =['wechatmp_appid' => $this->param['app_id'], 'wechatmp_timeStamp'=>$newRe['ts'], 'wechatmp_signature'=>$signature]; } $result = $this->curlPost($url, $newRe['reqData'] ?: '', $headerArray ?: []); $headers = $this->httpParseHeaders($result['header']); $result = json_decode($result['body'], true); if (!$type) { $result = $this->decryptToString($url_, $headers['Wechatmp-TimeStamp'], $result, $this->param['app_id']); } if ($result['errcode'] === 10000) { return retry(3, function () use ($store_id, $url_, $data, $type) { return $this->getUrlData($store_id, $url_, $data, $type); }); } return $result; } //加密 public function getRequestParam($req = [], $url_ = '', $params_data = []) { $param = $this->param; $time = time(); //16位随机字符串 $nonce = base64_encode(random_bytes(16)); $addReq = ["_n" => $nonce, "_appid" => $params_data['app_id'], "_timestamp" => $time]; $realReq = array_merge($addReq, $req); $realReq = json_encode($realReq); //额外参数 $aad = $url_ . "|" . $params_data['app_id'] . "|" . $time . "|" . $param['local_sym_sn']; //12位随机字符 $iv = random_bytes(12); $cipher = openssl_encrypt($realReq, "aes-256-gcm", base64_decode($param['local_sym_key']), OPENSSL_RAW_DATA, $iv, $tag, $aad); $iv = base64_encode($iv); $data = base64_encode($cipher); $authTag = base64_encode($tag); $reqData = ["iv" => $iv, "data" => $data, "authtag" => $authTag]; return ['ts' => $time, 'reqData' => json_encode($reqData)]; } //加签 public function getSignature($newRe, $url_, $params_data = []) { $keys = $this->key; $time = $newRe['ts']; $url = $url_; $appId = $params_data['app_id']; $reqData = $newRe['reqData']; $payload = $url . "\n" . $appId . "\n" . $time . "\n" . $reqData; // mb_internal_encoding($payload); // $payload = mb_convert_encoding($payload, 'UTF-8', "auto"); // $key = openssl_pkey_get_private($keys['key']); // // $signature = ''; // openssl_sign($payload, $signature, $key, OPENSSL_ALGO_SHA256); // openssl_free_key($key); $signature = RSA::loadPrivateKey($keys['rsa_private_key']) ->withPadding(RSA::SIGNATURE_PSS) ->withHash('sha256') ->withMGFHash('sha256') ->sign($payload); return base64_encode($signature); } //请求 public function curlPost($url, $field, $header) { $headerArray = array('Content-Type: application/json;charset=utf-8', 'Accept:application/json', 'Content-Length:' . strlen($field), 'Wechatmp-Appid:' . $header['wechatmp_appid'], 'Wechatmp-TimeStamp:' . $header['wechatmp_timeStamp'], 'Wechatmp-Signature:' . $header['wechatmp_signature'] ); $curl = curl_init(); curl_setopt($curl, CURLOPT_HTTPHEADER, $headerArray); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, $field); //输出响应头部 curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_HEADER, true); $str = curl_exec($curl); $headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE); $headers = substr($str, 0, $headerSize); $body = substr($str, $headerSize); curl_close($curl); return ['body' => $body, 'header' => $headers]; // $header_ = array('Content-Type: application/json;charset=utf-8', // 'Accept:application/json', // 'Content-Length:' . strlen($field), // 'Wechatmp-Appid:' . $header['wechatmp_appid'], // 'Wechatmp-TimeStamp:' . $header['wechatmp_timeStamp'], // 'Wechatmp-Signature:' . $header['wechatmp_signature'] // // ); // $ch = curl_init(); // curl_setopt($ch, CURLOPT_POST, 1); // curl_setopt($ch, CURLOPT_URL, $url); // curl_setopt($ch, CURLOPT_POSTFIELDS, $field); // curl_setopt($ch, CURLOPT_HTTPHEADER, $header_); // ob_start(); // curl_exec($ch); // $return_content = ob_get_contents(); // ob_end_clean(); // return $return_content; } //解析加密信息 public function decryptToString($url, $ts, $result_, $app_id) { try { $param = $this->param; $message = $url . '|' . $app_id . '|' . $ts . '|' . $param['local_sym_sn']; $iv = base64_decode($result_['iv']); $data = base64_decode($result_['data']); $authTag = base64_decode($result_['authtag']); $result = openssl_decrypt($data, "aes-256-gcm", base64_decode($param['local_sym_key']), OPENSSL_RAW_DATA, $iv, $authTag, $message); if (!$result) { throw new \Exception(openssl_error_string(), 10000); } return json_decode($result, true); } catch (\Exception $e) { return [ 'errcode' => $e->getCode() ?: 1, 'errmsg' => $e->getMessage() ]; } // $config = self::$config; } /** * Name:解析头部信息 * User: zcw * Date: 2023/7/14 * Time: 10:28 * @param $headerString * @return array */ private function httpParseHeaders($headerString) { $headers = []; $lines = explode("\r\n", $headerString); foreach ($lines as $line) { $line = trim($line); if (!empty($line)) { $parts = explode(':', $line, 2); $key = trim($parts[0]); $value = isset($parts[1]) ? trim($parts[1]) : ''; $headers[$key] = $value; } } return $headers; } }