|
@@ -0,0 +1,500 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+|--------------------------------------------------------------------------
|
|
|
+| core.php 微信平台核心类
|
|
|
+|--------------------------------------------------------------------------
|
|
|
+*/
|
|
|
+namespace Token\Lib;
|
|
|
+
|
|
|
+use Dever;
|
|
|
+
|
|
|
+class Wechat
|
|
|
+{
|
|
|
+
|
|
|
+ * config
|
|
|
+ *
|
|
|
+ * @var array
|
|
|
+ */
|
|
|
+ private $config;
|
|
|
+
|
|
|
+
|
|
|
+ * project
|
|
|
+ *
|
|
|
+ * @var string
|
|
|
+ */
|
|
|
+ private $project;
|
|
|
+
|
|
|
+
|
|
|
+ * alert
|
|
|
+ *
|
|
|
+ * @var int
|
|
|
+ */
|
|
|
+ private $alert;
|
|
|
+
|
|
|
+
|
|
|
+ * 构造函数 初始化
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ */
|
|
|
+ public function __construct($project = false, $type = '')
|
|
|
+ {
|
|
|
+ $this->config = Dever::config('wechat', $type)->cAll;
|
|
|
+ $type = $this->config['type'];
|
|
|
+
|
|
|
+ if (!$project) {
|
|
|
+ $appid = Dever::input('appid');
|
|
|
+ if ($appid) {
|
|
|
+ $project = Dever::db('token/project')->one(array('option_type' => $type, 'option_appid' => $appid));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if (!$project) {
|
|
|
+ $project = Dever::db('token/project')->one(array('option_type' => $type));
|
|
|
+ }
|
|
|
+ */
|
|
|
+
|
|
|
+ if (!$project) {
|
|
|
+ $project = Dever::input('project', 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (is_numeric($project)) {
|
|
|
+ $project = Dever::db('token/project')->one(array('option_type' => $type, 'option_id' => $project));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!$project) {
|
|
|
+ Dever::alert('project is not exits!');
|
|
|
+ }
|
|
|
+
|
|
|
+ $this->project = $project;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 获取当前站点的配置
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ */
|
|
|
+ public function project()
|
|
|
+ {
|
|
|
+ return $this->project;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 获取当前基本的配置
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ */
|
|
|
+ public function config()
|
|
|
+ {
|
|
|
+ return $this->config;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 更新数据库
|
|
|
+ * state true为强制更新数据库中的token数据
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ */
|
|
|
+ private function save($type = 'ticket', $value, $expires = false, $interval = 2000, $data = false, $state = false)
|
|
|
+ {
|
|
|
+ $refresh = false;
|
|
|
+ if (strpos($type, '.')) {
|
|
|
+ $temp = explode('.', $type);
|
|
|
+ if ($temp[1] == 'refresh') {
|
|
|
+ $refresh = true;
|
|
|
+ $temp[1] = 'oauth';
|
|
|
+ }
|
|
|
+ $table = 'token/' . $temp[1];
|
|
|
+ } else {
|
|
|
+ $table = 'token/' . $type;
|
|
|
+ }
|
|
|
+ $db = Dever::db($table);
|
|
|
+
|
|
|
+ if ($data) {
|
|
|
+ if (isset($data['id'])) {
|
|
|
+ $where['option_id'] = $data['id'];
|
|
|
+ unset($data['id']);
|
|
|
+ }
|
|
|
+ if (isset($data['openid'])) {
|
|
|
+ $where['option_openid'] = $data['openid'];
|
|
|
+ }
|
|
|
+ if (isset($data['unionid'])) {
|
|
|
+ $where['option_unionid'] = $data['unionid'];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $where['option_project_id'] = $this->project['id'];
|
|
|
+ $info = $db->one($where);
|
|
|
+
|
|
|
+ $update = false;
|
|
|
+
|
|
|
+ if ($state == true) {
|
|
|
+ $update = true;
|
|
|
+ } elseif ($info && time() - $info['mdate'] >= $info['expires']) {
|
|
|
+ $update = true;
|
|
|
+ } elseif($info) {
|
|
|
+ return $info;
|
|
|
+ }
|
|
|
+ if (!$info) {
|
|
|
+ $update = false;
|
|
|
+ }
|
|
|
+ if (!$value) {
|
|
|
+ $value = $this->param($type, $info);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (is_array($value) || (is_string($value) && strstr($value, 'http://'))) {
|
|
|
+ if ($refresh) {
|
|
|
+ $result = $this->curl(false, $value, false, $type);
|
|
|
+ } else {
|
|
|
+ $result = $this->curl(false, $value, true, $type);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ $key = $type;
|
|
|
+ if ($result && isset($result[$key])) {
|
|
|
+ $data = $result;
|
|
|
+ $data['value'] = $result[$key];
|
|
|
+ $data['expires'] = $result['expires_in'];
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ $data['value'] = $value;
|
|
|
+ $data['expires'] = $expires;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($data && isset($data['value']) && $data['value']) {
|
|
|
+ $data['project_id'] = $this->project['id'];
|
|
|
+ $expires = $data['expires'] - $interval;
|
|
|
+ if ($expires <= 0) {
|
|
|
+ $data['expires'] = $data['expires'] - 300;
|
|
|
+ } else {
|
|
|
+ $data['expires'] = $expires;
|
|
|
+ }
|
|
|
+ if ($update == true) {
|
|
|
+ $data['where_id'] = $info['id'];
|
|
|
+ $id = $info['id'];
|
|
|
+ $db->update($data);
|
|
|
+ } else {
|
|
|
+ $id = $db->insert($data);
|
|
|
+ }
|
|
|
+ $data['id'] = $id;
|
|
|
+ if ($id > 0 && isset($result['callback'])) {
|
|
|
+ foreach ($result['callback'] as $v) {
|
|
|
+ Dever::load($v[0], $id, $v[1], $this->project['id']);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } elseif($info && $info['value']) {
|
|
|
+ $data = $info;
|
|
|
+ $data['value'] = $info['value'];
|
|
|
+ }
|
|
|
+
|
|
|
+ return $data;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 获取最新的token
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ */
|
|
|
+ public function token($value = false, $expires = false, $interval = 2000, $state = false)
|
|
|
+ {
|
|
|
+ $result = $this->save('token', $value, $expires, $interval, false, $state);
|
|
|
+ return $result['value'];
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 获取最新的ticket
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ */
|
|
|
+ public function ticket($value = false, $expires = false, $interval = 200, $state = false)
|
|
|
+ {
|
|
|
+ $result = $this->save('ticket', $value, $expires, $interval, false, $state);
|
|
|
+ return $result['value'];
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 获取最新的oauth token
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ */
|
|
|
+ public function oauth($param, $interval = 2000, $state = false)
|
|
|
+ {
|
|
|
+ if (is_array($param) && isset($param['auth_code'])) {
|
|
|
+ $result = $this->curl('oauth.oauth', $param);
|
|
|
+ if (isset($result['oauth.oauth'])) {
|
|
|
+ return $this->save('oauth.oauth', $result['oauth.oauth'], $result['expires_in'], $interval, $result);
|
|
|
+ } else {
|
|
|
+ Dever::alert('oauth error');
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (is_numeric($param)) {
|
|
|
+ $id = $param;
|
|
|
+ $param = array();
|
|
|
+ $param['id'] = $id;
|
|
|
+ }
|
|
|
+ return $this->save('oauth.refresh', false, false, $interval, $param, $state);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 获取最新的code
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ */
|
|
|
+ public function code($value = false, $expires = false, $interval = 200)
|
|
|
+ {
|
|
|
+ $result = $this->save('oauth.code', $value, $expires, $interval, false, true);
|
|
|
+ return $result['value'];
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 获取最新的openid
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ */
|
|
|
+ public function openid($id, $key = 'openid')
|
|
|
+ {
|
|
|
+ $info = Dever::db('token/oauth')->one($id);
|
|
|
+ return $info[$key];
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 获取最新的oauth login地址
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ */
|
|
|
+ public function login($callback = false, $code = false, $location = true)
|
|
|
+ {
|
|
|
+ if (!$code) {
|
|
|
+ $code = $this->code();
|
|
|
+ }
|
|
|
+ $callback = Dever::url($callback);
|
|
|
+ $config = $this->param('oauth.login', array('code' => $code, 'redirect' => $callback));
|
|
|
+ $url = $config['url'] . http_build_query($config['param']);
|
|
|
+ if ($location == true) {
|
|
|
+ Dever::location($url);
|
|
|
+ }
|
|
|
+ return $url;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 从wechat获取数据
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ */
|
|
|
+ public function curl($method, $param = array(), $alert = true, $type = false)
|
|
|
+ {
|
|
|
+ if (!$this->alert) {
|
|
|
+ if (!$alert) {
|
|
|
+ $this->alert = 1;
|
|
|
+ } else {
|
|
|
+ $this->alert = 2;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if (is_string($param)) {
|
|
|
+ $result = json_decode(Dever::curl($param), true);
|
|
|
+ } else {
|
|
|
+ if ($method) {
|
|
|
+ $param = $this->param($method, $param);
|
|
|
+ }
|
|
|
+
|
|
|
+ $result = json_decode(Dever::curl($param['url'], $param['param'], $param['method'], $param['json']), true);
|
|
|
+ $this->log($result, $param['name'], $param['url'], $param['param']);
|
|
|
+ }
|
|
|
+ if (isset($result['errcode']) && $result['errcode'] != 0) {
|
|
|
+
|
|
|
+ if ($result['errcode'] == 40001) {
|
|
|
+ $token = $this->token(false, false, 2000, true);
|
|
|
+ return $this->curl($type, array(), $alert);
|
|
|
+ }
|
|
|
+ */
|
|
|
+ $result = $param + $result;
|
|
|
+ Dever::log($result);
|
|
|
+ if ($alert && $this->alert == 2) {
|
|
|
+ Dever::alert(json_encode($result, JSON_UNESCAPED_UNICODE));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($param['response'])) {
|
|
|
+ foreach ($param['response'] as $k => $v) {
|
|
|
+ if (strpos($k, '.')) {
|
|
|
+ $temp = explode('.', $k);
|
|
|
+ if (isset($result[$temp[0]][$temp[1]])) {
|
|
|
+ $result[$v] = $result[$temp[0]][$temp[1]];
|
|
|
+ }
|
|
|
+ } elseif (isset($result[$k])) {
|
|
|
+ $result[$v] = $result[$k];
|
|
|
+ }
|
|
|
+
|
|
|
+ if (strpos($v, 'callback.') !== false) {
|
|
|
+ $temp = explode('callback.', $v);
|
|
|
+ $result['callback'][] = array($temp[1], $result[$v]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return $result;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function log($result, $name, $url, $param)
|
|
|
+ {
|
|
|
+ $insert['name'] = $name;
|
|
|
+ $insert['url'] = $url;
|
|
|
+ $insert['result'] = json_encode($result, JSON_UNESCAPED_UNICODE);
|
|
|
+ $insert['param'] = json_encode($param, JSON_UNESCAPED_UNICODE);
|
|
|
+ Dever::db('token/log')->insert($insert);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 拼装wechat需要的参数
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ */
|
|
|
+ public function param($method, $param = array())
|
|
|
+ {
|
|
|
+ if (strpos($method, '.')) {
|
|
|
+ $temp = explode('.', $method);
|
|
|
+ $config = $this->config[$temp[0]][$temp[1]];
|
|
|
+ } else {
|
|
|
+ if (!isset($this->config[$method])) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ $config = $this->config[$method];
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!$param) {
|
|
|
+ $param = array();
|
|
|
+ }
|
|
|
+
|
|
|
+ $param = $this->project + $param;
|
|
|
+
|
|
|
+
|
|
|
+ foreach ($config['param'] as $k => $v) {
|
|
|
+ if ($v == 'token') {
|
|
|
+ $config['url'] .= $k . '=' . $this->token();
|
|
|
+ unset($config['param'][$k]);
|
|
|
+ } elseif ($v == 'ticket') {
|
|
|
+ $config['param'][$k] = $this->ticket();
|
|
|
+ } elseif ($v == 'code') {
|
|
|
+ $config['param'][$k] = $this->code();
|
|
|
+ } elseif ($v == 'oauth') {
|
|
|
+ if (!isset($param['oauth'])) {
|
|
|
+ Dever::alert('oauth erorr');
|
|
|
+ } elseif (is_numeric($param['oauth'])) {
|
|
|
+ $oauth = $this->oauth($param['oauth']);
|
|
|
+ $param['oauth'] = $oauth['value'];
|
|
|
+ }
|
|
|
+ $config['url'] .= $k . '=' . $param['oauth'];
|
|
|
+ unset($config['param'][$k]);
|
|
|
+ } elseif (!is_array($v) && isset($param[$v])) {
|
|
|
+ $config['param'][$k] = $param[$v];
|
|
|
+ } elseif($v) {
|
|
|
+ $config['param'][$k] = $this->replace($v, $param);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $config['json'] = isset($config['json']) && $config['json'] ? true : false;
|
|
|
+
|
|
|
+ return $config;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 替换{}
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ */
|
|
|
+ public function replace($value, $param)
|
|
|
+ {
|
|
|
+ if (isset($param['domain']) && strpos($value, '{domain}') !== false) {
|
|
|
+ foreach ($param['domain'] as $k => $v) {
|
|
|
+ $param['domain'][$k] = str_replace('{domain}', $v, $value);
|
|
|
+ }
|
|
|
+ return $param['domain'];
|
|
|
+ }
|
|
|
+
|
|
|
+ return $value;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 消息解密
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ */
|
|
|
+ public function decode($sign, $timestamp, $nonce, $encrypt)
|
|
|
+ {
|
|
|
+ include(DEVER_PROJECT_PATH . 'example/wxBizMsgCrypt.php');
|
|
|
+ $crypt = new \WXBizMsgCrypt($this->project['token'], $this->project['key'], $this->project['appid']);
|
|
|
+
|
|
|
+ $format = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%s]]></Encrypt></xml>";
|
|
|
+ $xml = sprintf($format, $encrypt);
|
|
|
+
|
|
|
+ $result = '';
|
|
|
+ $code = $crypt->decryptMsg($sign, $timestamp, $nonce, $xml, $result);
|
|
|
+ if ($code == 0) {
|
|
|
+ libxml_disable_entity_loader(true);
|
|
|
+ $result = (array) simplexml_load_string($result, 'SimpleXMLElement', LIBXML_NOCDATA);
|
|
|
+ }
|
|
|
+
|
|
|
+ return $result;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 获取nonce
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ */
|
|
|
+ public function nonce()
|
|
|
+ {
|
|
|
+ return substr(md5(microtime()), rand(10, 15));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 获取signature
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ */
|
|
|
+ public function signature($ticket, $url, $timestamp, $noncestr)
|
|
|
+ {
|
|
|
+
|
|
|
+ $info = array();
|
|
|
+ $info['jsapi_ticket'] = $ticket;
|
|
|
+ $info['url'] = $url;
|
|
|
+ $info['timestamp'] = $timestamp;
|
|
|
+ $info['noncestr'] = $noncestr;
|
|
|
+ ksort($info);
|
|
|
+ */
|
|
|
+
|
|
|
+ $signature_string = "jsapi_ticket=$ticket&noncestr=$noncestr×tamp=$timestamp&url=$url";
|
|
|
+
|
|
|
+
|
|
|
+ return sha1($signature_string);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 获取sign签名数据包
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ */
|
|
|
+ public function sign($url)
|
|
|
+ {
|
|
|
+ $ticket = $this->ticket();
|
|
|
+ if (!$url) {
|
|
|
+ $url = Dever::url();
|
|
|
+ } else {
|
|
|
+ $url = htmlspecialchars_decode($url);
|
|
|
+ }
|
|
|
+ $timestamp = time();
|
|
|
+ $noncestr = $this->nonce();
|
|
|
+ $signature = $this->signature($ticket, $url, $timestamp, $noncestr);
|
|
|
+
|
|
|
+ $sign = array();
|
|
|
+ $sign['appId'] = $this->project['appid'];
|
|
|
+ $sign['nonceStr'] = $noncestr;
|
|
|
+ $sign['timestamp'] = $timestamp;
|
|
|
+ $sign['url'] = $url;
|
|
|
+ $sign['signature'] = $signature;
|
|
|
+ return $sign;
|
|
|
+
|
|
|
+ }
|
|
|
+}
|