最近公司搞了個運營活動,入口放在了微信公衆號裏,很久沒碰過微信了,剛拾起來瞬間感受有點懵逼。。。。彷佛把以前的坑又都從新踩了一遍,雖然過程曲折,不過好在順利完成了,並且印象也更加深入了,抽時間記錄一下過程,否則下次再被絆倒的話,就要被老鐵們笑話啦。好了,廢話很少說,進入正題(先捋一捋流程,再說要注意的問題):node
1.公衆號配置相關:api
登陸微信開放平臺,找到對應公衆號,設置業務域名,JS接口安全域名,網頁受權域名等亂七八糟的一堆(文檔或者網上都有,可對比參考一下,這裏就不贅述了)。安全
2.接入JSSDK:微信
頁面頭部引入便可:session
<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
3.獲取access_token、ticket及signature:微信開發
//appid、祕鑰、隨機字符串(不要試圖用這些作壞事,由於機智的我已經預料到了。。。)
const APPID = 'wxdc8b4f8e3esdfsdfasd'; const SECRET = 'f5a5fa443923e3fdsfasdasf0'; const NONCESTR = 'Wm3WZYTPz0fdsfasdf';
let timestamp = (new Date()).getTime(); let access_token = ''; let jsapi_ticket = '';
//獲取access_token
function getToken(callback) {
const newsUrl= 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' + APPID + '&secret=' + SECRET;
request(newsUrl, function (error, response, body) {
if(error || response.statusCode != '200'){
getToken(callback);
return;
}
let obj;
try{
obj = JSON.parse(body);
}catch(e){
console.log(e);
}
if(!obj || !('access_token' in obj)){
console.log('Get access token error!', error, response.statusCode, body);
getToken(callback);
return;
}
access_token = JSON.parse(body).access_token;
console.log('Got new access token:', access_token);
timestamp = (new Date()).getTime();
getTicket(callback);
});
}
function getTicket(callback) {
const newsUrl= 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' + access_token + '&type=jsapi';
request(newsUrl, function (error, response, body) {
if (!error && response.statusCode == '200') {
if (JSON.parse(body).errcode === 0) {
jsapi_ticket = JSON.parse(body).ticket;
if(callback) {
callback();
}
}
} else {
response.status(response.statusCode).send(body);
}
});
}
//簽名加密
function getSignature(url) {
const str = 'jsapi_ticket=' + jsapi_ticket + '&noncestr=' + NONCESTR + '×tamp=' + timestamp + '&url=' + url;
const sha1 = crypto.createHash('sha1');
return sha1.update(str).digest('hex');
}
4.經過接口將appId、timestamp、noncestr、signature返回值頁面:app
router.get('/wxconfig', function(req, res) { let date = (new Date()).getTime();
//access_token有效時間爲2小時 if(date - timestamp >= 7200 * 1000) { getToken(function () { let signature = getSignature(req.query.url); res.send({ appId: APPID, timestamp: timestamp, nonceStr: NONCESTR, signature: signature }); }); } else {
//這裏的url是調用接口時傳來的,必須是動態獲取當前頁面地址‘#’號以前的部分 let signature = getSignature(req.query.url); res.send({ appId: APPID, timestamp: timestamp, nonceStr: NONCESTR, signature: signature }); } });
5.調用'/wxconfig'接口,配置自定義分享信息:this
getSignature() {
let url = window.location.href.split('#')[0]; this.$http.get('/lucky-draw/wxconfig', { params: { url: url } }).then(data => { this.wxConfig(data.body); this.wxReady(); }, error => { console.log(error); }); }
wxConfig(options) {
wx.config({
debug: false,
appId: options.appId,
timestamp: options.timestamp,
nonceStr: options.nonceStr,
signature: options.signature,
openid: '',
jsApiList: [
'onMenuShareTimeline',
'onMenuShareAppMessage'
]
});
}
wxReady() {
let _this = this;
let id = _this.getQueryString('acti');
let options = {
title: 'xxxxxxxxxxx',
desc: 'xxxxxxxxxxx',
link: 'xxxxxxxxx',
imgUrl: 'xxxxxxxxxx',
success: function (data) {
_this.addOneChanceAfterShare();
},
cancel: function (data) {
console.log(data);
}
};
wx.ready(function () {
wx.onMenuShareTimeline(options);
wx.onMenuShareAppMessage(options);
});
}
6.獲取微信受權:加密
由於受權登陸的權限較高,因此微信平臺會對連接的順序進行校驗,順序是固定的。連接格式以下:url
「https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect」
解釋一下參數:
appid:公衆號的appid;
redirect_uri:微信受權成功以後須要跳轉的連接(你但願受權成功以後跳轉到哪一個頁面,這裏就填哪一個);
response_type:固定參數「code」(告訴微信給你返回code,這個解釋是否是簡單粗暴了些);
scope:受權權限,主要分兩種,「snsapi_base」:靜默受權,用戶無感知,可是隻能獲取到openid;「snsapi_userinfo 」:彈出詢問框,須要用戶手動確認,能夠拿到openid及用戶基本信息;
state:沒搞太明白是幹什麼用的,但有一點能夠肯定,你傳什麼參數,微信受權跳轉以後還會給你帶回去這個參數,特定場合可能會用到;
wechat_redirect:這個能夠去掉,沒有影響。
7.拿code換取openid:
由於我此次作的是一個活動的單頁面,頁面上須要根據用戶的openid及unionid從後臺獲取數據,因此我但願在進入頁面以前就已經拿到我須要的東西了(openid和unionid以及一些數據),個人作法是把回調地址寫在node裏,以接口的形式接受微信的跳轉(這裏用詞是否是太生硬了啊,哈哈,自行理解一下吧):
//獲取code時微信回調地址 router.get('/is_wx_redirect', function (req, res) { //拿code let code = req.query.code || ''; console.log('Get code success!!!', code); //拿活動id let id = req.query.acti; req.session.acti_id = id; let openid, unionid, subscribe; getOpenId(); //獲取openid function getOpenId () { let url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' + APPID + '&secret=' + SECRET + '&code=' + code + '&grant_type=authorization_code'; request(url, function (error, response, body) { if (error || response.statusCode != '200') { console.log('獲取openid時發生錯誤:', error); res.send('獲取openid時發生錯誤,錯誤信息爲:' + error); return false; } let data = JSON.parse(body); if (!data.openid) { console.log('發送請求openid成功,可是返回信息中沒有openid'); res.send(body); } //獲取openid成功後,獲取unionid和是否關注等信息 openid = data.openid; console.log('我拿到openid了,openid是:', openid); getIsFocus(); }); } //獲取unionid和是否關注等信息 function getIsFocus () { let url = 'https://api.weixin.qq.com/cgi-bin/user/info?access_token=' + access_token + '&openid=' + openid + '&lang=zh_CN'; request(url, function (error, response, body) { if (error || response.statusCode != '200') { console.log('獲取unionid時發生錯誤:', error); res.send('獲取unionid時發生錯誤,錯誤信息爲:' + error); return false; } let data = JSON.parse(body); subscribe = data.subscribe || ''; unionid = data.unionid || ''; //存session req.session.openid = openid; req.session.unionid = unionid; req.session.subscribe = subscribe; console.log('我拿到unionid了,unionid是:', unionid); console.log('我拿到subscribe了,subscribe是:', subscribe); if (!subscribe) { //未關注 console.log('The user is not focus!!'); res.redirect('/lucky-draw/focus_page', 302); return false; } if (!unionid) { //返回信息中無unionid res.send('返回信息中未包含unionid' + data); return false; } //重定向至主頁面 console.log('Start redirect!!!!'); res.redirect('/lucky-draw/?acti=' + id, 302); console.log('Redirect success!!'); }); } });
眼尖的老鐵可能一眼就發現了幾個問題:
1)我並無用拿到的code去換access_token,而是用以前自定分享時獲取過的全局token,這裏須要注意了,全局access_token和網頁受權access_token是有區別的,獲取地址、參數、時效及用途都不一樣,千萬可別弄混淆了,否則非得坑死你(別胡說,我纔沒被這個坑過。。。。),關於這兩個access_toekn的具體區別及用途,能夠自行查一下。由於我只須要判斷用戶是否關注了公衆號,從而完成一項業務需求,並不須要用戶的基本信息,因此我只拿code換取了openid,而後用openid和全局access_token獲取unionid;
2)大堆的log有木有!!微信開發的調試相對來講仍是比較簡(惡)單(心)的,關鍵位置的log獲取錯誤處理,會讓咱們少走不少彎路。
8.其實代碼已經寫在上一步了,就是拿到初始化頁面所須要的信息以後,重定向至該頁面。