微信公众号介绍
1.公众号的分类
我们平常在微信应用上会看到有很多的公众号,但是各自并不一样,公众号也分很多种类型,不过最常见的就是服务号和订阅号了。下面我们来看一下他们的区别:
订阅号
:为媒体和个人提供一种信息传播方式,主要偏于为用户传达资讯(类似报纸杂志),主要的定位是阅读,每天可以群发1条消息服务号
:为企业,政府或组织提供对用户进行服务,主要偏于服务交互(类似银行提供服务查询),每个月只可群发4条消息企业微信
:为企业,政府,事业单位,实现生产管理和协作运营的移动化,主要用于公司内部通讯使用,旨在为用户提供移动办公,需要先有成员的通讯信息验证才可以关注成功企业微信
订阅号和服务号有一个比较明显的区别就是:订阅号都是存放在一个名叫订阅号的文件夹中,点开才能看到所有关注过的订阅号,但是服务号却和好友一样直接就显示在聊天列表中。这个大家打开微信客户端便能看到
2.公众号注册流程
以注册订阅号为例,访问注册地址:https://mp.weixin.qq.com/cgi-bin/registermidpage?action=index&lang=zh_CN&token=
填写基本信息,验证邮箱
选择账号类型
账号主体类型选择个人,填写信息
填写公众号信息
创建成功后,可以进入公众号的管理界面
3.公众号的管理模式
3.1 编辑模式
主要针对非编程人员及信息发布类公众帐号使用。开启该模式后,可以方便地通过界面配置“自定义菜单”和“自动回复的消息”。好处是可视化界面配置,操作简单,快捷,但是功能有限
我们可以给自己的公众号设置关键词回复,收到已关注用户发送的消息,匹配到你好时回复一个你好~~
编辑模式只能预先自定义一些固定的规则和数据,这些数据会保存在微信服务器,只能完成一些简单的功能。如果要完成更复杂的功能,比如根据用户输入信息动态获取数据返回,则需要使用开发模式
3.2 开发模式
主要针对具备开发能力的人使用。开启该模式后,能够使用微信公众平台开放的接口,但是编辑模式的设置会失效
,比如“自定义菜单”和“自动回复的消息”功能。通过编程方式可以实现更多复杂的功能,提供个性化服务。
总的来说,编辑模式就是为所有人提供的,如果你的需求仅仅只是最常见的菜单,自动回复等,使用编辑模式已经满足,但是如果你需求的功能比较复杂,比如需要从自己的业务系统数据库中查询数据返回给微信用户,就需要使用到开发模式。
接下来我们介绍开发模式的各种功能使用步骤。出于安全考虑,普通的订阅号权限非常有限,可以调用的接口非常有限,不便于测试
所以,为了帮助开发者快速了解和上手微信公众号开发,熟悉各个接口的调用,微信推出了公众帐号测试号,无需公众帐号、快速申请接口测试号,通过手机微信扫描二维码即可获得,利用测试号我们可以体验和测试更多高级功能。测试号申请地址:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
但测试号也不是万能的,部分高级功能,如微信支付,卡券功能等也是不开放的。如果要实现支付功能还是得去注册个正式的公众号。本文后续所有测试都基于测试公众号
接入开发模式
我们首先了解下微信与我们的服务器交互的过程:
当我们在微信app上,给公众号发送一条内容的时候,实际会发送到微信的服务器上,此时微信的服务器就会对内容进行封装成某种格式的数据比如xml格式,再转发到我们配置好的某个URL上,所以该URL实际就是我们处理数据的一个请求路径。所以该URL必须是能暴露给外界访问的一个公网地址,不能使用内网地址,生产环境可以申请腾讯云,阿里云服务器等,但是在开发环境中可以暂时利用一些软件来完成内网穿透
在进行和微信公众号服务器交互之前,需要进行接入验证。接入验证涉及的2个关键参数如下:
URL
:就是指我们自己的服务器地址。该URL是开发者用来接收和响应微信消息和事件的接口URL(必须以http://或https://开头,分别支持80端口和443端口)Token
:可任意填写,用作生成签名(必须为英文或数字,长度为3-32字符)
接入验证的大致流程如下:
- 开发者提交URL和token信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带参数如下表所示:
参数 | 描述 |
---|---|
signature | 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 |
timestamp | 时间戳 |
nonce | 随机数 |
echostr | 随机字符串 |
-
开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。加密/校验流程如下:
- 将token、timestamp、nonce三个参数进行字典序排序
- 将三个参数字符串拼接成一个字符串进行sha1加密
- 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
注意到微信发送的GET请求中并没有携带我们填写的token,而签名
signature
的生成过程中token是一个入参,所以我们填写的url处理程序中,校验signature
时也需要token,这就要求我们在url对应的后端程序中定义token,要和通过微信页面填写的token一致。这样做是为了安全性,只有知道token才可以接入成功,避免了他人盗用公众号做操作
根据上述验证流程,我们创建一个Springboot应用,编写验证接口
@RestController
@RequestMapping("wechat/publicAccount")
public class WechatPublicAccountController {
// 微信页面填写的token,必须保密
private static final String TOKEN = "";
@GetMapping("validate")
public String validate(String signature,String timestamp,String nonce,String echostr){
// 1. 将token、timestamp、nonce三个参数进行字典序排序
String[] arr = {timestamp, nonce, TOKEN};
Arrays.sort(arr);
// 2. 将三个参数字符串拼接成一个字符串进行sha1加密
StringBuilder sb = new StringBuilder();
for (String temp : arr) {
sb.append(temp);
}
// 这里利用了hutool的加密工具类
String sha1 = SecureUtil.sha1(sb.toString());
// 3. 加密后的字符串与signature对比,如果相同则该请求来源于微信,原样返回echostr
if (sha1.equals(signature)){
return echostr;
}
// 接入失败
return null;
}
}
利用内网穿透工具,将我们的本地应用地址映射到公网域名
然后在测试号页面填写回调url和正确的token,即可接入成功。如果token填错,将接入失败
特别注意:
- 在接入成功后,后续所有微信发送过来的消息都会携带
signature
、timestamp
、nonce
这3个参数,我们每次接收微信消息时都要跟初始接入一样去校验signature
,以确保接收到的消息是微信发过来的,而不是其他人发过来的- 仅接入消息会携带
echostr
,后续所有微信发过来的消息不会携带echostr
,所以可以根据请求是否携带了echostr
来判断是否是接入消息。接入消息除了校验signature
外要原样返回echostr
,其他后续消息只需校验signature
即可
消息接收与响应
1.接收消息
接入成功以后,我们就可以利用微信提供的接口实现各种功能。首先来看一下基本的消息接收和回复,官方文档位置如下
当关注了公众号的用户向公众号发送消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。所以我们要在Controller中新建一个处理方法
微信会将用户发送的消息信息封装到请求体的xml中,根据消息类型的不同,xml的格式也有所不同:
- 文本消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
</xml>
参数 | 描述 |
---|---|
ToUserName | 开发者微信号 |
FromUserName | 发送方帐号(一个OpenID) |
CreateTime | 消息创建时间 (整型) |
MsgType | 消息类型,文本为text |
Content | 文本消息内容 |
MsgId | 消息id,64位整型 |
- 图片消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[image]]></MsgType>
<PicUrl><![CDATA[this is a url]]></PicUrl>
<MediaId><![CDATA[media_id]]></MediaId>
<MsgId>1234567890123456</MsgId>
</xml>
参数 | 描述 |
---|---|
ToUserName | 开发者微信号 |
FromUserName | 发送方帐号(一个OpenID) |
CreateTime | 消息创建时间 (整型) |
MsgType | 消息类型,图片为image |
PicUrl | 图片链接(由系统生成) |
MediaId | 图片消息媒体id,可以调用获取临时素材接口拉取数据。 |
MsgId | 消息id,64位整型 |
- 语音消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1357290913</CreateTime>
<MsgType><![CDATA[voice]]></MsgType>
<MediaId><![CDATA[media_id]]></MediaId>
<Format><![CDATA[Format]]></Format>
<MsgId>1234567890123456</MsgId>
</xml>
参数 | 描述 |
---|---|
ToUserName | 开发者微信号 |
FromUserName | 发送方帐号(一个OpenID) |
CreateTime | 消息创建时间 (整型) |
MsgType | 语音为voice |
MediaId | 语音消息媒体id,可以调用获取临时素材接口拉取数据。 |
Format | 语音格式,如amr,speex等 |
MsgId | 消息id,64位整型 |
- 视频消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1357290913</CreateTime>
<MsgType><![CDATA[video]]></MsgType>
<MediaId><![CDATA[media_id]]></MediaId>
<ThumbMediaId><![CDATA[thumb_media_id]]></ThumbMediaId>
<MsgId>1234567890123456</MsgId>
</xml>
参数 | 描述 |
---|---|
ToUserName | 开发者微信号 |
FromUserName | 发送方帐号(一个OpenID) |
CreateTime | 消息创建时间 (整型) |
MsgType | 视频为video |
MediaId | 视频消息媒体id,可以调用获取临时素材接口拉取数据。 |
ThumbMediaId | 视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。 |
MsgId | 消息id,64位整型 |
- 小视频消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1357290913</CreateTime>
<MsgType><![CDATA[shortvideo]]></MsgType>
<MediaId><![CDATA[media_id]]></MediaId>
<ThumbMediaId><![CDATA[thumb_media_id]]></ThumbMediaId>
<MsgId>1234567890123456</MsgId>
</xml>
参数 | 描述 |
---|---|
ToUserName | 开发者微信号 |
FromUserName | 发送方帐号(一个OpenID) |
CreateTime | 消息创建时间 (整型) |
MsgType | 小视频为shortvideo |
MediaId | 视频消息媒体id,可以调用获取临时素材接口拉取数据。 |
ThumbMediaId | 视频消息缩略图的媒体id,可以调用获取临时素材接口拉取数据。 |
MsgId | 消息id,64位整型 |
- 地理位置消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1351776360</CreateTime>
<MsgType><![CDATA[location]]></MsgType>
<Location_X>23.134521</Location_X>
<Location_Y>113.358803</Location_Y>
<Scale>20</Scale>
<Label><![CDATA[位置信息]]></Label>
<MsgId>1234567890123456</MsgId>
</xml>
参数 | 描述 |
---|---|
ToUserName | 开发者微信号 |
FromUserName | 发送方帐号(一个OpenID) |
CreateTime | 消息创建时间 (整型) |
MsgType | 消息类型,地理位置为location |
Location_X | 地理位置纬度 |
Location_Y | 地理位置经度 |
Scale | 地图缩放大小 |
Label | 地理位置信息 |
MsgId | 消息id,64位整型 |
- 链接消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1351776360</CreateTime>
<MsgType><![CDATA[link]]></MsgType>
<Title><![CDATA[公众平台官网链接]]></Title>
<Description><![CDATA[公众平台官网链接]]></Description>
<Url><![CDATA[url]]></Url>
<MsgId>1234567890123456</MsgId>
</xml>
参数 | 描述 |
---|---|
ToUserName | 接收方微信号 |
FromUserName | 发送方微信号,若为普通用户,则是一个OpenID |
CreateTime | 消息创建时间 |
MsgType | 消息类型,链接为link |
Title | 消息标题 |
Description | 消息描述 |
Url | 消息链接 |
MsgId | 消息id,64位整型 |
我们对比一下不同类型的xml数据包中的参数,ToUserName
,FromUserName
,CreateTime
,MsgType
,MsgId
这五个是公共的,所有类型都会带上这些参数。接下来,我们需要来了解这5个参数的具体意义:
ToUserName
:文档上描述的是开发者微信号,实际上,直接把它当做你的公众号的微信号即可,表示的是发到那个公众号的意思。FromUserName
:与ToUserName相反,这是代表是由哪个用户发过来的,同一个用户发多条信息过来,FromUserName都是不变的。但这并不是用户的微信号,而是一个OpenID。那什么是OpenID呢:当用户和公众号发生了交互,微信服务器会为每个用户针对每个公众号产生一个OpenID(也就是指该OpenID是利用两个因素:用户和公众号来产生的,也就意味着如果该用户跟另外一个公众号交互,产生的OpenID也是不同的,这样安全性会比较高),如果一个公司有多个公众号,并且需要在多公众号、移动应用之间做用户共通,则需要使用UnionID,前往微信开放平台,将这些公众号和应用绑定到一个开放平台账号下,绑定后,一个用户虽然对多个公众号和应用有多个不同的OpenID,但他对所有这些同一开放平台账号下的公众号和应用,只有一个UnionID,可以在用户管理-获取用户基本信息(UnionID机制)文档了解详情。CreateTime
:消息创建时间MsgType
:用户发送的消息的类型,如text代表文本消息,image代表图片消息等。MsgId
:用户发送的每个消息都有自己的id,可以用于消息排重,比如微信服务器把xml消息包发送到URL了,但是五秒内微信服务器没有收到我们的响应,则会重新发起请求,总共重试三次。如果不做消息排重,那么用户可能就收到多条相同的响应消息了。
微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试
接下来,我们可以创建一个封装消息的实体类,把所有可接收到的参数都放进入,其他类型的暂时不演示,所以只在最后加入了文本和图片的参数。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class InMessage {
// 开发者微信号
protected String FromUserName;
// 发送方帐号(一个OpenID)
protected String ToUserName;
// 消息创建时间
protected Long CreateTime;
/**
* 消息类型
* text 文本消息
* image 图片消息
* voice 语音消息
* video 视频消息
* music 音乐消息
*/
protected String MsgType;
// 消息id
protected Long MsgId;
// 文本内容
private String Content;
// 图片链接(由系统生成)
private String PicUrl;
// 图片消息媒体id,可以调用多媒体文件下载接口拉取数据
private String MediaId;
}
这时候大家可能会有个疑问,为什么字段名称都是大写开头呢?因为微信服务器传过来的xml数据包中的xml元素都是大写开头的,如下所示:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
</xml>
因为xml解析是大小写敏感的,所以为了方便封装,我直接把字段名设置为大写开头。当然,如果还是想要小写开头的字段,也是可以的,我们待会再说处理方式。
实体已经建好之后,我们就可以开始接收微信传过来的xml数据了。
第1步:在handleMessage方法的形参上添加消息实体类型的参数:
第2步:需要配合JAXB的注解来解析xml。在消息实体类上添加以下两个注解:
这2个注解的含义如下:
@XmlRootElement
:是一个类级别
注解,主要属性为name,意为指定根节点的名字。往上面看前面举了个微信传过来的xml数据的例子里,里面的根节点就是"xml",所以这里就直接设置name=“xml”@XmlAccessorType
:用于定义这个类中的何种类型需要映射到XML中XmlAccessType.PROPERTY
:代表映射这个类中的属性(get/set方法)到XMLXmlAccessType.FIELD
:代表映射这个类中的所有字段到XML
之前我们为了方便解析,将消息实体类的属性命名成和xml中的节点一样都是大写开头,现在我们优化一下,按照java规范命名属性小写开头,然后利用@XmlElement
给每个属性标注对应的xml节点名称
@Data
@AllArgsConstructor
@NoArgsConstructor
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class InMessage {
/**
* 开发者微信号
*/
@XmlElement(name="FromUserName")
protected String fromUserName;
/**
* 发送方帐号(一个OpenID)
*/
@XmlElement(name="ToUserName")
protected String toUserName;
/**
* 消息创建时间
*/
@XmlElement(name="CreateTime")
protected Long createTime;
/**
* 消息类型
* text 文本消息
* image 图片消息
* voice 语音消息
* video 视频消息
* music 音乐消息
*/
@XmlElement(name="MsgType")
protected String msgType;
/**
* 消息id
*/
@XmlElement(name="MsgId")
protected Long msgId;
/**
* 文本内容
*/
@XmlElement(name="Content")
private String content;
/**
* 图片链接(由系统生成)
*/
@XmlElement(name="PicUrl")
private String picUrl;
/**
* 图片消息媒体id,可以调用多媒体文件下载接口拉取数据
*/
@XmlElement(name="MediaId")
private String mediaId;
}
然后我们在Controller接收消息的方法中设置断点,关注测试公众号,给它发送一个文本消息"你好",就可以接收到了
2.响应消息
官方文档:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Passive_user_reply_message.html
当用户发送消息给公众号时(或某些特定的用户操作引发的事件推送时),会产生一个POST请求,开发者可以在响应包(Get)中返回特定XML结构,来对该消息进行响应(现支持回复文本、图片、图文、语音、视频、音乐)。严格来说,发送被动响应消息其实并不是一种接口,而是对微信服务器发过来消息的一次回复
。
微信服务器在将用户的消息发给公众号的开发者服务器地址(开发者中心处配置)后,微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次,如果在调试中,发现用户无法收到响应的消息,可以检查是否消息处理超时。关于重试的消息排重,有msgid的消息推荐使用msgid排重。事件类型消息推荐使用FromUserName + CreateTime 排重。
如果开发者希望增强安全性,可以在开发者中心处开启消息加密,这样,用户发给公众号的消息以及公众号被动回复用户消息都会继续加密。
假如服务器无法保证在五秒内处理并回复,必须做出下述回复,这样微信服务器才不会对此作任何处理,并且不会发起重试(这种情况下,可以使用客服消息接口进行异步回复),否则,将出现严重的错误提示:
- 直接回复success(推荐方式)
- 直接回复空串(指字节长度为0的空字符串,而不是XML结构体中content字段的内容为空)
一旦遇到以下情况,微信都会在公众号会话中,向用户下发系统提示“该公众号暂时无法提供服务,请稍后再试”:
- 开发者在5秒内未回复任何内容
- 开发者回复了异常数据,比如JSON数据等
另外,请注意,回复图片(不支持gif动图)等多媒体消息时需要预先通过素材管理接口上传临时素材到微信服务器
,可以使用素材管理中的临时素材,也可以使用永久素材。
各种类型的响应消息格式如下:
- 回复文本消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
</xml>
参数 | 是否必须 | 描述 |
---|---|---|
ToUserName | 是 | 接收方帐号(收到的OpenID) |
FromUserName | 是 | 开发者微信号 |
CreateTime | 是 | 消息创建时间 (整型) |
MsgType | 是 | 消息类型,文本为text |
Content | 是 | 回复的消息内容(换行:在content中能够换行,微信客户端就支持换行显示) |
- 回复图片消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[image]]></MsgType>
<Image>
<MediaId><![CDATA[media_id]]></MediaId>
</Image>
</xml>
参数 | 是否必须 | 说明 |
---|---|---|
ToUserName | 是 | 接收方帐号(收到的OpenID) |
FromUserName | 是 | 开发者微信号 |
CreateTime | 是 | 消息创建时间 (整型) |
MsgType | 是 | 消息类型,图片为image |
MediaId | 是 | 通过素材管理中的接口上传多媒体文件,得到的id。 |
- 回复语音消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[voice]]></MsgType>
<Voice>
<MediaId><![CDATA[media_id]]></MediaId>
</Voice>
</xml>
参数 | 是否必须 | 说明 |
---|---|---|
ToUserName | 是 | 接收方帐号(收到的OpenID) |
FromUserName | 是 | 开发者微信号 |
CreateTime | 是 | 消息创建时间戳 (整型) |
MsgType | 是 | 消息类型,语音为voice |
MediaId | 是 | 通过素材管理中的接口上传多媒体文件,得到的id |
- 回复视频消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[video]]></MsgType>
<Video>
<MediaId><![CDATA[media_id]]></MediaId>
<Title><![CDATA[title]]></Title>
<Description><![CDATA[description]]></Description>
</Video>
</xml>
参数 | 是否必须 | 说明 |
---|---|---|
ToUserName | 是 | 接收方帐号(收到的OpenID) |
FromUserName | 是 | 开发者微信号 |
CreateTime | 是 | 消息创建时间 (整型) |
MsgType | 是 | 消息类型,视频为video |
MediaId | 是 | 通过素材管理中的接口上传多媒体文件,得到的id |
Title | 否 | 视频消息的标题 |
Description | 否 | 视频消息的描述 |
- 回复音乐消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[music]]></MsgType>
<Music>
<Title><![CDATA[TITLE]]></Title>
<Description><![CDATA[DESCRIPTION]]></Description>
<MusicUrl><![CDATA[MUSIC_Url]]></MusicUrl>
<HQMusicUrl><![CDATA[HQ_MUSIC_Url]]></HQMusicUrl>
<ThumbMediaId><![CDATA[media_id]]></ThumbMediaId>
</Music>
</xml>
参数 | 是否必须 | 说明 |
---|---|---|
ToUserName | 是 | 接收方帐号(收到的OpenID) |
FromUserName | 是 | 开发者微信号 |
CreateTime | 是 | 消息创建时间 (整型) |
MsgType | 是 | 消息类型,音乐为music |
Title | 否 | 音乐标题 |
Description | 否 | 音乐描述 |
MusicURL | 否 | 音乐链接 |
HQMusicUrl | 否 | 高质量音乐链接,WIFI环境优先使用该链接播放音乐 |
ThumbMediaId | 是 | 缩略图的媒体id,通过素材管理中的接口上传多媒体文件,得到的id |
- 回复图文消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<ArticleCount>1</ArticleCount>
<Articles>
<item>
<Title><![CDATA[title1]]></Title>
<Description><![CDATA[description1]]></Description>
<PicUrl><![CDATA[picurl]]></PicUrl>
<Url><![CDATA[url]]></Url>
</item>
</Articles>
</xml>
参数 | 是否必须 | 说明 |
---|---|---|
ToUserName | 是 | 接收方帐号(收到的OpenID) |
FromUserName | 是 | 开发者微信号 |
CreateTime | 是 | 消息创建时间 (整型) |
MsgType | 是 | 消息类型,图文为news |
ArticleCount | 是 | 图文消息个数;当用户发送文本、图片、语音、视频、图文、地理位置这六种消息时,开发者只能回复1条图文消息;其余场景最多可回复8条图文消息 |
Articles | 是 | 图文消息信息,注意,如果图文数超过限制,则将只发限制内的条数 |
Title | 是 | 图文消息标题 |
Description | 是 | 图文消息描述 |
PicUrl | 是 | 图片链接,支持JPG、PNG格式,较好的效果为大图360*200,小图200*200 |
Url | 是 | 点击图文消息跳转链接 |
我们可以看到回复的格式中除了都没有MsgId,只有普通文本和接收的格式基本是一样的,图片消息或其他类型的消息与接收到的格式相比,多了xml元素的嵌套,所以原先定义的接收消息实体类无法复用,我们再封装一个响应消息的实体类,这里暂时只考虑文本和图片类型
@Data
@AllArgsConstructor
@NoArgsConstructor
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class OutMessage {
/**
* 开发者微信号
*/
@XmlElement(name="FromUserName")
protected String fromUserName;
/**
* 发送方帐号(一个OpenID)
*/
@XmlElement(name="ToUserName")
protected String toUserName;
/**
* 消息创建时间
*/
@XmlElement(name="CreateTime")
protected Long createTime;
/**
* 消息类型
* text 文本消息
* image 图片消息
* voice 语音消息
* video 视频消息
* music 音乐消息
*/
@XmlElement(name="MsgType")
protected String msgType;
/**
* 文本内容
*/
@XmlElement(name="Content")
private String content;
/**
* 图片的媒体id列表
*/
@XmlElementWrapper(name="Image") // 表示MediaId属性内嵌于<Image>标签
@XmlElement(name = "MediaId")
private String[] mediaId;
}
@XmlElementWrapper
注解可以在原xml结点上再包装一层xml,但仅允许出现在数组或集合属性上
现在我们来实现一下简单的回复消息:用户给我们发什么,我们就回复什么,只需要把接收到InMessage
的内容设置到OutMessage
上,并且把ToUserName
与FromUserName
的值设置为相反即可
@PostMapping(value = "validate", produces = "application/xml;charset=UTF-8")
public Object handleMessage(@RequestBody InMessage inMessage){
// 创建响应消息实体对象
OutMessage outMessage = new OutMessage();
// 把原来的接收方设置为发送方
outMessage.setFromUserName(inMessage.getToUserName());
// 把原来的发送方设置为接收方
outMessage.setToUserName(inMessage.getFromUserName());
// 设置消息类型
outMessage.setMsgType(inMessage.getMsgType());
// 设置消息时间
outMessage.setCreateTime(System.currentTimeMillis());
// 根据接收到消息类型,响应对应的消息内容
if ("text".equals(inMessage.getMsgType())){
// 文本
outMessage.setContent(inMessage.getContent());
}else if ("image".equals(inMessage.getMsgType())){
// 图片
outMessage.setMediaId(new String[]{inMessage.getMediaId()});
}
return outMessage;
}
注意:这里要在接口的
@PostMapping
中指定produces = "application/xml;charset=UTF-8"
,表示返回的数据格式是xml,否则将默认以json格式返回
测试效果如下
关键字回复
在开发模式下要实现关键字回复非常简单,只要判断发送过来的文本消息中是否包含关键字,然后给予相应的回复即可
@PostMapping(value = "validate", produces = "application/xml;charset=UTF-8")
public Object handleMessage(@RequestBody InMessage inMessage){
// 创建响应消息实体对象
OutMessage outMessage = new OutMessage();
// 把原来的接收方设置为发送方
outMessage.setFromUserName(inMessage.getToUserName());
// 把原来的发送方设置为接收方
outMessage.setToUserName(inMessage.getFromUserName());
// 设置消息类型
outMessage.setMsgType(inMessage.getMsgType());
// 设置消息时间
outMessage.setCreateTime(System.currentTimeMillis() / 1000);
// 根据接收到消息类型,响应对应的消息内容
if ("text".equals(inMessage.getMsgType())){
// 根据不同的关键字回复消息
String inContent = inMessage.getContent();
if (inContent.contains("游戏")){
outMessage.setContent("仙剑");
}else if (inContent.contains("动漫")){
outMessage.setContent("进击的巨人");
}else {
outMessage.setContent(inContent);
}
}else if ("image".equals(inMessage.getMsgType())){
// 图片
outMessage.setMediaId(new String[]{inMessage.getMediaId()});
}
return outMessage;
}
事件推送
官方文档:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html
我们之前的案例都是用户发送信息过来,我们才回复的。但是,如果是关注的时候需要马上回复,就要使用到事件消息,实际上,微信已经提供给我们很多的事件
在微信用户和公众号产生交互的过程中,用户的某些操作会使得微信服务器通过事件推送的形式通知到开发者在开发者中心处设置的服务器地址,从而开发者可以获取到该信息。其中,某些事件推送在发生后,是允许开发者回复用户的,某些则不允许
1.关注/取消关注事件
用户在关注与取消关注公众号时,微信会把这个事件推送到开发者填写的URL。方便开发者给用户下发欢迎消息或者做帐号的解绑。为保护用户数据隐私,开发者收到用户取消关注事件时需要删除该用户的所有信息。
微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。
关于重试的消息排重,推荐使用FromUserName + CreateTime 排重。
假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试。
推送XML数据包示例:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event>
</xml>
参数 | 描述 |
---|---|
ToUserName | 开发者微信号 |
FromUserName | 发送方帐号(一个OpenID) |
CreateTime | 消息创建时间 (整型) |
MsgType | 消息类型,event |
Event | 事件类型,subscribe(订阅)、unsubscribe(取消订阅) |
2.扫描带参数二维码事件
用户扫描带场景值二维码时,可能推送以下两种事件:
- 如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event>
<EventKey><![CDATA[qrscene_123123]]></EventKey>
<Ticket><![CDATA[TICKET]]></Ticket>
</xml>
参数 | 描述 |
---|---|
ToUserName | 开发者微信号 |
FromUserName | 发送方帐号(一个OpenID) |
CreateTime | 消息创建时间 (整型) |
MsgType | 消息类型,event |
Event | 事件类型,subscribe |
EventKey | 事件KEY值,qrscene_为前缀,后面为二维码的参数值 |
Ticket | 二维码的ticket,可用来换取二维码图片 |
- 如果用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者。
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[SCAN]]></Event>
<EventKey><![CDATA[SCENE_VALUE]]></EventKey>
<Ticket><![CDATA[TICKET]]></Ticket>
</xml>
参数 | 描述 |
---|---|
ToUserName | 开发者微信号 |
FromUserName | 发送方帐号(一个OpenID) |
CreateTime | 消息创建时间 (整型) |
MsgType | 消息类型,event |
Event | 事件类型,SCAN |
EventKey | 事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id |
Ticket | 二维码的ticket,可用来换取二维码图片 |
3.上报地理位置事件
这个事件仅用于服务号,订阅号不行
。用户同意上报地理位置后,每次进入公众号会话时,都会在进入时上报地理位置,或在进入会话后每5秒上报一次地理位置,公众号可以在公众平台网站中修改以上设置。上报地理位置时,微信会将上报地理位置事件推送到开发者填写的URL。
推送XML数据包示例:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[LOCATION]]></Event>
<Latitude>23.137466</Latitude>
<Longitude>113.352425</Longitude>
<Precision>119.385040</Precision>
</xml>
参数 | 描述 |
---|---|
ToUserName | 开发者微信号 |
FromUserName | 发送方帐号(一个OpenID) |
CreateTime | 消息创建时间 (整型) |
MsgType | 消息类型,event |
Event | 事件类型,LOCATION |
Latitude | 地理位置纬度 |
Longitude | 地理位置经度 |
Precision | 地理位置精度 |
4.自定义菜单事件
用户点击自定义菜单后,微信会把点击事件推送给开发者,请注意,点击菜单弹出子菜单,不会产生上报
。
点击菜单拉取消息时的事件推送
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[CLICK]]></Event>
<EventKey><![CDATA[EVENTKEY]]></EventKey>
</xml>
参数 | 描述 |
---|---|
ToUserName | 开发者微信号 |
FromUserName | 发送方帐号(一个OpenID) |
CreateTime | 消息创建时间 (整型) |
MsgType | 消息类型,event |
Event | 事件类型,CLICK |
EventKey | 事件KEY值,与自定义菜单接口中KEY值对应 |
点击菜单跳转链接时的事件推送
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[VIEW]]></Event>
<EventKey><![CDATA[www.qq.com]]></EventKey>
</xml>
参数 | 描述 |
---|---|
ToUserName | 开发者微信号 |
FromUserName | 发送方帐号(一个OpenID) |
CreateTime | 消息创建时间 (整型) |
MsgType | 消息类型,event |
Event | 事件类型,VIEW |
EventKey | 事件KEY值,设置的跳转URL |
事件推送案例演示
我们来实现一下用户关注公众号时接收推送消息并自动回复的功能
事件和消息都是推送到我们的URL上,怎么区分他们也很简单,通过MsgType
这个属性,那么进一步再区分是关注还是取消关注,根据Event
属性即可。所以,我们在原来的InMessage类,再添加一个Event属性
然后在Controller处理方法中添加对应逻辑
然后取消关注后再关注,即可展现效果
自定义菜单
1.自定义菜单简介
官方文档:https://developers.weixin.qq.com/doc/offiaccount/Custom_Menus/Creating_Custom-Defined_Menu.html
自定义菜单能够帮助公众号丰富界面,让用户更好更快地理解公众号的功能。开启自定义菜单后,公众号界面如图所示:
请注意:
- 自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。
- 一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“…”代替。
- 创建自定义菜单后,菜单的刷新策略是,在用户进入公众号会话页或公众号profile页时,如果发现上一次拉取菜单的请求在5分钟以前,就会拉取一下菜单,如果菜单有更新,就会刷新客户端的菜单。测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果。
自定义菜单接口可实现多种类型按钮,如下:
- click:点击推事件用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互;
- view:跳转URL用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的网页URL,可与网页授权获取用户基本信息接口结合,获得用户基本信息。
- scancode_push:扫码推事件用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后显示扫描结果(如果是URL,将进入URL),且会将扫码的结果传给开发者,开发者可以下发消息。
- scancode_waitmsg:扫码推事件且弹出“消息接收中”提示框用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后,将扫码的结果传给开发者,同时收起扫一扫工具,然后弹出“消息接收中”提示框,随后可能会收到开发者下发的消息。
- pic_sysphoto:弹出系统拍照发图用户点击按钮后,微信客户端将调起系统相机,完成拍照操作后,会将拍摄的相片发送给开发者,并推送事件给开发者,同时收起系统相机,随后可能会收到开发者下发的消息。
- pic_photo_or_album:弹出拍照或者相册发图用户点击按钮后,微信客户端将弹出选择器供用户选择“拍照”或者“从手机相册选择”。用户选择后即走其他两种流程。
- pic_weixin:弹出微信相册发图器用户点击按钮后,微信客户端将调起微信相册,完成选择操作后,将选择的相片发送给开发者的服务器,并推送事件给开发者,同时收起相册,随后可能会收到开发者下发的消息。
- location_select:弹出地理位置选择器用户点击按钮后,微信客户端将调起地理位置选择工具,完成选择操作后,将选择的地理位置发送给开发者的服务器,同时收起位置选择工具,随后可能会收到开发者下发的消息。
- media_id:下发消息(除文本消息)用户点击media_id类型按钮后,微信服务器会将开发者填写的永久素材id对应的素材下发给用户,永久素材类型可以是图片、音频、视频、图文消息。请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id。
- view_limited:跳转图文消息URL用户点击view_limited类型按钮后,微信客户端将打开开发者在按钮中填写的永久素材id对应的图文消息URL,永久素材类型只支持图文消息。请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id。
请注意,3到8的所有事件,仅支持微信iPhone5.4.1以上版本,和Android5.4以上版本的微信用户,旧版本微信用户点击后将没有回应,开发者也不能正常接收到事件推送。9和10,是专门给第三方平台旗下未微信认证(具体而言,是资质认证未通过)的订阅号准备的事件类型,它们是没有事件推送的,能力相对受限,其他类型的公众号不必使用。
如果我们要给公众号创建自定义菜单,需要发送下列请求
http请求方式:POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN
请求体示例如下:
{
"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"
}]
}]
}
参数 | 是否必须 | 说明 |
---|---|---|
button | 是 | 一级菜单数组,个数应为1~3个 |
sub_button | 否 | 二级菜单数组,个数应为1~5个 |
type | 是 | 菜单的响应动作类型,view表示网页类型,click表示点击类型,miniprogram表示小程序类型 |
name | 是 | 菜单标题,不超过16个字节,子菜单不超过60个字节 |
key | click等点击类型必须 | 菜单KEY值,用于消息接口推送,不超过128字节 |
url | view、miniprogram类型必须 | 网页 链接,用户点击菜单可打开链接,不超过1024字节。 type为miniprogram时,不支持小程序的老版本客户端将打开本url。 |
media_id | media_id类型和view_limited类型必须 | 调用新增永久素材接口返回的合法media_id |
appid | miniprogram类型必须 | 小程序的appid(仅认证公众号可配置) |
pagepath | miniprogram类型必须 | 小程序的页面路径 |
正确时的返回JSON数据包如下:
{"errcode":0,"errmsg":"ok"}
错误时的返回JSON数据包如下(示例为无效菜单名长度):
{"errcode":40018,"errmsg":"invalid button name size"}
其他类型的菜单示例如下
{
"button": [
{
"name": "扫码",
"sub_button": [
{
"type": "scancode_waitmsg",
"name": "扫码带提示",
"key": "rselfmenu_0_0",
"sub_button": [ ]
},
{
"type": "scancode_push",
"name": "扫码推事件",
"key": "rselfmenu_0_1",
"sub_button": [ ]
}
]
},
{
"name": "发图",
"sub_button": [
{
"type": "pic_sysphoto",
"name": "系统拍照发图",
"key": "rselfmenu_1_0",
"sub_button": [ ]
},
{
"type": "pic_photo_or_album",
"name": "拍照或者相册发图",
"key": "rselfmenu_1_1",
"sub_button": [ ]
},
{
"type": "pic_weixin",
"name": "微信相册发图",
"key": "rselfmenu_1_2",
"sub_button": [ ]
}
]
},
{
"name": "发送位置",
"type": "location_select",
"key": "rselfmenu_2_0"
},
{
"type": "media_id",
"name": "图片",
"media_id": "MEDIA_ID1"
},
{
"type": "view_limited",
"name": "图文消息",
"media_id": "MEDIA_ID2"
}
]
}
2.获取access_token
在我们调用创建菜单接口之前,必须先获取调用微信接口的access_token
。access_token
是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token
。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效
。
公众平台的API调用所需的access_token的使用及生成方式说明:
- 建议公众号开发者使用中控服务器统一获取和刷新access_token,其他业务逻辑服务器所使用的access_token均来自于该中控服务器,不应该各自去刷新,否则容易造成冲突,导致access_token覆盖而影响业务;
- 目前access_token的有效期通过返回的expire_in来传达,目前是7200秒之内的值。中控服务器需要根据这个有效时间提前去刷新新access_token。在刷新过程中,中控服务器可对外继续输出的老access_token,此时公众平台后台会保证在5分钟内,新老access_token都可用,这保证了第三方业务的平滑过渡;
- access_token的有效时间可能会在未来有调整,所以中控服务器不仅需要内部定时主动刷新,还需要提供被动刷新access_token的接口,这样便于业务服务器在API调用获知access_token已超时的情况下,可以触发access_token的刷新流程。
- 对于可能存在风险的调用,在开发者进行获取 access_token调用时进入风险调用确认流程,需要用户管理员确认后才可以成功获取。具体流程为:开发者通过某IP发起调用->平台返回错误码[89503]并同时下发模板消息给公众号管理员->公众号管理员确认该IP可以调用->开发者使用该IP再次发起调用->调用成功。如公众号管理员第一次拒绝该IP调用,用户在1个小时内将无法使用该IP再次发起调用,如公众号管理员多次拒绝该IP调用,该IP将可能长期无法发起调用。平台建议开发者在发起调用前主动与管理员沟通确认调用需求,或请求管理员开启IP白名单功能并将该IP加入IP白名单列表。
公众号和小程序均可以使用AppID和AppSecret调用本接口来获取access_token。AppID和AppSecret可在“微信公众平台-开发-基本配置”页中获得(需要已经成为开发者,且帐号没有异常状态)。
调用接口时,请登录“微信公众平台-开发-基本配置”提前将服务器IP地址添加到IP白名单中,否则将无法调用成功。
小程序无需配置IP白名单。
获取access_token
需要调用的接口如下
https请求方式: GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
参数 | 是否必须 | 说明 |
---|---|---|
grant_type | 是 | 获取access_token填写client_credential |
appid | 是 | 公众号唯一凭证,注册成功后由微信提供 |
secret | 是 | 公众号唯一凭证密钥,注册成功后由微信提供 |
正常情况下,微信会返回下述JSON数据包给公众号:
{"access_token":"ACCESS_TOKEN","expires_in":7200}
参数 | 说明 |
---|---|
access_token | 获取到的凭证 |
expires_in | 凭证有效时间,单位:秒 |
错误时微信会返回错误码等信息,JSON数据包示例如下(该示例为AppID无效错误):
{"errcode":40013,"errmsg":"invalid appid"}
返回码 | 说明 |
---|---|
-1 | 系统繁忙,此时请开发者稍候再试 |
0 | 请求成功 |
40001 | AppSecret错误或者AppSecret不属于这个公众号,请开发者确认AppSecret的正确性 |
40002 | 请确保grant_type字段值为client_credential |
40164 | 调用接口的IP地址不在白名单中,请在接口IP白名单中进行设置。(小程序及小游戏调用不要求IP地址在白名单内。) |
89503 | 此IP调用需要管理员确认,请联系管理员 |
89501 | 此IP正在等待管理员确认,请联系管理员 |
89506 | 24小时内该IP被管理员拒绝调用两次,24小时内不可再使用该IP调用 |
89507 | 1小时内该IP被管理员拒绝调用一次,1小时内不可再使用该IP调用 |
在Controller中创建获取access_token
的方法,获取成功后将其保存到redis中
@GetMapping("getAccessToken")
public String getAccessToken(){
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + APPID +
"&secret=" + APPSECRET;
// 利用hutool的http工具类请求获取access_token
String result = HttpUtil.get(url);
// 将结果解析为json
JSONObject jsonObject = JSONUtil.parseObj(result);
// 获取access_token
String accessToken = jsonObject.getStr("access_token");
if (!StringUtils.isEmpty(accessToken)){
// 将access_token存入redis
stringRedisTemplate.opsForValue().set("access_token", accessToken);
}
return accessToken;
}
3.创建自定义菜单
在Controller中新增方法,发送创建菜单的请求
@GetMapping("createMenu")
public String createMenu(){
// 从redis中取出access_token
String accessToken = stringRedisTemplate.opsForValue().get("access_token");
String url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=" + accessToken;
// 创建菜单的请求体
String body = "{\n" +
" \"button\":[\n" +
" {\t\n" +
" \"type\":\"click\",\n" +
" \"name\":\"位置\",\n" +
" \"key\":\"button_location\"\n" +
" }]}";
return HttpUtil.post(url, body);
}
发送请求后返回结果
{"errcode":0,"errmsg":"ok"}
创建菜单后效果如下
发送模板消息
官方文档:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html
在我们的生活中,无论是微商城消费,还是日常生活消费,都可能收到这种提示,比如订单通知,快递状态通知,银行卡支付通知,都属于业务通知,很多公众号也都实现了这种功能,当触发了某种行为或状态改变,就会发送这么一个消息给你,因为这种消息都是按照一定的的格式来编辑,所以也叫模板消息
认证后的服务号才可以申请模板消息的使用权限并获得该权限,否则就只能使用测试号
我们要发送模板消息,第一步是需要创建一个模板,有了模板之后,我们才能填充内容来进行发送。创建模板不需要调用接口,在公众号后台即可设置
现在我们来按照下面案例来新建一个模板。但是模板的内容是有一定的规则的,不能随便添加:
- 测试模板的模板ID仅用于测试,不能用来给正式帐号发送模板消息
- 为方便测试,测试模板可任意指定内容,但实际上正式帐号的模板消息,只能从模板库中获得
- 需为正式帐号申请新增符合要求的模板,需使用正式号登录公众平台,按指引申请
- 模板内容可设置参数(模板标题不可),供接口调用时使用,参数需以{{开头,以.DATA}}结尾
保存之后,微信会给该模板分配一个ID,待我们要发送模板消息的时候就需要用到这个ID了
发送模板消息需要如下请求:
http请求方式: POST https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN
请求体示例如下
{
"touser":"OPENID",
"template_id":"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY",
"url":"http://weixin.qq.com/download",
"miniprogram":{
"appid":"xiaochengxuappid12345",
"pagepath":"index?foo=bar"
},
"data":{
"goodsName":{
"value":"巧克力",
"color":"#173177"
},
"price": {
"value":"39.8元",
"color":"#173177"
},
"time": {
"value":"2014年9月22日",
"color":"#173177"
},
"remark":{
"value":"欢迎再次购买!",
"color":"#173177"
}
}
}
参数 | 是否必填 | 说明 |
---|---|---|
touser | 是 | 接收者openid |
template_id | 是 | 模板ID |
url | 否 | 模板跳转链接(海外帐号没有跳转能力) |
miniprogram | 否 | 跳小程序所需数据,不需跳小程序可不用传该数据 |
appid | 是 | 所需跳转到的小程序appid(该小程序appid必须与发模板消息的公众号是绑定关联关系,暂不支持小游戏) |
pagepath | 否 | 所需跳转到小程序的具体页面路径,支持带参数,(示例index?foo=bar),要求该小程序已发布,暂不支持小游戏 |
data | 是 | 模板数据 |
color | 否 | 模板内容字体颜色,不填默认为黑色 |
在调用模板消息接口后,会返回JSON数据包。正常时的返回JSON数据包示例:
{
"errcode":0,
"errmsg":"ok",
"msgid":200228332
}
评论区