diff --git a/qq/Account.php b/qq/Account.php
new file mode 100644
index 0000000..9d0601f
--- /dev/null
+++ b/qq/Account.php
@@ -0,0 +1,211 @@
+savePath = $path;
+ }
+
+ /**
+ * @param $code
+ * @return Result
+ */
+ public function login($code)
+ {
+ $param['appid'] = $this->config->getAppid();
+ $param['secret'] = $this->config->getAppsecret();
+ $param['js_code'] = $code;
+ $param['grant_type'] = 'authorization_code';
+
+ $this->request->setHost('api.q.qq.com');
+ $this->request->setMethod(WxClient::GET);
+ $this->request->addHeader('Content-Type', 'text/xml');
+
+ return $this->request->get('sns/jscode2session', $param);
+ }
+
+ /**
+ * @param $openid
+ * @return array|mixed|Result
+ * @throws \Exception
+ */
+ public function getPublicUserInfo($openid)
+ {
+ $query = [
+ 'access_token' => $this->getAccessToken(),
+ 'openid' => $openid,
+ 'lang' => 'zh_CN'
+ ];
+
+ $this->request->setMethod(WxClient::GET);
+ $this->request->setIsSSL(true);
+ return $this->request->get($this->publicInfo, $query);
+ }
+
+
+ /**
+ * @param $encryptedData
+ * @param $iv
+ * @param $sessionKey
+ * @param $asArray
+ * @return object|array
+ * @throws
+ *
+ * *
-41001: encodingAesKey 非法
+ * -41003: aes 解密失败
+ * -41004: 解密后得到的buffer非法
+ * -41005: base64加密失败
+ * -41016: base64解密失败
+ */
+ public function decode($encryptedData, $iv, $sessionKey, $asArray = false)
+ {
+ $config = Wx::getMiniProGaRamPage()->getConfig();
+ if (strlen($sessionKey) != 24) {
+ throw new \Exception('encodingAesKey 非法', $this->IllegalAesKey);
+ }
+
+ $aesKey = base64_decode($sessionKey);
+ if (strlen($iv) != 24) {
+ throw new \Exception('base64解密失败', $this->IllegalIv);
+ }
+
+ $aesIV = base64_decode($iv);
+ $aesCipher = base64_decode($encryptedData);
+ $result = openssl_decrypt($aesCipher, "AES-128-CBC", $aesKey, OPENSSL_RAW_DATA, $aesIV);
+ if ($result === false) {
+ throw new \Exception('aes 解密失败', $this->IllegalBuffer);
+ }
+
+ $dataObj = json_decode($result);
+ if ($dataObj->watermark->appid != $config->getAppid()) {
+ throw new \Exception('aes 解密失败', $this->IllegalBuffer);
+ }
+
+ if ($asArray) {
+ return get_object_vars($dataObj);
+ }
+
+ return $dataObj;
+ }
+
+
+ /**
+ * @param $path
+ * @param $width
+ * @return array|mixed|Result
+ * @throws \Exception
+ */
+ public function createwxaqrcode($path, $width)
+ {
+ $url = $this->wxaqr . $this->getAccessToken();
+
+ $sendBody['path'] = $path;
+ $sendBody['width'] = $width;
+
+ $this->request->setMethod(WxClient::POST);
+ $this->request->setCallback([$this, 'saveByPath']);
+
+ return $this->request->post($url, $sendBody);
+ }
+
+
+ /**
+ * @param $path
+ * @param $width
+ * @param bool $is_hyaline
+ * @param bool $auto_color
+ * @param string $line_color
+ * @return array|mixed|Result
+ * @throws \Exception
+ */
+ public function getwxacode($path, $width, $is_hyaline = false, $auto_color = false, $line_color = '')
+ {
+ $sendBody['path'] = $path;
+ $sendBody['width'] = $width;
+ $sendBody['auto_color'] = $auto_color;
+ $sendBody['is_hyaline'] = $is_hyaline;
+ if ($auto_color) {
+ $sendBody['line_color'] = $line_color;
+ }
+
+ $url = $this->getwxacode . $this->getAccessToken();
+
+ $this->request->setMethod(WxClient::POST);
+ $this->request->setCallback([$this, 'saveByPath']);
+ return $this->request->post($url, $sendBody);
+ }
+
+
+ /**
+ * @param $path
+ * @param $width
+ * @param bool $is_hyaline
+ * @param bool $auto_color
+ * @param string $line_color
+ * @return array|mixed|Result
+ * @throws \Exception
+ */
+ public function getwxacodeunlimit($path, $width, $is_hyaline = false, $auto_color = false, $line_color = '')
+ {
+ $sendBody['path'] = $path;
+ $sendBody['width'] = $width;
+ $sendBody['auto_color'] = $auto_color;
+ $sendBody['is_hyaline'] = $is_hyaline;
+ if ($auto_color) {
+ $sendBody['line_color'] = $line_color;
+ }
+
+ $url = $this->getwxacodeunlimit . $this->getAccessToken();
+
+ $this->request->setMethod(WxClient::POST);
+ $this->request->setCallback([$this, 'saveByPath']);
+ return $this->request->post($url, $sendBody);
+ }
+
+ /**
+ * @param mixed $body
+ * @return string
+ * @throws \Exception
+ */
+ public function saveByPath($body)
+ {
+ if (!is_null($json = json_decode($body))) {
+ throw new \Exception($json['errmsg'], $json['errcode']);
+ }
+
+ $push = md5_file($body) . '.png';
+ file_put_contents($this->savePath . $push, $this->savePath);
+ return $this->savePath . $push;
+ }
+}
diff --git a/qq/Message.php b/qq/Message.php
new file mode 100644
index 0000000..a6adfe5
--- /dev/null
+++ b/qq/Message.php
@@ -0,0 +1,314 @@
+openid = $openid;
+ $this->msgData['touser'] = $openid;
+ }
+
+ /**
+ * @param string $content
+ * @return Result
+ * @throws \Exception
+ */
+ public function sendTextNews(string $content)
+ {
+ $this->msgData['msgtype'] = 'text';
+ $this->msgData['text[content]'] = $content;
+
+ return $this->sendKefuMsg();
+ }
+
+ /**
+ * @param $media_id
+ * @return Result
+ * @throws \Exception
+ */
+ public function sendImageNews(string $media_id)
+ {
+ $this->msgData['msgtype'] = 'image';
+ $this->msgData['image[media_id]'] = $media_id;
+
+ return $this->sendKefuMsg();
+ }
+
+
+ /**
+ * @param $media_id
+ * @return Result
+ * @throws \Exception
+ */
+ public function sendVoiceNews(string $media_id)
+ {
+ $this->msgData['msgtype'] = 'voice';
+ $this->msgData['voice[media_id]'] = $media_id;
+
+ return $this->sendKefuMsg();
+ }
+
+ /**
+ * @param $media_id
+ * @return Result
+ * @throws \Exception
+ */
+ public function sendMpNewsNews(string $media_id)
+ {
+ $this->msgData['msgtype'] = 'mpnews';
+ $this->msgData['mpnews[media_id]'] = $media_id;
+
+ return $this->sendKefuMsg();
+ }
+
+
+ /**
+ * @param string $title
+ * @param string $description
+ * @param string $url
+ * @param string $picurl
+ * @return Result
+ * @throws \Exception
+ */
+ public function sendNewsNews(string $title, string $description, string $url, string $picurl)
+ {
+ $this->msgData['msgtype'] = 'news';
+ $this->msgData['news[articles][0][title]'] = $title;
+ $this->msgData['news[articles][0][description]'] = $description;
+ $this->msgData['news[articles][0][url]'] = $url;
+ $this->msgData['news[articles][0][picurl]'] = $picurl;
+
+ return $this->sendKefuMsg();
+ }
+
+
+ /**
+ * @param string $title
+ * @return Result
+ * @throws \Exception
+ */
+ public function sendCardNews(string $title)
+ {
+ $this->msgData['msgtype'] = 'wxcard';
+ $this->msgData['wxcard[card_id]'] = $title;
+
+ return $this->sendKefuMsg();
+ }
+
+
+ /**
+ * @param string $media_id
+ * @param string $thumb_media_id
+ * @param string $title
+ * @param string $description
+ * @return Result
+ * @throws \Exception
+ */
+ public function sendVideoNews(string $media_id, string $thumb_media_id, string $title, string $description)
+ {
+ $this->msgData['msgtype'] = 'video';
+ $this->msgData['video[media_id]'] = $media_id;
+ $this->msgData['video[thumb_media_id]'] = $thumb_media_id;
+ $this->msgData['video[title]'] = $title;
+ $this->msgData['video[description]'] = $description;
+
+ return $this->sendKefuMsg();
+ }
+
+
+ /**
+ * @param string $musicurl
+ * @param string $hqmusicurl
+ * @param string $thumb_media_id
+ * @param string $title
+ * @param string $description
+ * @return Result
+ * @throws \Exception
+ */
+ public function sendMusicNews(string $musicurl, string $hqmusicurl, string $thumb_media_id, string $title, string $description)
+ {
+ $this->msgData['msgtype'] = 'music';
+ $this->msgData['music[title]'] = $title;
+ $this->msgData['music[description]'] = $description;
+ $this->msgData['music[musicurl]'] = $musicurl;
+ $this->msgData['music[hqmusicurl]'] = $hqmusicurl;
+ $this->msgData['music[thumb_media_id]'] = $thumb_media_id;
+
+ return $this->sendKefuMsg();
+ }
+
+
+ /**
+ * @param string $head_content
+ * @param string $tail_content
+ * @param array $menus
+ * @return Result
+ * @throws \Exception
+ */
+ public function sendMenuNews(string $head_content, string $tail_content, array $menus = [])
+ {
+ $this->msgData['msgtype'] = 'msgmenu';
+ $this->msgData['msgmenu[head_content]'] = $head_content;
+ $this->msgData['msgmenu[tail_content]'] = $tail_content;
+
+ if (empty($menus) || !is_array($menus) || count($menus) < 2) {
+ throw new \Exception('菜单选项必须有2个');
+ }
+
+ foreach ($menus as $key => $val) {
+ $this->addNewsMenu($val['id'], $val['name']);
+ }
+
+ return $this->sendKefuMsg();
+ }
+
+ private $index = 0;
+
+ /**
+ * @param $id
+ * @param $menuName
+ * @return $this
+ */
+ public function addNewsMenu($id, $menuName)
+ {
+ $this->msgData['msgmenu[list][' . $this->index . '][id]'] = $id;
+ $this->msgData['msgmenu[list][' . $this->index . '][content]'] = $menuName;
+
+ ++$this->index;
+
+ return $this;
+ }
+
+ /**
+ * @param $title
+ * @param $appid
+ * @param $pagepath
+ * @param $thumb_media_id
+ * @return Result
+ * @throws \Exception
+ */
+ public function sendMiniprogrampageNews(string $title, string $appid, string $pagepath, string $thumb_media_id)
+ {
+ $this->msgData['msgtype'] = 'msgmenu';
+ $this->msgData['miniprogrampage[title]'] = $title;
+ $this->msgData['miniprogrampage[appid]'] = $appid;
+ $this->msgData['miniprogrampage[pagepath]'] = $pagepath;
+ $this->msgData['miniprogrampage[thumb_media_id]'] = $thumb_media_id;
+
+ return $this->sendKefuMsg();
+ }
+
+ /**
+ * @param string $filePath
+ * @param string $type
+ * @param bool $isPermanent
+ * @param string $title
+ * @param string $introduction
+ * @return mixed
+ * @throws \Exception
+ */
+ public function upload(string $filePath, string $type, $isPermanent = false, string $title = '', string $introduction = '')
+ {
+ if (!file_exists($filePath)) {
+ throw new \Exception('文件不存在');
+ }
+
+ if (!in_array($type, ['image', 'voice', 'video', 'thumb'])) {
+ throw new \Exception('暂不支持的文件类型');
+ }
+
+ $token = $this->getAccessToken();
+ if ($isPermanent) {
+ $url = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token={$token}&type={$type}";
+ } else {
+ $url = "https://api.weixin.qq.com/cgi-bin/media/upload?access_token={$token}&type={$type}";
+ }
+
+ $mime = mime_content_type($filePath);
+
+ $real_path = new \CURLFile(realpath($filePath));
+
+ $data = array("media" => $real_path, 'form-data[filename]' => $filePath, 'form-data[content-type]' => $mime);
+ if ($isPermanent && $mime == 'video/mp3') {
+ $data = ['media' => $real_path, 'description[title]' => $title, 'description[introduction]' => $introduction];
+ }
+
+ $this->request->setMethod(WxClient::POST);
+
+ /** @var Result $body */
+ $data = $this->request->post($url, $data);
+ if (!$data->isResultsOK()) {
+ throw new \Exception($data->getMessage());
+ }
+
+ return $data->getData();
+ }
+
+ /**
+ * @param $mime
+ * @throws \Exception
+ */
+ private function checkExtinfo($mime)
+ {
+ switch (strtolower($mime)) {
+ case 'image/bmp':
+ case 'image/png':
+ case 'image/jpeg':
+ case 'image/jpg':
+ case 'image/gif':
+ break;
+ case 'mp3/wma/wav/amr':
+ break;
+ case 'mp4';
+ break;
+ case 'jpg';
+ break;
+ default:
+ throw new \Exception('不支持的文件格式');
+ }
+ }
+
+ /**
+ * @param $data
+ * @return Result
+ * @throws \Exception
+ */
+ private function sendKefuMsg()
+ {
+ $data = json_encode($this->msgData, JSON_UNESCAPED_UNICODE);
+
+ $url = '/cgi-bin/message/custom/send?access_token=' . $this->getAccessToken();
+ $this->request->setMethod(WxClient::POST);
+
+ /** @var Result $body */
+ $body = $this->request->post($url, $data);
+
+ if (!$body->isResultsOK()) {
+ throw new \Exception($body->getMessage());
+ }
+ return $body;
+ }
+}
diff --git a/qq/Notify.php b/qq/Notify.php
new file mode 100644
index 0000000..1e624e4
--- /dev/null
+++ b/qq/Notify.php
@@ -0,0 +1,600 @@
+getReturnCode() != "SUCCESS") {
+ return false;
+ }
+
+ if ($this->getResultCode() != 'SUCCESS') {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param array $params
+ * @return $this
+ */
+ public function setPayNotifyData(array $params)
+ {
+ if (!$this->validation($params)) {
+ $this->setResultCode('FAIL');
+ $this->setErrCodeDes('签名错误');
+ unset($params['result_code'], $params['err_code_des']);
+ }
+ foreach ($params as $key => $val) {
+ if (!property_exists($this, $key)) {
+ continue;
+ }
+ $this->$key = $val;
+ }
+ return $this;
+ }
+
+
+ /**
+ * @param array $params
+ * @return bool
+ */
+ public function validation(array $params)
+ {
+ $sign = $params['sign'];
+ unset($params['sign']);
+
+ $signType = $this->config->getSignType();
+ $privateKey = $this->config->getKey();
+ $nowSign = Help::sign($params, $privateKey, $signType);
+ if ($sign === $nowSign) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getAppid()
+ {
+ return $this->appid;
+ }
+
+ /**
+ * @param mixed $appid
+ * @return Notify
+ */
+ public function setAppid($appid)
+ {
+ $this->appid = $appid;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getMchId()
+ {
+ return $this->mch_id;
+ }
+
+ /**
+ * @param mixed $mch_id
+ * @return Notify
+ */
+ public function setMchId($mch_id)
+ {
+ $this->mch_id = $mch_id;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getDeviceInfo()
+ {
+ return $this->device_info;
+ }
+
+ /**
+ * @param mixed $device_info
+ * @return Notify
+ */
+ public function setDeviceInfo($device_info)
+ {
+ $this->device_info = $device_info;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getNonceStr()
+ {
+ return $this->nonce_str;
+ }
+
+ /**
+ * @param mixed $nonce_str
+ * @return Notify
+ */
+ public function setNonceStr($nonce_str)
+ {
+ $this->nonce_str = $nonce_str;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getSign()
+ {
+ return $this->sign;
+ }
+
+ /**
+ * @param mixed $sign
+ * @return Notify
+ */
+ public function setSign($sign)
+ {
+ $this->sign = $sign;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getSignType()
+ {
+ return $this->sign_type;
+ }
+
+ /**
+ * @param mixed $sign_type
+ * @return Notify
+ */
+ public function setSignType($sign_type)
+ {
+ $this->sign_type = $sign_type;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getResultCode()
+ {
+ return $this->result_code;
+ }
+
+ /**
+ * @param mixed $result_code
+ * @return Notify
+ */
+ public function setResultCode($result_code)
+ {
+ $this->result_code = $result_code;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getErrCode()
+ {
+ return $this->err_code;
+ }
+
+ /**
+ * @param mixed $err_code
+ * @return Notify
+ */
+ public function setErrCode($err_code)
+ {
+ $this->err_code = $err_code;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getErrCodeDes()
+ {
+ return $this->err_code_des;
+ }
+
+ /**
+ * @param mixed $err_code_des
+ * @return Notify
+ */
+ public function setErrCodeDes($err_code_des)
+ {
+ $this->err_code_des = $err_code_des;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getOpenid()
+ {
+ return $this->openid;
+ }
+
+ /**
+ * @param mixed $openid
+ * @return Notify
+ */
+ public function setOpenid($openid)
+ {
+ $this->openid = $openid;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getIsSubscribe()
+ {
+ return $this->is_subscribe;
+ }
+
+ /**
+ * @param mixed $is_subscribe
+ * @return Notify
+ */
+ public function setIsSubscribe($is_subscribe)
+ {
+ $this->is_subscribe = $is_subscribe;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getTradeType()
+ {
+ return $this->trade_type;
+ }
+
+ /**
+ * @param mixed $trade_type
+ * @return Notify
+ */
+ public function setTradeType($trade_type)
+ {
+ $this->trade_type = $trade_type;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getBankType()
+ {
+ return $this->bank_type;
+ }
+
+ /**
+ * @param mixed $bank_type
+ * @return Notify
+ */
+ public function setBankType($bank_type)
+ {
+ $this->bank_type = $bank_type;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getTotalFee()
+ {
+ return $this->total_fee;
+ }
+
+ /**
+ * @param mixed $total_fee
+ * @return Notify
+ */
+ public function setTotalFee($total_fee)
+ {
+ $this->total_fee = $total_fee;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getSettlementTotalFee()
+ {
+ return $this->settlement_total_fee;
+ }
+
+ /**
+ * @param mixed $settlement_total_fee
+ * @return Notify
+ */
+ public function setSettlementTotalFee($settlement_total_fee)
+ {
+ $this->settlement_total_fee = $settlement_total_fee;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getFeeType()
+ {
+ return $this->fee_type;
+ }
+
+ /**
+ * @param mixed $fee_type
+ * @return Notify
+ */
+ public function setFeeType($fee_type)
+ {
+ $this->fee_type = $fee_type;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCashFee()
+ {
+ return $this->cash_fee;
+ }
+
+ /**
+ * @param mixed $cash_fee
+ * @return Notify
+ */
+ public function setCashFee($cash_fee)
+ {
+ $this->cash_fee = $cash_fee;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCashFeeType()
+ {
+ return $this->cash_fee_type;
+ }
+
+ /**
+ * @param mixed $cash_fee_type
+ * @return Notify
+ */
+ public function setCashFeeType($cash_fee_type)
+ {
+ $this->cash_fee_type = $cash_fee_type;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCouponFee()
+ {
+ return $this->coupon_fee;
+ }
+
+ /**
+ * @param mixed $coupon_fee
+ * @return Notify
+ */
+ public function setCouponFee($coupon_fee)
+ {
+ $this->coupon_fee = $coupon_fee;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCouponCount()
+ {
+ return $this->coupon_count;
+ }
+
+ /**
+ * @param mixed $coupon_count
+ * @return Notify
+ */
+ public function setCouponCount($coupon_count)
+ {
+ $this->coupon_count = $coupon_count;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCouponTypeN()
+ {
+ return $this->coupon_type_n;
+ }
+
+ /**
+ * @param mixed $coupon_type_n
+ * @return Notify
+ */
+ public function setCouponTypeN($coupon_type_n)
+ {
+ $this->coupon_type_n = $coupon_type_n;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCouponIdN()
+ {
+ return $this->coupon_id_n;
+ }
+
+ /**
+ * @param mixed $coupon_id_n
+ * @return Notify
+ */
+ public function setCouponIdN($coupon_id_n)
+ {
+ $this->coupon_id_n = $coupon_id_n;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCouponFeeN()
+ {
+ return $this->coupon_fee_n;
+ }
+
+ /**
+ * @param mixed $coupon_fee_n
+ * @return Notify
+ */
+ public function setCouponFeeN($coupon_fee_n)
+ {
+ $this->coupon_fee_n = $coupon_fee_n;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getTransactionId()
+ {
+ return $this->transaction_id;
+ }
+
+ /**
+ * @param mixed $transaction_id
+ * @return Notify
+ */
+ public function setTransactionId($transaction_id)
+ {
+ $this->transaction_id = $transaction_id;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getOutTradeNo()
+ {
+ return $this->out_trade_no;
+ }
+
+ /**
+ * @param mixed $out_trade_no
+ * @return Notify
+ */
+ public function setOutTradeNo($out_trade_no)
+ {
+ $this->out_trade_no = $out_trade_no;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getAttach()
+ {
+ return $this->attach;
+ }
+
+ /**
+ * @param mixed $attach
+ * @return Notify
+ */
+ public function setAttach($attach)
+ {
+ $this->attach = $attach;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getTimeEnd()
+ {
+ return $this->time_end;
+ }
+
+ /**
+ * @param mixed $time_end
+ * @return Notify
+ */
+ public function setTimeEnd($time_end)
+ {
+ $this->time_end = $time_end;
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getReturnCode()
+ {
+ return $this->return_code;
+ }
+
+ /**
+ * @param mixed $return_code
+ * @return Notify
+ */
+ public function setReturnCode($return_code)
+ {
+ $this->return_code = $return_code;
+ return $this;
+ }
+
+}
diff --git a/qq/PublicTemplate.php b/qq/PublicTemplate.php
new file mode 100644
index 0000000..7044d30
--- /dev/null
+++ b/qq/PublicTemplate.php
@@ -0,0 +1,157 @@
+keywords = $keywords;
+ }
+
+ /**
+ * @param string $templateId
+ */
+ public function setTemplateId(string $templateId)
+ {
+ $this->templateId = $templateId;
+ }
+
+ /**
+ * @param string $openId
+ */
+ public function setOpenId(string $openId)
+ {
+ $this->openId = $openId;
+ }
+
+ /**
+ * @param string $defaultUrl
+ */
+ public function setDefaultUrl(string $defaultUrl)
+ {
+ $this->defaultUrl = $defaultUrl;
+ }
+
+ /**
+ * @param $name
+ * @param $context
+ * @param string $color
+ */
+ public function replaceKeyword($name, $context, $color = '')
+ {
+ $this->keywords[$name] = ['value' => $context, 'color' => $color];
+ }
+
+
+ /**
+ * @param $name
+ * @param $context
+ * @param null $color
+ */
+ public function addKeyword($name, $context, $color = null)
+ {
+ if (empty($color)) {
+ $color = '#000';
+ }
+ $this->keywords[$name] = [
+ 'value' => $context,
+ 'color' => $color
+ ];
+ }
+
+ /**
+ * @param $context
+ * @param string $color
+ */
+ public function setFirst($context, $color = '#f00')
+ {
+ $this->first = [
+ 'value' => $context,
+ 'color' => $color
+ ];
+ }
+
+ /**
+ * @param $context
+ * @param string $color
+ */
+ public function setRemark($context, $color = '#000')
+ {
+ $this->remark = [
+ 'value' => $context,
+ 'color' => $color
+ ];
+ }
+
+ /**
+ * @param $appid
+ * @param $pagepath
+ */
+ public function setMiniprogram($appid, $pagepath)
+ {
+ $this->miniprogram = [
+ 'appid' => $appid,
+ 'pagepath' => $pagepath
+ ];
+ }
+
+ /**
+ * @return Result
+ * @throws \Exception
+ *
+ * 奴隶交易通知
+ */
+ public function sendTemplate()
+ {
+ $url = $this->sendUrl . '?access_token=' . $this->getAccessToken();
+
+ $keywords = $this->keywords;
+ $keywords['first'] = $this->first;
+ $keywords['remark'] = $this->remark;
+
+ $default = [
+ "touser" => $this->openId,
+ "template_id" => $this->templateId,
+ "url" => $this->defaultUrl,
+ "data" => $keywords,
+ ];
+
+ if (!empty($this->miniprogram)) {
+ $default['miniprogram'] = $this->miniprogram;
+ }
+
+ $params = json_encode($default, JSON_UNESCAPED_UNICODE);
+
+ $this->request->setIsSSL(true);
+ $this->request->addHeader('content-type', 'application/json');
+
+ $result = $this->request->post($url, $params);
+ $result->append('postBody', $params);
+
+ return $result;
+ }
+}
diff --git a/qq/Recharge.php b/qq/Recharge.php
new file mode 100644
index 0000000..e83e723
--- /dev/null
+++ b/qq/Recharge.php
@@ -0,0 +1,182 @@
+ 500, 'message' => '充值金额不能小于0.']);
+ }
+ $this->money = $money;
+ $this->orderNo = $orderNo;
+ $this->data['openid'] = $openId;
+
+ $config = $this->config;
+ $this->request->setCallback(function ($result, $body) use ($config) {
+ $data = Help::toArray($result);
+ if (isset($data['sign'])) {
+ $sign = $data['sign'];
+ unset($data['sign']);
+ }
+ $return = [];
+ $_sign = Help::sign($data, $config->getKey(), $config->getSignType());
+ if (!isset($sign) || $sign != $_sign) {
+ $return['code'] = -1;
+ $return['message'] = $data['return_msg'] ?? '返回数据签名验证失败';
+ } else {
+ $return['code'] = 0;
+ $return['data'] = $data;
+ $return['data']['postBody'] = $body;
+ if ($data['return_code'] == 'FAIL') {
+ $return['code'] = -1;
+ $return['message'] = $data['return_msg'];
+ }
+ }
+ return $return;
+ });
+ return $this->send($this->unifiedorder, $this->builder());
+ }
+
+ /**
+ * 'appId' => $result['appid'],
+ * 'nonceStr' => $result['nonce_str'],
+ * 'package' => 'prepay_id=' . $result['prepay_id'],
+ * 'signType' => 'MD5',
+ * 'timeStamp' => (string)time(),
+ * @param $prepay_id
+ * @return array
+ */
+ public function reception($prepay_id)
+ {
+ $array = [
+ 'appId' => $this->config->getAppid(),
+ 'nonceStr' => Help::random(32),
+ 'package' => 'prepay_id=' . $prepay_id,
+ 'signType' => 'MD5',
+ 'timeStamp' => (string)time(),
+ ];
+ $key = $this->config->getKey();
+ $sign_type = $this->config->getSignType();
+ $array['paySign'] = Help::sign($array, $key, $sign_type);
+ $array['appId'] = $this->config->getAppid();
+ return $array;
+ }
+
+ /**
+ * @return string
+ */
+ protected function builder()
+ {
+ $data = [
+ 'appid' => $this->config->getAppid(),
+ 'mch_id' => $this->config->getMchId(),
+ 'nonce_str' => Help::random(32),
+ 'body' => $this->config->getBody(),
+ 'out_trade_no' => $this->orderNo,
+ 'total_fee' => $this->money,
+ 'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
+ 'notify_url' => $this->config->getNotifyUrl(),
+ 'trade_type' => $this->config->getTradeType(),
+ ];
+
+ $this->data = array_merge($data, $this->data);
+
+ $key = $this->config->getKey();
+ $sign_type = $this->config->getSignType();
+
+ $this->data['sign_type'] = $this->config->getSignType();
+ $this->data['sign'] = Help::sign($this->data, $key, $sign_type);
+
+ return Help::toXml($this->data);
+ }
+
+ public function getSignData()
+ {
+ $data = $this->data;
+ $data['key'] = $this->config->getKey();
+ return $data;
+ }
+
+ /**
+ * @param $money
+ * @param $openid
+ * @param $order
+ * @param $desc
+ * @return Result
+ * @throws
+ *
+ * 提现
+ */
+ public function cashWithdrawal($money, $openid, $order, $desc = '零钱提现')
+ {
+ $array = [
+ 'nonce_str' => Help::random(32),
+ 'partner_trade_no' => $order,
+ 'mchid' => $this->config->getMchId(),
+ 'mch_appid' => $this->config->getAppid(),
+ 'openid' => $openid,
+ 'check_name' => 'NO_CHECK',
+ 'amount' => $money * 100,
+ 'spbill_create_ip' => $this->config->getRemoteAddr(),
+ 'desc' => $desc,
+ ];
+
+ $key = $this->config->getKey();
+ $sign_type = $this->config->getSignType();
+ $array['sign'] = Help::sign($array, $key, $sign_type);
+
+ $this->request->setCallback(function ($data) {
+ $array = Help::toArray($data);
+ if ($array['result_code'] != 'SUCCESS') {
+ $data = ['code' => $array['err_code'], 'message' => $array['err_code_des']];
+ } else {
+ $data = ['code' => 0, 'message' => '支付成功'];
+ }
+ return new Result($data);
+ });
+ return $this->send($this->transfers, Help::toXml($array));
+ }
+
+ /**
+ * @param $url
+ * @param $data
+ * @return array|mixed|Result
+ * @throws \Exception
+ */
+ private function send($url, $data)
+ {
+ $this->request->setIsSSL(true);
+ $this->request->setMethod(WxClient::POST);
+ $this->request->addHeader('Content-Type', 'text/xml');
+ return $this->request->send($url, $data);
+ }
+}
diff --git a/qq/Template.php b/qq/Template.php
new file mode 100644
index 0000000..ef58ece
--- /dev/null
+++ b/qq/Template.php
@@ -0,0 +1,155 @@
+keywords = $keywords;
+ }
+
+ /**
+ * @param string $templateId
+ */
+ public function setTemplateId(string $templateId)
+ {
+ $this->templateId = $templateId;
+ }
+
+ /**
+ * @param string $formId
+ */
+ public function setFormId(string $formId)
+ {
+ $this->formId = $formId;
+ }
+
+ /**
+ * @param string $openId
+ */
+ public function setOpenId(string $openId)
+ {
+ $this->openId = $openId;
+ }
+
+ /**
+ * @param string $defaultUrl
+ */
+ public function setDefaultUrl(string $defaultUrl)
+ {
+ $this->defaultUrl = $defaultUrl;
+ }
+
+ /**
+ * @param string $page
+ */
+ public function setPage(string $page)
+ {
+ $this->page = $page;
+ }
+
+ /**
+ * @param string $emphasis_keyword
+ */
+ public function setEmphasisKeyword(string $emphasis_keyword)
+ {
+ $this->emphasis_keyword = $emphasis_keyword;
+ }
+
+ /**
+ * @param $index
+ * @param $context
+ * @param $color
+ */
+ public function replaceKeyword($index, $context, $color = '')
+ {
+ if (empty($color)) {
+ $color = '#000';
+ }
+ $this->keywords['keyword' . $index] = [
+ 'value' => $context,
+ 'color' => $color
+ ];
+ }
+
+
+ /**
+ * @param $color
+ * @param $context
+ */
+ public function addKeyword($context, $color = null)
+ {
+ if (empty($color)) {
+ $color = '#000';
+ }
+ $this->keywords['keyword' . (count($this->keywords) + 1)] = [
+ 'value' => $context,
+ 'color' => $color
+ ];
+ }
+
+ /**
+ * @return Result
+ * @throws \Exception
+ *
+ * 奴隶交易通知
+ */
+ public function sendTemplate()
+ {
+ $url = $this->sendUrl . '?access_token=' . $this->config->getAccessToken();
+
+ $params = json_encode([
+ "touser" => $this->openId,
+ "template_id" => $this->templateId,
+ "page" => $this->page,
+ "form_id" => $this->formId,
+ "data" => $this->keywords,
+ ], JSON_UNESCAPED_UNICODE);
+
+ if (!empty($this->emphasis_keyword)) {
+ $params['emphasis_keyword'] = $this->emphasis_keyword;
+ }
+
+ $this->request->setIsSSL(true);
+ $this->request->addHeader('content-type', 'application/json');
+
+ $result = $this->request->post($url, $params);
+ $result->append('postBody', $params);
+
+ $this->openId = '';
+ $this->keywords = [];
+ $this->formId = '';
+ $this->templateId = '';
+ $this->page = '';
+
+ return $result;
+ }
+}
diff --git a/qq/Token.php b/qq/Token.php
new file mode 100644
index 0000000..b7c3070
--- /dev/null
+++ b/qq/Token.php
@@ -0,0 +1,32 @@
+ 'client_credential',
+ 'appid' => $this->config->getAppid(),
+ 'secret' => $this->config->getAppsecret()
+ ];
+ $param = $this->request->get($this->url, $query);
+ if (!$param->isResultsOK()) {
+ return null;
+ }
+ return $param->getData('access_token');
+ }
+
+}
diff --git a/qq/Wx.php b/qq/Wx.php
new file mode 100644
index 0000000..202f969
--- /dev/null
+++ b/qq/Wx.php
@@ -0,0 +1,116 @@
+config;
+ }
+
+
+ /**
+ * @param Config $config
+ * @return $this
+ */
+ public function setConfig(Config $config)
+ {
+ $this->config = $config;
+ return $this;
+ }
+
+ /**
+ * @return Template
+ */
+ public function getTemplate()
+ {
+ return Template::getInstance($this->config);
+ }
+
+
+ /**
+ * @return PublicTemplate
+ */
+ public function getPublicTemplate()
+ {
+ return PublicTemplate::getInstance($this->config);
+ }
+
+ /**
+ * @return Account
+ */
+ public function getAccount()
+ {
+ return Account::getInstance($this->config);
+ }
+
+ /**
+ * @return Message
+ */
+ public function getMessage()
+ {
+ return Message::getInstance($this->config);
+ }
+
+ /**
+ * @return Recharge
+ */
+ public function getRecharge()
+ {
+ return Recharge::getInstance($this->config);
+ }
+
+ /**
+ * @return Notify
+ */
+ public function getNotify()
+ {
+ return Notify::getInstance($this->config);
+ }
+
+ /**
+ * @return WxClient
+ */
+ public function getClient()
+ {
+ $client = WxClient::getInstance();
+ $client->setAgent($this->config->getAgent());
+ return $client;
+ }
+
+
+ /**
+ * @return WxClient
+ */
+ public function getAccessToken()
+ {
+ return Token::getInstance($this->config);
+ }
+
+}
diff --git a/wx/Template.php b/wx/Template.php
index 4eeff22..2f13aff 100644
--- a/wx/Template.php
+++ b/wx/Template.php
@@ -118,14 +118,13 @@ class Template extends Miniprogarampage
*/
public function sendTemplate()
{
- $url = $this->sendUrl . '?access_token=' . $this->getAccessToken();
-
+ $url = $this->sendUrl . '?access_token=' . $this->config->getAccessToken();
$params = json_encode([
- "touser" => $this->openId,
+ "touser" => $this->openId,
"template_id" => $this->templateId,
- "page" => $this->page,
- "form_id" => $this->formId,
- "data" => $this->keywords,
+ "page" => $this->page,
+ "form_id" => $this->formId,
+ "data" => $this->keywords,
], JSON_UNESCAPED_UNICODE);
if (!empty($this->emphasis_keyword)) {
diff --git a/wx/Token.php b/wx/Token.php
new file mode 100644
index 0000000..ade606d
--- /dev/null
+++ b/wx/Token.php
@@ -0,0 +1,31 @@
+ 'client_credential',
+ 'appid' => $this->config->getAppid(),
+ 'secret' => $this->config->getAppsecret()
+ ];
+ $param = $this->request->get($this->url, $query);
+ if (!$param->isResultsOK()) {
+ return null;
+ }
+ return $param->getData('access_token');
+ }
+
+
+}
diff --git a/wx/Wx.php b/wx/Wx.php
index 02d3dfd..f6b73d8 100644
--- a/wx/Wx.php
+++ b/wx/Wx.php
@@ -92,6 +92,15 @@ class Wx
return Notify::getInstance($this->config);
}
+
+ /**
+ * @return Token
+ */
+ public function getToken()
+ {
+ return Token::getInstance($this->config);
+ }
+
/**
* @return WxClient
*/