12 changed files with 1613 additions and 13 deletions
-
31app/Common/Factory.php
-
172app/Common/RedisSession.php
-
19app/Common/ReturnData.php
-
889app/Common/WxComponent.php
-
4app/Common/aes/ReadMe.txt
-
40app/Common/aes/demo.php
-
35app/Common/aes/errorCode.php
-
166app/Common/aes/pkcs7Encoder.php
-
36app/Common/aes/sha1.php
-
150app/Common/aes/wxBizMsgCrypt.php
-
54app/Common/aes/xmlparse.php
-
30lqycms.sql
@ -0,0 +1,31 @@ |
|||||
|
<?php |
||||
|
// +----------------------------------------------------------------------
|
||||
|
// | PHP设计模式-单例模式
|
||||
|
// | 单例模式解决的是如何在整个项目中创建唯一对象实例的问题
|
||||
|
// +----------------------------------------------------------------------
|
||||
|
namespace app\common\lib; |
||||
|
|
||||
|
class Factory |
||||
|
{ |
||||
|
private static $Factory; |
||||
|
|
||||
|
private function __construct() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public static function getInstance($className, $options = null) |
||||
|
{ |
||||
|
if(!isset(self::$Factory[$className]) || !self::$Factory[$className]) |
||||
|
{ |
||||
|
self::$Factory[$className] = new $className($options); |
||||
|
} |
||||
|
|
||||
|
return self::$Factory[$className]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 示例 |
||||
|
* Factory::getInstance(\app\api\controller\Oauth::class); |
||||
|
*/ |
@ -0,0 +1,172 @@ |
|||||
|
<?php |
||||
|
namespace App\Common; |
||||
|
|
||||
|
//通过Redis实现Session共享
|
||||
|
class RedisSession |
||||
|
{ |
||||
|
/** |
||||
|
* 保存session的数据库表的信息 |
||||
|
*/ |
||||
|
private $_options = array( |
||||
|
'handler' => null, //数据库连接句柄
|
||||
|
'host' => null, |
||||
|
'port' => null, |
||||
|
'lifeTime' => null, |
||||
|
'prefix' => 'PHPREDIS_SESSION:' |
||||
|
); |
||||
|
|
||||
|
/** |
||||
|
* 构造函数 |
||||
|
* @param $options 设置信息数组 |
||||
|
*/ |
||||
|
public function __construct($options=array()) |
||||
|
{ |
||||
|
if(!class_exists("redis", false)){ |
||||
|
die("必须安装redis扩展"); |
||||
|
} |
||||
|
if(!isset($options['lifeTime']) || $options['lifeTime'] <= 0){ |
||||
|
$options['lifeTime'] = ini_get('session.gc_maxlifetime'); |
||||
|
} |
||||
|
$this->_options = array_merge($this->_options, $options); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 开始使用该驱动的session |
||||
|
*/ |
||||
|
public function begin() |
||||
|
{ |
||||
|
if($this->_options['host'] === null || $this->_options['port'] === null || $this->_options['lifeTime'] === null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
//设置session处理函数
|
||||
|
session_set_save_handler( |
||||
|
array($this, 'open'), |
||||
|
array($this, 'close'), |
||||
|
array($this, 'read'), |
||||
|
array($this, 'write'), |
||||
|
array($this, 'destory'), |
||||
|
array($this, 'gc') |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 自动开始回话或者session_start()开始回话后第一个调用的函数 |
||||
|
* 类似于构造函数的作用 |
||||
|
* @param $savePath 默认的保存路径 |
||||
|
* @param $sessionName 默认的参数名,PHPSESSID |
||||
|
*/ |
||||
|
public function open($savePath, $sessionName) |
||||
|
{ |
||||
|
if(is_resource($this->_options['handler'])) return true; |
||||
|
//连接redis
|
||||
|
$redisHandle = new Redis(); |
||||
|
$redisHandle->connect($this->_options['host'], $this->_options['port']); |
||||
|
if(!$redisHandle){ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
$this->_options['handler'] = $redisHandle; |
||||
|
// $this->gc(null);
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 类似于析构函数,在write之后调用或者session_write_close()函数之后调用 |
||||
|
*/ |
||||
|
public function close() |
||||
|
{ |
||||
|
return $this->_options['handler']->close(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 读取session信息 |
||||
|
* @param $sessionId 通过该Id唯一确定对应的session数据 |
||||
|
* @return session信息/空串 |
||||
|
*/ |
||||
|
public function read($sessionId) |
||||
|
{ |
||||
|
$sessionId = $this->_options['prefix'].$sessionId; |
||||
|
return $this->_options['handler']->get($sessionId); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 写入或者修改session数据 |
||||
|
* @param $sessionId 要写入数据的session对应的id |
||||
|
* @param $sessionData 要写入的数据,已经序列化过了 |
||||
|
*/ |
||||
|
public function write($sessionId, $sessionData) |
||||
|
{ |
||||
|
$sessionId = $this->_options['prefix'].$sessionId; |
||||
|
return $this->_options['handler']->setex($sessionId, $this->_options['lifeTime'], $sessionData); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 主动销毁session会话 |
||||
|
* @param $sessionId 要销毁的会话的唯一id |
||||
|
*/ |
||||
|
public function destory($sessionId) |
||||
|
{ |
||||
|
$sessionId = $this->_options['prefix'].$sessionId; |
||||
|
// $array = $this->print_stack_trace();
|
||||
|
// log::write($array);
|
||||
|
return $this->_options['handler']->delete($sessionId) >= 1 ? true : false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 清理绘画中的过期数据 |
||||
|
* @param 有效期 |
||||
|
*/ |
||||
|
public function gc($lifeTime) |
||||
|
{ |
||||
|
//获取所有sessionid,让过期的释放掉
|
||||
|
//$this->_options['handler']->keys("*");
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
//打印堆栈信息
|
||||
|
public function print_stack_trace() |
||||
|
{ |
||||
|
$array = debug_backtrace(); |
||||
|
//截取用户信息
|
||||
|
$var = $this->read(session_id()); |
||||
|
$s = strpos($var, "index_dk_user|"); |
||||
|
$e = strpos($var, "}authId|"); |
||||
|
$user = substr($var,$s+14,$e-13); |
||||
|
$user = unserialize($user); |
||||
|
//print_r($array);//信息很齐全
|
||||
|
unset ( $array [0] ); |
||||
|
|
||||
|
if(!empty($user)) |
||||
|
{ |
||||
|
$traceInfo = $user['id'].'|'.$user['user_name'].'|'.$user['user_phone'].'|'.$user['presona_name'].'++++++++++++++++\n'; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
$traceInfo = '++++++++++++++++\n'; |
||||
|
} |
||||
|
|
||||
|
$time = date ( "y-m-d H:i:m" ); |
||||
|
foreach ( $array as $t ) |
||||
|
{ |
||||
|
$traceInfo .= '[' . $time . '] ' . $t ['file'] . ' (' . $t ['line'] . ') '; |
||||
|
$traceInfo .= $t ['class'] . $t ['type'] . $t ['function'] . '('; |
||||
|
$traceInfo .= implode ( ', ', $t ['args'] ); |
||||
|
$traceInfo .= ")\n"; |
||||
|
} |
||||
|
|
||||
|
$traceInfo .= '++++++++++++++++'; |
||||
|
return $traceInfo; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//-------------------------------------------
|
||||
|
//示例
|
||||
|
//入口处调用
|
||||
|
|
||||
|
/* $handler = new redisSession(array( |
||||
|
'host' => "127.0.0.1", |
||||
|
'port' => "6379" |
||||
|
)); |
||||
|
$handler->begin(); */ |
@ -0,0 +1,889 @@ |
|||||
|
<?php |
||||
|
namespace App\Common; |
||||
|
|
||||
|
if (!class_exists("WXBizMsgCrypt")) |
||||
|
{ |
||||
|
include_once "aes/wxBizMsgCrypt.php"; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 微信第三方平台代小程序或者公众号 |
||||
|
*/ |
||||
|
class WxComponent |
||||
|
{ |
||||
|
const API_URL_PREFIX = 'https://api.weixin.qq.com/cgi-bin/component'; |
||||
|
const GET_ACCESS_TOKEN_URL = '/api_component_token'; |
||||
|
const GET_PREAUTHCODE_URL = '/api_create_preauthcode?component_access_token='; |
||||
|
const GET_WX_AUTH_INFO_URL = '/api_query_auth?component_access_token='; |
||||
|
const GET_WX_ACCESS_TOKEN_URL = '/api_authorizer_token?component_access_token='; |
||||
|
const GET_WX_ACCOUNT_INFO_URL = '/api_get_authorizer_info?component_access_token='; |
||||
|
const GET_WX_OPTION_INFO_URL = '/api_get_authorizer_option?component_access_token='; |
||||
|
const SET_WX_OPTION_INFO_URL = '/api_set_authorizer_option?component_access_token='; |
||||
|
const WX_AUTH_CB_URL = 'https://mp.weixin.qq.com/cgi-bin/componentloginpage?'; |
||||
|
|
||||
|
// 代公众号发起网页授权相关
|
||||
|
// 在{网页开发域名}或者下级域名 $APPID$.{网页开发域名} 的形式,可以代公众号发起网页授权。
|
||||
|
const OAUTH_PREFIX = 'https://open.weixin.qq.com/connect/oauth2'; |
||||
|
const OAUTH_AUTHORIZE_URL = '/authorize?'; |
||||
|
const API_BASE_URL_PREFIX = 'https://api.weixin.qq.com'; //以下API接口URL需要使用此前缀
|
||||
|
const OAUTH_TOKEN_URL = '/sns/oauth2/component/access_token?'; |
||||
|
const OAUTH_REFRESH_URL = '/sns/oauth2/component/refresh_token?'; |
||||
|
const OAUTH_USERINFO_URL = '/sns/userinfo?'; |
||||
|
const OAUTH_AUTH_URL = '/sns/auth?'; |
||||
|
// 代实现小程序
|
||||
|
const API_URL_PREFIX_MINI_PROGRAM = 'https://api.weixin.qq.com'; // 小程序
|
||||
|
const SET_DOMAIN = '/wxa/modify_domain'; |
||||
|
const BIND_TEST_USER = '/wxa/bind_tester'; // 绑定小程序体验者
|
||||
|
const UNBIND_TEST_USER = '/wxa/unbind_tester'; |
||||
|
const GET_DRAFT_TEMPLATE = '/wxa/gettemplatedraftlist'; |
||||
|
const AUDIT_DRAFT_TEMPLATE = '/wxa/addtotemplate'; |
||||
|
const TEMPLATE_LIST = '/wxa/gettemplatelist'; |
||||
|
const DELETE_TEMPLATE = '/wxa/deletetemplate'; |
||||
|
const UPLOAD_TEMPLATE = '/wxa/commit'; |
||||
|
const TEST_QR_CODE = '/wxa/get_qrcode'; |
||||
|
const GET_CATEGORY = '/wxa/get_category'; |
||||
|
const GET_PAGES = '/wxa/get_page'; |
||||
|
const AUDIT_TEMPLATE = '/wxa/submit_audit'; |
||||
|
const PUBLISH_TEMPLATE = '/wxa/release'; |
||||
|
const AUDIT_STATUS = '/wxa/get_auditstatus'; |
||||
|
|
||||
|
public $component_appid; |
||||
|
public $component_appsecret; |
||||
|
public $component_verify_ticket; |
||||
|
public $encodingAesKey = ""; |
||||
|
public $token = ""; |
||||
|
|
||||
|
public $debug = false; |
||||
|
public $errCode = 40001; |
||||
|
public $errMsg = "no access"; |
||||
|
private $_logcallback; |
||||
|
|
||||
|
/** |
||||
|
* 构造函数,填入配置信息 |
||||
|
* @param string $component_appid 平台appId |
||||
|
* @param string $component_appsecret 平台appsecret |
||||
|
* @param string $component_verify_ticket 平台票据,微信服务器定时推送过来 |
||||
|
* @param string $encodingAesKey 公众号消息加解密Key |
||||
|
* @param string $token 公众号消息校验Token |
||||
|
*/ |
||||
|
public function __construct($component_appid, $component_appsecret, $component_verify_ticket, $encodingAesKey, $token) |
||||
|
{ |
||||
|
$this->component_appid = $component_appid; |
||||
|
$this->component_appsecret = $component_appsecret; |
||||
|
$this->component_verify_ticket = $component_verify_ticket; |
||||
|
$this->encodingAesKey = $encodingAesKey; |
||||
|
$this->token = $token; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @return mixed |
||||
|
*/ |
||||
|
public function getLogcallback() |
||||
|
{ |
||||
|
return $this->_logcallback; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param callable $logcallback |
||||
|
*/ |
||||
|
public function setLogcallback($logcallback) |
||||
|
{ |
||||
|
$this->_logcallback = $logcallback; |
||||
|
return $this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置新的票据 |
||||
|
* @param $component_verify_ticket |
||||
|
*/ |
||||
|
public function setComponentVerifyTicket($component_verify_ticket) |
||||
|
{ |
||||
|
$this->component_verify_ticket = $component_verify_ticket; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 得到公众号服务授权的URL |
||||
|
* @param string $pre_auth_code |
||||
|
* @param string $redirect_uri |
||||
|
* @return string |
||||
|
*/ |
||||
|
public function getAuthCbUrl($pre_auth_code, $redirect_uri) |
||||
|
{ |
||||
|
return self::WX_AUTH_CB_URL . "component_appid=" . urlencode($this->component_appid) |
||||
|
. "&pre_auth_code=" . urlencode($pre_auth_code) . "&redirect_uri=" . urlencode($redirect_uri); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获得服务访问授权key |
||||
|
* @return bool|mixed { |
||||
|
* "component_access_token":"61W3mEpU66027wgNZ_MhGHNQDHnFATkDa9-2llqrMBjUwxRSNPbVsMmyD-yq8wZETSoE5NQgecigDrSHkPtIYA", |
||||
|
* "expires_in":7200 |
||||
|
* } |
||||
|
*/ |
||||
|
public function getAccessToken() |
||||
|
{ |
||||
|
$arr = array('component_appid' => $this->component_appid, |
||||
|
'component_appsecret' => $this->component_appsecret, |
||||
|
'component_verify_ticket' => $this->component_verify_ticket, |
||||
|
); |
||||
|
$result = $this->httpPost(self::API_URL_PREFIX . self::GET_ACCESS_TOKEN_URL, json_encode($arr)); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} |
||||
|
return $json; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获得预授权码 |
||||
|
* @param $access_token |
||||
|
* @return bool|mixed{ |
||||
|
* "pre_auth_code":"Cx_Dk6qiBE0Dmx4EmlT3oRfArPvwSQ-oa3NL_fwHM7VI08r52wazoZX2Rhpz1dEw", |
||||
|
* "expires_in":600 |
||||
|
* } |
||||
|
*/ |
||||
|
public function getPreauthCode($access_token) |
||||
|
{ |
||||
|
$arr = array('component_appid' => $this->component_appid); |
||||
|
$result = $this->httpPost(self::API_URL_PREFIX . self::GET_PREAUTHCODE_URL . $access_token, json_encode($arr)); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} |
||||
|
return $json; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 使用授权码换取公众号的授权信息 |
||||
|
* @param $access_token |
||||
|
* @param $auth_code |
||||
|
* @return bool|mixed{ "authorization_info": { |
||||
|
* "authorizer_appid": "wxf8b4f85f3a794e77", |
||||
|
* "authorizer_access_token": "QXjUqNqfYVH0yBE1iI_7vuN_9gQbpjfK7hYwJ3P7xOa88a89-Aga5x1NMYJyB8G2yKt1KCl0nPC3W9GJzw0Zzq_dBxc8pxIGUNi_bFes0qM", |
||||
|
* "expires_in": 7200, |
||||
|
* "authorizer_refresh_token": "dTo-YCXPL4llX-u1W1pPpnp8Hgm4wpJtlR6iV0doKdY", |
||||
|
* "func_info": [{ "funcscope_category": { "id": 1 } }, |
||||
|
* {"funcscope_category": {"id": 2 }}, |
||||
|
* {"funcscope_category": {"id": 3}}] |
||||
|
* } |
||||
|
*/ |
||||
|
public function getWxAuthInfo($access_token, $auth_code) |
||||
|
{ |
||||
|
$arr = array('component_appid' => $this->component_appid, 'authorization_code' => $auth_code); |
||||
|
$result = $this->httpPost(self::API_URL_PREFIX . self::GET_WX_AUTH_INFO_URL . $access_token, json_encode($arr)); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->log('test--------------' . $result); |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} |
||||
|
return $json; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取(刷新)授权公众号的令牌 |
||||
|
* @param $access_token |
||||
|
* @param $authorizer_appid |
||||
|
* @param $authorizer_refresh_token |
||||
|
* @return bool|mixed { |
||||
|
* "authorizer_access_token": "aaUl5s6kAByLwgV0BhXNuIFFUqfrR8vTATsoSHukcIGqJgrc4KmMJ-JlKoC_-NKCLBvuU1cWPv4vDcLN8Z0pn5I45mpATruU0b51hzeT1f8", |
||||
|
* "expires_in": 7200, |
||||
|
* "authorizer_refresh_token": "BstnRqgTJBXb9N2aJq6L5hzfJwP406tpfahQeLNxX0w" |
||||
|
* } |
||||
|
*/ |
||||
|
public function getWxAccessToken($access_token, $authorizer_appid, $authorizer_refresh_token) |
||||
|
{ |
||||
|
$arr = array('component_appid' => $this->component_appid, |
||||
|
'authorizer_appid' => $authorizer_appid, |
||||
|
'authorizer_refresh_token' => $authorizer_refresh_token); |
||||
|
$result = $this->httpPost(self::API_URL_PREFIX . self::GET_WX_ACCESS_TOKEN_URL . $access_token, json_encode($arr)); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} |
||||
|
return $json; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取授权方的账户信息 |
||||
|
* @param $access_token |
||||
|
* @param $authorizer_appid |
||||
|
* @return bool|mixed {"authorizer_info": { |
||||
|
* "nick_name": "微信SDK Demo Special", |
||||
|
* "head_img": "http://wx.qlogo.cn/mmopen/GPyw0pGicibl5Eda4GmSSbTguhjg9LZjumHmVjybjiaQXnE9XrXEts6ny9Uv4Fk6hOScWRDibq1fI0WOkSaAjaecNTict3n6EjJaC/0", |
||||
|
* "service_type_info": { "id": 2 }, |
||||
|
* "verify_type_info": { "id": 0 }, |
||||
|
* "user_name":"gh_eb5e3a772040", |
||||
|
* "alias":"paytest01" |
||||
|
* }, |
||||
|
* "authorization_info": { |
||||
|
* "appid": "wxf8b4f85f3a794e77", |
||||
|
* "func_info": [ { "funcscope_category": { "id": 1 } }, { "funcscope_category": { "id": 2 } }, { "funcscope_category": { "id": 3 } }] |
||||
|
* }} |
||||
|
*/ |
||||
|
public function getWxAccountInfo($access_token, $authorizer_appid) |
||||
|
{ |
||||
|
$arr = array('component_appid' => $this->component_appid, |
||||
|
'authorizer_appid' => $authorizer_appid); |
||||
|
$result = $this->httpPost(self::API_URL_PREFIX . self::GET_WX_ACCOUNT_INFO_URL . $access_token, json_encode($arr)); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->log('test###--------------' . $result); |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} |
||||
|
return $json; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取授权方的选项信息 |
||||
|
* @param $access_token |
||||
|
* @param $authorizer_appid |
||||
|
* @param $option_name |
||||
|
* @return bool|mixed { "authorizer_appid":"wx7bc5ba58cabd00f4", |
||||
|
* "option_name":"voice_recognize", |
||||
|
* "option_value":"1" } |
||||
|
*/ |
||||
|
public function getWxOptionInfo($access_token, $authorizer_appid, $option_name) |
||||
|
{ |
||||
|
$arr = array('component_appid' => $this->component_appid, |
||||
|
'authorizer_appid' => $authorizer_appid, |
||||
|
'option_name' => $option_name); |
||||
|
$result = $this->httpPost(self::API_URL_PREFIX . self::GET_WX_OPTION_INFO_URL . $access_token, json_encode($arr)); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} |
||||
|
return $json; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置授权方的选项信息 |
||||
|
* @param $access_token |
||||
|
* @param $authorizer_appid |
||||
|
* @param $option_name |
||||
|
* @param $option_value |
||||
|
* @return bool|mixed { "errcode":0, "errmsg":"ok" } |
||||
|
*/ |
||||
|
public function setWxOptionInfo($access_token, $authorizer_appid, $option_name, $option_value) |
||||
|
{ |
||||
|
$arr = array('component_appid' => $this->component_appid, |
||||
|
'authorizer_appid' => $authorizer_appid, |
||||
|
'option_name' => $option_name, |
||||
|
'option_value' => $option_value); |
||||
|
$result = $this->httpPost(self::API_URL_PREFIX . self::SET_WX_OPTION_INFO_URL . $access_token, json_encode($arr)); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || $json['errcode'] > 0) { |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} |
||||
|
return $json; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 处理component_verify_ticket |
||||
|
* |
||||
|
*/ |
||||
|
|
||||
|
/** |
||||
|
* @return array|bool |
||||
|
* <xml> |
||||
|
* <AppId> </AppId> |
||||
|
* <CreateTime>1413192605 </CreateTime> |
||||
|
* <InfoType> </InfoType> |
||||
|
* <ComponentVerifyTicket> </ComponentVerifyTicket> |
||||
|
* </xml> |
||||
|
*/ |
||||
|
public function processEventNotify($raw = '') |
||||
|
{ |
||||
|
if ($_SERVER['REQUEST_METHOD'] == "POST") { |
||||
|
$dec_msg = ""; |
||||
|
|
||||
|
$postStr = $raw ?? file_get_contents("php://input"); |
||||
|
if (!$postStr) { |
||||
|
$postStr = $GLOBALS['HTTP_RAW_POST_DATA']; |
||||
|
} |
||||
|
|
||||
|
if (!$postStr) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
$pc = new \WXBizMsgCrypt($this->token, $this->encodingAesKey, $this->component_appid); |
||||
|
$ret = $pc->decryptMsg($_GET['msg_signature'], $_GET['timestamp'], $_GET['nonce'], $postStr, $dec_msg); |
||||
|
if ($ret === 0) { |
||||
|
$arr = (array) simplexml_load_string($dec_msg, 'SimpleXMLElement', LIBXML_NOCDATA); |
||||
|
return $arr; |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public function responseEvent() |
||||
|
{ |
||||
|
die("success"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 代公众号发起网页授权 oauth 授权跳转接口 |
||||
|
* @param string $appid 公众号appId |
||||
|
* @param string $callback 跳转URL |
||||
|
* @param string $state 状态信息,最多128字节 |
||||
|
* @param string $scope 授权作用域 snsapi_base或者snsapi_userinfo 或者 snsapi_base,snsapi_userinfo |
||||
|
* @return string |
||||
|
*/ |
||||
|
public function getOauthRedirect($appid, $callback, $state = '', $scope = 'snsapi_base') |
||||
|
{ |
||||
|
return self::OAUTH_PREFIX . self::OAUTH_AUTHORIZE_URL . 'appid=' . $appid . '&redirect_uri=' . urlencode($callback) . |
||||
|
'&response_type=code&scope=' . $scope . '&state=' . $state . '&component_appid=' . urlencode($this->component_appid) |
||||
|
. '#wechat_redirect'; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 代公众号发起网页授权 回调URL时,通过code获取Access Token |
||||
|
* @return array|boolean {access_token,expires_in,refresh_token,openid,scope} |
||||
|
*/ |
||||
|
public function getOauthAccessToken($appid, $component_access_token) |
||||
|
{ |
||||
|
$code = isset($_GET['code']) ? $_GET['code'] : ''; |
||||
|
if (!$code) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
$result = $this->httpPost(self::API_BASE_URL_PREFIX . self::OAUTH_TOKEN_URL . 'appid=' . $appid |
||||
|
. '&code=' . $code . '&grant_type=authorization_code' |
||||
|
. '&component_appid=' . urlencode($this->component_appid) |
||||
|
. '&component_access_token=' . $component_access_token); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} |
||||
|
return $json; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 代公众号发起网页授权 刷新access token并续期 |
||||
|
* @param string $refresh_token |
||||
|
* @return boolean|mixed |
||||
|
*/ |
||||
|
public function getOauthRefreshToken($appId, $refresh_token, $component_access_token) |
||||
|
{ |
||||
|
$result = $this->httpPost(self::API_BASE_URL_PREFIX . self::OAUTH_REFRESH_URL |
||||
|
. 'appid=' . $appId . '&grant_type=refresh_token&refresh_token=' . $refresh_token |
||||
|
. '&component_appid=' . urlencode($this->component_appid) |
||||
|
. '&component_access_token=' . $component_access_token |
||||
|
); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} |
||||
|
return $json; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取授权后的用户资料 |
||||
|
* @param string $access_token |
||||
|
* @param string $openid |
||||
|
* @return array|boolean {openid,nickname,sex,province,city,country,headimgurl,privilege,[unionid]} |
||||
|
* 注意:unionid字段 只有在用户将公众号绑定到微信开放平台账号后,才会出现。建议调用前用isset()检测一下 |
||||
|
*/ |
||||
|
public function getOauthUserinfo($access_token, $openid) |
||||
|
{ |
||||
|
$result = $this->httpPost(self::API_BASE_URL_PREFIX . self::OAUTH_USERINFO_URL . 'access_token=' . $access_token . '&openid=' . $openid); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} |
||||
|
return $json; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检验授权凭证是否有效 |
||||
|
* @param string $access_token |
||||
|
* @param string $openid |
||||
|
* @return boolean 是否有效 |
||||
|
*/ |
||||
|
public function getOauthAuth($access_token, $openid) |
||||
|
{ |
||||
|
$result = $this->httpPost(self::API_BASE_URL_PREFIX . self::OAUTH_AUTH_URL . 'access_token=' . $access_token . '&openid=' . $openid); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} else |
||||
|
if ($json['errcode'] == 0) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public function setMiniProgramDomain($appID, $params, $accessToken) |
||||
|
{ |
||||
|
$result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::SET_DOMAIN . '?access_token=' . $accessToken, json_encode($params)); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} else { |
||||
|
if ($json['errcode'] == 0) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public function uploadTemplate($params, $accessToken) |
||||
|
{ |
||||
|
$result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::UPLOAD_TEMPLATE . '?access_token=' . $accessToken, json_encode($params, JSON_UNESCAPED_UNICODE)); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} else { |
||||
|
if ($json['errcode'] == 0) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public function getDraftTemplateList($accessToken) |
||||
|
{ |
||||
|
$result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::GET_DRAFT_TEMPLATE . '?access_token=' . $accessToken, ''); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
return $json; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public function getTemplateList($accessToken) |
||||
|
{ |
||||
|
$result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::TEMPLATE_LIST . '?access_token=' . $accessToken, ''); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
return $json; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public function auditDraftTemplate($accessToken, $draftID) |
||||
|
{ |
||||
|
$result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::AUDIT_DRAFT_TEMPLATE . '?access_token=' . $accessToken, json_encode(['draft_id' => $draftID])); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} else { |
||||
|
if ($json['errcode'] == 0) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public function deleteTemplate($accessToken, $templateID) |
||||
|
{ |
||||
|
$result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::DELETE_TEMPLATE . '?access_token=' . $accessToken, json_encode(['template_id' => $templateID])); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} else { |
||||
|
if ($json['errcode'] == 0) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public function getQrCode($accessToken) |
||||
|
{ |
||||
|
$result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::TEST_QR_CODE . '?access_token=' . $accessToken, ''); |
||||
|
if ($result) { |
||||
|
return $result; // 图片二进制流
|
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
public function getCategory($accessToken) |
||||
|
{ |
||||
|
$result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::GET_CATEGORY . '?access_token=' . $accessToken, ''); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
return $json; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public function getPages($accessToken) |
||||
|
{ |
||||
|
$result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::GET_PAGES . '?access_token=' . $accessToken, ''); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
return $json; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public function auditTemplate($params, $accessToken) |
||||
|
{ |
||||
|
$result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::AUDIT_TEMPLATE . '?access_token=' . $accessToken, json_encode($params, JSON_UNESCAPED_UNICODE)); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} else { |
||||
|
if ($json['errcode'] == 0) { |
||||
|
return $json; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public function bindTestUser($params, $accessToken) |
||||
|
{ |
||||
|
$result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::BIND_TEST_USER . '?access_token=' . $accessToken, json_encode($params)); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} else { |
||||
|
if ($json['errcode'] == 0) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public function unbindTestUser($params, $accessToken) |
||||
|
{ |
||||
|
$result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::UNBIND_TEST_USER . '?access_token=' . $accessToken, json_encode($params)); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} else { |
||||
|
if ($json['errcode'] == 0) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public function publishTemplate($accessToken) |
||||
|
{ |
||||
|
$result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::PUBLISH_TEMPLATE . '?access_token=' . $accessToken, json_encode(new stdClass())); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} else { |
||||
|
if ($json['errcode'] == 0) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public function getAuditStatus($auditid, $accessToken) |
||||
|
{ |
||||
|
$result = $this->httpPost(static::API_URL_PREFIX_MINI_PROGRAM . static::AUDIT_STATUS . '?access_token=' . $accessToken, json_encode(['auditid' => $auditid])); |
||||
|
if ($result) { |
||||
|
$json = json_decode($result, true); |
||||
|
if (!$json || !empty($json['errcode'])) { |
||||
|
$this->errCode = $json['errcode']; |
||||
|
$this->errMsg = $json['errmsg']; |
||||
|
return false; |
||||
|
} else { |
||||
|
if ($json['errcode'] == 0) { |
||||
|
return $json; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
protected function log($log) |
||||
|
{ |
||||
|
if ($this->debug && is_callable($this->_logcallback)) { |
||||
|
if (is_array($log)) { |
||||
|
$log = print_r($log, true); |
||||
|
} |
||||
|
|
||||
|
return call_user_func($this->_logcallback, $log); |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* POST 请求 |
||||
|
* @param string $url |
||||
|
* @param string|array $param |
||||
|
* @param boolean $post_file 是否文件上传 |
||||
|
* @return string content |
||||
|
*/ |
||||
|
private function httpPost($url, $param = "", $post_file = false) |
||||
|
{ |
||||
|
$oCurl = curl_init(); |
||||
|
if (stripos($url, "https://") !== false) { |
||||
|
curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, false); |
||||
|
curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false); |
||||
|
curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
|
||||
|
} |
||||
|
if (is_string($param) || $post_file) { |
||||
|
$strPOST = $param; |
||||
|
} else { |
||||
|
$aPOST = array(); |
||||
|
foreach ($param as $key => $val) { |
||||
|
if (is_array($val)) { |
||||
|
foreach ($val as $_k => $_v) { |
||||
|
$aPOST[] = $key . "[]=" . urlencode($_v); |
||||
|
} |
||||
|
} else { |
||||
|
$aPOST[] = $key . "=" . urlencode($val); |
||||
|
} |
||||
|
} |
||||
|
$strPOST = join("&", $aPOST); |
||||
|
} |
||||
|
curl_setopt($oCurl, CURLOPT_URL, $url); |
||||
|
curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1); |
||||
|
if ($strPOST != "") { |
||||
|
curl_setopt($oCurl, CURLOPT_POST, true); |
||||
|
curl_setopt($oCurl, CURLOPT_POSTFIELDS, $strPOST); |
||||
|
} |
||||
|
$sContent = curl_exec($oCurl); |
||||
|
$aStatus = curl_getinfo($oCurl); |
||||
|
curl_close($oCurl); |
||||
|
if (intval($aStatus["http_code"]) == 200) { |
||||
|
$this->log("wxcomponent httpPost: {$strPOST} recv:" . $sContent); |
||||
|
return $sContent; |
||||
|
} else { |
||||
|
$this->log("wxcomponent httpPost: {$strPOST} recv error {$url}, param:{$param} aStatus:" . print_r($aStatus, true)); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 微信api不支持中文转义的json结构 |
||||
|
* @param array $arr |
||||
|
*/ |
||||
|
public static function jsonEncode($arr) |
||||
|
{ |
||||
|
$parts = array(); |
||||
|
$is_list = false; |
||||
|
//Find out if the given array is a numerical array
|
||||
|
$keys = array_keys($arr); |
||||
|
$max_length = count($arr) - 1; |
||||
|
if (($keys[0] === 0) && ($keys[$max_length] === $max_length)) { //See if the first key is 0 and last key is length - 1
|
||||
|
$is_list = true; |
||||
|
for ($i = 0; $i < count($keys); $i++) { //See if each key correspondes to its position
|
||||
|
if ($i != $keys[$i]) { //A key fails at position check.
|
||||
|
$is_list = false; //It is an associative array.
|
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
foreach ($arr as $key => $value) { |
||||
|
if (is_array($value)) { //Custom handling for arrays
|
||||
|
if ($is_list) { |
||||
|
$parts[] = self::jsonEncode($value); |
||||
|
} |
||||
|
/* :RECURSION: */ |
||||
|
else { |
||||
|
$parts[] = '"' . $key . '":' . self::jsonEncode($value); |
||||
|
} |
||||
|
/* :RECURSION: */ |
||||
|
} else { |
||||
|
$str = ''; |
||||
|
if (!$is_list) { |
||||
|
$str = '"' . $key . '":'; |
||||
|
} |
||||
|
|
||||
|
//Custom handling for multiple data types
|
||||
|
if (!is_string($value) && is_numeric($value) && $value < 2000000000) { |
||||
|
$str .= $value; |
||||
|
} |
||||
|
//Numbers
|
||||
|
elseif ($value === false) { |
||||
|
$str .= 'false'; |
||||
|
} |
||||
|
//The booleans
|
||||
|
elseif ($value === true) { |
||||
|
$str .= 'true'; |
||||
|
} else { |
||||
|
$str .= '"' . addslashes($value) . '"'; |
||||
|
} |
||||
|
//All other things
|
||||
|
// :TODO: Is there any more datatype we should be in the lookout for? (Object?)
|
||||
|
$parts[] = $str; |
||||
|
} |
||||
|
} |
||||
|
$json = implode(',', $parts); |
||||
|
if ($is_list) { |
||||
|
return '[' . $json . ']'; |
||||
|
} |
||||
|
//Return numerical JSON
|
||||
|
return '{' . $json . '}'; //Return associative JSON
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取微信授权链接 |
||||
|
* |
||||
|
* @param string $redirect_uri 回调地址,授权后重定向的回调链接地址,请使用urlEncode对链接进行处理 |
||||
|
* @param mixed $state 可以为空,重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节 |
||||
|
*/ |
||||
|
public function get_authorize_url($redirect_uri = '', $state = '') |
||||
|
{ |
||||
|
return "https://open.weixin.qq.com/connect/oauth2/authorize?appid=".$this->app_id."&redirect_uri=".urlencode($redirect_uri)."&response_type=code&scope=snsapi_userinfo&state=".$state."#wechat_redirect"; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 微信PC扫码授权登录链接 |
||||
|
* |
||||
|
* @param string $redirect_uri 回调地址,授权后重定向的回调链接地址,请使用urlEncode对链接进行处理 |
||||
|
* @param mixed $state 可以为空,重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节 |
||||
|
*/ |
||||
|
public function get_qrconnect_url($redirect_uri = '', $state = '') |
||||
|
{ |
||||
|
return "https://open.weixin.qq.com/connect/qrconnect?appid".$this->app_id."&redirect_uri=".urlencode($redirect_uri)."&response_type=code&scope=snsapi_login&state=".$state."#wechat_redirect"; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取授权token |
||||
|
* |
||||
|
* @param string $code 通过get_authorize_url获取到的code |
||||
|
*/ |
||||
|
public function get_access_token($code = '') |
||||
|
{ |
||||
|
$token_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->app_id}&secret={$this->app_secret}&code={$code}&grant_type=authorization_code"; |
||||
|
$token_data = $this->http($token_url); |
||||
|
|
||||
|
return json_decode($token_data, true); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取授权后的微信用户信息 |
||||
|
* |
||||
|
* @param string $access_token |
||||
|
* @param string $open_id |
||||
|
*/ |
||||
|
public function get_user_info($access_token = '', $open_id = '') |
||||
|
{ |
||||
|
$info_url = "https://api.weixin.qq.com/sns/userinfo?access_token={$access_token}&openid={$open_id}&lang=zh_CN"; |
||||
|
$info_data = $this->http($info_url); |
||||
|
|
||||
|
return json_decode($info_data, true); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取用户基本信息(包括UnionID机制) |
||||
|
* |
||||
|
* @param string $access_token |
||||
|
* @param string $open_id |
||||
|
*/ |
||||
|
public function get_user_unionid($access_token = '', $open_id = '') |
||||
|
{ |
||||
|
$info_url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token={$access_token}&openid={$open_id}&lang=zh_CN"; |
||||
|
$info_data = $this->http($info_url); |
||||
|
|
||||
|
return json_decode($info_data, true); |
||||
|
} |
||||
|
|
||||
|
// cURL函数简单封装
|
||||
|
public function http($url, $data = null) |
||||
|
{ |
||||
|
$curl = curl_init(); |
||||
|
curl_setopt($curl, CURLOPT_URL, $url); |
||||
|
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); |
||||
|
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); |
||||
|
|
||||
|
if (!empty($data)) |
||||
|
{ |
||||
|
curl_setopt($curl, CURLOPT_POST, 1); |
||||
|
curl_setopt($curl, CURLOPT_POSTFIELDS, $data); |
||||
|
} |
||||
|
|
||||
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); |
||||
|
$output = curl_exec($curl); |
||||
|
curl_close($curl); |
||||
|
|
||||
|
return $output; |
||||
|
} |
||||
|
} |
@ -0,0 +1,4 @@ |
|||||
|
注意事项: |
||||
|
1.WXBizMsgCrypt.php文件提供了WXBizMsgCrypt类的实现,是用户接入企业微信的接口类。Sample.php提供了示例以供开发者参考。errorCode.php, pkcs7Encoder.php, sha1.php, xmlparse.php文件是实现这个类的辅助类,开发者无须关心其具体实现。 |
||||
|
2.WXBizMsgCrypt类封装了 DecryptMsg, EncryptMsg两个接口,分别用于开发者解密以及开发者回复消息的加密。使用方法可以参考Sample.php文件。 |
||||
|
3.加解密协议请参考微信公众平台官方文档。 |
@ -0,0 +1,40 @@ |
|||||
|
<?php |
||||
|
|
||||
|
include_once "wxBizMsgCrypt.php"; |
||||
|
|
||||
|
// 第三方发送消息给公众平台
|
||||
|
$encodingAesKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG"; |
||||
|
$token = "pamtest"; |
||||
|
$timeStamp = "1409304348"; |
||||
|
$nonce = "xxxxxx"; |
||||
|
$appId = "wxb11529c136998cb6"; |
||||
|
$text = "<xml><ToUserName><![CDATA[oia2Tj我是中文jewbmiOUlr6X-1crbLOvLw]]></ToUserName><FromUserName><![CDATA[gh_7f083739789a]]></FromUserName><CreateTime>1407743423</CreateTime><MsgType><![CDATA[video]]></MsgType><Video><MediaId><![CDATA[eYJ1MbwPRJtOvIEabaxHs7TX2D-HV71s79GUxqdUkjm6Gs2Ed1KF3ulAOA9H1xG0]]></MediaId><Title><![CDATA[testCallBackReplyVideo]]></Title><Description><![CDATA[testCallBackReplyVideo]]></Description></Video></xml>"; |
||||
|
|
||||
|
|
||||
|
$pc = new WXBizMsgCrypt($token, $encodingAesKey, $appId); |
||||
|
$encryptMsg = ''; |
||||
|
$errCode = $pc->encryptMsg($text, $timeStamp, $nonce, $encryptMsg); |
||||
|
if ($errCode == 0) { |
||||
|
print("加密后: " . $encryptMsg . "\n"); |
||||
|
} else { |
||||
|
print($errCode . "\n"); |
||||
|
} |
||||
|
|
||||
|
$xml_tree = new DOMDocument(); |
||||
|
$xml_tree->loadXML($encryptMsg); |
||||
|
$array_e = $xml_tree->getElementsByTagName('Encrypt'); |
||||
|
$array_s = $xml_tree->getElementsByTagName('MsgSignature'); |
||||
|
$encrypt = $array_e->item(0)->nodeValue; |
||||
|
$msg_sign = $array_s->item(0)->nodeValue; |
||||
|
|
||||
|
$format = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%s]]></Encrypt></xml>"; |
||||
|
$from_xml = sprintf($format, $encrypt); |
||||
|
|
||||
|
// 第三方收到公众号平台发送的消息
|
||||
|
$msg = ''; |
||||
|
$errCode = $pc->decryptMsg($msg_sign, $timeStamp, $nonce, $from_xml, $msg); |
||||
|
if ($errCode == 0) { |
||||
|
print("解密后: " . $msg . "\n"); |
||||
|
} else { |
||||
|
print($errCode . "\n"); |
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/** |
||||
|
* error code 说明. |
||||
|
* <ul> |
||||
|
* <li>-40001: 签名验证错误</li> |
||||
|
* <li>-40002: xml解析失败</li> |
||||
|
* <li>-40003: sha加密生成签名失败</li> |
||||
|
* <li>-40004: encodingAesKey 非法</li> |
||||
|
* <li>-40005: appid 校验错误</li> |
||||
|
* <li>-40006: aes 加密失败</li> |
||||
|
* <li>-40007: aes 解密失败</li> |
||||
|
* <li>-40008: 解密后得到的buffer非法</li> |
||||
|
* <li>-40009: base64加密失败</li> |
||||
|
* <li>-40010: base64解密失败</li> |
||||
|
* <li>-40011: 生成xml失败</li> |
||||
|
* </ul> |
||||
|
*/ |
||||
|
class ErrorCode |
||||
|
{ |
||||
|
public static $OK = 0; |
||||
|
public static $ValidateSignatureError = -40001; |
||||
|
public static $ParseXmlError = -40002; |
||||
|
public static $ComputeSignatureError = -40003; |
||||
|
public static $IllegalAesKey = -40004; |
||||
|
public static $ValidateAppidError = -40005; |
||||
|
public static $EncryptAESError = -40006; |
||||
|
public static $DecryptAESError = -40007; |
||||
|
public static $IllegalBuffer = -40008; |
||||
|
public static $EncodeBase64Error = -40009; |
||||
|
public static $DecodeBase64Error = -40010; |
||||
|
public static $GenReturnXmlError = -40011; |
||||
|
} |
||||
|
|
||||
|
?>
|
@ -0,0 +1,166 @@ |
|||||
|
<?php |
||||
|
|
||||
|
include_once "errorCode.php"; |
||||
|
|
||||
|
/** |
||||
|
* PKCS7Encoder class |
||||
|
* |
||||
|
* 提供基于PKCS7算法的加解密接口. |
||||
|
*/ |
||||
|
class PKCS7Encoder |
||||
|
{ |
||||
|
public static $block_size = 32; |
||||
|
|
||||
|
/** |
||||
|
* 对需要加密的明文进行填充补位 |
||||
|
* @param $text 需要进行填充补位操作的明文 |
||||
|
* @return 补齐明文字符串 |
||||
|
*/ |
||||
|
function encode($text) |
||||
|
{ |
||||
|
$block_size = PKCS7Encoder::$block_size; |
||||
|
$text_length = strlen($text); |
||||
|
//计算需要填充的位数
|
||||
|
$amount_to_pad = PKCS7Encoder::$block_size - ($text_length % PKCS7Encoder::$block_size); |
||||
|
if ($amount_to_pad == 0) { |
||||
|
$amount_to_pad = PKCS7Encoder::block_size; |
||||
|
} |
||||
|
//获得补位所用的字符
|
||||
|
$pad_chr = chr($amount_to_pad); |
||||
|
$tmp = ""; |
||||
|
for ($index = 0; $index < $amount_to_pad; $index++) { |
||||
|
$tmp .= $pad_chr; |
||||
|
} |
||||
|
return $text . $tmp; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 对解密后的明文进行补位删除 |
||||
|
* @param decrypted 解密后的明文 |
||||
|
* @return 删除填充补位后的明文 |
||||
|
*/ |
||||
|
function decode($text) |
||||
|
{ |
||||
|
|
||||
|
$pad = ord(substr($text, -1)); |
||||
|
if ($pad < 1 || $pad > 32) { |
||||
|
$pad = 0; |
||||
|
} |
||||
|
return substr($text, 0, (strlen($text) - $pad)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Prpcrypt class |
||||
|
* |
||||
|
* 提供接收和推送给公众平台消息的加解密接口. |
||||
|
*/ |
||||
|
class Prpcrypt |
||||
|
{ |
||||
|
public $key; |
||||
|
|
||||
|
function __construct($k) |
||||
|
{ |
||||
|
$this->key = base64_decode($k . "="); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 对明文进行加密 |
||||
|
* @param string $text 需要加密的明文 |
||||
|
* @return string 加密后的密文 |
||||
|
*/ |
||||
|
public function encrypt($text, $appid) |
||||
|
{ |
||||
|
|
||||
|
try { |
||||
|
//获得16位随机字符串,填充到明文之前
|
||||
|
$random = $this->getRandomStr(); |
||||
|
$text = $random . pack("N", strlen($text)) . $text . $appid; |
||||
|
// 网络字节序
|
||||
|
$size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); |
||||
|
$module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); |
||||
|
$iv = substr($this->key, 0, 16); |
||||
|
//使用自定义的填充方式对明文进行补位填充
|
||||
|
$pkc_encoder = new PKCS7Encoder; |
||||
|
$text = $pkc_encoder->encode($text); |
||||
|
mcrypt_generic_init($module, $this->key, $iv); |
||||
|
//加密
|
||||
|
$encrypted = mcrypt_generic($module, $text); |
||||
|
mcrypt_generic_deinit($module); |
||||
|
mcrypt_module_close($module); |
||||
|
|
||||
|
//print(base64_encode($encrypted));
|
||||
|
//使用BASE64对加密后的字符串进行编码
|
||||
|
return array(ErrorCode::$OK, base64_encode($encrypted)); |
||||
|
} catch (Exception $e) { |
||||
|
//print $e;
|
||||
|
return array(ErrorCode::$EncryptAESError, null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 对密文进行解密 |
||||
|
* @param string $encrypted 需要解密的密文 |
||||
|
* @return string 解密得到的明文 |
||||
|
*/ |
||||
|
public function decrypt($encrypted, $appid) |
||||
|
{ |
||||
|
|
||||
|
try { |
||||
|
//使用BASE64对需要解密的字符串进行解码
|
||||
|
$ciphertext_dec = base64_decode($encrypted); |
||||
|
$module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); |
||||
|
$iv = substr($this->key, 0, 16); |
||||
|
mcrypt_generic_init($module, $this->key, $iv); |
||||
|
|
||||
|
//解密
|
||||
|
$decrypted = mdecrypt_generic($module, $ciphertext_dec); |
||||
|
mcrypt_generic_deinit($module); |
||||
|
mcrypt_module_close($module); |
||||
|
} catch (Exception $e) { |
||||
|
return array(ErrorCode::$DecryptAESError, null); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
try { |
||||
|
//去除补位字符
|
||||
|
$pkc_encoder = new PKCS7Encoder; |
||||
|
$result = $pkc_encoder->decode($decrypted); |
||||
|
//去除16位随机字符串,网络字节序和AppId
|
||||
|
if (strlen($result) < 16) |
||||
|
return ""; |
||||
|
$content = substr($result, 16, strlen($result)); |
||||
|
$len_list = unpack("N", substr($content, 0, 4)); |
||||
|
$xml_len = $len_list[1]; |
||||
|
$xml_content = substr($content, 4, $xml_len); |
||||
|
$from_appid = substr($content, $xml_len + 4); |
||||
|
} catch (Exception $e) { |
||||
|
//print $e;
|
||||
|
return array(ErrorCode::$IllegalBuffer, null); |
||||
|
} |
||||
|
if ($from_appid != $appid) |
||||
|
return array(ErrorCode::$ValidateAppidError, null); |
||||
|
return array(0, $xml_content); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 随机生成16位字符串 |
||||
|
* @return string 生成的字符串 |
||||
|
*/ |
||||
|
function getRandomStr() |
||||
|
{ |
||||
|
|
||||
|
$str = ""; |
||||
|
$str_pol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; |
||||
|
$max = strlen($str_pol) - 1; |
||||
|
for ($i = 0; $i < 16; $i++) { |
||||
|
$str .= $str_pol[mt_rand(0, $max)]; |
||||
|
} |
||||
|
return $str; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
?>
|
@ -0,0 +1,36 @@ |
|||||
|
<?php |
||||
|
|
||||
|
include_once "errorCode.php"; |
||||
|
|
||||
|
/** |
||||
|
* SHA1 class |
||||
|
* |
||||
|
* 计算公众平台的消息签名接口. |
||||
|
*/ |
||||
|
class SHA1 |
||||
|
{ |
||||
|
/** |
||||
|
* 用SHA1算法生成安全签名 |
||||
|
* @param string $token 票据 |
||||
|
* @param string $timestamp 时间戳 |
||||
|
* @param string $nonce 随机字符串 |
||||
|
* @param string $encrypt 密文消息 |
||||
|
*/ |
||||
|
public function getSHA1($token, $timestamp, $nonce, $encrypt_msg) |
||||
|
{ |
||||
|
//排序
|
||||
|
try { |
||||
|
$array = array($encrypt_msg, $token, $timestamp, $nonce); |
||||
|
sort($array, SORT_STRING); |
||||
|
$str = implode($array); |
||||
|
return array(ErrorCode::$OK, sha1($str)); |
||||
|
} catch (Exception $e) { |
||||
|
//print $e . "\n";
|
||||
|
return array(ErrorCode::$ComputeSignatureError, null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
?>
|
@ -0,0 +1,150 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/** |
||||
|
* 对公众平台发送给公众账号的消息加解密示例代码. |
||||
|
* |
||||
|
* @copyright Copyright (c) 1998-2014 Tencent Inc. |
||||
|
*/ |
||||
|
|
||||
|
|
||||
|
include_once "sha1.php"; |
||||
|
include_once "xmlparse.php"; |
||||
|
include_once "pkcs7Encoder.php"; |
||||
|
include_once "errorCode.php"; |
||||
|
|
||||
|
/** |
||||
|
* 1.第三方回复加密消息给公众平台; |
||||
|
* 2.第三方收到公众平台发送的消息,验证消息的安全性,并对消息进行解密。 |
||||
|
*/ |
||||
|
class WXBizMsgCrypt |
||||
|
{ |
||||
|
private $token; |
||||
|
private $encodingAesKey; |
||||
|
private $appId; |
||||
|
|
||||
|
/** |
||||
|
* 构造函数 |
||||
|
* @param $token string 公众平台上,开发者设置的token |
||||
|
* @param $encodingAesKey string 公众平台上,开发者设置的EncodingAESKey |
||||
|
* @param $appId string 公众平台的appId |
||||
|
*/ |
||||
|
public function __construct($token, $encodingAesKey, $appId) |
||||
|
{ |
||||
|
$this->token = $token; |
||||
|
$this->encodingAesKey = $encodingAesKey; |
||||
|
$this->appId = $appId; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 将公众平台回复用户的消息加密打包. |
||||
|
* <ol> |
||||
|
* <li>对要发送的消息进行AES-CBC加密</li> |
||||
|
* <li>生成安全签名</li> |
||||
|
* <li>将消息密文和安全签名打包成xml格式</li> |
||||
|
* </ol> |
||||
|
* |
||||
|
* @param $replyMsg string 公众平台待回复用户的消息,xml格式的字符串 |
||||
|
* @param $timeStamp string 时间戳,可以自己生成,也可以用URL参数的timestamp |
||||
|
* @param $nonce string 随机串,可以自己生成,也可以用URL参数的nonce |
||||
|
* @param &$encryptMsg string 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串, |
||||
|
* 当return返回0时有效 |
||||
|
* |
||||
|
* @return int 成功0,失败返回对应的错误码 |
||||
|
*/ |
||||
|
public function encryptMsg($replyMsg, $timeStamp, $nonce, &$encryptMsg) |
||||
|
{ |
||||
|
$pc = new Prpcrypt($this->encodingAesKey); |
||||
|
|
||||
|
//加密
|
||||
|
$array = $pc->encrypt($replyMsg, $this->appId); |
||||
|
$ret = $array[0]; |
||||
|
if ($ret != 0) { |
||||
|
return $ret; |
||||
|
} |
||||
|
|
||||
|
if ($timeStamp == null) { |
||||
|
$timeStamp = time(); |
||||
|
} |
||||
|
$encrypt = $array[1]; |
||||
|
|
||||
|
//生成安全签名
|
||||
|
$sha1 = new SHA1; |
||||
|
$array = $sha1->getSHA1($this->token, $timeStamp, $nonce, $encrypt); |
||||
|
$ret = $array[0]; |
||||
|
if ($ret != 0) { |
||||
|
return $ret; |
||||
|
} |
||||
|
$signature = $array[1]; |
||||
|
|
||||
|
//生成发送的xml
|
||||
|
$xmlparse = new XMLParse; |
||||
|
$encryptMsg = $xmlparse->generate($encrypt, $signature, $timeStamp, $nonce); |
||||
|
return ErrorCode::$OK; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 检验消息的真实性,并且获取解密后的明文. |
||||
|
* <ol> |
||||
|
* <li>利用收到的密文生成安全签名,进行签名验证</li> |
||||
|
* <li>若验证通过,则提取xml中的加密消息</li> |
||||
|
* <li>对消息进行解密</li> |
||||
|
* </ol> |
||||
|
* |
||||
|
* @param $msgSignature string 签名串,对应URL参数的msg_signature |
||||
|
* @param $timestamp string 时间戳 对应URL参数的timestamp |
||||
|
* @param $nonce string 随机串,对应URL参数的nonce |
||||
|
* @param $postData string 密文,对应POST请求的数据 |
||||
|
* @param &$msg string 解密后的原文,当return返回0时有效 |
||||
|
* |
||||
|
* @return int 成功0,失败返回对应的错误码 |
||||
|
*/ |
||||
|
public function decryptMsg($msgSignature, $timestamp = null, $nonce, $postData, &$msg) |
||||
|
{ |
||||
|
if (strlen($this->encodingAesKey) != 43) { |
||||
|
return ErrorCode::$IllegalAesKey; |
||||
|
} |
||||
|
|
||||
|
$pc = new Prpcrypt($this->encodingAesKey); |
||||
|
|
||||
|
//提取密文
|
||||
|
$xmlparse = new XMLParse; |
||||
|
$array = $xmlparse->extract($postData); |
||||
|
$ret = $array[0]; |
||||
|
|
||||
|
if ($ret != 0) { |
||||
|
return $ret; |
||||
|
} |
||||
|
|
||||
|
if ($timestamp == null) { |
||||
|
$timestamp = time(); |
||||
|
} |
||||
|
|
||||
|
$encrypt = $array[1]; |
||||
|
$touser_name = $array[2]; |
||||
|
|
||||
|
//验证安全签名
|
||||
|
$sha1 = new SHA1; |
||||
|
$array = $sha1->getSHA1($this->token, $timestamp, $nonce, $encrypt); |
||||
|
$ret = $array[0]; |
||||
|
|
||||
|
if ($ret != 0) { |
||||
|
return $ret; |
||||
|
} |
||||
|
|
||||
|
$signature = $array[1]; |
||||
|
if ($signature != $msgSignature) { |
||||
|
return ErrorCode::$ValidateSignatureError; |
||||
|
} |
||||
|
|
||||
|
$result = $pc->decrypt($encrypt, $this->appId); |
||||
|
if ($result[0] != 0) { |
||||
|
return $result[0]; |
||||
|
} |
||||
|
$msg = $result[1]; |
||||
|
|
||||
|
return ErrorCode::$OK; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
@ -0,0 +1,54 @@ |
|||||
|
<?php |
||||
|
include_once "errorCode.php"; |
||||
|
|
||||
|
/** |
||||
|
* XMLParse class |
||||
|
* |
||||
|
* 提供提取消息格式中的密文及生成回复消息格式的接口. |
||||
|
*/ |
||||
|
class XMLParse |
||||
|
{ |
||||
|
|
||||
|
/** |
||||
|
* 提取出xml数据包中的加密消息 |
||||
|
* @param string $xmltext 待提取的xml字符串 |
||||
|
* @return string 提取出的加密消息字符串 |
||||
|
*/ |
||||
|
public function extract($xmltext) |
||||
|
{ |
||||
|
try { |
||||
|
$xml = new DOMDocument(); |
||||
|
$xml->loadXML($xmltext); |
||||
|
$array_e = $xml->getElementsByTagName('Encrypt'); |
||||
|
$array_a = $xml->getElementsByTagName('ToUserName'); |
||||
|
$encrypt = $array_e->item(0)->nodeValue; |
||||
|
$tousername = $array_a && $array_a->item(0) ? $array_a->item(0)->nodeValue : ""; |
||||
|
return array(0, $encrypt, $tousername); |
||||
|
} catch (Exception $e) { |
||||
|
//print $e . "\n";
|
||||
|
return array(ErrorCode::$ParseXmlError, null, null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 生成xml消息 |
||||
|
* @param string $encrypt 加密后的消息密文 |
||||
|
* @param string $signature 安全签名 |
||||
|
* @param string $timestamp 时间戳 |
||||
|
* @param string $nonce 随机字符串 |
||||
|
*/ |
||||
|
public function generate($encrypt, $signature, $timestamp, $nonce) |
||||
|
{ |
||||
|
$format = "<xml>
|
||||
|
<Encrypt><![CDATA[%s]]></Encrypt> |
||||
|
<MsgSignature><![CDATA[%s]]></MsgSignature> |
||||
|
<TimeStamp>%s</TimeStamp> |
||||
|
<Nonce><![CDATA[%s]]></Nonce> |
||||
|
</xml>";
|
||||
|
return sprintf($format, $encrypt, $signature, $timestamp, $nonce); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
?>
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue