微信公众号开发简明介绍

一、公众号介绍

  • 微信公众号分类
  1. 订阅号:主要偏于为用户传达资讯(类似报纸杂志),认证前后都是每天只可以群发一条消息;
  2. 服务号:主要偏于服务交互(类似银行,114,提供服务查询),认证前后都是每个月可群发4条消息;
  3. 企业号:主要用于公司内部通讯使用,需要先有成员的通讯信息验证才可以关注成功企业号;
  • 公众号交互模式

公众号的交互可以看作是,微信客户端,微信服务器和公众号后台三方的交互,其交互模式如图所示:


微信公众号交互模式

在公众号后台调用用户信息相关接口和消息推送时,以用户的OpenID作为唯一标识;

什么是OpenID

为了识别用户,每个用户针对每个公众号会产生一个安全的OpenID,如果需要在多公众号、移动应用之间做用户共通,则需前往微信开放平台,将这些公众号和应用绑定到一个开放平台账号下,绑定后,一个用户虽然对多个公众号和应用有多个不同的OpenID,但他对所有这些同一开放平台账号下的公众号和应用,只有一个UnionID;

二、开发注意事项

  1. 微信公众平台开发是指为微信公众号进行业务开发,为移动应用、PC端网站、公众号第三方平台(为各行各业公众号运营者提供服务)的开发,请前往微信开放平台接入。
  2. 在申请到认证公众号之前,你可以先通过测试号申请系统,快速申请一个接口测试号,立即开始接口测试开发。
  3. 在开发过程中,可以使用接口调试工具来在线调试某些接口。
  4. 每个接口都有每日接口调用频次限制,可以在公众平台官网-开发者中心处查看具体频次。
  5. 在开发出现问题时,可以通过接口调用的返回码,以及报警排查指引(在公众平台官网-开发者中心处可以设置接口报警),来发现和解决问题。
  6. 公众平台以access_token为接口调用凭据,来调用接口,所有接口的调用需要先获取access_token,access_token在2小时内有效,过期需要重新获取,但1天内获取次数有限,开发者需自行存储,详见获取接口调用凭据(access_token)文档。
  7. 公众平台接口调用仅支持80端口。
  8. 微信平台推送给公众号后台的数据均为xml格式,而调用接口返回的数据均为json格式

三、开发准备

  • 准备服务器,搭建后台服务

在注册成为微信公众号开发者之前,需要先搭建一个web应用服务来作为公众号的后台服务,这里以SpringMVC为例,在做好基础配置和部署后,编写这样一个Controller来作为处理微信服务的接口:

@Controller
@RequestMapping("/wx")
public class WechatController {

    @RequestMapping("")
    public void core(HttpServletRequest request, HttpServletResponse response) throws IOException {

        //这里对request进行处理
    }

}
  • 进行开发者配置

在成为微信公众号开发者后,登录公众号平台官网会要求开发者配置服务器相关的信息

公众号配置

这里的URL(服务器地址)格式为:http://外网IP(域名):端口号/微信服务接口http的端口号固定使用 80https443不可填写其他。这个url为微信平台将该公众号中的用户信息/操作推送到公众号后台的请求地址,在每次修改配置时会发送一条用于校验服务器地址的请求,后台服务需要对请求中的校验字段进行处理,并做出相应,配置才会生效。
Token:用于验证后台服务器的标识,自行设置。

  • 对微信平台的发送的验证消息进行处理

对微信平台推送来的请求处理逻辑流程图如下:

微信消息处理流程

用java代码的处理方式如下:

