自从腾讯发布了JS-SDK,这对于基于微信的HTML5开发者来说是个不折不扣的好消息,从此开发者们告别了用箭头来提示右上角可以分享,并且随时可以使用微信的原生能力,微信变成了一个超级浏览器。本文摘录了基于Node.js的微信JS-SDK的应用介绍。

一、准备工作

  1. 在微信公众平台申请测试账号,并设置好好 JS 接口安全域名 (注:域名必须可以外网访问)
  2. 查看微信JS-SDK开发文档

二、开始编码

使用微信 sdk 必须自己实现微信的签名算法。

大概需要4个步骤:

  1. 获取 access_token
  2. 根据 access_token 获取 jsapi_ticket
  3. 根据 appId(公众号唯一id)、noncestr(随机字符串)、timestamp(时间戳)、url(当前页面完整url,不包括#aaa=bbb) 通过sha1算法签名
  4. 将信息返回给前端,设置wx.config

由于获取access_token 和 jsapi_ticket 的接口都有访问限制,所以明确指出需要第三方做缓存处理。此处我们缓存jsapi_ticket 就可以了。

/config/wechat.cfg.js

module.exports = {
grant_type: 'client_credential',
appid: 'xxxxxxxxxxxxxxx',
secret: 'xxxxxxxxxxxxxxxxxxxxxxxxxx',
noncestr:'Wm3WZYTPz0wzccnW',
accessTokenUrl:'https://api.weixin.qq.com/cgi-bin/token',
ticketUrl:'https://api.weixin.qq.com/cgi-bin/ticket/getticket',
cache_duration:1000*60*60*24 //缓存时长为24小时
}

最主要部分是签名:

signature.js

var request = require('request'),
cache = require('memory-cache'),
sha1 = require('sha1'),
config = require('../config/wechat.cfg');
exports.sign = function (url,callback) {
var noncestr = config.noncestr,
timestamp = Math.floor(Date.now()/1000), //精确到秒
        jsapi_ticket;
if(cache.get('ticket')){
jsapi_ticket = cache.get('ticket');
console.log('1' + 'jsapi_ticket=' + jsapi_ticket + '&noncestr=' + noncestr + '&timestamp=' + timestamp + '&url=' + url);
callback({
noncestr:noncestr,
timestamp:timestamp,
url:url,
jsapi_ticket:jsapi_ticket,
signature:sha1('jsapi_ticket=' + jsapi_ticket + '&noncestr=' + noncestr + '&timestamp=' + timestamp + '&url=' + url)
});
}else{
request(config.accessTokenUrl + '?grant_type=' + config.grant_type + '&appid=' + config.appid + '&secret=' + config.secret ,function(error, response, body){
if (!error && response.statusCode == 200) {
var tokenMap = JSON.parse(body);
request(config.ticketUrl + '?access_token=' + tokenMap.access_token + '&type=jsapi', function(error, resp, json){
if (!error && response.statusCode == 200) {
var ticketMap = JSON.parse(json);
cache.put('ticket',ticketMap.ticket,config.cache_duration);  //加入缓存
console.log('jsapi_ticket=' + ticketMap.ticket + '&noncestr=' + noncestr + '&timestamp=' + timestamp + '&url=' + url);
callback({
noncestr:noncestr,
timestamp:timestamp,
url:url,
jsapi_ticket:ticketMap.ticket,
signature:sha1('jsapi_ticket=' + ticketMap.ticket + '&noncestr=' + noncestr + '&timestamp=' + timestamp + '&url=' + url)
});
}
})
}
})
}
}

由于只是简单的demo , 也就没有采用promise,而是采用的普通的回调。

客户端部分

document.getElementById('refresh').onclick = function(){location.reload();}
/**
*  以下内容多摘自官方demo
*
**/
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: appId, // 必填,公众号的唯一标识
timestamp: timestamp, // 必填,生成签名的时间戳
nonceStr: nonceStr, // 必填,生成签名的随机串
signature: signature,// 必填,签名,见附录1
jsApiList: ['checkJsApi',
'onMenuShareTimeline',
'onMenuShareAppMessage',
'onMenuShareQQ',
'onMenuShareWeibo',
'hideMenuItems',
'showMenuItems',
'hideAllNonBaseMenuItem',
'showAllNonBaseMenuItem',
'translateVoice',
'startRecord',
'stopRecord',
'onRecordEnd',
'playVoice',
'pauseVoice',
'stopVoice',
'uploadVoice',
'downloadVoice',
'chooseImage',
'previewImage',
'uploadImage',
'downloadImage',
'getNetworkType',
'openLocation',
'getLocation',
'hideOptionMenu',
'showOptionMenu',
'closeWindow',
'scanQRCode',
'chooseWXPay',
'openProductSpecificView',
'addCard',
'chooseCard',
'openCard'] // 必填,需要使用的JS接口列表,
});
wx.ready(function(){
// 1 判断当前版本是否支持指定 JS 接口,支持批量判断
document.querySelector('#checkJsApi').onclick = function () {
wx.checkJsApi({
jsApiList: [
'getNetworkType',
'previewImage'
],
success: function (res) {
alert(JSON.stringify(res));
}
});
};
// 2. 分享接口
// 2.1 监听"分享给朋友",按钮点击、自定义分享内容及分享结果接口
document.querySelector('#onMenuShareAppMessage').onclick = function () {
wx.onMenuShareAppMessage({
title: '互联网之子',
desc: '在长大的过程中,我才慢慢发现,我身边的所有事,别人跟我说的所有事,那些所谓本来如此,注定如此的事,它们其实没有非得如此,事情是可以改变的。更重要的是,有些事既然错了,那就该做出改变。',
link: 'http://movie.douban.com/subject/25785114/',
imgUrl: 'http://demo.open.weixin.qq.com/jssdk/images/p2166127561.jpg',
trigger: function (res) {
// 不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回
alert('用户点击发送给朋友');
},
success: function (res) {
alert('已分享');
},
cancel: function (res) {
alert('已取消');
},
fail: function (res) {
alert(JSON.stringify(res));
}
});
alert('已注册获取"发送给朋友"状态事件');
};
// 5 图片接口
// 5.1 拍照、本地选图
var images = {
localId: [],
serverId: []
};
document.querySelector('#chooseImage').onclick = function () {
wx.chooseImage({
success: function (res) {
images.localId = res.localIds;
alert('已选择 ' + res.localIds.length + ' 张图片');
}
});
};
// 5.2 图片预览
document.querySelector('#previewImage').onclick = function () {
wx.previewImage({
current: 'http://img5.douban.com/view/photo/photo/public/p1353993776.jpg',
urls: [
'http://img3.douban.com/view/photo/photo/public/p2152117150.jpg',
'http://img5.douban.com/view/photo/photo/public/p1353993776.jpg',
'http://img3.douban.com/view/photo/photo/public/p2152134700.jpg'
]
});
};
// 7.2 获取当前地理位置
document.querySelector('#getLocation').onclick = function () {
wx.getLocation({
success: function (res) {
alert(JSON.stringify(res));
},
cancel: function (res) {
alert('用户拒绝授权获取地理位置');
}
});
};
// 9 微信原生接口
// 9.1.1 扫描二维码并返回结果
document.querySelector('#scanQRCode0').onclick = function () {
wx.scanQRCode();
};
});
wx.error(function(res){
JSON.stringify(res)
});

至此,基本功能已经完成。附上效果图

071934383467571.jpg071935133778301.jpg071936023143352.jpg

踩过的坑

  1. 签名算法不一致:通过 http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 验证算法正确性
  2. url必须完全一致,并且外网可访问。 将代码部署到BAE,或者其他应用引擎服务器上
  3. timestamp 需要精确到秒

本文转自:飘摇的枫叶