本文将介绍微信公众号开发中涉及的一些技术概念,以便读者可以快速掌握公众号开发过程中的基本操作和流程。文档不涉及具体开发技术和流程的介绍,该文档作用相当于官方文档中关键点的详细注解。
在公众号开发流程中,涉及到三种系统:
响应系统和业务系统需要自己开发;其中业务系统中的页面即为下文中提到的第三方页面;
公众号是为微信用户提供资讯和服务的平台;公众平台开发接口则是通过后台业务系统向用户提供服务的基础;
不同的公众号类型有不同数量的接口权限,服务号要多于订阅号;
开启公众号开发模式后,微信系统将以事件推送的形式告知响应系统用户在公众号里的相关行为,包括:关注/取关事件、二维码扫描事件、自定义菜单事件、跳转链接事件时的事件、点击菜单拉取消息时的事件,从而开发者可以获取到该消息并做出响应;
这些步骤都是在微信官方公众号后台里开发->基本配置里完成的
配置图示:
服务器代码:
public class WeChatServer extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
PrintWriter writer=resp.getWriter();
System.out.println(req.getParameter("echostr"));
writer.println(req.getParameter("echostr"));
}
}
//微信系统对响应系统地址的有效性验证是通过返回值(echostr)完成的,这里没有添加对微信系统身份的检查,直接输出
完整代码见WeChatWebSystem
为识别用户,微信为每一个用户提供了针对特定公众号平台的Open ID;同一个用户在不同公众号里的有不同的Open ID;同一个公众号里不同用户有不同的Open ID;获取用户的Open ID是无需用户同意的,但是获取用户的基本信息(所在地址、昵称等)是需要用户同意的;
为了实现在多个公众平台、移动应用之间做到用户统一管理,提供了Union ID机制。同一用户在同一开放平台账号下的所有公众号和应用里Union ID是相同的;
Open ID的获取和使用
获取
使用:
Union ID的获取和使用
从公众号进入第三方页面时,业务系统可以选择获取Union ID来实现业务逻辑,其具体流程为:
准备阶段:
获取阶段:
引导用户进入授权页面,用户同意后,获得code;
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
其中APPID/REDIRECT_URI/SCOPE/STATE为变量,参数顺序不可改变;跳转回调的redirect_uri,应当使用https链接来确保授权code的安全性。以上链接需要自微信客户端里打开,如果用户同意授权,则页面跳转到:redirect_uri/?code=CODE&state=STATE
参数 是否必须 说明
appid 是 公众号的唯一标识
redirect_uri 是 授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理
response_type 是 返回类型,请填写code
scope 是 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
state 否 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
#wechat_redirect 是 无论直接打开还是做页面302重定向时候,必须带此参数
使用code换取网页授权access_token(用于获取用户基本信息的token,不是微信基础服务里的access_token,在这一步里,获取access_token的同时也会获得用户的open ID);
获取code后,请求以下链接获取access_token: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
其中APPID/SECRET/CODE为变量
正确返回的JSON数据如下:
{ "access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE" }
如有必要,开发者刷新该access_token,避免过期;
获取第二步的refresh_token后,请求以下链接获取access_token:
https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
access_token拥有较短的有效期,refresh_token则有30天的有效期;
返回结果同上
{ "access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE" }
通过access_token和openID获得用户基本信息;
https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
access_token为网页授权时使用的access_token,不是调用微信接口时使用的access_token;
返回数据:
{ "openid":" OPENID",
" nickname": NICKNAME,
"sex":"1",
"province":"PROVINCE"
"city":"CITY",
"country":"COUNTRY",
"headimgurl": "http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
"privilege":[ "PRIVILEGE1" "PRIVILEGE2" ],
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
从响应系统内获得Union ID是通过其他接口实现的:
请求方式: GET
请求地址:https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
参数说明:access_token即为接口访问令牌,openid即为消息的发送者
注意事项
使用snaapi_base为scope的网页授权对用户是静默的,用户感觉直接进入了公众号网页;
对于已关注公众号的用户,如果用户从公众号会话或者自定义菜单进入网页授权页,即使scope为snaapi_userInfo,也是静默的;
access_token是公众号内全局唯一接口调用凭据;其有效期为2小时,需要定时刷新,重新获取将导致上次access_token失效;
https请求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
正确返回结果
{"access_token":"ACCESS_TOKEN","expires_in":7200}
出粗返回结果
{"errcode":40013,"errmsg":"invalid appid"}
下图为访问失败时的截图,因为本地开发中使用的IP地址不在公众号后台的IP访问白名单所致,正式部署后将不存在该问题:
下图为服务器端浏览器接口调试页面
开启服务模式后,将无法在微信公众号后台内实现对菜单的自定义,但是微信公众号后台内可以自定义的菜单点击类型十分有限(2/10),可以通过使用微信提供的菜单管理接口来对菜单进行管理。
接口说明:
接口地址:https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN
访问方式:POST
参数说明:
ACCESS_TOKEN即为获取的access_token;
POST的数据为JSON字符串,其中button定义了菜单,为一个JSON数组;数组中每一个元素都是一个一级菜单,其中sub_button属性为该一级菜单的二级菜单,同样也是JSON数组;每一个菜单项包含type(上面提到的10种)、name、key等信息。
{
"button":[
{
"type":"click",
"name":"今日歌曲",
"key":"V1001_TODAY_MUSIC"
},
{
"name":"菜单",
"sub_button":[
{
"type":"view",
"name":"搜索",
"url":"http://www.soso.com/"
},
{//跳转小程序
"type":"miniprogram",
"name":"wxa",
"url":"http://mp.weixin.qq.com",
"appid":"wx286b93c14bbf93aa",
"pagepath":"pages/lunar/index"
},
{
"type":"click",
"name":"赞一下我们",
"key":"V1001_GOOD"
}]
}]
}
正确返回消息:
{"errcode":0,"errmsg":"ok"}
出错时返回消息:
{"errcode":40018,"errmsg":"invalid button name size"}
这里需要注意的是,POST的内容类型(content-type)需要设置为application/json;
创建自定义菜单后,可使用该接口查询自定义菜单的结构。如果使用了个性化菜单,那么该接口将返回默认菜单和全部个性化菜单的信息;
请求方式:GET
https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN
返回数据(无个性化菜单时):
{
"menu": {
"button": [
{
"type": "click",
"name": "今日歌曲",
"key": "V1001_TODAY_MUSIC",
"sub_button": [ ]
},
{
"type": "click",
"name": "歌手简介",
"key": "V1001_TODAY_SINGER",
"sub_button": [ ]
},
{
"name": "菜单",
"sub_button": [
{
"type": "view",
"name": "搜索",
"url": "http://www.soso.com/",
"sub_button": [ ]
},
{
"type": "view",
"name": "视频",
"url": "http://v.qq.com/",
"sub_button": [ ]
},
{
"type": "click",
"name": "赞一下我们",
"key": "V1001_GOOD",
"sub_button": [ ]
}
]
}
]
}
}
返回结果(有个性化菜单时):
{
"menu": {
"button": [
{
"type": "click",
"name": "今日歌曲",
"key": "V1001_TODAY_MUSIC",
"sub_button": [ ]
}
],
"menuid": 208396938
},
"conditionalmenu": [
{
"button": [
{
"type": "click",
"name": "今日歌曲",
"key": "V1001_TODAY_MUSIC",
"sub_button": [ ]
},
{
"name": "菜单",
"sub_button": [
{
"type": "view",
"name": "搜索",
"url": "http://www.soso.com/",
"sub_button": [ ]
},
{
"type": "view",
"name": "视频",
"url": "http://v.qq.com/",
"sub_button": [ ]
},
{
"type": "click",
"name": "赞一下我们",
"key": "V1001_GOOD",
"sub_button": [ ]
}
]
}
],
"matchrule": {
"group_id": 2,
"sex": 1,
"country": "中国",
"province": "广东",
"city": "广州",
"client_platform_type": 2
},
"menuid": 208396993
}
]
}
接口说明:
http请求方式:GET
https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN
正确返回消息:
{"errcode":0,"errmsg":"ok"}
错误返回消息同上
这里需要注意的是,该方式将删除整个菜单。
为了帮助公众号实现灵活的业务运营,微信公众平台新增了个性化菜单接口,开发者可以通过该接口,让公众号的不同用户群体看到不一样的自定义菜单。该接口开放给已认证订阅号和已认证服务号。
开发者可以使用如下方式标志用户:
使用个性化菜单需要有以下几点注意:
创建个性化菜单:
请求方式:POST(请使用https协议)
https://api.weixin.qq.com/cgi-bin/menu/addconditional?access_token=ACCESS_TOKEN
POST数据为JSON对象;
{
"button":[
{
"type":"click",
"name":"今日歌曲",
"key":"V1001_TODAY_MUSIC" },
{ "name":"菜单",
"sub_button":[
{
"type":"view",
"name":"搜索",
"url":"http://www.soso.com/"},
{
"type":"miniprogram",
"name":"wxa",
"url":"http://mp.weixin.qq.com",
"appid":"wx286b93c14bbf93aa",
"pagepath":"pages/lunar/index"
},
{
"type":"click",
"name":"赞一下我们",
"key":"V1001_GOOD"
}]
}],
"matchrule":{
"tag_id":"2",
"sex":"1",
"country":"中国",
"province":"广东",
"city":"广州",
"client_platform_type":"2",
"language":"zh_CN"
}
}
正确返回消息:
{"menuid":"208379533"}——menuid即为该菜单的标记;可用于以后删除使用;
删除个性化菜单:
请求方式:POST(请使用https协议)
https://api.weixin.qq.com/cgi-bin/menu/delconditional?access_token=ACCESS_TOKEN
参数说明:
POST数据为JSON字符串
{"menuid":"208379533"}
正确返回:
{"errcode":0,"errmsg":"ok"}
错误返回:
通用
注意,第3个到第8个的所有事件,仅支持微信iPhone5.4.1以上版本,和Android5.4以上版本的微信用户,旧版本微信用户点击后将没有回应,开发者也不能正常接收到事件推送。
click类型的消息推送:
123456789
view类型的消息推送:
123456789
MENUID //指菜单ID,如果是个性化菜单,则可以通过这个字段,知道是哪个规则的菜单被点击了。
scancode_push类型的消息推送:
1408090502
scancode_waitmsg类型的消息推送:
1408090606
pic_sysphoto类型的消息推送:
1408090651
1
pic_photo_or_album类型的消息推送:
1408090816
1
pic_weixin类型的消息推送:
1408090816
1
location_select类型的消息推送:
1408091189
开发者可以使用用户标签管理的相关接口,实现对公众号标签的管理;标签可以用于对用户的分类管理;
每个公众号可以创建100个标签;创建方式如下:
请求方式:POST(使用https协议);
请求地址:https://api.weixin.qq.com/cgi-bin/tags/create?access_token=ACCESS_TOKEN
POST数据格式:
{ "tag":{ "name" : "广东"//标签名长度不能超过30字节}}
返回结果:
{ "tag":{ "id":134,id "name":"广东" } }//包含tag-id
获取公众号已创建的标签:
请求方式:GET(使用https协议)
请求地址:https://api.weixin.qq.com/cgi-bin/tags/get?access_token=ACCESS_TOKEN
返回说明:
{ "tags":[{ "id":1, "name":"每天一罐可乐星人", "count":0 //此标签下粉丝数 },{ "id":2, "name":"星标组", "count":0 },{ "id":127, "name":"广东", "count":5 } ] }
编辑标签:
请求方式:POST(请使用https协议)
请求地址:https://api.weixin.qq.com/cgi-bin/tags/update?access_token=ACCESS_TOKEN
POST数据:
{ "tag" : { "id" : 134, "name" : "广东人" } }
返回说明:
正确返回:{ "errcode":0, "errmsg":"ok" }
错误返回:详见错误返回码
删除标签:
请注意,当某个标签下的粉丝超过10w时,后台不可直接删除标签。此时,开发者可以对该标签下的openid列表,先进行取消标签的操作,直到粉丝数不超过10w后,才可直接删除该标签。
请求方式:POST(使用https协议)
请求地址:https://api.weixin.qq.com/cgi-bin/tags/delete?access_token=ACCESS_TOKEN
POST数据:
{ "tag":{ "id" : 134 } }
返回说明:
正确返回:{ "errcode":0, "errmsg":"ok" }
错误返回:详见错误返回码
获取标签下的粉丝列表:
请求方式:POST(请使用https协议)
请求地址:https://api.weixin.qq.com/cgi-bin/user/tag/get?access_token=ACCESS_TOKEN
POST数据格式:JSON
{ "tagid" : 134, "next_openid":""//第一个拉取的OPENID,不填默认从头开始拉取 }
返回说明:
{
"count":2,//这次获取的粉丝数量
"data":{//粉丝列表
"openid":[
"ocYxcuAEy30bX0NXmGn4ypqx3tI0",
"ocYxcuBt0mRugKZ7tGAHPnUaOW7Y"
]
},
"next_openid":"ocYxcuBt0mRugKZ7tGAHPnUaOW7Y"//拉取列表最后一个用户的openid
}
标签功能支持为公众号用户进行打标签、取消标签等操作;该功能可以实现个性化菜单定制
批量为用户打标签
请求方式:POST(请使用https协议)
请求地址:https://api.weixin.qq.com/cgi-bin/tags/members/batchtagging?access_token=ACCESS_TOKEN
POST数据:
{
"openid_list" : [//粉丝列表
"ocYxcuAEy30bX0NXmGn4ypqx3tI0",
"ocYxcuBt0mRugKZ7tGAHPnUaOW7Y"],
"tagid" : 134
}
返回说明:
{
"errcode":0,
"errmsg":"ok"
}
批量为用户取消标签
请求方式:POST(请使用https协议)
请求地址:https://api.weixin.qq.com/cgi-bin/tags/members/batchuntagging?access_token=ACCESS_TOKEN
POST数据格式:JSON
{
"openid_list" : [//粉丝列表
"ocYxcuAEy30bX0NXmGn4ypqx3tI0",
"ocYxcuBt0mRugKZ7tGAHPnUaOW7Y"],
"tagid" : 134
}
返回说明:{"errcode":0, "errmsg":"ok"}
获取用户的标签列表
请求方式:POST(请使用https协议)
请求地址:https://api.weixin.qq.com/cgi-bin/tags/getidlist?access_token=ACCESS_TOKEN
POST数据格式:JSON
{ "openid" : "ocYxcuBt0mRugKZ7tGAHPnUaOW7Y" }
返回说明:{ "tagid_list":[//被置上的标签列表 134, 2 ] }
请求方式: POST(请使用https协议)
请求地址:https://api.weixin.qq.com/cgi-bin/user/info/updateremark?access_token=ACCESS_TOKEN
POST数据格式:JSON
POST数据例子:
{
"openid":"oDF3iY9ffA-hqb2vVvbr7qxf6A0Q",
"remark":"pangzi"//标签名
}
返回说明:{"errcode":0,"errmsg":"ok"}
注意,这里获的用户基本信息是在关注者和公众号产生消息交互后,公众号可获得该用户的Open ID,之后在响应系统中,通过Open ID获的Union ID以及基本信息
请求方式: GET
请求地址:https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
返回说明:正常情况下,微信会返回下述JSON数据包给公众号
{
"subscribe": 1,
"openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M",
"nickname": "Band",
"sex": 1,
"language": "zh_CN",
"city": "广州",
"province": "广东",
"country": "中国",
"headimgurl":"http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
"subscribe_time": 1382694957,
"unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
"remark": "",
"groupid": 0,
"tagid_list":[128,2],
"subscribe_scene": "ADD_SCENE_QR_CODE",
"qr_scene": 98765,
"qr_scene_str": ""
}
批量获得用户基本信息
请求方式: POST
请求地址:https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token=ACCESS_TOKEN
POST:JSON
{
"user_list": [
{
"openid": "otvxTs4dckWG7imySrJd6jSi0CWE",
"lang": "zh_CN"
},
{
"openid": "otvxTs_JZ6SEiP0imdhpi50fuSZg",
"lang": "zh_CN"
}
]
}
返回说明:
{
"user_info_list": [
{
"subscribe": 1,
"openid": "otvxTs4dckWG7imySrJd6jSi0CWE",
"nickname": "iWithery",
"sex": 1,
"language": "zh_CN",
"city": "揭阳",
"province": "广东",
"country": "中国",
"headimgurl": "http://thirdwx.qlogo.cn/mmopen/xbIQx1GRqdvyqkMMhEaGOX802l1CyqMJNgUzKP8MeAeHFicRDSnZH7FY4XB7p8XHXIf6uJA2SCunTPicGKezDC4saKISzRj3nz/0",
"subscribe_time": 1434093047,
"unionid": "oR5GjjgEhCMJFyzaVZdrxZ2zRRF4",
"remark": "",
"groupid": 0,
"tagid_list":[128,2],
"subscribe_scene": "ADD_SCENE_QR_CODE",
"qr_scene": 98765,
"qr_scene_str": ""
},
{
"subscribe": 0,
"openid": "otvxTs_JZ6SEiP0imdhpi50fuSZg"
}
]
}
公众号可通过本接口来获取帐号的关注者列表,关注者列表由一串OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的)组成。一次拉取调用最多拉取10000个关注者的OpenID,可以通过多次拉取的方式来满足需求。
请求方式: GET(请使用https协议)
请求地址:https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID
返回说明:
{
"total":2,
"count":2,
"data":{
"openid":["OPENID1","OPENID2"]
},
"next_openid":"NEXT_OPENID"
}
当公众号关注者数量超过10000时,可通过填写next_openid的值,从而多次拉取列表的方式来满足需求。
具体而言,就是在调用接口时,将上一次调用得到的返回中的next_openid值,作为下一次调用中的next_openid值。
当普通用户向公众号发送消息时,微信系统将POST消息的XML数据包到开发者填写的URL上;
注意事项:
各种消息体结构(XML格式组织,可使用输入流的方式读取):
注意,首次出现的XML标签将给出注释,第二次出现则不再注释,该规则同样适用于后面的接收事件消息
文本消息
< ![CDATA[toUser] ]> //开发者微信号
< ![CDATA[fromUser] ]> //用户的openid
1348831860
< ![CDATA[text] ]> //文本为text
< ![CDATA[this is a test] ]>
1234567890123456 //64位整型
图片消息
< ![CDATA[toUser] ]>
< ![CDATA[fromUser] ]>
1348831860
< ![CDATA[image] ]> //图片为image
< ![CDATA[this is a url] ]>
< ![CDATA[media_id] ]> //可使用图片消息媒体id,可以调用多媒体文件下载接口拉取数据。
1234567890123456
语音消息
< ![CDATA[toUser] ]>
< ![CDATA[fromUser] ]>
1357290913
< ![CDATA[voice] ]> //语音为voice
< ![CDATA[media_id] ]>
< ![CDATA[Format] ]> //语音格式,如amr,speex等
1234567890123456
< ![CDATA[腾讯微信团队] ]>
这里需要注意的是,如果开启了语音识别,用户每次发送语音给公众号时,微信会在推送的语音消息XML数据包中,增加一个Recongnition字段标志识别结果;
视频消息
< ![CDATA[toUser] ]>
< ![CDATA[fromUser] ]>
1357290913
< ![CDATA[video] ]> //视频为video
< ![CDATA[media_id] ]> //可使用图片消息媒体id,可以调用多媒体文件下载接口拉取数据。
< ![CDATA[thumb_media_id] ]> //视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。
1234567890123456
小视频消息
< ![CDATA[toUser] ]>
< ![CDATA[fromUser] ]>
1357290913
< ![CDATA[shortvideo] ]> //小视频为shortvideo
< ![CDATA[media_id] ]>
< ![CDATA[thumb_media_id] ]>
1234567890123456
地理位置消息
< ![CDATA[toUser] ]>
< ![CDATA[fromUser] ]>
1351776360
< ![CDATA[location] ]> //地理位置为 location
23.134521 // 地理纬度
113.358803 // 地理经度
20 //地图缩放大小
//地理位置信息
1234567890123456
链接消息
< ![CDATA[toUser] ]>
< ![CDATA[fromUser] ]>
1351776360
< ![CDATA[link] ]> //链接消息为 link
< ![CDATA[公众平台官网链接] ]> //标题
< ![CDATA[公众平台官网链接] ]> //描述
< ![CDATA[url] ]> //消息链接
1234567890123456
在微信用户和公众号产生交互的过程中,用户的某些操作会使得微信系统通过事件推送的形式通知响应系统,从而开发者可以获取到该信息。其中,某些事件推送在发生后,是允许开发者回复用户的,某些则不允许,详细内容如下:
各种事件介绍:
关注/取消关注事件
用户在关注与取消关注公众号时,微信系统会把这个事件推送到响应系统,方便开发者给用户下发欢迎消息或者做帐号的解绑。为保护用户数据隐私,开发者收到用户取消关注事件时需要删除该用户的所有信息。
推送XML数据包示例:
< ![CDATA[toUser] ]> //开发者微信号
< ![CDATA[FromUser] ]> //用户的OennID
123456789 //创建时间,int整型
< ![CDATA[event] ]>
//消息类型为event< ![CDATA[subscribe] ]> //事件类型,subscribe(订阅)、unsubscribe(取消订阅)
扫描带参数二维码事件:
用户扫描带场景值二维码时,可能推送以下两种事件:
推送XML数据包示例:
用户未关注时,进行关注后的事件推送
< ![CDATA[toUser] ]>
< ![CDATA[FromUser] ]>
123456789
< ![CDATA[event] ]> //消息类型为event
< ![CDATA[subscribe] ]> //事件类型,subscribe
< ![CDATA[qrscene_123123] ]> //事件KEY值,qrscene_为前缀,后面为二维码的参数值
< ![CDATA[TICKET] ]> //二维码的ticket,可用来换取二维码图片
用户已关注时的事件推送
< ![CDATA[toUser] ]>
< ![CDATA[FromUser] ]>
123456789
< ![CDATA[event] ]>
< ![CDATA[SCAN] ]>
< ![CDATA[SCENE_VALUE] ]> //创建二维码是的scene_id
< ![CDATA[TICKET] ]> //二维码的ticket,可用来换取图片
上报地理位置事件
用户同意上报地理位置后,每次进入公众号会话时,都会进入上报地理位置,或在进入会话后每5秒报一次地理位置。上报地理位置时,微信系统将上报地理位置事件推送到响应系统;
推送XML数据包示例:
< ![CDATA[toUser] ]>
< ![CDATA[fromUser] ]>
123456789
< ![CDATA[event] ]>
< ![CDATA[LOCATION] ]>
23.137466 //维度
113.352425 //经度
119.385040 //地理位置精度
自定义菜单事件
用户点击自定义菜单后,微信系统会把点击事件推送给响应系统,点击菜单弹出子菜单,不会产生上报。
推送XML数据包示例:
点击菜单拉取消息时的事件推送
< ![CDATA[toUser] ]>
< ![CDATA[FromUser] ]>
123456789
< ![CDATA[event] ]>
< ![CDATA[CLICK] ]> //事件类型为CLICK
< ![CDATA[EVENTKEY] ]> //事件KEY值,与自定义菜单接口中KEY值对应
点击菜单跳转链接时的事件推送
< ![CDATA[toUser] ]>
< ![CDATA[FromUser] ]>
123456789
< ![CDATA[event] ]>
< ![CDATA[VIEW] ]> //事件类型为 VIEW
< ![CDATA[www.qq.com] ]> //事件的KEY值,设置的跳转URL
当用户发送消息给公众号时,或某些特定的用户操作引发的事件推送时,微信系统会将一个POST请求发送到响应系统,开发者可以在响应包(Get)中返回特定XML结构,来对该消息进行响应,这种方式称为被动回复用户消息。
现支持回复文本、图片、图文、语音、视频、音乐。
发送被动响应消息其实并不是一种接口,而是对微信系统发过来消息的一次回复,只是这次回复将发送到用户。
被动回复消息的数据格式:
注意:
回复图片(不支持gif动图)等多媒体消息时需要预先通过素材管理接口上传临时素材到微信服务器,可以使用素材管理中的临时素材,也可以使用永久素材。
一旦遇到以下情况,微信都会在公众号会话中,向用户下发系统提示“该公众号暂时无法提供服务,请稍后再试”:
回复文本消息
XML格式数据:
< ![CDATA[toUser] ]> //用户Open id
< ![CDATA[fromUser] ]> //开发者 微信号;这两者都可以从响应系统收到的消息里获得
12345678
< ![CDATA[text] ]> //消息类型为:text
< ![CDATA[你好] ]>
回复图片消息
XML格式数据:
< ![CDATA[toUser] ]>
< ![CDATA[fromUser] ]>
12345678
< ![CDATA[image] ]> //消息类型为:image
< ![CDATA[media_id] ]> //通过素材管理中的接口上传多媒体文件,得到的id。
回复语音消息
XML格式数据:
< ![CDATA[toUser] ]>
< ![CDATA[fromUser] ]>
12345678
< ![CDATA[voice] ]> //消息类型为:voice
< ![CDATA[media_id] ]>
回复视频消息
XML格式数据:
< ![CDATA[toUser] ]>
< ![CDATA[fromUser] ]>
12345678
< ![CDATA[video] ]> //消息类型为:video
回复音乐消息
XML格式数据:
< ![CDATA[toUser] ]>
< ![CDATA[fromUser] ]>
12345678
< ![CDATA[music] ]>
< ![CDATA[TITLE] ]>
< ![CDATA[DESCRIPTION] ]>
< ![CDATA[MUSIC_Url] ]>
< ![CDATA[HQ_MUSIC_Url] ]>
< ![CDATA[media_id] ]>
回复图文消息
XML格式数据:
< ![CDATA[toUser] ]>
< ![CDATA[fromUser] ]>
12345678
< ![CDATA[news] ]>
2
-
< ![CDATA[title1] ]>
< ![CDATA[description1] ]>
< ![CDATA[picurl] ]>
< ![CDATA[url] ]>
-
< ![CDATA[title] ]>
< ![CDATA[description] ]>
< ![CDATA[picurl] ]>
< ![CDATA[url] ]>
主动发送消息是通过模板消息实现的,模板消息仅用于公众号向用户发送重要的服务通知,只能用于符合其要求的服务场景中,如信用卡刷卡通知,商品购买成功通知等。
为了发送模板消息,需要首先设置公众号所属的行业,之后公众号则只能使用该行业内的模板来发送模板消息;设置行业可以通过微信公众平台后台来完成,每月可修改一次;也可以通过接口调用的方式来修改所属行业;
定义模板消息时需要注意的地方:
设置所属行业:
请求方式: POST
请求地址:https://api.weixin.qq.com/cgi-bin/template/api_set_industry?access_token=ACCESS_TOKEN
POST数据格式:
{"industry_id1":"1","industry_id2":"4"}
获得模板ID:
该过程可在公众号后台完成,同时提供接口:
请求方式: POST
请求地址:https://api.weixin.qq.com/cgi-bin/template/api_add_template?access_token=ACCESS_TOKEN
POST数据格式:
{"template_id_short":"TM00015"}
正确返回消息说明:JSON
{"errcode":0,"errmsg":"ok","template_id":"Doclyl5uP7Aciu-qZ7mJNPtWkbkYnWBWVja26EGbNyk"}
错误返回消息说明:
错误码通用。
获取已添加到微信公众号里的所有模板列表:
请求方式:GET
请求地址:https//api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token=ACCESS_TOKEN
返回结果说明:
{
"template_list": [{
"template_id": "iPk5sOIt5X_flOVKn5GrTFpncEYTojx6ddbt8WYoV5s",
"title": "领取奖金提醒",
"primary_industry": "IT科技",
"deputy_industry": "互联网|电子商务",
"content": "{ {result.DATA} }\n\n领奖金额:{ {withdrawMoney.DATA} }\n领奖 时间:{ {withdrawTime.DATA} }\n银行信息:{ {cardInfo.DATA} }\n到账时间: { {arrivedTime.DATA} }\n{ {remark.DATA} }",
"example": "您已提交领奖申请\n\n领奖金额:xxxx元\n领奖时间:2013-10-10 12:22:22\n银行信息:xx银行(尾号xxxx)\n到账时间:预计xxxxxxx\n\n预计将于xxxx到达您的银行卡"
}]
}
删除模板:
该过程可在公众号后台完成,同时提供接口:
请求方式:POST
请求地址:https://api.weixin.qq.com/cgi-bin/template/del_private_template?access_token=ACCESS_TOKEN
POST数据格式:
{"template_id" : "Dyvp3-Ff0cnail_CDSzk1fIc6-9lOkxsQE7exTJbwUE"}
正确返回消息说明:JSON
错误返回消息说明:
{"errcode" : 0,"errmsg" : "ok"}
错误码通用。
发送模板消息:
请求方式: POST
请求地址:https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN
POST数据格式:
{
"touser":"OPENID",
"template_id":"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY",
"url":"http://weixin.qq.com/download",
"miniprogram":{
"appid":"xiaochengxuappid12345",
"pagepath":"index?foo=bar"
},
"data":{
"first": {
"value":"恭喜你购买成功!",
"color":"#173177"
},
"keyword1":{
"value":"巧克力",
"color":"#173177"
},
"keyword2": {
"value":"39.8元",
"color":"#173177"
},
"keyword3": {
"value":"2014年9月22日",
"color":"#173177"
},
"remark":{
"value":"欢迎再次购买!",
"color":"#173177"
}
}
}
正确返回消息格式:
{"errcode":0,"errmsg":"ok","msgid":200228332}
事件推送:
在模版消息发送任务完成后,微信系统会将是否送达成功作为通知,发送到响应系统。
送达成功时,推送的XML如下:
< ![CDATA[gh_7f083739789a] ]>
< ![CDATA[oia2TjuEGTNoeX76QEjQNrcURxG8] ]>
1395658920
< ![CDATA[event] ]>
< ![CDATA[TEMPLATESENDJOBFINISH] ]>
200163836
< ![CDATA[success] ]>
没有送达时,推送的XML如下:
< ![CDATA[gh_7f083739789a] ]>
< ![CDATA[oia2TjuEGTNoeX76QEjQNrcURxG8] ]>
1395658984
< ![CDATA[event] ]>
< ![CDATA[TEMPLATESENDJOBFINISH] ]>
200163840
< ![CDATA[failed:user block] ]> //failed: system failed——其他原因;failed:user block——用户拒绝接收该公众号的消息
公众号消息加解密是为了进一步加强公众号安全保障,提供的新机制。公众账号主动调用API的情况不受影响。只有被动回复用户的消息时,才需要进行消息加解密。消息加解密方式:
启用加解密功能(即选择兼容模式或安全模式)后,微信系统在向响应系统推送消息时,URL将新增加两个参数(加密类型和消息体签名),并以此来体现新功能。加密算法采用AES。具体方案见该模块代码实现。
出于安全等考虑,如需要获知微信服务器的IP地址列表,可以通过该接口获得微信服务器IP地址列表或者IP网段信息。
请求方式: GET
请求地址:https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token=ACCESS_TOKEN
返回消息:JSON格式
{"ip_list":["127.0.0.1","127.0.0.2","101.226.103.0/25"]}
当用户和公众号产生特定动作的交互时(具体动作列表请见下方说明),微信系统将会把消息数据推送给响应系统,响应系统可以在一段时间内(目前修改为48小时)调用客服接口,通过POST一个JSON数据包来发送消息给普通用户。此接口主要用于客服等有人工消息处理环节的功能,方便开发者为用户提供更加优质的服务。
目前允许的动作列表如下(公众平台会根据运营情况更新该列表,不同动作触发后,允许的客服接口下发消息条数不同,下发条数达到上限后,会遇到错误返回码,具体请见返回码说明页):
客服账号管理:
请注意,必须先在公众平台官网为公众号设置微信号后才能使用该能力。
添加客服账号
请求方式: POST
请求地址:https://api.weixin.qq.com/customservice/kfaccount/add?access_token=ACCESS_TOKEN
POST数据示例如下:
{
"kf_account" : "test1@test",
"nickname" : "客服1",
"password" : "pswmd5",
}
返回说明(正确时的JSON返回结果):
{"errcode" : 0,"errmsg" : "ok"}
返回错误码是通用的
删除客服账号
请求方式: GET
请求地址:https://api.weixin.qq.com/customservice/kfaccount/del?access_token=ACCESS_TOKEN
POST数据示例如下:
{
"kf_account" : "test1@test",
"nickname" : "客服1",
"password" : "pswmd5",
}
返回说明(正确时的JSON返回结果):
{"errcode" : 0,"errmsg" : "ok"}
返回错误码是通用的
修改客服账号
请求方式: POST
请求地址:https://api.weixin.qq.com/customservice/kfaccount/update?access_token=ACCESS_TOKEN
POST数据示例如下:
{
"kf_account" : "test1@test",
"nickname" : "客服1",
"password" : "pswmd5",
}
返回说明(正确时的JSON返回结果):
{"errcode" : 0,"errmsg" : "ok"}
返回错误码是通用的
设置客服账号头像
开发者可调用该接口来上传图片作为客服人员的头像,头像图片文件必须是jpg格式,推荐使用640*640大小的图片以达到最佳效果。该接口调用请求如下:
请求方式: POST/FORM
请求地址:http://api.weixin.qq.com/customservice/kfaccount/uploadheadimg?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT
这里需要注意的是,官方文档中提到的是使用curl工具POST一个多媒体文件;这里我想只要通过代码将文件提交到上述地址就OK,因为curl是一个命令行工具,网上有在php中使用curl的,但是对Java的支持很惨淡,这一部分详见代码验证及图示。
获取所有客服账号
请求方式: GET
请求地址:https://api.weixin.qq.com/cgi-bin/customservice/getkflist?access_token=ACCESS_TOKEN
返回数据:
{
"kf_list": [
{
"kf_account": "test1@test",
"kf_nick": "ntest1",
"kf_id": "1001" //这里需要注意的是,kf_id是第一次在这里出现,创建的时候并没有返回该字段;
"kf_headimgurl": " http://mmbiz.qpic.cn/mmbiz/4whpV1VZl2iccsvYbHvnphkyGtnvjfUS8Ym0GSaLic0FD3vN0V8PILcibEGb2fPfEOmw/0"
},
{
"kf_account": "test2@test",
"kf_nick": "ntest2",
"kf_id": "1002"
"kf_headimgurl": " http://mmbiz.qpic.cn/mmbiz/4whpV1VZl2iccsvYbHvnphkyGtnvjfUS8Ym0GSaLic0FD3vN0V8PILcibEGb2fPfEOmw /0"
}
]
}
客服接口-发送消息
请求方式: POST
请求地址:https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
各消息类型所需的JSON数据包如下:
文本消息:
{
"touser":"OPENID",
"msgtype":"text",
"text":
{
"content":"Hello World"
}
}
发送文本消息时,文本内容中可以携带跳转小程序的文字链:点击跳小程序
参数说明:
1.data-miniprogram-appid 项,填写小程序appid,则表示该链接跳小程序;
2.data-miniprogram-path项,填写小程序路径,路径与app.json中保持一致,可带参数;
3.对于不支持data-miniprogram-appid 项的客户端版本,如果有herf项,则仍然保持跳href中的网页链接;
注意,data-miniprogram-appid对应的小程序必须与公众号有绑定关系。
发送图片消息:
{
"touser":"OPENID",
"msgtype":"image",
"image":
{
"media_id":"MEDIA_ID"
}
}
发送语音消息:
{
"touser":"OPENID",
"msgtype":"voice",
"voice":
{
"media_id":"MEDIA_ID"
}
}
发送视频消息:
{
"touser":"OPENID",
"msgtype":"video",
"video":
{
"media_id":"MEDIA_ID",
"thumb_media_id":"MEDIA_ID",
"title":"TITLE",
"description":"DESCRIPTION"
}
}
发送音乐消息:
{
"touser":"OPENID",
"msgtype":"music",
"music":
{
"title":"MUSIC_TITLE",
"description":"MUSIC_DESCRIPTION",
"musicurl":"MUSIC_URL",
"hqmusicurl":"HQ_MUSIC_URL",
"thumb_media_id":"THUMB_MEDIA_ID"
}
}
发送图文消息(点击跳转到外链) 图文消息条数限制在8条以内,注意,如果图文数超过8,则将会无响应。
{
"touser":"OPENID",
"msgtype":"news",
"news":{
"articles": [
{
"title":"Happy Day",
"description":"Is Really A Happy Day",
"url":"URL",
"picurl":"PIC_URL"
},
{
"title":"Happy Day",
"description":"Is Really A Happy Day",
"url":"URL",
"picurl":"PIC_URL"
}
]
}
}
发送图文消息:
{
"touser":"OPENID",
"msgtype":"mpnews",
"mpnews":
{
"media_id":"MEDIA_ID"
}
}
发送卡券:
{
"touser":"OPENID",
"msgtype":"wxcard",
"wxcard":{
"card_id":"123dsdajkasd231jhksad"
},
}
发送小程序卡片:
{
"touser":"OPENID",
"msgtype":"miniprogrampage",
"miniprogrampage":
{
"title":"title",
"appid":"appid",
"pagepath":"pagepath",
"thumb_media_id":"thumb_media_id"
}
}
用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。
这里需要注意的是同用户管理中的获取用户基本信息的区别:用户管理是在响应系统的完成的;而用户授权是在业务系统中完成的;两者工作的环境也是不同的,所以请求接口是不同的:第三方授权是在公众号内通过微信客户端进入网页时发生,而用户管理是公众号内部的管理行为;
使用流程:
两种请求模式:
以snsapi_base为scope发起的网页授权,用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务系统的入口);
以snsapi_userinfo为scope发起的网页授权,用来获取用户的基本信息的。这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。
注意:用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。该接口以及包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。
在OAuth2.0用户授权机制下,存在一个反应用户是否同意授权的标记,该标记在OAuth2.0的环境下称为:access_token。这和响应系统用于请求微信接口时使用的access_token是不一样的,但是它们的性质是相同的;
特殊环境下的静默授权:
具体而言,网页授权流程分为四步:
引导用户进入授权页面同意授权,获取code
请求地址:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
参数说明:APPID/REDIRECT_URI/SCOPE/StATE等为变量,其余为常量;参数顺序不可变;
如果用户同意授权,页面将跳转至 REDIRECT_URI/?code=CODE&state=STATE。 code说明 : code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。
通过code换取网页授权access_token(与基础支持中的access_token不同)
请求地址:https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
参数说明:APPID/SECRET/CODE等为变量,其余为常量;参数顺序可变;
返回数据:JSON
{
"access_token":"ACCESS_TOKEN",// 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",//用户刷新access_token
"openid":"OPENID",
"scope":"SCOPE"
}
注意:由于公众号的secret和获取到的access_token安全级别都非常高,必须只保存在服务器,不允许传给客户端。后续刷新access_token、通过access_token获取用户信息等步骤,也必须从服务器发起。即当前请求是可以通过客户端进行的。
另外,此时已经获得open id,即如果不需要使用union id 的话,网页授权到此结束;
如果需要,开发者可以刷新网页授权access_token,避免过期
请求地址:https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
参数说明:APPID/REFRESH_TOKEN为变量,REFRESH_TOKEN即为第二步获得数据
通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)
请求方式:GET
请求地址:https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
参数说明:ACCESS_TOKEN即为第二步获得网页授权数据;OPENID一样;
返回数据说明:JSON
{
"openid":" OPENID",
" nickname": NICKNAME,
"sex":"1",
"province":"PROVINCE"
"city":"CITY",
"country":"COUNTRY",
"headimgurl": "http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
"privilege":[ "PRIVILEGE1" "PRIVILEGE2" ],
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
整体来看,App Secret作为秘密数据,其获取也应该通过业务系统的验证以检查正在请求App Secret的网页是不是友军;
JS-SDK为第三方网页在微信客户端内使用本地资源提供JS封装的接口;这里不涉及具体API的使用,仅介绍JS-SDK的引入、调用接口权限的验证配置。
引入JS文件:
同普通JS文件。
权限验证:
所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用,同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复。
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名,签名算法见后专门模块
jsApiList: [] // 必填,需要使用的JS接口列表
});
wx.ready(function(){
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
});
wx.error(function(res){
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
});
所有接口通过wx对象(也可使用jWeixin对象)来调用,参数是一个对象,除了每个接口本身需要传的参数之外,还有以下通用参数:
以上几个函数都带有一个参数,类型为对象,其中除了每个接口本身返回的数据之外,还有一个通用属性errMsg,其值格式如下:
生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。 开发者必须在自己的服务全局缓存jsapi_ticket 。
生成签名的流程:
获得access_token:具体见上文目录;
用第一步拿到的access_token获得jsapi_ticket
请求方法:GET
请求地址:https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
返回数据说明:JSON
{
"errcode":0,
"errmsg":"ok",
"ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
"expires_in":7200
}
签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。 sha1加密后的结果即为signature;