// 微信加密签名
String signature = request.getParameter("signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
// 随机字符串
String echostr = request.getParameter("echostr");

PrintWriter out = response.getWriter();
// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
if (SignUtil.checkSignature(signature, timestamp, nonce)) {
    if(echostr != null) {      //
        out.print(echostr);
    } else {
        //进行对应的消息/操作处理
    }
}
out.close();         
/**
 * 验证签名算法
 * 
 * @param signature
 * @param timestamp
 * @param nonce
 * @return
 */
public static boolean checkSignature(String signature, String timestamp, String nonce) {

    String[] arr = new String[] { TOKEN, timestamp, nonce };
    // 将token、timestamp、nonce三个参数进行字典序排序
    Arrays.sort(arr);
    StringBuilder content = new StringBuilder();
    for (int i = 0; i < arr.length; i++) {
        content.append(arr[i]);
    }
    MessageDigest md = null;
    String tmpStr = null;

    try {
        md = MessageDigest.getInstance("SHA-1");
        // 将三个参数字符串拼接成一个字符串进行sha1加密
        byte[] digest = md.digest(content.toString().getBytes());
        tmpStr = byteToStr(digest);
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }

    content = null;
    // 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
    return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
}

当配置页面tonken验证成功时,将保存该配置

四、常见场景

  • 自动回复用户一条消息

预实现功能
粉丝给公众号一条文本消息,公众号立马回复一条文本消息给粉丝,不需要通过公众平台网页操作。

具体流程
粉丝给公众号发送文本消息:“欢迎开启公众号开发者模式”,在开发者后台,收到公众平台发送的xml 如下:

<xml>
<ToUserName><![CDATA[公众号]]></ToUserName>
<FromUserName><![CDATA[粉丝号]]></FromUserName>
<CreateTime>1460537339</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[欢迎开启公众号开发者模式]]></Content>
<MsgId>6272960105994287618</MsgId>
</xml> 

解释
createTime 是微信公众平台记录粉丝发送该消息的具体时间
MsgType 用于标记消息类型,一般用于区别判断,text说明该xml 是文本消息
MsgId 是公众平台为记录识别该消息的一个标记数值, 微信后台系统自动产生

接下来对推送这个消息的请求作出响应,则可以对发送消息的用户进行回复

特别强调

  1. 被动回复消息,即发送被动响应消息,不同于客服消息接口
  2. 它其实并不是一种接口,而是对微信服务器发过来消息的一次回复
  3. 收到粉丝消息后不想或者不能5秒内回复时,需回复“success”字符串(下文详细介绍)
  4. 客服接口在满足一定条件下随时调用

公众号想回复给粉丝一条文本消息,内容为“test”, 那么开发者发送给公众平台后台的xml 内容如下:

<xml>
<ToUserName><![CDATA[粉丝号]]></ToUserName>
<FromUserName><![CDATA[公众号]]></FromUserName>
<CreateTime>1460541339</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[test]]></Content>
</xml>

备注

  1. ToUserName(接受者)、FromUserName(发送者) 字段按际填写
  2. createtime 只用于标记开发者回复消息的时间,微信后台发送此消息都是不受这个字段约束
  3. text : 用于标记 此次行为是发送文本消息 (当然可以是image/voice等类型,不同类型的具体参数参考官方文档)
  4. 文本换行使用‘\n’

将以上内容写入response的响应体中,则可对相应消息进行回复,其流程图如下图所示:

微信消息回复流程
  • 自定义公众号菜单

自定义菜单需要主动调用微信的相关接口,这就需要使用到Access_token

什么是 access_token
access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。

具体调用方法可查看公众平台wiki(官方防止盗链,请复制地址在浏览器查看), 在实际使用时,对access_token的控制需要使用中控服务器的方式来进行获取

由于几乎所有主动调用微信平台的接口都需要使用access_token,这里以自定义菜单为例,其余接口都会以相同模式进行调用。

预实现功能
服务端自行定义菜单样式,在用户点击菜单后,打开对应页面或者回复相应消息。

具体流程
首先查看公众平台wiki得知新建自定义菜单的接口为
POST https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN

发送的json数据格式为

{
     "button":[
     {  
          "type":"click",
          "name":"今日歌曲",
          "key":"V1001_TODAY_MUSIC"
      },
      {
           "name":"菜单",
           "sub_button":[
           {    
               "type":"view",
               "name":"搜索",
               "url":"http://www.soso.com/"
            },
            {
               "type":"click",
               "name":"赞一下我们",
               "key":"V1001_GOOD"
            }]
       }]
 }

其中type字段标识了菜单按钮的类型,常用的类型为clickview,分别代表点击推送事件类型和跳转指定URL类型。其它类型可参考公众平台wiki。

在微信接口调用正确时,均会返回如下数据格式:

{"errcode":0,"errmsg":"ok"}

否则返回如下数据:

{"errcode":40018,"errmsg":"invalid button name size"}

其中errmsg将说明具体错误信息

