From 0e96d49e0e2814c6542e1a700203e02076ac8931 Mon Sep 17 00:00:00 2001 From: as2252258 Date: Thu, 19 Jul 2018 12:10:22 +0800 Subject: [PATCH] add --- .gitignore | 1 + composer.json | 22 +++ wx/Account.php | 27 ++++ wx/Base.php | 357 ++++++++++++++++++++++++++++++++++++++++++++++++ wx/Http.php | 199 +++++++++++++++++++++++++++ wx/Recharge.php | 94 +++++++++++++ wx/Result.php | 153 +++++++++++++++++++++ wx/Template.php | 30 ++++ 8 files changed, 883 insertions(+) create mode 100644 .gitignore create mode 100644 composer.json create mode 100644 wx/Account.php create mode 100644 wx/Base.php create mode 100644 wx/Http.php create mode 100644 wx/Recharge.php create mode 100644 wx/Result.php create mode 100644 wx/Template.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..016b7e8 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.idea/php.xml diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..2b9fdcc --- /dev/null +++ b/composer.json @@ -0,0 +1,22 @@ +{ + "name": "wchat/wchat", + "description": "一个参数效验扩展", + "keywords": [ + "validate" + ], + "license": "MIT", + "authors": [ + { + "name": "xianglin", + "email": "xianglin@163.com" + } + ], + "autoload": { + "psr-4": { + "Validate\\": "src" + } + }, + "require": { + "php": ">= 7.2" + } +} diff --git a/wx/Account.php b/wx/Account.php new file mode 100644 index 0000000..0cfc2ca --- /dev/null +++ b/wx/Account.php @@ -0,0 +1,27 @@ + $this->app_id, + 'secret' => $this->app_secret, + 'js_code' => $code, + 'grant_type' => 'authorization_code' + ], null, ['Content-Type' => 'text/xml']); + } + + +} \ No newline at end of file diff --git a/wx/Base.php b/wx/Base.php new file mode 100644 index 0000000..148fe4e --- /dev/null +++ b/wx/Base.php @@ -0,0 +1,357 @@ +init($configPath); + } + + /** + * + */ + public function init($configPath) + { + $config = require $configPath; + if (empty($config)) { + return; + } + foreach ($config as $key => $val) { + if (!property_exists($this, $key)) { + continue; + } + $this->$key = $val; + } + } + + /** + * @param $url + * @param array $data + * @param callable|null $callback + * @return Result + */ + public function push($url, $data = [], callable $callback = NULL) + { + return http::post($url, $data, $callback); + } + + /** + * @param int $length + * @return string + * + * 随机字符串 + */ + public function random($length = 20) + { + $res = []; + $str = 'abcdefghijklmnopqrstuvwxyz'; + $str .= strtoupper($str) . '1234567890'; + for ($i = 0; $i < $length; $i++) { + $rand = substr($str, rand(0, strlen($str) - 2), 1); + if (empty($rand)) { + $rand = substr($str, strlen($str) - 3, 1); + } + array_push($res, $rand); + } + + return $this->nonce_str = implode($res); + } + + /** + * @param array $data + * @return string + */ + protected function toXml(array $data) + { + $xml = ""; + foreach ($data as $key => $val) { + if (is_numeric($val)) { + $xml .= "<" . $key . ">" . $val . ""; + } else { + $xml .= "<" . $key . ">"; + } + } + $xml .= ""; + return $xml; + } + + + /** + * @return string + */ + public function sign(array $array) + { + ksort($array, SORT_STRING); + $string = ''; + foreach ($array as $key => $val) { + if (empty($string)) { + $string = $key . '=' . $val; + } else { + $string .= '&' . $key . '=' . $val; + } + } + $string .= '&key=' . $this->key; + +// var_dump($string); + + if ($this->sign_type == 'MD5') { + return strtoupper(md5($string)); + } else { + return hash('sha256', $string); + } + } + + /** + * @param $xml + * @return mixed + */ + public function toArray($xml) + { + if (!is_null($json = json_decode($xml, TRUE))) { + return $json; + } + if (is_array($json)) { + return $json; + } + $data = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA); + return json_decode(json_encode($data), TRUE); + } + + /** + * @param $openId + * @return bool|mixed|string + * @throws \Exception + */ + protected function getAccessToken($openId) + { + $data = http::get('https://api.weixin.qq.com/cgi-bin/token', [ + 'grant_type' => 'client_credential', + 'appid' => $this->app_id, + 'secret' => $this->app_secret, + ]); + + if (!$data->isResultsOK()) { + throw new \Exception($data->getMessage()); + } + return $data->getData('access_token'); + } + + /** + * @param $data + * @return mixed + * @throws \Exception + */ + protected function buildResult($data, $body = NULL) + { + $data = $this->checkSign($data); + if (!$data) { + $return['code'] = -1; + $return['message'] = '签名错误.'; + } else { + if (isset($data['return_code'])) { + if ($data['return_code'] == 'FAIL') { + $return['code'] = -1; + $return['message'] = $data['return_msg']; + } else { + $return['code'] = 0; + $return['data'] = $data; + $return['data']['postBody'] = $body; + } + } else { + if ($data['errcode'] == 'FAIL') { + $return['code'] = -1; + $return['message'] = $data['errmsg']; + } else { + $return['code'] = 0; + $return['data'] = $data; + $return['data']['postBody'] = $body; + } + } + } + return $return; + } + + /** + * @param $result + * @return mixed + * @throws \Exception + */ + protected function checkSign($result) + { + $data = $this->toArray($result); + + if (!isset($data['sign'])) { + return $data; + } + + $sign = $data['sign']; + + unset($data['sign']); + + $_sign = $this->sign($data); + if ($sign != $_sign) { + return FALSE; + } + return $data; + } + + + public static $OK = 0; + public static $IllegalAesKey = -41001; + public static $IllegalIv = -41002; + public static $IllegalBuffer = -41003; + public static $DecodeBase64Error = -41004; + + public static function d($code) + { + $messages = [ + static::$OK => '', + static::$IllegalAesKey => '', + static::$IllegalIv => '', + static::$IllegalBuffer => '', + static::$DecodeBase64Error => '', + ]; + return $messages[$code] ?? static::$DecodeBase64Error; + } + + /** + * @param $encryptedData + * @param $iv + * @param $data + * @return int + */ + public static function decode($encryptedData, $iv, $sessionKey, &$data) + { + if (strlen($sessionKey) != 24) { + return self::$IllegalAesKey; + } + + $aesKey = base64_decode($sessionKey); + if (strlen($iv) != 24) { + return self::$IllegalIv; + } + + $aesIV = base64_decode($iv); + + $aesCipher = base64_decode($encryptedData); + + $result = openssl_decrypt($aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV); + + $dataObj = json_decode($result); + + if ($dataObj == NULL) { + return self::$IllegalBuffer; + } + if ($dataObj->watermark->appid != static::$app_id) { + return self::$IllegalBuffer; + } + $data = $dataObj; + return self::$OK; + } +} \ No newline at end of file diff --git a/wx/Http.php b/wx/Http.php new file mode 100644 index 0000000..c893c62 --- /dev/null +++ b/wx/Http.php @@ -0,0 +1,199 @@ +url . '/' . $url; + } + + return $this->curl_push($url, $pushType, $data, $callback); + } + + /** + * @param $url + * @param string $type + * @param array $data + * + * @return Result + * curl请求 + */ + public function curl_push($url, $type = 'get', $data = [], callable $callback = NULL) + { + $_data = $this->paramEncode($data); + if ($type == 'get' && is_array($_data)) { + $url .= '?' . http_build_query($_data); + } + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_TIMEOUT, 5);// 超时设置 + curl_setopt($ch, CURLOPT_HEADER, TRUE); + if (!empty($this->header)) { + curl_setopt($ch, CURLOPT_HTTPHEADER, $this->header); + } + curl_setopt($ch, CURLOPT_NOBODY, FALSE); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1);// 超时设置 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);//返回内容 + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);// 跟踪重定向 + curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate'); + switch (strtolower($type)) { + case 'post': + curl_setopt($ch, CURLOPT_POST, 1); + if (is_array($_data)) { + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($_data)); + } else { + curl_setopt($ch, CURLOPT_POSTFIELDS, $_data); + } + break; + case 'delete': + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE'); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($_data)); + break; + case 'put': + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); + curl_setopt($ch, CURLOPT_POSTFIELDS, $_data); + break; + default: + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET'); + } + $output = curl_exec($ch); + if ($output === FALSE) { + return new Result(['code' => 500, 'message' => curl_error($ch)]); + } + curl_close($ch); + + list($header, $body) = explode("\r\n\r\n", $output, 2); + $result = []; + if ($callback === NULL) { + if (is_null($results = json_decode($body, TRUE))) { + return new Result(['code' => 500, 'message' => '服务器连接失败']); + } + if (isset($results['errcode'])) { + $result['code'] = $results['errcode']; + $result['message'] = $results['errmsg']; + } else { + $result['code'] = 0; + $result['message'] = 'system success.'; + $result['data'] = $results; + } + } else { + $result = call_user_func($callback, $body, $_data); + } + + if (!is_array($result)) { + return $result; + } + + return new Result($result); + } + + /** + * @param $arr + * @param string $pushType + * + * @return array|string + * 将请求参数进行编码 + */ + private function paramEncode($arr, $pushType = 'post') + { + if (!is_array($arr)) { + return $arr; + } + $_tmp = []; + foreach ($arr as $Key => $val) { + $_tmp[$Key] = $val; + } + + return ($pushType == 'post' ? $_tmp : http_build_query($_tmp)); + } + + /** + * @param $url + * @param array $data + * @param array $header + * + * @return Result + */ + public static function post($url, $data = [], callable $callback = NULL, array $header = NULL) + { + static $_class = NULL; + if ($_class == NULL) $_class = new Http(); + if (!empty($header)) { + $_class->setHeaders($header); + } + return $_class->request($url, 'post', $data, $callback); + } + + /** + * @param $url + * @param array $data + * @param array $header + * + * @return Result + */ + public static function get($url, $data = [], callable $callback = NULL, $header = []) + { + static $_class = NULL; + if ($_class == NULL) $_class = new Http(); + return $_class->request($url, 'get', $data, $callback); + } + + /** + * @param $url + * @param array $data + * @param array $header + * + * @return Result + */ + public static function option($url, $data = [], $header = []) + { + static $_class = NULL; + if ($_class == NULL) $_class = new Http(); + return $_class->request($url, 'option', $data); + } + + /** + * @param $url + * @param array $data + * @param array $header + * + * @return Result + */ + public static function delete($url, $data = [], $header = []) + { + static $_class = NULL; + if ($_class == NULL) $_class = new Http(); + return $_class->request($url, 'delete', $data); + } + + + public function setHeaders(array $headers) + { + if (empty($headers)) { + return []; + } + foreach ($headers as $key => $val) { + $this->header[] = $key . ':' . $val; + } + return $this->header; + } +} diff --git a/wx/Recharge.php b/wx/Recharge.php new file mode 100644 index 0000000..63bf4bb --- /dev/null +++ b/wx/Recharge.php @@ -0,0 +1,94 @@ + 500, 'message' => '充值金额不能小于0.']); + } + $this->money = $money; + $this->orderNo = $orderNo; + $this->data['openid'] = $openId; + return http::post($this->createPayUrl(), $this->builder(), + function ($result, $body) use ($_this) { + $data = $_this->toArray($result); + if(isset($data['sign'])){ + $sign = $data['sign']; + unset($data['sign']); + $_sign = $_this->sign($data); + } + $return = []; + if (!isset($sign) || $sign != $_sign) { + $return['code'] = -1; + $return['message'] = $data['return_msg'] ?? '返回数据签名验证失败'; + } else { + if ($data['return_code'] == 'FAIL') { + $return['code'] = -1; + $return['message'] = $data['return_msg']; + } else { + $return['code'] = 0; + $return['data'] = $data; + $return['data']['postBody'] = $body; + } + } + return $return; + }, ['Content-Type' => 'text/xml'] + ); + } + + + + /** + * @return string + */ + protected function builder() + { + $data = [ + 'appid' => $this->app_id, + 'mch_id' => $this->mch_id, + 'nonce_str' => $this->random(32), + 'body' => $this->body, + 'out_trade_no' => $this->orderNo, + 'total_fee' => $this->money, + 'sign_type' => $this->sign_type, + 'spbill_create_ip' => $_SERVER['REMOTE_ADDR'], + 'notify_url' => $this->notify_url, + 'trade_type' => $this->trade_type, + ]; + + $data = array_merge($data, $this->data); + + $data['sign'] = $this->sign($data); + + return $this->toXml($data); + } + + private function createPayUrl() + { + return $this->mch_host . '/pay/unifiedorder'; + } +} \ No newline at end of file diff --git a/wx/Result.php b/wx/Result.php new file mode 100644 index 0000000..5509144 --- /dev/null +++ b/wx/Result.php @@ -0,0 +1,153 @@ + $val) { + $this->$key = $val; + } + + $this->header = $this->reloadResponse($this->header); + } + + + /** + * @param $header + * + * @return array + */ + private function reloadResponse($header) + { + $data = []; + $load = explode("\n", $header); + if (!empty($load) && is_array($load)) { + foreach ($load as $key => $val) { + if (empty($val)) continue; + $ex = explode(': ', $val); + if (!empty($ex[0]) && !empty($ex[1])) { + $data[trim($ex[0])] = trim($ex[1]); + } + } + } + return $data; + } + + public function __get($name) + { + return $this->$name; + } + + + public function __set($name, $value) + { + $this->$name = $value; + + return $this; + } + + public function getTime() + { + return [ + 'startTime' => $this->startTime, + 'requestTime' => $this->requestTime, + 'runTime' => $this->runTime, + ]; + } + + /** + * @param $key + * @param $data + * @return $this + * @throws Exception + */ + public function setAttr($key, $data) + { + if (!property_exists($this, $key)) { + throw new \Exception('未查找到相应对象属性'); + } + $this->$key = $data; + return $this; + } + + + public function isResultsOK() + { + return $this->code == 0; + } + + /** + * @param array $headers + * 批量设置返回头 + */ + public function setHeaders(array $headers) + { + foreach ($headers as $key => $val) { + $this->setHeader($key, $val); + } + } + + /** + * @param $key + * @param $val + * 设置返回头 + */ + public function setHeader($key, $val) + { + header($key . ':' . $val); + } + + /** + * @param string $name + * @return mixed + */ + public function getData($name = '') + { + if (!$this->isResultsOK()) { + return ''; + } + if (!empty($name) && isset($this->data[$name])) { + return $this->data[$name]; + } + return $this->data; + } + + /** + * @param $key + * @param $data + * @return $this + */ + public function append($key, $data) + { + $this->data[$key] = $data; + return $this; + } + + public function getMessage() + { + return $this->message; + } + + public function getCode() + { + return $this->code; + } +} diff --git a/wx/Template.php b/wx/Template.php new file mode 100644 index 0000000..4fbcf86 --- /dev/null +++ b/wx/Template.php @@ -0,0 +1,30 @@ + 'application/json']) + ->append('postBody', $postBody); + } +} \ No newline at end of file