本篇概要:
公众账号类型和区别
- 订阅号:主要适用于个人或者组织
- 服务号:政府机构、组织、企业,不适用于个人
- 企业号:大型企业
基础接口介绍
- 事件推送(订阅公众账号):微信用户在微信中关注自己感兴趣的公众账号之后,微信服务器会推送一个订阅事件到与公众账号绑定的第三方服务器上
- 消息响应(发送、接收普通信息)
- 基础接口(获取 access_token、获取微信服务器地址)
1. 接入公众账号 API;
验证配置介绍
- 填写第三方服务器 URL、token(只支持 80 端口)。第三方服务器主要作用就是接收微信推送消息。
- 填写验证配置:首页 - 开发 - 基本配置
验证服务器地址的有效性,加密、校验流程
- 将 token、timestamp、nonce 三个参数进行字段序排序
- 将三个参数字符串拼接成一个字符串进行 sha1 加密
- 开发者获得加密后的字符串可与 signature 对比,标识该请求来源于微信
- 实例:http://xx.com/token.php 下写入如下代码后,提交
<?php
$timestamp = $_GET['timestamp'];
$nonce = $_GET['nonce'];
$token = 'wxTestToken001';
$signature = $_GET['signature'];
$echostr = $_GET['echostr'];
$array = [$timestamp, $nonce, $token];
sort($array);
$tmpstr = implode('', $array);
$tmpstr = sha1($tmpstr);
if($tmpstr == $signature && $echostr){
// 第一次接入微信 API 接口的时候验证
echo $echostr;
}else{
// 事件推送
}
exit;
2. 消息推送;
- 接收用户订阅事件推送并回复
<?php
// 参考:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html
// 获取微信推送过来的 POST 数据(XML)
$postArr = file_get_contents("php://input");
// 处理消息类型,并设置回复类型和内容
$postObj = simplexml_load_string($postArr);
//$postObj->ToUserName = ''; // 开发者微信号
//$postObj->FromUserName = ''; // 发送方帐号(一个OpenID)
//$postObj->CreateTime = '';
//$postObj->MsgType = '';
//$postObj->Event = '';
// 判断该数据包是否消息订阅的时间推送
if(strtolower($postObj->MsgType) == 'event'){
// 如果是关注事件 subscribe
if(strtolower($postObj->Event) == 'subscribe'){
// 回复用户消息
$toUser = $postObj->FromUserName;
$fromUser = $postObj->ToUserName;
$time = time();
$Msgtype = 'text';
$Content = '欢迎关注微信账号';
$template = "<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[%s]]></MsgType>
<Content><![CDATA[%s]]></Content>
</xml>";
$info = sprintf($template, $toUser, $fromUser, $time, $Msgtype, $Content);
echo $info;
}
}
3. 消息回复;
普通消息回复:回复纯文本、回复图文
- 纯文本回复代码实现
<?php
$postArr = file_get_contents("php://input");
$postObj = simplexml_load_string($postArr);
// 判断是否文字消息
if(strtolower($postObj->MsgType) == 'text'){
// 文字消息和对应的回复内容
switch(trim($postObj->Content)){
case 1:
$Content = "type 1";
break;
case 2:
$Content = "<a href='www.baidu.com'>type 2</a>";
break;
default:
$Content = "input again";
break;
}
$toUser = $postObj->FromUserName;
$fromUser = $postObj->ToUserName;
$time = time();
$Msgtype = 'text';
$template = "<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[%s]]></MsgType>
<Content><![CDATA[%s]]></Content>
</xml>";
$info = sprintf($template, $toUser, $fromUser, $time, $Msgtype);
echo $info;
}
- 图文消息回复代码实现
<?php
// 参考:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Passive_user_reply_message.html#5
// 显示样式改变:
// https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncement&announce_id=115383153198yAvN&version=&lang=zh_CN&token=
// https://developers.weixin.qq.com/community/develop/doc/000a86a6374400262ce7a3fb458c00
$postArr = file_get_contents("php://input");
//file_put_contents('test.log', $postArr, FILE_APPEND);
// 处理消息类型,并设置回复类型和内容
$postObj = simplexml_load_string($postArr);
if(strtolower($postObj->MsgType) == 'text'){
// 文字消息和对应的回复内容
if($postObj->Content == 'pic'){
$toUser = $postObj->FromUserName;
$fromUser = $postObj->ToUserName;
$time = time();
$Msgtype = 'news';
$arr = [
[
"title" => "test_news",
"description" => "just a test",
"picUrl" => "https://img-blog.csdnimg.cn/2019091811584121.jpeg",
"url" => "http://www.baidu.com"
]
];
$items = '';
foreach ($arr as $k => $v) {
$items .= "<item>
<Title><![CDATA[".$v['title']."]]></Title>
<Description><![CDATA[".$v['description']."]]></Description>
<PicUrl><![CDATA[".$v['picUrl']."]]></PicUrl>
<Url><![CDATA[".$v['url']."]]></Url>
</item>";
}
$template = "<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[%s]]></MsgType>
<ArticleCount>" .count($arr). "</ArticleCount>
<Articles>";
$template .= $items;
$template .= "</Articles>
</xml>";
$info = sprintf($template, $toUser, $fromUser, $time, $Msgtype);
echo $info;
}
}
4. 关于 access_token;
<?php
// 参考:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
// 开发-配置-公众号开发信息中设置 app_key、app_secret 和 ip 白名单
function curl($url){
$ch = curl_init(); // 初始化
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$res = curl_exec($ch); // 调用接口
if(curl_errno($ch)){
var_dump(curl_error($ch));
}
curl_close($ch);
$arr = json_decode($res, true);
var_dump($arr);
}
// 1. 获取 access_token
function geWxAccessToken($appid, $appsecret){
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" .$appid. "&secret=" . $appsecret;
curl($url);
}
$appid = "xxxx";
$appsecret = "xxxx";
geWxAccessToken($appid, $appsecret)
// 2. 获取微信服务器 ip
function getWxServerIp($access_token){
$url = "https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token=" . $access_token;
curl($url);
}
$access_token = "xxxx";
getWxServerIp($access_token);
5. 自定义菜单;
基本概念:
- 自定义菜单最多包括 3 个一级菜单,每个一级菜单最多包含 5 个二级菜单
- 一级菜单最多四个汉字,二级菜单最多七个汉字,多出来的部分用“…” 代替
自定义菜单可以实现的多种类型按钮
- click(点击推事件):用户点击就会产生一个推送事件,推送到开发者填写的 url 地址上,数据参数类型会带有一个 value 值,拿到 value 值后做一些判断和处理,比如回复图文等
- view(跳转 url):用户点击后跳转到一个地址
- 更多参考:https://developers.weixin.qq.com/doc/offiaccount/Custom_Menus/Creating_Custom-Defined_Menu.html
注意点:
- 由于未认证的订阅号可以调用的接口有限,需要申请测试号:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Requesting_an_API_Test_Account.html
创建菜单代码:
<?php
// 封装 curl
class Http {
public static function call($url, $data = '', $method = 'POST', $headers = []){
$ch = curl_init();
// curl_setopt($ch, CURLOPT_NOSIGNAL, 1);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_DNS_CACHE_TIMEOUT, 600);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_NOBODY, false);
$hh = [];
if(!empty($headers)){
foreach($headers as $k => $v){
$hh[] = $k . ':' . $v;
}
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $hh);
if($method == 'POST') curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
if($method == 'DELETE') curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
if($method == 'PUT') curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
if($method == 'PATCH') curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
if(!empty($data)){
if(is_object($data) || is_array($data)){
$data = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
}
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
$response = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if($code > 200){
echo 'error - ' . $code;
exit();
}
// $array = json_decode($response, true);
// if($array == false || $array == null) return $response;
// return $array;
return $response;
}
public static function post($method, $data = '' ){
return self::call($method, $data, 'POST');
}
public static function patch($method, $data = '' ) {
return self::call($method, $data, 'PATCH');
}
public static function get($method, $data = []){
return self::call($method, $data, 'GET');
}
public static function put($method, $raw, $headers = []){
return self::call($method, $raw, 'PUT', $headers);
}
public static function delete($method, $raw = ''){
return self::call($method, $raw, 'DELETE');
}
}
// 获取菜单
function definedItem($access_token){
$url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=" . $access_token;
$postArr = [
'button' => [
[
'name' => '菜单1',
'type' => 'click',
'key' => 'item1'
],
[
'name' => '菜单2',
'sub_button' => [
[
'name' => '歌曲',
'type' => 'click',
'url' => 'http://music.qq.com',
'key' => 'songs'
],
[
'name' => '电影',
'type' => 'view',
'url' => 'http://www.qq.com'
],
]
]
]
];
echo Http::call($url, $postArr,"post");
}
$access_token = "xxxx";
definedItem($access_token);
点击菜单事件推送:
<?php
$postArr = file_get_contents("php://input");
$postObj = simplexml_load_string($postArr);
if(strtolower($postObj->MsgType) == 'event'){
if(strtolower($postObj->Event) == 'click'){
if(strtolower($postObj->EventKey) == 'item1'){
$toUser = $postObj->FromUserName;
$fromUser = $postObj->ToUserName;
$time = time();
$Msgtype = 'text';
$Content = '点击菜单';
$template = "<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[%s]]></MsgType>
<Content><![CDATA[%s]]></Content>
</xml>";
$info = sprintf($template, $toUser, $fromUser, $time, $Msgtype, $Content);
file_put_contents('test.log', $info, FILE_APPEND);
echo $info;
}
}
}