实现方式
使用Apache的httpclient工具包进行微信接口的请求,其中的数据可以先定义对象,再使用第三方json包进行序列化。具体实现过程不再赘述。

自定义菜单成功后,将生成如下类似界面


自定义菜单

若其中一个为click类型按钮,则客户端点击该按钮后,服务端将接收到内容如下格式的请求:

<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>

其中Event标识的事件类型为“CLICK”,EventKey与自定义接口时传入的key值相对应
对事件的回复方式与普通消息类似,不同的在于某些事件不可以进行回复,且服务器未作响应时,微信平台不会重复发送请求。

  • 通过微信认证打开页面,并校验用户是否绑定已有账号

如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。因此,可以通过微信网页的授权机制,在用户访问公众号后台网页时获取访问用户的相关信息。

微信的认证授权是基于OAuth2.0机制实现的,有关OAuth2.0的知识可参考网上的资料。OAuth2.0基本模式可以理解为用户在授权方进行登录,再由授权方回调请求授权方的接口并带上接口调用凭证,请求授权方再使用该凭证和秘钥信息调用授权方用户信息的相关接口

微信授权的基本步骤如下:

  1. 用户同意授权,获取code
  2. 通过code换取网页授权access_token
  3. 刷新access_token(如果需要)
  4. 拉取用户信息(需scope为 snsapi_userinfo)
  5. 检验授权凭证(access_token)是否有效

若是针对已关注公众号的微信用户,则仅需前面两步即可取得用户的相关信息。每一步的具体操作可以参考公众平台wiki

实现方式
这里以SpringMVC的实现方式为例,定义一个链接为
"https://open.weixin.qq.com/connect/oauth2/authorize?appid="+Config.instance().getAppid()+"&redirect_uri=http://wx.keyil.cn/99/api/oauth2&response_type=code&scope=snsapi_base&state=userInfo#wechat_redirect"
的按钮

参数顺序要保证一致,微信授权对该链接的校验较为严格

用户在点击该链接后,将进行微信授权,已关注用户会通过静默授权的方式跳转至回调页面。此时,在回调接口对微信返回的code进行处理:

@Controller
@RequestMapping("/oauth2")
public class OAthuController {

    @Autowired
    private AccountService accountService;

    @RequestMapping("")
    public String oathu2(@RequestParam(required = true) String code, String state, HttpServletRequest request,
            HttpServletResponse response) {

        String openId = accountService.getOauth2OpenId(code);

        Assert.notNull(openId, "openId为空");
        CookieUtil.setCookie(response, "openid", openId, "hostname", "/api", 60 * 5);

        String redirectUrl = "redirect:http://hostname";
        if (accountService.getAccountInfo(openId) == null) {
            redirectUrl += "userbind.html";
        } else {
            switch (state) {
            case "userInfo":
                redirectUrl += "userInfo.html";
                break;
            case "addressList":
                redirectUrl += "addressList.html";
                break;
            }
        }
        return redirectUrl;
    }
}

其中的AccountService已经对微信跟用户相关的接口进行了封装;对于已经关注公众号的用户,通过微信返回的code字段,便可以获取用户的OpenID,进而获取用户的相关信息;将该OpenID写入cookie,以保证该用户在后续的绑定请求中携带该字段。

    @RequestMapping(value = "/bind", method = RequestMethod.POST)
    public void bindUser(@RequestParam(required = true) String phoneNumber, @CookieValue(required = true) String openid,
            HttpServletResponse response) {

        accountService.bindUser(openid, phoneNumber);
        this.writeSuccessResponse(response, "success");
    }

在调用绑定请求时,通过cookie获取用户的OpenID,通过用户验证后,对该用户进行微信绑定。以上是demo的写法,仅供参考。

五、结语

以上内容便是微信公众号开发的一些主要模式的总结,微信后台的大部分功能均可遵循以上模式进行开发。除此之外,还有微信提供的js-jdk用于公众号后台网页的开发,该内容将会在后续的开发过程中进行补充。内容中如有疏漏,还往读者积极指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,898评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,401评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,058评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,539评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,382评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,319评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,706评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,370评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,664评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,715评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,476评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,326评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,730评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,003评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,275评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,683评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,877评论 2 335

推荐阅读更多精彩内容