需求背景
采用微信登录的授权的 网页应用 需要用到 服务号 的模板下发功能
例如在某电商网页上进行了购买操作,需要相关联的服务号发送一条购买成功的模板消息。(如果关注了京东的服务号,每次在京东上买东西的时候会受到服务号下发模板消息)
需要达成效果如下:
实现思路
微信开发会遇到两种常见的用户ID,openid 和 unionid
每个用户对开放平台有唯一的unionid
每个用户对于每个服务号或者网页/APP应用有不同的openid
需要以unionid为桥梁打通 网页应用 和 服务号 的数据关联。
实现步骤
- 服务号和开放平台认证 并 服务号绑定开放平台。
- 用户在网页应用采用微信授权登录,授权成功后根据 code 获取用户 unionid
- 部署服务号消息订阅服务器,接受微信推送的事件消息(例如服务号关注事件,取消关注事件)
- 在用户关注公众号的时候,订阅服务器获取订阅用户的服务号openid
- 用服务号openid获取userinfo(用户信息),其中包括unionid
- 通过unionid确定用户身份,将服务号openid写入该用户表
- 在业务发生的时候,通过服务号openid直接下发模板消息。
网页应用授权
参考官方文档 https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=4d3543ff030ecd73c21923bd8426d08678986d88&lang=
第一步:请求CODE
步骤1:在页面中先引入如下JS文件(支持https):
http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js
步骤2:在需要使用微信登录的地方实例以下JS对象:
var obj = new WxLogin({
self_redirect:false,
id:"login_container",
appid: "appid",
scope: "snsapi_login",
redirect_uri: "",
state: "",
style: "",
href: ""
});
步骤3: 用户在手机上确认授权后,页面会自动重定向到redirect_uri上,并且带上code和state参数。
第二步:通过code获取access_token
通过code获取access_token
直接调下面的接口即可
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
正确的返回结果如下:
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE",
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
获取用户信息
access_token 可以用来进一步获取用户公开信息(包括头像,名字,地理位置)
直接调下面的接口即可
https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
正确的返回消息如下
{
"openid": "oAX1fwQevayrMNxAZGvoa8kvZCMc",
"nickname": "Band",
"sex": 1,
"language": "zh_CN",
"city": "Guangzhou",
"province": "Guangdong",
"country": "CN",
"headimgurl": "http://thirdwx.qlogo.cn/mmopen/vi_32/4WFBCHqe458kAocTXqVPV5CHxHmYQs414DcqRqmC3jTZVYYqGvRbSgNYGRIeUaCge6r8AD6bcsU1RzfldGHJKg/132",
"privilege": [
"chinaunicom"
],
"unionid": "oTq_VwExIiigeoJtP8r0FrQj7Cqk"
}
部署服务号消息订阅服务器
参考官方文档 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319
第一步: 填写服务器配置
登录微信公众平台官网后,在公众平台官网的开发-基本设置页面,勾选协议成为开发者,点击“修改配置”按钮,填写服务器地址(URL)、Token和EncodingAESKey,其中URL是开发者用来接收微信消息和事件的接口URL。Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)。EncodingAESKey由开发者手动填写或随机生成,将用作消息体加解密密钥。
第二步:验证消息的确来自微信服务器
开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带参数如下表所示:
参数 | 描述 |
---|---|
signature | 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 |
timestamp | 时间戳 |
nonce | 随机数 |
echostr | 随机字符串 |
开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。加密/校验流程如下:
1)将token、timestamp、nonce三个参数进行字典序排序 2)将三个参数字符串拼接成一个字符串进行sha1加密 3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
express 版本的验证代码
// var crypto = require('crypto');
// const hash = crypto.createHash('sha1')
var wxReceiver = function(req, res, next) {
console.log('wxReceiver-body: ', req.body);
const signature = req.query.signature
const timestamp = req.query.timestamp
const nonce = req.query.nonce
const echostr = req.query.echostr
const arr = [nonce, 'njjnjn', timestamp]
arr.sort()
const tStr = arr[0] + arr[1] + arr[2]
hash.update(tStr)
const hashResult = hash.digest('hex')
if(hashResult == signature) {
// 这是来自微信官方的消息
res.send(echostr)
}
};
依据接口文档实现业务逻辑
验证URL有效性成功后即接入生效,成为开发者。你可以在公众平台网站中申请微信认证,认证成功后,将获得更多接口权限,满足更多业务需求。
成为开发者后,用户每次向公众号发送消息、或者产生自定义菜单、或产生微信支付订单等情况时,开发者填写的服务器配置URL将得到微信服务器推送过来的消息和事件,开发者可以依据自身业务逻辑进行响应,如回复消息
消息是XML格式的,这是xml parse后的 消息模式长这样
{
tousername: [ 'gh_15a5ec8f6116' ],
fromusername: [ 'oC9vJwnxrquE5Ss2PEL49TX-3hpI' ],
createtime: [ '1524646369' ],
msgtype: [ 'event' ],
event: [ 'TEMPLATESENDJOBFINISH' ],
msgid: [ '252565859871080449' ],
status: [ 'success' ]
}
这里面fromusername就是服务号的openid
推送模板消息
官方文档 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277
这个功能需要审核才能用,审核之后先添加模板
然后直接调用接口即可发送模板消息
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",
"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"
}
}
}