Files
youlegames/codes/agent/game/api/framework/class/weixin.account.class.php
2026-03-15 01:27:05 +08:00

504 lines
20 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
defined('IN_IA') or exit('Access Denied');
load()->func('communication');
class WeiXinAccount
{
protected $account = null;// 公众号信息
public function __construct($account = array())
{
// 初始化子公号信息
$this->account = $account;
}
// 通过微信接口获得粉丝信息
public function fansQueryInfo($uniid, $db, $pdo, $isOpen = true)
{
if ($isOpen) {
$openid = $uniid;
} else {
exit('error');
}
$token = $this->getAccessToken($db, $pdo);
if (is_error($token)) {
return $token;
}
$url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token={$token}&openid={$openid}&lang=zh_CN";
$response = ihttp_get($url);
if (is_error($response)) {
return error(-1, "访问公众平台接口失败, 错误: {$response['message']}");
}
$result = @json_decode($response['content'], true);
if (empty($result)) {
return error(-1, "接口调用失败, 元数据: {$response['meta']}");
} elseif (!empty($result['errcode'])) {
return error(-1, "访问微信接口错误, 错误代码: {$result['errcode']}, 错误信息: {$result['errmsg']},错误详情:{$this->error_code($result['errcode'])}");
}
return $result;
}
public function fansBatchQueryInfo($data, $db)
{
if (empty($data)) {
return error(-1, '粉丝openid错误');
}
foreach ($data as $da) {
$post[] = array(
'openid' => trim($da),
'lang' => 'zh-CN',
);
}
$data = array();
$data['user_list'] = $post;
$token = $this->getAccessToken($db);
if (is_error($token)) {
return $token;
}
$url = "https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token={$token}";
$response = ihttp_post($url, json_encode($data));
if (is_error($response)) {
return error(-1, "访问公众平台接口失败, 错误: {$response['message']}");
}
$result = @json_decode($response['content'], true);
if (empty($result)) {
return error(-1, "接口调用失败, 元数据: {$response['meta']}");
} elseif (!empty($result['errcode'])) {
return error(-1, "访问微信接口错误, 错误代码: {$result['errcode']}, 错误信息: {$result['errmsg']},错误详情:{$this->error_code($result['errcode'])}");
}
return $result['user_info_list'];
}
// 获得微信调用accessToken
public function getAccessToken($db, $pdo)
{
load()->func('communication');
// 此处改动过
$cachekey = "accesstoken:{$this->account['key']}";
$cache = cache_load($cachekey, $db);
$timeFlag = $cache['expire'] > TIMESTAMP;
if (!empty($cache) && !empty($cache['token']) && $cache['expire'] > TIMESTAMP) {
$this->account['access_token'] = $cache;
return $cache['token'];
}
if (empty($this->account['key']) || empty($this->account['secret'])) {
return error('-1', '未填写公众号的 appid 或 appsecret');
}
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->account['key']}&secret={$this->account['secret']}";
$content = ihttp_get($url);
if (is_error($content)) {
return error('-2', '获取微信公众号授权失败, 请稍后重试!错误详情: ' . $content['message']);
}
$token = @json_decode($content['content'], true);
if (empty($token) || !is_array($token) || empty($token['access_token']) || empty(
$token['expires_in'])
) {
$errorinfo = substr($content['meta'], strpos($content['meta'], '{'));
$errorinfo = @json_decode($errorinfo, true);
return error('-3', '获取微信公众号授权失败, 请稍后重试! 公众平台返回原始数据为: 错误代码-' . $errorinfo['errcode'] . ',错误信息-' . $errorinfo['errmsg']);
}
$record = array();
$record['token'] = $token['access_token'];
$record['expire'] = TIMESTAMP + $token['expires_in'] - 200;
$this->account['access_token'] = $record;
cache_write($cachekey, $record, $db, $pdo);
return $record['token'];
}
// 通过微信接口获得调用微信JS接口的临时票据
public function getJsApiTicket($db, $pdo)
{
load()->func('communication');
// 首先从本地缓存中查询未过期的JSApi调用票据
$cachekey = "jsticket:{$this->account['key']}";
$cache = cache_load($cachekey, $db);
if (!empty($cache) && !empty($cache['ticket']) && $cache['expire'] > TIMESTAMP) {
return $cache['ticket'];
}
load()->func('communication');
// 获得微信调用accessToken
$access_token = $this->getAccessToken($db, $pdo);
if (is_error($access_token)) {
return $access_token;
}
// 生成签名之前必须先了解一下jsapi_ticketjsapi_ticket是公众号用于调用
// 微信JS接口的临时票据。正常情况下jsapi_ticket的有效期为7200秒
// 通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限
// 频繁刷新jsapi_ticket会导致api调用受限影响自身业务开发者必须在自
// 己的服务全局缓存jsapi_ticket。
$url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={$access_token}&type=jsapi";
$content = ihttp_get($url);
if (is_error($content)) {
return error(-1, '调用接口获取微信公众号 jsapi_ticket 失败, 错误信息: ' . $content['message']);
}
$result = @json_decode($content['content'], true);
if (empty($result) || intval(($result['errcode'])) != 0 || $result['errmsg'] != 'ok') {
return error(-1, '获取微信公众号 jsapi_ticket 结果错误, 错误信息: ' . $result['errmsg']);
}
$record = array();
$record['ticket'] = $result['ticket'];// 临时票据
$record['expire'] = TIMESTAMP + $result['expires_in'] - 200;// 到期时间
$this->account['jsapi_ticket'] = $record;
cache_write($cachekey, $record, $db, $pdo);// 将票据信息回写到缓存中进行缓存
return $record['ticket'];// 返回调用微信JS接口的临时票据
}
// 返回调用微信JS接口的配置数组
public function getJssdkConfig()
{
global $_W;
// 通过微信接口获得调用微信JS接口的临时票据
$jsapiTicket = $this->getJsApiTicket();
if (is_error($jsapiTicket)) {
$jsapiTicket = $jsapiTicket['message'];
}
// 生成指定长度的随机字符
// 第一个参数:要生成的长度
// 第二个参数:是否生成数字形式
$nonceStr = random(16);
$timestamp = TIMESTAMP;// 当前系统时间
$url = $_W['siteurl'];// 当前脚本访问的全路径,包括参数(原始链接)
// 获得jsapi_ticket之后就可以生成JS-SDK权限验证的签名了。
// 拼接待签名的字符串
$string1 = "jsapi_ticket={$jsapiTicket}&noncestr={$nonceStr}&timestamp={$timestamp}&url={$url}";
// 对字符串进行sha1签名得到signature
$signature = sha1($string1);
// 组装调用配置
$config = array(
"appId" => $this->account['key'],
// 子公号AppId
"nonceStr" => $nonceStr,
// 随机字符
"timestamp" => "$timestamp",
// 当前时间戳
"signature" => $signature,
// 签名
);
// 定义是否为开发者模式
//if(DEVELOPMENT) {
$config['url'] = $url;
$config['string1'] = $string1;
$config['name'] = $this->account['name'];
//}
return $config;
}
// 返回调用微信JS接口的配置数组
public function getAjaxJssdkConfig($siteurl, $db, $pdo)
{
// 通过微信接口获得调用微信JS接口的临时票据
$jsapiTicket = $this->getJsApiTicket($db, $pdo);
if (is_error($jsapiTicket)) {
$jsapiTicket = $jsapiTicket['message'];
}
// 生成指定长度的随机字符
// 第一个参数:要生成的长度
// 第二个参数:是否生成数字形式
$nonceStr = random(16);
$timestamp = TIMESTAMP;// 当前系统时间
$url = $siteurl;// 当前脚本访问的全路径,包括参数(原始链接)
// 获得jsapi_ticket之后就可以生成JS-SDK权限验证的签名了。
// 拼接待签名的字符串
$string1 = "jsapi_ticket={$jsapiTicket}&noncestr={$nonceStr}&timestamp={$timestamp}&url={$url}";
// 对字符串进行sha1签名得到signature
$signature = sha1($string1);
// 组装调用配置
$config = array(
"appId" => $this->account['key'], // 子公号AppId
"nonceStr" => $nonceStr, // 随机字符
"timestamp" => "$timestamp", // 当前时间戳
"signature" => $signature, // 签名
);
// 定义是否为开发者模式
return $config;
}
// 秘钥错误消息枚举,通过错误消息代码获得消息正文
private function encrypt_error_code($code)
{
$errors = array(
'40001' => '签名验证错误',
'40002' => 'xml解析失败',
'40003' => 'sha加密生成签名失败',
'40004' => 'encodingAesKey 非法',
'40005' => 'appid 校验错误',
'40006' => 'aes 加密失败',
'40007' => 'aes 解密失败',
'40008' => '解密后得到的buffer非法',
'40009' => 'base64加密失败',
'40010' => 'base64解密失败',
'40011' => '生成xml失败',
);
if ($errors[$code]) {
return $errors[$code];
} else {
return '未知错误';
}
}
public function error_code($code)
{
$errors = array(
'-1' => '系统繁忙',
'0' => '请求成功',
'40001' => '获取access_token时AppSecret错误或者access_token无效',
'40002' => '不合法的凭证类型',
'40003' => '不合法的OpenID',
'40004' => '不合法的媒体文件类型',
'40005' => '不合法的文件类型',
'40006' => '不合法的文件大小',
'40007' => '不合法的媒体文件id',
'40008' => '不合法的消息类型',
'40009' => '不合法的图片文件大小',
'40010' => '不合法的语音文件大小',
'40011' => '不合法的视频文件大小',
'40012' => '不合法的缩略图文件大小',
'40013' => '不合法的APPID',
'40014' => '不合法的access_token',
'40015' => '不合法的菜单类型',
'40016' => '不合法的按钮个数',
'40017' => '不合法的按钮个数',
'40018' => '不合法的按钮名字长度',
'40019' => '不合法的按钮KEY长度',
'40020' => '不合法的按钮URL长度',
'40021' => '不合法的菜单版本号',
'40022' => '不合法的子菜单级数',
'40023' => '不合法的子菜单按钮个数',
'40024' => '不合法的子菜单按钮类型',
'40025' => '不合法的子菜单按钮名字长度',
'40026' => '不合法的子菜单按钮KEY长度',
'40027' => '不合法的子菜单按钮URL长度',
'40028' => '不合法的自定义菜单使用用户',
'40029' => '不合法的oauth_code',
'40030' => '不合法的refresh_token',
'40031' => '不合法的openid列表',
'40032' => '不合法的openid列表长度',
'40033' => '不合法的请求字符,不能包含\uxxxx格式的字符',
'40035' => '不合法的参数',
'40038' => '不合法的请求格式',
'40039' => '不合法的URL长度',
'40050' => '不合法的分组id',
'40051' => '分组名字不合法',
'41001' => '缺少access_token参数',
'41002' => '缺少appid参数',
'41003' => '缺少refresh_token参数',
'41004' => '缺少secret参数',
'41005' => '缺少多媒体文件数据',
'41006' => '缺少media_id参数',
'41007' => '缺少子菜单数据',
'41008' => '缺少oauth code',
'41009' => '缺少openid',
'42001' => 'access_token超时',
'42002' => 'refresh_token超时',
'42003' => 'oauth_code超时',
'43001' => '需要GET请求',
'43002' => '需要POST请求',
'43003' => '需要HTTPS请求',
'43004' => '需要接收者关注',
'43005' => '需要好友关系',
'44001' => '多媒体文件为空',
'44002' => 'POST的数据包为空',
'44003' => '图文消息内容为空',
'44004' => '文本消息内容为空',
'45001' => '多媒体文件大小超过限制',
'45002' => '消息内容超过限制',
'45003' => '标题字段超过限制',
'45004' => '描述字段超过限制',
'45005' => '链接字段超过限制',
'45006' => '图片链接字段超过限制',
'45007' => '语音播放时间超过限制',
'45008' => '图文消息超过限制',
'45009' => '接口调用超过限制',
'45010' => '创建菜单个数超过限制',
'45015' => '回复时间超过限制',
'45016' => '系统分组,不允许修改',
'45017' => '分组名字过长',
'45018' => '分组数量超过上限',
'46001' => '不存在媒体数据',
'46002' => '不存在的菜单版本',
'46003' => '不存在的菜单数据',
'46004' => '不存在的用户',
'47001' => '解析JSON/XML内容错误',
'48001' => 'api功能未授权',
'50001' => '用户未授权该api',
'40070' => '基本信息baseinfo中填写的库存信息SKU不合法。',
'41011' => '必填字段不完整或不合法,参考相应接口。',
'40056' => '无效code请确认code长度在20个字符以内且处于非异常状态转赠、删除。',
'43009' => '无自定义SN权限请参考开发者必读中的流程开通权限。',
'43010' => '无储值权限,请参考开发者必读中的流程开通权限。',
'43011' => '无积分权限,请参考开发者必读中的流程开通权限。',
'40078' => '无效卡券,未通过审核,已被置为失效。',
'40079' => '基本信息base_info中填写的date_info不合法或核销卡券未到生效时间。',
'45021' => '文本字段超过长度限制,请参考相应字段说明。',
'40080' => '卡券扩展信息cardext不合法。',
'40097' => '基本信息base_info中填写的url_name_type或promotion_url_name_type不合法。',
'49004' => '签名错误。',
'43012' => '无自定义cell跳转外链权限请参考开发者必读中的申请流程开通权限。',
'40099' => '该code已被核销。',
'61005' => '缺少接入平台关键数据等待微信开放平台推送数据请十分钟后再试或是检查“授权事件接收URL”是否写错index.php?c=account&amp;a=auth&amp;do=ticket地址中的&amp;符号容易被替换成&amp;amp;',
'61023' => '请重新授权接入该公众号',
);
$code = strval($code);
if ($code == '40001' || $code == '42001') {
$cachekey = "accesstoken:{$this->account['acid']}";
cache_delete($cachekey);
return '微信公众平台授权异常, 系统已修复这个错误, 请刷新页面重试.';
}
if ($errors[$code]) {
return $errors[$code];
} else {
return '未知错误';
}
}
// 通过网页授权拉取用户详细信息,非静默授权
public function getOauthUserInfo($accesstoken, $openid)
{
// 网页授权接口调用凭证,注意此access_token与基础支持的access_token不同
$apiurl = "https://api.weixin.qq.com/sns/userinfo?access_token={$accesstoken}&openid={$openid}&lang=zh_CN";
$response = ihttp_get($apiurl);
if (is_error($response)) {
return $response;
}
return @json_decode($response['content'], true);
}
// 通过code换取网页授权access_token
public function getOauthInfo($code)
{
$url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->account['key']}&secret={$this->account['secret']}&code={$code}&grant_type=authorization_code";
$response = ihttp_get($url);
if (is_error($response)) {
return $response;
}
return @json_decode($response['content'], true);
}
// 返回网页静默授权的授权访问地址
// 参数1回调页面地址
// 参数2重定向后会带上state参数开发者可以填写a-zA-Z0-9的参数值最多128字节
public function getOauthCodeUrl($callback, $state = '')
{
return "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->account['key']}&redirect_uri={$callback}&response_type=code&scope=snsapi_base&state={$state}#wechat_redirect";
}
// 返回网页非静默授权的授权访问地址
// 参数1回调页面地址
// 参数2重定向后会带上state参数开发者可以填写a-zA-Z0-9的参数值最多128字节
public function getOauthUserInfoUrl($callback, $state = '')
{
return "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->account['key']}&redirect_uri={$callback}&response_type=code&scope=snsapi_userinfo&state={$state}#wechat_redirect";
}
// 发送模板消息
// 参数1要发送的目标用户openId
// 参数2模板标识ID
// 参数3要发送的模板消息内容
// 参数4模板消息跳转地址可以留空
// 参数5模板消息背景颜色(新版本不支持)
public function sendTplNotice($db, $pdo, $touser, $template_id, $postdata, $url = '', $topcolor = '#FF683F')
{
// 要发送给的目标粉丝openid
if (empty($touser))
return error(25000, '参数错误,粉丝openid不能为空');
// 要发送的模板标识
if (empty($template_id))
return error(250001, '参数错误,模板标示不能为空');
// 模板消息内容
if (empty($postdata) || !(is_array($postdata) || is_object($postdata)))
return error(250002, '参数错误,请根据模板规则完善消息内容');
// 获得微信调用accessToken
$token = $this->getAccessToken($db, $pdo);
if (is_error($token)) {
$access_errno = $token['errno'];
return error(250003, '获取AccessToken失败');
}
$data = [
'touser' => trim($touser), /// 要发送给的目标粉丝openid
'template_id' => trim($template_id), // 要发送的模板标识
'url' => trim($url), // 模板消息跳转地址(留空IOS点击显示空白 Android 无法点击 非空跳转至该URL)
//'topcolor' => trim($topcolor), /// 模板背景颜色(新版本中不再支持)
'data' => $postdata, /// 模板消息内容
];
$data = json_encode($data);
// 发送模板消息接口地址
$post_url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={$token}";
$response = ihttp_request($post_url, $data, false);
if (is_error($response))
return error(250004, "访问公众平台接口失败, 错误: {$response['message']}");
$result = @json_decode($response['content'], true);
if (empty($result))
return error(250006, "接口调用失败, 元数据: {$response['meta']}");
elseif (!empty($result['errcode']))
return error(250007, "访问微信接口错误, 错误代码: {$result['errcode']}, 错误信息: {$result['errmsg']},信息详情:" . $this->error_code($result['errcode']));
else
return true;
}
public function SendTemplateMessage($to_user, $template_id, $target_url, $parameter, $color = '#7b68ee', $db = null, $pdo = null)
{
/// 要发送给的目标粉丝openid
if (empty($to_user))
return error(25000, '参数错误,粉丝openid不能为空');
/// 要发送的模板标识
if (empty($template_id))
return error(250001, '参数错误,模板标示不能为空');
/// 模板消息内容
if (empty($parameter) || !(is_array($parameter) || is_object($parameter)))
return error(250002, '参数错误,请根据模板规则完善消息内容');
/// 获得微信调用accessToken
$token = $this->getAccessToken($db, $pdo);
if (is_error($token))
return error(250003, '获取AccessToken失败');
$data = [];
foreach ((array)$parameter as $key => $value) {
if (is_array($value) || is_object($value))
$data[$key] = (array)$value;
else
$data[$key] = ['value' => $value, 'color' => $color, ];
}
$data = [
'touser' => trim($to_user), /// 要发送给的目标粉丝openid
'template_id' => trim($template_id), // 要发送的模板标识
'url' => trim($target_url), // 模板消息跳转地址(留空IOS点击显示空白 Android 无法点击 非空跳转至该URL)
'data' => $data, /// 模板消息内容
];
$data = json_encode($data);
/// 发送模板消息接口地址
$post_url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={$token}";
$response = ihttp_request($post_url, $data, false);
if (is_error($response))
return error(250004, "访问公众平台接口失败, 错误: {$response['message']}");
$result = @json_decode($response['content'], true);
if (empty($result))
return error(250006, "接口调用失败, 元数据: {$response['meta']}");
elseif (!empty($result['errcode']))
return error(250007, "访问微信接口错误, 错误代码: {$result['errcode']}, 错误信息: {$result['errmsg']},信息详情:" . $this->error_code($result['errcode']));
else
return true;
}
}