,'component_appsecret'=>,'encodingAesKey'=>,'token')
*/
protected $wxComponentConfig;
/**
* 缓存类
* @var BaseCache
*/
protected $cache;
/**
* @var callable $logCallback
*/
public $logCallback = null;
/**
* @param BaseCache $cache
*/
public function setCache(BaseCache $cache)
{
$this->cache = $cache;
}
/**
* @param callable $logCallback
*/
public function setLogCallback(callable $logCallback)
{
$this->logCallback = $logCallback;
}
/**
* @param bool $debug
*/
public function setDebug(bool $debug)
{
$this->debug = $debug;
}
/**
* @var bool
*/
public $debug = false;
public function __construct($wxComponentConfig)
{
$this->cache = new FileCache(ROOT_PATH."data/wxopen/");
$this->wxComponentAppId = $wxComponentConfig['component_appid'];
$this->wxComponentConfig = $wxComponentConfig;
}
/**
* 得到第三方对象,配置采用全局配置
* @return WxComponent
*/
public function getWxComponent()
{
$cfg = $this->wxComponentConfig;
$cfg['component_verify_ticket'] = $this->getComponentVerifyTicket();
if (!$this->wxComponent) {
$this->wxComponent = new WxComponent($cfg['component_appid'],
$cfg['component_appsecret'], $cfg['component_verify_ticket'],
$cfg['encodingAesKey'], $cfg['token']);
}
return $this->wxComponent;
}
protected function log($log)
{
if ($this->debug) {
file_put_contents(ROOT_PATH.'data/wxopen/log.txt', $log.PHP_EOL,FILE_APPEND);
}
return true;
}
/**
* 得到跳转授权公众号的URL,回调返回时,会有component_appid参数
* @param string $redirectUrl 跳转回来的URL,可以含有参数用于回调识别
* @return string
*/
public function getAuthorizeUrl($redirectUrl)
{
$linkSymbo = strpos($redirectUrl, "?") ? "&" : "?";
$redirect_uri = $redirectUrl . $linkSymbo . "component_appid=" . urlencode($this->wxComponentAppId);
$preAuthCode = $this->getPreAuthCode();
return $this->getWxComponent()->getAuthCbUrl($preAuthCode, $redirect_uri);
}
/**
* 公众号授权回调处理过程
* @param $authCode
* @param $expireIn
* @return array
* 错误返回:array('code'=>(!=0),'msg'=>)
* 成功返回:array('code'=>0,'appAccountInfo'=>$appAccountInfo)
* $appAccountInfo 授权的公众号信息,格式如下:{"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 authorizeCallbackProcess($authCode, $expireIn)
{
$authName = "wxAppAuthCode" . $this->wxComponentAppId; // 通过authcode换取公众号的接口调用凭据
try {
$this->cache->setCache($authName, $authCode, $expireIn);
$componentAccessToken = $this->getComponentAccessTocken();
//使用授权码换取公众号的授权信息
$authInfo = $this->getWxComponent()->getWxAuthInfo($componentAccessToken, $authCode);
if (!$authInfo) {
return array('code' => $this->getWxComponent()->errCode, 'msg' => $this->getWxComponent()->errMsg);
}
$authName = "wxAppAccessToken" . $this->wxComponentAppId . "_" . $authInfo['authorization_info']['authorizer_appid'];
$this->cache->setCache($authName, $authInfo['authorization_info']['authorizer_access_token'], $authInfo['authorization_info']['expires_in']);
$authName = "wxAppRefreshToken" . $this->wxComponentAppId . "_" . $authInfo['authorization_info']['authorizer_appid'];
$this->cache->setCache($authName, $authInfo['authorization_info']['authorizer_refresh_token'],-1);
//获取授权方的账户信息
$appAccountInfo = $this->getWxComponent()->getWxAccountInfo($componentAccessToken, $authInfo['authorization_info']['authorizer_appid']);
if (!$appAccountInfo) {
return array('code' => $this->getWxComponent()->errCode, 'msg' => $this->getWxComponent()->errMsg);
}
return array('code' => 0, 'appAccountInfo' => $appAccountInfo, 'authInfo' => $authInfo);
} catch (\Throwable $e) {
$this->log($e->__toString());
return [
'code' => $e->getCode(),
'msg' => $e->getMessage(),
];
}
}
/**
* 第三方平台事件接收处理:
* 1、微信服务器每隔10分钟会向第三方的消息接收地址推送一次component_verify_ticket,用于获取第三方平台接口调用凭据
* 2、取消授权事件的处理
* @param string $raw
* @return array
* AppId 授权公众号
* InfoType component_verify_ticket(ticket通知) unauthorized(取消授权通知) authorized(授权成功通知) updateauthorized(授权更新通知)
* POST数据示例(component_verify_ticket通知)
*
*
* 1413192605
* component_verify_ticket
*
*
* POST数据示例(取消授权通知)
*
* 第三方平台appid
* 1413192760
* unauthorized
* 公众号appid
*
* POST数据示例(授权成功通知)
*
* 第三方平台appid
* 1413192760
* authorized
* 公众号appid
* 授权码(code)
* 过期时间
*
* POST数据示例(授权更新通知)
*
* 第三方平台appid
* 1413192760
* updateauthorized
* 公众号appid
* 授权码(code)
* 过期时间
*
*/
public function onComponentEventNotify()
{
$ret = $this->getWxComponent()->processEventNotify();
file_put_contents(ROOT_PATH.'data/wxopen/component_verify_ticket.txt', json_encode($ret).date('Y-m-d H:i:s').PHP_EOL,FILE_APPEND);
if (is_array($ret)) {
switch ($ret['InfoType']) {
case "component_verify_ticket":
$authName = "wxComponentVerifyTicket".$this->wxComponentAppId;
//$this->cache->setCache($authName,$ret['ComponentVerifyTicket'],-1);
$arr = ['v' => $ret['ComponentVerifyTicket'], 'et' =>-1];
file_put_contents(ROOT_PATH.'data/wxopen/'.$authName.".json",json_encode($arr));
break;
case "unauthorized":
// 移除授权缓存
$authName = "wxAppAccessToken" . $this->wxComponentAppId . "_" . $ret['AuthorizerAppid'];
$this->cache->removeCache($authName);
$authName = "wxAppRefreshToken" . $this->wxComponentAppId . "_" . $ret['AuthorizerAppid'];
$this->cache->removeCache($authName);
break;
case "authorized":
//授权成功通知
$this->authorizeCallbackProcess($ret['AuthorizationCode'], $ret['AuthorizationCodeExpiredTime']);
break;
case "updateauthorized":
//授权更新通知
$this->authorizeCallbackProcess($ret['AuthorizationCode'], $ret['AuthorizationCodeExpiredTime']);
break;
}
}
return $ret;
}
/**
* 到授权后的公众号对象,代理处理公众号实现业务操作
* @param string $appId 公众号appId
* @return \Wechat2|boolean
*/
public function getWechat($appId)
{
try {
$cfg = $this->wxComponentConfig;
$appAccessToken = $this->getAppAccessToken($appId);
if (!$appAccessToken) {
return false;
}
$Wechat2_options = array(
'token' => $cfg['token'],
'encodingaeskey' => $cfg['encodingAesKey'],
'appid' => $cfg['component_appid'],
'appsecret' => $cfg['component_appsecret'],
'access_token' => $appAccessToken,
);
return new \Wechat2($Wechat2_options);
} catch (\Throwable $e) {
$this->log($e->__toString());
return false;
}
// $_ins[$appId] = new Wechat2($Wechat2_options);
// return $_ins[$appId];
}
public function componentTest($authCode)
{
try {
$componentAccessToken = $this->getComponentAccessTocken();
//使用授权码换取公众号的授权信息
$authInfo = $this->getWxComponent()->getWxAuthInfo($componentAccessToken, $authCode);
if (!$authInfo) {
return array('code' => $this->getWxComponent()->errCode, 'msg' => $this->getWxComponent()->errMsg);
}
return $authInfo;
} catch (\Throwable $e) {
return [
'code' => $e->getCode(),
'msg' => $e->getMessage(),
];
}
}
/**
* 判断是否授权公众号是否有效,如果授权过期或者公众号取消授权,则返回false。
* @param string $appId 授权的公众号
* @return bool|string
*/
public function isValidAuthorizedAppId($appId)
{
return $this->getAppAccessToken($appId);
}
/**
* 得到授权公众号的接口调用凭据
* @param string $appId 授权公众号AppId
* @return bool|string 接口调用凭据
*/
public function getAppAccessToken($appId)
{
$authName = "wxAppAccessToken" . $this->wxComponentAppId . "_" . $appId;
$appAccessToken = $this->cache->getCache($authName);
if ($appAccessToken) {
return $appAccessToken;
}
$componentAccessToken = $this->getComponentAccessTocken();
$authName = "wxAppRefreshToken" . $this->wxComponentAppId . "_" . $appId;
$appRefreshToken = $this->cache->getCache($authName);
if(empty($appRefreshToken)){
$appRefreshToken = Db::table('cps_wxmp')->where('appid',$appId)->value('authorizer_refresh_token');
}
if (!$appRefreshToken) {
return false;
}
$refreshTokenInfo = $this->getWxComponent()->getWxAccessToken($componentAccessToken, $appId, $appRefreshToken);
if (!$refreshTokenInfo) {
return false;
}
$authName = "wxAppAccessToken" . $this->wxComponentAppId . "_" . $appId;
$this->cache->setCache($authName, $refreshTokenInfo['authorizer_access_token'], $refreshTokenInfo['expires_in']);
$authName = "wxAppRefreshToken" . $this->wxComponentAppId . "_" . $appId;
$this->cache->setCache($authName, $refreshTokenInfo['authorizer_refresh_token'],-1);
return $refreshTokenInfo['authorizer_access_token'];
}
/**
* 得到预授权码
* @return string
*/
protected function getPreAuthCode()
{
$authName = "wxPreAuthCode" . $this->wxComponentAppId;
$preAuthCode = $this->cache->getCache($authName);
if ($preAuthCode) {
return $preAuthCode;
}
$componentAccessToken = $this->getComponentAccessTocken();
$preAuthCodeArr = $this->getWxComponent()->getPreauthCode($componentAccessToken);
$this->cache->setCache($authName, $preAuthCodeArr['pre_auth_code'], $preAuthCodeArr['expires_in'] - 10);
return $preAuthCodeArr['pre_auth_code'];
}
/**
* 得到接口调用凭据
* @return bool|string
*/
protected function getComponentAccessTocken()
{
$authName = "wxComponentAccessTocken" . $this->wxComponentAppId;
$componentAccessTocken = $this->cache->getCache($authName);
if ($componentAccessTocken) {
return $componentAccessTocken;
}
$accessArr = $this->getWxComponent()->getAccessToken();
$this->cache->setCache($authName, $accessArr['component_access_token'], $accessArr['expires_in'] - 10);
return $accessArr['component_access_token'];
}
/**
* 得到微信服务器定时推过来的component_verify_ticket
* @return bool|string
* @throws \Exception
*/
protected function getComponentVerifyTicket()
{
$authName= "wxComponentVerifyTicket" . $this->wxComponentAppId;
$ComponentVerifyTicket = $this->cache->getCache($authName);
return $ComponentVerifyTicket;
}
/**
* 代公众号发起网页授权 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 $this->getWxComponent()->getOauthRedirect($appId, $callback, $state, $scope);
}
/**
* 代公众号发起网页授权 回调URL时,通过code获取Access Token
* @param string $appId
* @return array {access_token,expires_in,refresh_token,openid,scope}
*/
public function getOauthAccessTokenForCode($appId)
{
$ret = $this->getWxComponent()->getOauthAccessToken($appId, $this->getComponentAccessTocken());
if ($ret) {
$authName = "wxComponentOauthToken" . $this->wxComponentAppId . "_" . $appId;
$this->cache->setCache($authName, $ret['access_token'], $ret['expires_in']);
$authName = "wxComponentOauthRefreshToken" . $this->wxComponentAppId . "_" . $appId;
$this->cache->setCache($authName, $ret['refresh_token'], 30 * 24 * 2600); // refresh_token30天有效期
}
return $ret;
}
/**
* 代公众号发起网页授权 获取缓存的accessToken,如果为缓存没有,则通过刷新token重新获取
* @param $appId
* @return bool|string
*/
public function getOauthAccessToken($appId)
{
$authName = "wxComponentOauthToken" . $this->wxComponentAppId . "_" . $appId;
$accessToken = $this->cache->getCache($authName);
if ($accessToken) {
return $accessToken;
}
$authName = "wxComponentOauthRefreshToken" . $this->wxComponentAppId . "_" . $appId;
$refreshToken = $this->cache->getCache($authName);
if (!$refreshToken) {
return false;
}
$ret = $this->getWxComponent()->getOauthRefreshToken($appId, $refreshToken, $this->getComponentAccessTocken());
if ($ret) {
$authName = "wxComponentOauthToken" . $this->wxComponentAppId . "_" . $appId;
$this->cache->setCache($authName, $ret['access_token'], $ret['expires_in']);
$authName = "wxComponentOauthRefreshToken" . $this->wxComponentAppId . "_" . $appId;
$this->cache->setCache($authName, $ret['refresh_token'], 30 * 24 * 2600); // refresh_token30天有效期
}
return $ret['access_token'];
}
/**
* 代公众号发起网页授权,取得openid
* @param string $appId
* @param string $callbackUrl 网页回调URL
* @return bool|string
*/
public function getOauthOpenId($appId, $callbackUrl = null)
{
if (!isset($_GET['code'])) {
$url = $this->getOauthRedirect($appId, $callbackUrl);
header("Location: {$url}");
exit;
} else {
$authInfo = $this->getOauthAccessTokenForCode($appId);
return $authInfo['openid'];
}
}
/**
* 获取授权后的用户资料
* @param string $accessToken
* @param string $openid
* @return array {openid,nickname,sex,province,city,country,headimgurl,privilege,[unionid]}
* 注意:unionid字段 只有在用户将公众号绑定到微信开放平台账号后,才会出现。建议调用前用isset()检测一下
*/
public function getOauthUserinfo($accessToken, $openid)
{
return $this->getWxComponent()->getOauthUserinfo($accessToken, $openid);
}
/**
* 代公众号使用JS SDK时,JS SDK的配置信息
* @param string $appId 公众号appId 必须经过授权过,并缓存了access_token
* @param string $url 当前页面URL
* @return array|bool
*/
public function getJsSign($appId, $url)
{
$jsTicket = $this->getJsTicket($appId);
if ($jsTicket) {
$weObj = $this->getWechat($appId);
$weObj->jsapi_ticket = $jsTicket;
$signPackage = $weObj->getJsSign($url);
$signPackage['appId'] = $appId;
return $signPackage;
}
return false;
}
/**
* 代替公众号使用JS SDK时,获取jsapi_ticket
* @param string $appId 公众号appId 必须经过授权过,并缓存了access_token
* @return bool
*/
public function getJsTicket($appId)
{
$authName = "wxComponentJsTicket" . $this->wxComponentAppId . "_" . $appId;
$jsTicket = $this->cache->getCache($authName);
if ($jsTicket) {
return $jsTicket;
}
$weObj = $this->getWechat($appId);
if (!$weObj) {
return false;
}
$json = $weObj->getJsTicket2($appId);
if ($json) {
$this->cache->setCache($authName, $json['ticket'], $json['expires_in']);
return $json['ticket'];
}
return false;
}
/**
* 获取拉取适用卡券列表的签名包 用于js sdk 的 wx.chooseCard
* @param string $appId 公众号appid
* @param string $card_type 卡券的类型,不可为空,官方jssdk文档说这个值可空,但签名验证工具又必填这个值,官方文档到处是坑,
* GROUPON团购券 CASH代金券 DISCOUNT折扣券 GIFT 优惠券 GENERAL_COUPON MEMBER_CARD
* @param string $card_id 卡券的ID,可空
* @param string $code 卡券自定义code
* @param string $location_id 卡券的适用门店ID,可空
* @return array|bool
*
* wx.chooseCard({
* shopId: '', // 门店Id
* cardType: '', // 卡券类型
* cardId: '', // 卡券Id
* timestamp: 0, // 卡券签名时间戳
* nonceStr: '', // 卡券签名随机串
* signType: '', // 签名方式,默认'SHA1'
* cardSign: '', // 卡券签名
* success: function (res) {
* var cardList= res.cardList; // 用户选中的卡券列表信息
* }
* });
*/
public function getChooseCardSign($appId, $card_type = '', $card_id = '', $code = '', $location_id = '')
{
$jsCardTicket = $this->getJsCardTicket($appId);
if ($jsCardTicket) {
$weObj = $this->getWechat($appId);
$weObj->api_ticket = $jsCardTicket;
$signPackage = $weObj->getCardSign($card_type, $card_id, $code, $location_id, $appId);
return $signPackage;
}
return false;
}
/**
* 获取添加卡券的签名信息 用于js sdk的wx.addCard
* @param string $appId 公众号appid
* @param string $card_id 卡券的ID,可空
* @param string $code 卡券自定义code
* @param string $openid 用户openid
* @param string $balance 用户余额
* @return array|bool
*
* wx.addCard({
* cardList: [{
* cardId: '',
* cardExt: ''
* }], // 需要添加的卡券列表
* success: function (res) {
* var cardList = res.cardList; // 添加的卡券列表信息
* }
* });
*/
public function getAddCardExt($appId, $card_id = '', $code = '', $openid = '', $balance = '')
{
$jsCardTicket = $this->getJsCardTicket($appId);
if ($jsCardTicket) {
$weObj = $this->getWechat($appId);
$weObj->api_ticket = $jsCardTicket;
$timestamp = 0;
$nonceStr = '';
$signPackage = $weObj->getAddCardSign($card_id, $code, $timestamp, $nonceStr, $openid, $balance);
$ext = array(
'code' => $code,
'openid' => $openid,
'timestamp' => strval($signPackage['timestamp']),
'signature' => $signPackage['cardSign'],
'nonce_str' => strval($signPackage['nonceStr']),
);
return $ext;
}
return false;
}
/**
* 代替公众号使用卡券时,获取卡券ticket
* @param $appId
* @return bool
*/
public function getJsCardTicket($appId)
{
$authName = "wxComponentJsCardTicket" . $this->wxComponentAppId . "_" . $appId;
$jsCardTicket = $this->cache->getCache($authName);
if ($jsCardTicket) {
return $jsCardTicket;
}
$weObj = $this->getWechat($appId);
if (!$weObj) {
return false;
}
$json = $weObj->getJsCardTicket($appId);
if ($json) {
$this->cache->setCache($authName, $json['ticket'], $json['expires_in']);
return $json['ticket'];
}
return false;
}
/**
* 代设置小程序域名
* @param $appID
* @param $params
* @return bool
*/
public function setMiniProgramDomain($appID, $params)
{
return $this->getWxComponent()->setMiniProgramDomain($appID, $params, $this->getAppAccessToken($appID));
}
/**
* 给小程序上传代码
* @param $appID
* @param $params
* @return bool
*/
public function uploadTemplate($appID, $params)
{
return $this->getWxComponent()->uploadTemplate($params, $this->getAppAccessToken($appID));
}
/**
* 获取草稿模板列表
* @return bool|mixed
*/
public function getDraftTemplateList()
{
return $this->getWxComponent()->getDraftTemplateList($this->getComponentAccessTocken());
}
/**
* 获取上线模板列表
* @return bool|mixed
*/
public function getTemplateList()
{
return $this->getWxComponent()->getTemplateList($this->getComponentAccessTocken());
}
/**
* 将草稿里面的模板上线
* @param $draftID
* @return bool
*/
public function auditDraftTemplate($draftID)
{
return $this->getWxComponent()->auditDraftTemplate($this->getComponentAccessTocken(), $draftID);
}
/**
* 删除模板库里面的模板
* @param $templateID
* @return bool
*/
public function deleteTemplate($templateID)
{
return $this->getWxComponent()->deleteTemplate($this->getComponentAccessTocken(), $templateID);
}
/**
* 获取体验二维码
* @param $appid
* @return bool|string
*/
public function getQrCode($appid)
{
return $this->getWxComponent()->getQrCode($this->getAppAccessToken($appid));
}
/**
* 获取小程序的分类配置
* @param $appid
* @return bool|mixed
*/
public function getCategory($appid)
{
return $this->getWxComponent()->getCategory($this->getAppAccessToken($appid));
}
/**
* 获取第三方提交的代码页面配置
* @param $appid
* @return bool|mixed
*/
public function getPages($appid)
{
return $this->getWxComponent()->getPages($this->getAppAccessToken($appid));
}
/**
* 提交代码审核
* @param $appid
* @param $params
* @return bool|mixed
*/
public function auditTemplate($appid, $params)
{
return $this->getWxComponent()->auditTemplate($params, $this->getAppAccessToken($appid));
}
/**
* 绑定测试用户
* @param $appid
* @param $username
* @return bool
*/
public function bindTestUser($appid, $username)
{
return $this->getWxComponent()->bindTestUser(['wechatid' => $username], $this->getAppAccessToken($appid));
}
/**
* 解绑定测试用户
* @param $appid
* @param $username
* @return bool
*/
public function unbindTestUser($appid, $username)
{
return $this->getWxComponent()->unbindTestUser(['wechatid' => $username], $this->getAppAccessToken($appid));
}
/**
* 全网发布小程序代码
* @param $appid
* @return bool
*/
public function publishTemplate($appid)
{
return $this->getWxComponent()->publishTemplate($this->getAppAccessToken($appid));
}
/**
* 获取小程序序代码审核状态
* @param $appid
* @param $auditid
* @return bool|mixed
*/
public function auditStatus($appid, $auditid)
{
return $this->getWxComponent()->getAuditStatus($auditid, $this->getAppAccessToken($appid));
}
}