适用于小程序,小游戏。
也适用于公众号的接入,但是并没有什么卵用,因为公众号的对话服务,只能接收消息,不能向用户发消息,开通发消息功能需要微信认证,而个人公众号不支持微信认证。看看给用户发消息会返回什么:{"errcode":48001,"errmsg":"api unauthorized rid: 62922e5a-050daf94-1dcd13f6"},api?unauthorized,就是权限不足。另外,对公众号发文也没有卵用,因为无法获取群发文的列表,微信客服也许都不禁笑了,你想要文章列表做什么?看看我们的客服是怎么说的:
公众号技术运营专员-livia 公众号技术运营专员-livia 05-05 你好,你可以提供相关发表记录的页面截图,查看发布这里是否有文章。获取发布列表:只有通过草稿箱进行自由发布的内容才能获取,话题发布和视频发布的内容,以及已群发的内容无法通过此接口获取
参见:接入概述 | 微信开放文档
Step1
- 如果是公众号:在 设置与开发=> 基本配置;
- 如果是小游戏:在?开发管理=>开发设置=>消息推送。
设置 URL, Token, EncodingAESKey:
- URL 是开发者用来接收微信消息和事件的接口URL。【就是服务器上的响应程序,例如: https://www.xxx.com/connect.php】
- Token可由开发者可以任意填写,用作生成签名(该 Token 会和接口 URL 中包含的 Token 进行比对,从而验证安全性)。【以下用宏定义 TOKEN】
- EncodingAESKey由开发者手动填写或随机生成,将用作消息体加解密密钥。【如果选用明文模式则用不上】
下载代码并放置在开发者服务器上。PHP示例代码下载:下载
Step2
然后在 URL?内(就是 https://www.xxx.com/connect.php 文件内)放置如下函数:
include_once "wxBizMsgCrypt.php";
define("TOKEN", "XXXXXX"); //填写自己的token
if (isset($_GET['echostr'])) { //校验服务器地址URL
valid();
}else{
// 业务代码
}
function valid()
{
$echoStr = $_GET["echostr"];
if(checkSignature()){
header('content-type:text');
echo $echoStr;
exit;
}else{
echo $echoStr.'+++'.TOKEN;
exit;
}
}
function checkSignature()
{
$signature = $_GET["signature"];
$timestamp = $_GET["timestamp"];
$nonce = $_GET["nonce"];
$token = TOKEN;
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr, SORT_STRING);
$tmpStr = implode( $tmpArr );
$tmpStr = sha1( $tmpStr );
if( $tmpStr == $signature ){
return true;
}else{
return false;
}
}
其原理是:微信服务器向 URL 发起请求,携带了 signature, timestamp, nonce, echostr?四个参数。URL 收到后,使用 TOKEN, timestamp, nonce 组合并加密得到 tmpStr,如果 tmpStr==signature,则向微信服务器返回 echostr,否则返回 failure。加密程序在示例代码 wxBizMsgCrypt.php 中,它又引用了以下文件:
include_once "sha1.php";
include_once "xmlparse.php";
include_once "pkcs7Encoder.php";
include_once "errorCode.php";
因此服务器端至少需要以上五个文件。
Step3
回到微信后台,如果是公众号:
- 在 设置与开发=> 公众号开发信息,设置白名单,就是开发者服务器 IP 地址;
- 在 设置与开发=> 基本配置,提交 Step1 填写的信息,如果通过 Step2 代码的验证,则开通成功。
如果是小游戏:
- 在 开发管理=>开发设置=>开发者 ID,设置白名单,就是开发者服务器 IP 地址;
- 在 开发管理=>开发设置=>消息推送,提交 Step1 填写的信息,如果通过 Step2 代码的验证,则开通成功。
Step4
小游戏内转到客服对话:判断条件满足后,直接调用 onContact(),就弹出对话框:「即将进入”xxx”客服会话」;
然后,用户在会话界面提交的消息,将被转发到 URL,并在 URL 中得到处理,再回复给用户。
下面才是重点:
Step 4.1
用户发来的消息,格式如下:
// 根据实测,postStr 的格式如下:
{
"ToUserName": "xxx",
"FromUserName": "xxx",
"CreateTime": ####,
"MsgType": "text",
"Content": "xxx",
"MsgId": ####
}
PHP 获取用户消息:
$postStr = file_get_contents('php://input');
//此处推荐使用file_get_contents('php://input')获取后台post过来的数据
$postArr = json_decode($postStr,true);
if(!empty($postArr['MsgType']){
if($postArr['MsgType'] == 'text'){ //用户发送文本消息
// TODO
}elseif($postArr['MsgType'] == 'image'){ //用户发送图文消息
// TODO
}elseif($postArr['MsgType'] == 'event' && $postArr['Event']=='user_enter_tempsession'){ //用户进入客服
// TODO
}else{
exit('error');
}
}
其中,postArr['FromUserName'],就是用户的 openid。
Step 4.2
向用户发送消息:
简单地说,要发文字消息,或图片,首先把目标用户(postArr['FromUserName'], openid)、消息类型(text/image)、消息(text/image)内容打成一个 json 包,然后再调用 requestAPI 发送。图片用 media_id 表示,可以通过网页访问 URL 从服务器向微信上传图片,记录并返回 media_id。getAccessToken(APPID,SECRET) 需要 APPID 和 SECRET。
注意,access_token 是一个具有时效性的口令,每次获取前要检查是否已经有,获取了新的之后还要保存。
function sendText2User($usr,$txt){
$data=array(
"touser"=>$usr,
"msgtype"=>"text",
"text"=>array("content"=>$txt)
);
$json = json_encode($data,JSON_UNESCAPED_UNICODE); //php5.4+
requestAPI($json);
}
function sendImage2User($usr,$media_id){
$data=array(
"touser"=>$usr,
"msgtype"=>"image",
"image"=>array("media_id"=>$media_id)
);
$json = json_encode($data,JSON_UNESCAPED_UNICODE); //php5.4+
requestAPI($json);
}
function requestAPI($json){
$access_token = get_accessToken();
/*
* POST发送https请求客服接口api
*/
if(!empty($access_token)){
$url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=".$access_token;
//以'json'格式发送post的https请求
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
if (!empty($json)){
curl_setopt($curl, CURLOPT_POSTFIELDS,$json);
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
//curl_setopt($curl, CURLOPT_HTTPHEADER, $headers );
$output = curl_exec($curl);
if (curl_errno($curl)) {
echo 'Errno'.curl_error($curl);//捕抓异常
}
curl_close($curl);
if($output == 0){
echo 'success';
exit;
}
}else{
echo "";
exit;
}
}
/* 调用微信api,获取access_token,有效期7200s*/
function get_accessToken(){
$token = "";
$tokenarr = readDataFile2Array("token.txt");
$tokentime0 = $tokenarr[1];
$tokentime1 = gmmktime();
if($tokentime1-$tokentime0 < 7200 && !empty($tokenarr[0])){
$token = $tokenarr[0];
}else{
$token = getAccessToken(APPID,SECRET);
$tokentime = gmmktime();
$tokendate = date('Y-m-d H:i:s');
writeString2File($token."\r\n".$tokentime."\r\n".$tokendate,"token.txt");
}
return $token;
}
function getAccessToken($appid,$secret){
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appid}&secret={$secret}";
$res = curl_get($url);
$res = json_decode($res,1);
if($res['errcode']!=0) throw new Exception($res['errmsg']);
return $res['access_token'];
}
function curl_get($url) {
$headers = array('User-Agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36');
$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
}
curl_setopt($oCurl, CURLOPT_TIMEOUT, 20);
curl_setopt($oCurl, CURLOPT_URL, $url);
curl_setopt($oCurl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );
$sContent = curl_exec($oCurl);
$aStatus = curl_getinfo($oCurl);
curl_close($oCurl);
if(intval($aStatus["http_code"])==200){
return $sContent;
}else{
return false;
}
}
function getMediaId_image($token, $filepath){
//$url = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=".$token."&type=image";
$url = "https://api.weixin.qq.com/cgi-bin/media/upload?access_token=".$token."&type=image";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_NOBODY, 0); //只取body头
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
//发送 POST 请求
curl_setopt($ch, CURLOPT_POST, true);
//全部数据使用HTTP协议中的 "POST" 操作来发送。
if (class_exists('\CURLFile')){
curl_setopt($ch, CURLOPT_SAFE_UPLOAD, true);
$data = array('media' => new \CURLFile($filepath));//
}else{
curl_setopt($ch,CURLOPT_SAFE_UPLOAD,false);
$data = array('media'=>'@'.$filepath);
}
//$data = array('media'=>'@'.$filepath);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$res = curl_exec( $ch );
curl_close( $ch );
if($res){
$res = json_decode($res,true);
return $res;
}
else return null;
}
附1 ?用户登录
?在用户登录小程序、小游戏的时候,就已经得到了 openid,方法如下:
在前端,用户登录时,获得用户的 code,然后发送给本小程序/小游戏服务器,请求微信得到 openid。
前端代码:
function getOpenId() {
wx.login({
success(res) {
//console.log('code===', res.code)
wx.request({
url: 'https://www.xxx.com/getOpenId.php',
data: {
code: res.code
},
success(res1) {
userInfo["openId"] = res1.data
//console.log('获取成功:' + userInfo["openId"] )
getUserInfo()
},
fail(res2) {
//console.log('获取失败:' + res2)
}
})
}
})
}
function getUserInfo(){
let exportJson = {};
let sysInfo = wx.getSystemInfoSync();
//获取微信界面大小
let width = sysInfo.screenWidth;
let height = sysInfo.screenHeight;
wx.getSetting({
success(res) {
//console.log(res.authSetting);
if (res.authSetting["scope.userInfo"]) {
//console.log("用户已授权");
wx.getUserInfo({
success(res) {
//console.log(res);
exportJson.userInfo = res.userInfo;
//此时可进行登录操作
userInfo["nickName"] = res.userInfo.nickName
userInfo["avatarUrl"] = res.userInfo.avatarUrl
userInfo["language"] = res.userInfo.language
userInfo["gender"] = res.userInfo.gender
userInfo["country"] = res.userInfo.country
userInfo["province"] = res.userInfo.province
recordLogin()
}
});
} else {
//console.log("用户未授权");
let button = wx.createUserInfoButton({
type: 'text',
text: '',
style: {
left: 0,
top: 0,
width: width,
height: height,
backgroundColor: '#00000000',//最后两位为透明度
color: '#ffffff',
fontSize: 20,
textAlign: "center",
lineHeight: height,
}
});
button.onTap((res) => {
if (res.userInfo) {
//console.log("用户授权:", res);
exportJson.userInfo = res.userInfo;
//此时可进行登录操作
button.destroy();
userInfo["nickName"] = res.userInfo.nickName
userInfo["avatarUrl"] = res.userInfo.avatarUrl
userInfo["language"] = res.userInfo.language
userInfo["gender"] = res.userInfo.gender
userInfo["country"] = res.userInfo.country
userInfo["province"] = res.userInfo.province
recordLogin()
} else {
//console.log("用户拒绝授权:", res);
// TODO
}
});
}
}
})
}
function recordLogin(){
var curTime = new Date();
var loginTime = util.formatTime(curTime);
wx.request({
url: 'https://XXX/userLogin.php', //仅为示例,并非真实的接口地址
data: {
openId: userInfo["openId"],
nickName: userInfo["nickName"],
avatarUrl: userInfo["avatarUrl"],
language: userInfo["language"],
gender: userInfo["gender"],
country: userInfo["country"],
province: userInfo["province"],
loginTime: loginTime,
},
header: {
'content-type': 'application/json' // 默认值
},
success(res) {
//console.log(res.data)
// TODO
}
})
}
后端代码:即前端里的?https://www.xxx.com/getOpenId.php
function getOpenid() {
$code = $_GET['code'];//小程序传来的code值
$appid = 'XXX';//小程序的appid
$appSecret = 'XXX';// 小程序的$appSecret
$wxUrl = 'https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code';
$getUrl = sprintf($wxUrl, $appid, $appSecret, $code);//把appid,appsecret,code拼接到url里
$result = curl_get($getUrl);//请求拼接好的url
$wxResult = json_decode($result, true);
if (empty($wxResult)) {
echo '获取openid时异常,微信内部错误';
} else {
$loginFail = array_key_exists('errcode', $wxResult);
if ($loginFail) {//请求失败
var_dump($wxResult);
} else {//请求成功
$openid = $wxResult['openid'];
//echo "获取openid成功成功:" . $openid;
echo $openid;
}
}
}
附2 ?关于口令的使用
上述过程中使用了多种口令:
- 开通接口:仅需要 TOKEN;
- 向用户发消息,需要 access_token;
- 上传图片,需要?access_token;
- 获取 access_token,需要 APPID, APPSECRET;【注意,access_token 是一个具有时效性的口令,每次获取前要检查是否已经有,获取了新的之后还要保存。】
- 获取用户 openid,需要 APPID, APPSECRET。
附3 ?关于接口调试
微信提供了在线接口调试:微信公众平台接口调试工具
要点:
- ToUserName,就是公众号 ID;
- FromUserName,可以用个人的 openid;
- CreateTime,MsgId,都填1;
- 数据类型:选 json,因为 php 用的是 json 解析;
- 检查问题后,返回调试信息,其中的内容来自:requestAPI($json)中的 echo 'success'; ,其中的空格将自动替换为 +。其实直接 echo $output; 更有用。
|