appId = $config['appid']; $this->mchId = $config['mchid']; $this->apiKey = $config['apikey']; $this->sslCertPath = $config['sslcert_path']; $this->sslKeyPath = $config['sslkey_path']; if (isset($config['sub_mchid'])) { $this->subMchId = $config['sub_mchid']; } if (isset($config['sub_appid'])) { $this->subAppId = $config['sub_appid']; } } /** * 请求接口并解析返回数据 * @param string $url url * @param array $params 请求参数 * @param bool $cert 是否需要证书 * @return mixed * @throws Exception */ public function execute(string $url, array $params, bool $cert = false) { $params = array_merge($this->publicParams, $params); $params['sign'] = $this->makeSign($params); $xml = $this->array2Xml($params); $response = $this->curl($url, $xml, $cert); $result = $this->xml2array($response); if (isset($result['return_code']) && $result['return_code'] == 'SUCCESS') { if (isset($result['result_code']) && $result['result_code'] == 'SUCCESS') { if (isset($result['sign']) && !$this->checkSign($result)) { throw new Exception('返回数据验签失败'); } return $result; } } throw new WeChatPayException($result); } /** * 验签 * @param $data * @return bool */ protected function checkSign($data): bool { if (!isset($data['sign'])) return false; $sign = $this->makeSign($data); return $sign === $data['sign']; } /** * 生成签名 * @param $data * @return string */ protected function makeSign($data): string { ksort($data); $signStr = ''; foreach ($data as $k => $v) { if($k != 'sign' && !is_array($v) && !$this->isEmpty($v)){ $signStr .= $k . '=' . $v . '&'; } } $signStr = trim($signStr, '&') . '&key=' . $this->apiKey; if (isset($data['sign_type']) && $data['sign_type'] == 'HMAC-SHA256') { $sign = hash_hmac("sha256", $signStr, $this->apiKey); } else { $sign = md5($signStr); } return strtoupper($sign); } /** * 校验某字符串或可被转换为字符串的数据,是否为 NULL 或均为空白字符. * * @param string|null $value * * @return bool */ protected function isEmpty(?string $value): bool { return $value === null || $value === ''; } /** * 产生随机字符串,不长于32位 * @param int $length * @return string 产生的随机字符串 */ protected function getNonceStr(int $length = 32): string { $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; $str = ""; for ($i = 0; $i < $length; $i++) { $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); } return $str; } /** * 转为XML数据 * @param array $data 源数据 * @return string */ protected function array2Xml(array $data): string { $xml = ''; foreach ($data as $key => $val) { $xml .= (is_numeric($val) ? "<{$key}>{$val}" : "<{$key}>"); } return $xml . ''; } /** * 解析XML数据 * @param string $xml 源数据 * @return mixed */ protected function xml2array(string $xml) { if (!$xml) { return false; } LIBXML_VERSION < 20900 && libxml_disable_entity_loader(true); return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true); } /** * 以post方式提交xml到对应的接口url * @param string $url url * @param mixed $xml 需要post的xml数据 * @param bool $useCert 是否需要证书 * @param int $second url执行超时时间 * @return string * @throws Exception */ protected function curl(string $url, $xml, bool $useCert = false, int $second = 10): string { $ch = curl_init(); $curlVersion = curl_version(); $ua = "WXPaySDK/" . self::$VERSION . " (" . PHP_OS . ") PHP/" . PHP_VERSION . " CURL/" . $curlVersion['version'] . " ". $this->mchId; curl_setopt($ch, CURLOPT_TIMEOUT, $second); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_USERAGENT, $ua); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); if ($useCert) { if (!file_exists($this->sslCertPath) || !file_exists($this->sslKeyPath)) { throw new Exception('商户证书文件不存在'); } //使用证书:cert 与 key 分别属于两个.pem文件 curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM'); curl_setopt($ch, CURLOPT_SSLCERT, $this->sslCertPath); curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM'); curl_setopt($ch, CURLOPT_SSLKEY, $this->sslKeyPath); } curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); $data = curl_exec($ch); if (curl_errno($ch) > 0) { $errmsg = curl_error($ch); curl_close($ch); throw new Exception($errmsg, 0); } curl_close($ch); return $data; } }