目录
概述
微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包。
通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫等微信特有的能力,为微信用户提供更优质的网页体验。
此文档面向网页开发者介绍微信JS-SDK如何使用及相关注意事项。
使用说明
在使用微信JS-SDK对应的JS接口前,需确保已获得使用对应JS接口的权限,可在下表中根据自己的帐号类型查看。 企业号帐号类型分为注册号和认证号,其中认证号拥有更多的JS-SDK权限,具体详见下方表格:
功能 | 接口 | 认证号 | 注册号 |
---|---|---|---|
基础接口 | 判断当前客户端版本是否支持指定JS接口 | 有 | 有 |
企业号 | 创建企业会话 | 有 | 有 |
打开企业通讯录选人 | 有 | 有 | |
分享接口 | 获取“分享到朋友圈”按钮点击状态及设置分享内容接口 | 有 | 无 |
获取“分享给朋友”按钮点击状态及设置分享内容接口 | 有 | 无 | |
获取“分享到QQ”按钮点击状态及设置分享内容接口 | 有 | 无 | |
获取“分享到腾讯微博”按钮点击状态及设置分享内容接 | 有 | 无 | |
图像接口 | 本地选图或拍照接口 | 有 | 有 |
图片预览接口 | 有 | 有 | |
上传图片接口 | 有 | 有 | |
下载图片接口 | 有 | 有 | |
音频接口 | 开始录音接口 | 有 | 有 |
停止录音接口 | 有 | 有 | |
播放音频接口 | 有 | 有 | |
暂停播放接口 | 有 | 有 | |
停止播放接口 | 有 | 有 | |
上传语音接口 | 有 | 有 | |
下载语音接口 | 有 | 有 | |
智能接口 | 识别音频并返回识别结果接口 | 有 | 有 |
设备信息 | 获取网络状态接口 | 有 | 有 |
地理位置 | 查看地理位置地图接口 | 有 | 有 |
获取地理位置接口 | 有 | 有 | |
界面操作 | 隐藏右上角菜单接口 | 有 | 有 |
显示右上角菜单接口 | 有 | 有 | |
关闭当前窗口接口 | 有 | 有 | |
批量隐藏菜单项接口 | 有 | 有 | |
批量显示菜单项接口 | 有 | 有 | |
隐藏所有非基本菜单项接口 | 有 | 有 | |
显示所有被隐藏的非基本菜单项接口 | 有 | 有 | |
微信扫一扫 | 扫一扫接口 | 有 | 有 |
注意: 所有的JS接口只能在企业号应用的可信域名下调用(包括子域名),可在企业号应用中心里设置应用可信域名。
步骤一:引入JS文件
在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.2.0.js
备注:支持使用 AMD/CMD 标准模块加载方法加载
步骤二:通过config接口注入权限验证配置
所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复)。
wx.config({ debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId: '', // 必填,企业号的唯一标识,此处填写企业号corpid timestamp: , // 必填,生成签名的时间戳 nonceStr: '', // 必填,生成签名的随机串 signature: '',// 必填,签名,见附录1 jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 });
步骤三:通过ready接口处理成功验证
wx.ready(function(){ // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。 });
步骤四:通过error接口处理失败验证
wx.error(function(res){ // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。 });
接口调用说明
所有接口通过wx对象(也可使用jWeixin对象)来调用,参数是一个对象,除了每个接口本身需要传的参数之外,还有以下通用参数:
- success:接口调用成功时执行的回调函数。
- fail:接口调用失败时执行的回调函数。
- complete:接口调用完成时执行的回调函数,无论成功或失败都会执行。
- cancel:用户点击取消时的回调函数,仅部分有用户取消操作的api才会用到。
- trigger: 监听Menu中的按钮点击时触发的方法,该方法仅支持Menu中的相关接口。
注意:不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回。
以上几个函数都带有一个参数,类型为对象,其中除了每个接口本身返回的数据之外,还有一个通用属性errMsg,其值格式如下:
- 调用成功时:"xxx:ok" ,其中xxx为调用的接口名
- 用户取消时:"xxx:cancel",其中xxx为调用的接口名
- 调用失败时:其值为具体错误信息
基础接口
判断当前客户端版本是否支持指定JS接口
wx.checkJsApi({ jsApiList: ['chooseImage'] // 需要检测的JS接口列表,所有JS接口列表见附录2, success: function(res) { // 以键值对的形式返回,可用的api值true,不可用为false // 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"} });
备注:checkJsApi接口是客户端6.0.2新引入的一个预留接口,第一期开放的接口均可不使用checkJsApi来检测。
企业号
企业号接口为企业号应用专有的JS-SDK接口
创建企业会话
wx.openEnterpriseChat({ userIds: 'zhangshan;lisi;wangwu', // 必填,参与会话的成员列表。格式为userid1;userid2;...,用分号隔开,最大限制为2000个。userid单个时为单聊,多个时为群聊。 groupName: 'openEnterpriseChat讨论组', // 必填,会话名称。单聊时该参数传入空字符串""即可。 success: function(res) { // 回调 }, fail: function(res) { if(res.errMsg.indexOf('function not exist') > -1){ alert('版本过低请升级') } } });
打开企业通讯录选人
注意:该JS-SDK需要验证企业号管理组权限,请参考:附录2-企业号管理组权限验证方法
var evalWXjsApi = function(jsApiFun) { if (typeof WeixinJSBridge == "object" && typeof WeixinJSBridge.invoke == "function") { jsApiFun(); } else { document.attachEvent && document.attachEvent("WeixinJSBridgeReady", jsApiFun); document.addEventListener && document.addEventListener("WeixinJSBridgeReady", jsApiFun); } } document.querySelector('#openEnterpriseContact_invoke').onclick = function() { evalWXjsApi(function() { WeixinJSBridge.invoke("openEnterpriseContact", { "groupId": "g50a5axxxx91706a", // 必填,管理组权限验证步骤1返回的group_id "timestamp": "1447334894", // 必填,管理组权限验证步骤2使用的时间戳 "nonceStr": "Wm3WZYTPz0wzccnW", // 必填,管理组权限验证步骤2使用的随机字符串 "signature": "f9afc6f80a0c81xxxxxxxxxxxxedff5001878", // 必填,管理组权限验证步骤2生成的签名 "params" : { 'departmentIds' : [1], // 非必填,可选部门ID列表(如果ID为0,表示可选管理组权限下所有部门) 'tagIds' : [1], // 非必填,可选标签ID列表(如果ID为0,表示可选所有标签) 'userIds' : ['zhangsan','lisi'], // 非必填,可选用户ID列表 'mode' : 'single', // 必填,选择模式,single表示单选,multi表示多选 'type' : ['department','tag','user'], // 必填,选择限制类型,指定department、tag、user中的一个或者多个 'selectedDepartmentIds' : [], // 非必填,已选部门ID列表 'selectedTagIds' : [], // 非必填,已选标签ID列表 'selectedUserIds' : [], // 非必填,已选用户ID列表 }, }, function(res) { if (res.err_msg.indexOf('function_not_exist') > -1) { alert('版本过低请升级'); } else if (res.err_msg.indexOf('openEnterpriseContact:fail') > -1) { return; } var result = JSON.parse(res.result); // 返回字符串,开发者需自行调用JSON.parse解析 var selectAll = result.selectAll; // 是否全选(如果是,其余结果不再填充) if (!selectAll) { var selectedDepartmentList = result.departmentList; // 已选的部门列表 for (var i = 0; i < selectedDepartmentList.length; i++) { var department = selectedDepartmentList[i]; var departmentId = department.id; // 已选的单个部门ID var departemntName = department.name; // 已选的单个部门名称 } var selectedTagList = result.tagList; // 已选的标签列表 for (var i = 0; i < selectedTagList.length; i++) { var tag = selectedTagList[i]; var tagId = tag.id; // 已选的单个标签ID var tagName = tag.name; // 已选的单个标签名称 } var selectedUserList = result.userList; // 已选的成员列表 for (var i = 0; i < selectedUserList.length; i++) { var user = selectedUserList[i]; var userId = user.id; // 已选的单个成员ID var userName = user.name; // 已选的单个成员名称 } } }) }); }
向当前企业会话发送消息
注意:该JS-SDK仅适用于从应用的“关联到会话”入口进入的网页
var evalWXjsApi = function(jsApiFun) { if (typeof WeixinJSBridge == "object" && typeof WeixinJSBridge.invoke == "function") { jsApiFun(); } else { document.attachEvent && document.attachEvent("WeixinJSBridgeReady", jsApiFun); document.addEventListener && document.addEventListener("WeixinJSBridgeReady", jsApiFun); } } // 发送文本消息 document.querySelector('#sendEnterpriseChat_text_invoke').onclick = function() { evalWXjsApi(function() { WeixinJSBridge.invoke("sendEnterpriseChat", { "type": "text", // 必填,text表示发送的内容是文字 "data": { "content": "text sent to enterprise chat", // 必填,表示发送的文字内容 } }, function(res) { alert(JSON.stringify(res)); }) }); } // 发送链接消息 document.querySelector('#sendEnterpriseChat_link_invoke').onclick = function() { evalWXjsApi(function() { WeixinJSBridge.invoke("sendEnterpriseChat", { "type": "link", // 必填,link表示发送的内容是链接 "data": { "imgUrl": "http://shp.qpic.cn/bizmp/PiajxSqBRaEJ60dDSjCqczZFTTHsyX5MvxibUq1L3jwokHORAYOV0OdQ/", // 必填,表示消息的图标 "title": "test title", // 非必填,但与desc不能同时为空 "desc": "test desc", // 非必填,但与title不能同时为空 "link": "http://qy.weixin.qq.com", // 必填,表示消息的链接 } }, function(res) { alert(JSON.stringify(res)); }) }); }
分享接口
请注意不要有诱导分享等违规行为,对于诱导分享行为将永久回收企业号接口权限,详细规则请查看:朋友圈管理常见问题 。
获取“分享到朋友圈”按钮点击状态及自定义分享内容接口
wx.onMenuShareTimeline({ title: '', // 分享标题 link: '', // 分享链接,该链接域名必须与当前企业的可信域名一致 imgUrl: '', // 分享图标 success: function () { // 用户确认分享后执行的回调函数 }, cancel: function () { // 用户取消分享后执行的回调函数 } });
获取“分享给朋友”按钮点击状态及自定义分享内容接口
wx.onMenuShareAppMessage({ title: '', // 分享标题 desc: '', // 分享描述 link: '', // 分享链接,该链接域名必须与当前企业的可信域名一致 imgUrl: '', // 分享图标 type: '', // 分享类型,music、video或link,不填默认为link dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空 success: function () { // 用户确认分享后执行的回调函数 }, cancel: function () { // 用户取消分享后执行的回调函数 } });
获取“分享到QQ”按钮点击状态及自定义分享内容接口
wx.onMenuShareQQ({ title: '', // 分享标题 desc: '', // 分享描述 link: '', // 分享链接 imgUrl: '', // 分享图标 success: function () { // 用户确认分享后执行的回调函数 }, cancel: function () { // 用户取消分享后执行的回调函数 } });
获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口
wx.onMenuShareWeibo({ title: '', // 分享标题 desc: '', // 分享描述 link: '', // 分享链接 imgUrl: '', // 分享图标 success: function () { // 用户确认分享后执行的回调函数 }, cancel: function () { // 用户取消分享后执行的回调函数 } });
获取“分享到QQ空间”按钮点击状态及自定义分享内容接口
wx.onMenuShareQZone({ title: '', // 分享标题 desc: '', // 分享描述 link: '', // 分享链接 imgUrl: '', // 分享图标 success: function () { // 用户确认分享后执行的回调函数 }, cancel: function () { // 用户取消分享后执行的回调函数 } });
图像接口
拍照或从手机相册中选图接口
wx.chooseImage({ count: 1, // 默认9 sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有 sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有 success: function (res) { var localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片 } });
预览图片接口
wx.previewImage({ current: '', // 当前显示图片的http链接 urls: [] // 需要预览的图片http链接列表 });
上传图片接口
wx.uploadImage({ localId: '', // 需要上传的图片的本地ID,由chooseImage接口获得 isShowProgressTips: 1// 默认为1,显示进度提示 success: function (res) { var serverId = res.serverId; // 返回图片的服务器端ID } });
备注:上传图片有效期3天,可用微信多媒体接口下载图片到自己的服务器,此处获得的 serverId 即 media_id,参考文档
下载图片接口
wx.downloadImage({ serverId: '', // 需要下载的图片的服务器端ID,由uploadImage接口获得 isShowProgressTips: 1// 默认为1,显示进度提示 success: function (res) { var localId = res.localId; // 返回图片下载后的本地ID } });
音频接口
开始录音接口
wx.startRecord();
停止录音接口
wx.stopRecord({ success: function (res) { var localId = res.localId; } });
监听录音自动停止接口
wx.onVoiceRecordEnd({ // 录音时间超过一分钟没有停止的时候会执行 complete 回调 complete: function (res) { var localId = res.localId; } });
播放语音接口
wx.playVoice({ localId: '' // 需要播放的音频的本地ID,由stopRecord接口获得 });
暂停播放接口
wx.pauseVoice({ localId: '' // 需要暂停的音频的本地ID,由stopRecord接口获得 });
停止播放接口
wx.stopVoice({ localId: '' // 需要停止的音频的本地ID,由stopRecord接口获得 });
监听语音播放完毕接口
wx.onVoicePlayEnd({ success: function (res) { var localId = res.localId; // 返回音频的本地ID } });
上传语音接口
wx.uploadVoice({ localId: '', // 需要上传的音频的本地ID,由stopRecord接口获得 isShowProgressTips: 1// 默认为1,显示进度提示 success: function (res) { var serverId = res.serverId; // 返回音频的服务器端ID } });
备注: 上传语音有效期3天,可用企业号多媒体接口下载语音到自己的服务器,此处获得的 serverId 即 media_id,参考文档
下载语音接口
wx.downloadVoice({ serverId: '', // 需要下载的音频的服务器端ID,由uploadVoice接口获得 isShowProgressTips: 1// 默认为1,显示进度提示 success: function (res) { var localId = res.localId; // 返回音频的本地ID } });
智能接口
识别音频并返回识别结果接口
wx.translateVoice({ localId: '', // 需要识别的音频的本地Id,由录音相关接口获得 isShowProgressTips: 1, // 默认为1,显示进度提示 success: function (res) { alert(res.translateResult); // 语音识别的结果 } });
设备信息
获取网络状态接口
wx.getNetworkType({ success: function (res) { var networkType = res.networkType; // 返回网络类型2g,3g,4g,wifi } });
地理位置
使用微信内置地图查看位置接口
wx.openLocation({ latitude: 0, // 纬度,浮点数,范围为90 ~ -90 longitude: 0, // 经度,浮点数,范围为180 ~ -180。 name: '', // 位置名 address: '', // 地址详情说明 scale: 1, // 地图缩放级别,整形值,范围从1~28。默认为最大 infoUrl: '' // 在查看位置界面底部显示的超链接,可点击跳转 });
获取地理位置接口
wx.getLocation({ type: 'wgs84', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02' success: function (res) { var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90 var longitude = res.longitude ; // 经度,浮点数,范围为180 ~ -180。 var speed = res.speed; // 速度,以米/每秒计 var accuracy = res.accuracy; // 位置精度 } });
界面操作
隐藏右上角菜单接口
wx.hideOptionMenu();
显示右上角菜单接口
wx.showOptionMenu();
关闭当前网页窗口接口
wx.closeWindow();
批量隐藏功能按钮接口
wx.hideMenuItems({ menuList: [] // 要隐藏的菜单项,所有menu项见附录3 });
批量显示功能按钮接口
wx.showMenuItems({ menuList: [] // 要显示的菜单项,所有menu项见附录3 });
隐藏所有非基础按钮接口
wx.hideAllNonBaseMenuItem();
显示所有功能按钮接口
wx.showAllNonBaseMenuItem();
微信扫一扫
调起微信扫一扫接口
wx.scanQRCode({ desc: 'scanQRCode desc', needResult: 0, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果, scanType: ["qrCode","barCode"], // 可以指定扫二维码还是一维码,默认二者都有 success: function (res) { // 回调 } error: function(res){ if(res.errMsg.indexOf('function_not_exist') > 0){ alert('版本过低请升级') } } });
微信卡券
微信卡券接口中使用的签名凭证api_ticket,与步骤二中config使用的签名凭证jsapi_ticket不同,开发者在调用微信卡券JS-SDK的过程中需依次完成两次不同的签名,并确保凭证的缓存。
获取api_ticket
api_ticket 是用于调用微信卡券JS API的临时票据,有效期为7200 秒,通过access_token 来获取。
开发者注意事项:
1.此用于卡券接口签名的api_ticket与步骤三中通过config接口注入权限验证配置使用的jsapi_ticket不同。
2.由于获取api_ticket 的api 调用次数非常有限,频繁刷新api_ticket 会导致api调用受限,影响自身业务,开发者需在自己的服务存储与更新api_ticket。
- 请求说明
Https请求方式:GET
https://qyapi.weixin.qq.com/cgi-bin/ticket/get?access_token=ACCESS_TOKEN&type=wx_card
- 参数说明
参数 | 必须 | 说明 |
---|---|---|
access_token | 是 | 调用接口凭证 |
type | 是 | 此处固定为wx_card |
- 返回说明
{ "errcode":0, "errmsg":"ok", "ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKdvsdshFKA", "expires_in":7200 }
- 参数说明
参数 | 说明 |
---|---|
errcode | 错误码 |
errmsg | 错误信息 |
ticket | api_ticket,卡券接口中签名所需凭证 |
expires_in | 有效时间 |
拉取适用卡券列表并获取用户选择信息
wx.chooseCard({ shopId: '', // 门店Id cardType: '', // 卡券类型 cardId: '', // 卡券Id timestamp: 0, // 卡券签名时间戳 nonceStr: '', // 卡券签名随机串 signType: '', // 签名方式,默认'SHA1' cardSign: '', // 卡券签名 success: function (res) { var cardList= res.cardList; // 用户选中的卡券列表信息 } });
- 参数说明
参数 | 必须 | 说明 |
---|---|---|
shopId | 否 | 门店ID。shopID用于筛选出拉起带有指定location_list(shopID)的卡券列表,非必填,企业号暂不支持。 |
cardType | 否 | 卡券类型,用于拉起指定卡券类型的卡券列表。当cardType为空时,默认拉起所有卡券的列表,非必填。 |
cardId | 否 | 卡券ID,用于拉起指定cardId的卡券列表,当cardId为空时,默认拉起所有卡券的列表,非必填。 |
timestamp | 是 | 时间戳。 |
nonceStr | 是 | 随机字符串。 |
signType | 是 | 签名方式,目前仅支持SHA1 |
cardSign | 是 | 签名。 |
cardSign详见附录5。开发者特别注意:签名错误会导致拉取卡券列表异常为空,请仔细检查参与签名的参数有效性。
特别提醒 拉取列表仅与用户本地卡券有关,拉起列表异常为空的情况通常有三种:签名错误、时间戳无效、筛选机制有误。请开发者依次排查定位原因。
批量添加卡券接口
wx.addCard({ cardList: [{ cardId: '', cardExt: '' }], // 需要添加的卡券列表 success: function (res) { var cardList = res.cardList; // 添加的卡券列表信息 } });
cardExt详见附录5,值得注意的是,这里的card_ext参数必须与参与签名的参数一致,格式为字符串而不是Object,否则会报签名错误。
查看微信卡包中的卡券接口
wx.openCard({ cardList: [{ cardId: '', code: : '' }], // // 需要打开的卡券列表 });
SOTER生物认证认证接口
获取本机支持的SOTER生物认证方式
使用场景:开发者调用该接口获取本机支持的SOTER生物认证方式,以便确定所开发功能是否可用,以及确定使用何种方式进行SOTER生物识别(兼容版本:ios/android 6.3.16及以上版本)
wx.config({ beta:true, }); wx.ready(function () { wx.invoke("getSupportSoter", {},function(res){ var ret = " support_mode: " + res.support_mode; alert(ret); }); });
- 返回参数说明
参数 | 说明 |
---|---|
support_mode | 该设备支持的可被SOTER识别的生物识别方式,十六进制无浮点整数。生物识别方式定义如表1(持续更新中) |
err_msg | 本地生物识别结果的错误信息 |
表1:生物识别方式定义
生物识别方式 | 十六进制定义 |
---|---|
指纹识别 | 0x1 |
例如:该设备不支持SOTER或者不具备任何被SOTER支持的生物识别方式,则返回0x0;该设备支持SOTER方案指纹识别接口,则返回0x1;该设备同时支持SOTER方案指纹和人脸识别接口,则返回0x3 (0x1 | 0x2);以此类推
请求SOTER生物认证接口
使用场景:开发者调用该接口使用给定的生物认证方式进行符合SOTER规范的可信鉴权
wx.config({ beta:true, }); wx.ready(function () { wx.invoke("requireSoterBiometricAuthentication", {"auth_mode": 0xff, "challenge": "sample_challenge", "auth_content": "请将使用指纹识别"},function(res){ if(res.errCode == 0) { //检查use_mode //使用result_json和result_son_signature本地验签是否合法 //使用所提供的后台接口将result_json和result_son_signature发送到微信企业号后台进行验签 //处理验签结果 } else { var ret = res.err_msg; ret += " errCode: " + res.resultCode; alert(ret); } }); });
- 请求参数说明
参数 | 必须 | 说明 |
---|---|---|
auth_mode | 是 | 生物识别方式(注1) |
challege | 是 | 挑战因子(注2) |
auth_content | 否 | 验证描述,即识别过程中显示在界面上的对话框提示内容。 |
- 返回示例
{ "err_code":0, "err_msg":"ok", "use_mode":"bxLdikRXVbTPdHSM05e5u5sUoXNKdvsdshFKA", "result_json":"$RESULRTJSON", "result_json_signature":"$RESULRTJSONSIGNATURE", }
- 参数说明
参数 | 说明 |
---|---|
err_code | 本地生物识别结果(错误码见表3) |
err_msg | 本地生物识别结果的错误信息 |
use_mode | 所使用的生物识别方式(定义见表1) |
result_json | 在设备安全区域(TEE)内获得的本机安全信息(如TEE名称版本号等以及防重放参数)以及本次认证信息(仅Android支持,本次认证的指纹ID)(注3) |
result_json_signature | 使用SOTER安全密钥对result_json的签名(SHA256withRSA/PSS, saltlen=20) |
注1:本次请求使用的生物识别方式。生物识别方式及其定义见表1。例如:如果本次请求使用指纹识别,则填入0x1;目前只支持指纹识别。
注2:挑战因子为调用者为此次生物鉴权准备的用于签名的字符串关键是别信息,将作为result_json的一部分,供调用者识别本次请求。例如:如果场景为请求用户对某订单进行授权确认,则可以将订单号填入此参数。
注3:此数据为设备TEE中,将传入的challenge和TEE内其他安全信息组成的数据进行组装而来的JSON,对下述字段的解释如表2。例子如下:
{ "raw":"msg", "fid":"2", "counter":123, "tee_n":"TEE Name", "tee_v":"TEE Version", "fp_n":"Fingerprint Sensor Name", "fp_v":"Fingerprint Sensor Version", "cpu_id":"CPU Id", "uid":"21"}
表1:生物识别方式定义
生物识别方式 | 十六进制定义 |
---|---|
指纹识别 | 0x1 |
表2:result_json中个字段含义
字段名 | 含义 |
---|---|
raw | 调用者传入的challenge |
fid | (仅Android支持)本次生物识别认证的生物信息编号(如指纹识别则是指纹信息在本设备内部编号) |
counter | 防重放特征参数 |
tee_n | TEE名称(如高通或者trustonic等) |
tee_v | TEE版本号 |
fp_n | 指纹以及相关逻辑模块提供商(如FPC等) |
fp_v | 指纹以及相关模块版本号 |
cpu_id | 机器唯一识别ID |
uid | 概念同Android系统定义uid,即应用程序编号 |
表3:本接口err_code以及对应的解释
错误码 | 解释 |
---|---|
0 | 识别成功 |
90001 | 本设备不支持SOTER |
90002 | 用户未授权微信使用该生物认证接口 |
90003 | 请求使用的生物识别方式不支持 |
90004 | 未传入challenge或challenge长度过长(最长512字符) |
90005 | auth_content长度超过限制(最长42个字符) |
90007 | 内部错误 |
90008 | 用户取消授权 |
90009 | 识别失败 |
90010 | 重试次数过多被冻结 |
对于本接口,官方推荐的流程如下:
1.调用请求SOTER生物认证jsapi
2.jsapi返回成功之后,即err_code返回0,开发者本地进行校验签名,校验方法参见注4(可选)
3.异步调用指纹认证后台接口校验
注4:此处使用SHA256withRSA/PSS作为签名算法进行验签。此公式数学定义如下: bool 验签结果=verify(result_json,result_json_signature,auth_key)。
SOTER生物认证后台接口
- 请求说明
Https请求方式: POST
https://qyapi.weixin.qq.com/cgi-bin/soter/verify_signature?access_token=ACCESS_TOKEN
post数据格式json:
{ "openid":"OWefdsjafasEFD", "json_string" : "JSON", "json_signature" : "FDJSFSOIDUSAIDASJDASD" }
- 请求参数说明
参数 | 必须 | 说明 |
---|---|---|
access_token | 是 | 企业号接口调用凭证 |
openid | 是 | 用户在企业号的openid |
json_string | 是 | requireSoterBiometricAuthentication接口返回的result_json_string字段(注意该字段需要json转义) |
json_signature | 是 | requireSoterBiometricAuthentication接口返回的result_json_signature字段 |
- 返回结果
如果验证成功,返回
{"is_ok":ok}
验证失败返回
{"is_ok":false}
附录1-JS-SDK使用权限签名算法
jsapi_ticket
生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是企业号号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket。
- 参考以下文档获取access_token(有效期7200秒,开发者必须在自己的服务全局缓存access_token):http://qydev.weixin.qq.com/wiki/index.php?title=%E4%B8%BB%E5%8A%A8%E8%B0%83%E7%94%A8
- 用第一步拿到的access_token 采用http GET方式请求获得jsapi_ticket(有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket):https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=ACCESS_TOKE
成功返回如下JSON:
{ "errcode":0, "errmsg":"ok", "ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA", "expires_in":7200 }
获得jsapi_ticket之后,就可以生成JS-SDK权限验证的签名了。
签名算法
签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式 (即 key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。
即signature=sha1(string1)。 示例
noncestr=Wm3WZYTPz0wzccnW jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg timestamp=1414587457 url=http://mp.weixin.qq.com
步骤1. 对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1:
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW×tamp=1414587457&url=http://mp.weixin.qq.com
步骤2. 对string1进行sha1签名,得到signature:
0f9de62fce790f9a083d5c99e95740ceb90c27ed
注意事项
- 签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。
- 签名用的url必须是调用JS接口页面的完整URL。
- 出于安全考虑,开发者必须在服务器端实现签名的逻辑。
如出现invalid signature 等错误详见附录5常见错误及解决办法。
附录2-企业号管理组权限验证方法
企业号部分JS-SDK(如:打开企业通讯录选人)的使用需要经过两部分的权限验证:
- 通过config接口进行JS-SDK权限验证,与普通JS-SDK相同,请参考:步骤二:通过config接口注入权限验证配置
- 企业号管理组权限验证,步骤如下:
步骤1:通过API获取管理组JS-SDK凭据
与附录1-JS-SDK使用权限签名算法中的jsapi_ticket类似,开发者也是用access_token通过http GET请求获取管理组临时票据:https://qyapi.weixin.qq.com/cgi-bin/ticket/get?access_token=ACCESS_TOKEN&type=contact
成功返回如下JSON:
{ "errcode": 0, "errmsg": "ok", "group_id": "g50a5axxxx91706a", "ticket": "WSfTOWPg35IBK9TpwfIi_dVRKrfigCi0TOCw-ZdFnLm4GgxxxxxxxxxxwB7AD64", "expires_in": 7200 }
- 与jsapi_ticket类似,该ticket有效期通常也为7200秒(以返回结果中的expires_in为准),开发者必须在自己的服务全局缓存该ticket)
- group_id是access_token对应管理组的标识符
步骤2:生成签名
签名算法与附录1-JS-SDK使用权限签名算法类似。参与签名的字段包括noncestr(随机字符串), 有效的group_ticket(在这里即是步骤1获取的ticket), timestamp(时间戳), url(要使用该JS-SDK的页面的url,生成的签名只允许在该url页面使用) 。对所有待签名参数按照字段名的ASCII码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1:
group_ticket=WSfTOWPg35xxxxxxxxxCi0TOCw-ZdFnLm4Ggvssk4suhakGwB7AD64&noncestr=Wm3WZYTPz0wzccnW×tamp=1447334894&url=http://your.website.com
将string1作sha1加密,得到签名signature:
f211c9c1b375aae79ad74685450149825b2b2353
附录3-所有JS接口列表
版本1.1.0接口
- openEnterpriseChat
- openEnterpriseContact
- onMenuShareTimeline
- onMenuShareAppMessage
- onMenuShareQQ
- onMenuShareWeibo
- onMenuShareQZone
- startRecord
- stopRecord
- onVoiceRecordEnd
- playVoice
- pauseVoice
- stopVoice
- onVoicePlayEnd
- uploadVoice
- downloadVoice
- chooseImage
- previewImage
- uploadImage
- downloadImage
- translateVoice
- getNetworkType
- openLocation
- getLocation
- hideOptionMenu
- showOptionMenu
- hideMenuItems
- showMenuItems
- hideAllNonBaseMenuItem
- showAllNonBaseMenuItem
- closeWindow
- scanQRCode
附录4-所有菜单项列表
基本类
- 举报: "menuItem:exposeArticle"
- 调整字体: "menuItem:setFont"
- 日间模式: "menuItem:dayMode"
- 夜间模式: "menuItem:nightMode"
- 刷新: "menuItem:refresh"
- 查看企业号(已添加): "menuItem:profile"
- 查看企业号(未添加): "menuItem:addContact"
传播类
- 发送给朋友: "menuItem:share:appMessage"
- 分享到朋友圈: "menuItem:share:timeline"
- 分享到QQ: "menuItem:share:qq"
- 分享到QQ空间: "menuItem:share:QZone"
- 分享到Weibo: "menuItem:share:weiboApp"
- 收藏: "menuItem:favorite"
- 分享到FB: "menuItem:share:facebook"
- 分享到QQ空间:"menuItem:share:QZone"
保护类
- 调试: "menuItem:jsDebug"
- 编辑标签: "menuItem:editTag"
- 删除: "menuItem:delete"
- 复制链接: "menuItem:copyUrl"
- 原网页: "menuItem:originPage"
- 阅读模式: "menuItem:readMode"
- 在QQ浏览器中打开: "menuItem:openWithQQBrowser"
- 在Safari中打开: "menuItem:openWithSafari"
- 邮件: "menuItem:share:email"
- 一些特殊企业号: "menuItem:share:brand"
附录5-卡券扩展字段及签名生成算法
JSSDK使用者请读这里,JSAPI用户可以跳过
卡券签名和JSSDK的签名完全独立,两者的算法和意义完全不同,请不要混淆。JSSDK的签名是使用所有JS接口都需要走的一层鉴权,用以标识调用者的身份,和卡券本身并无关系。其次,卡券的签名考虑到协议的扩展性和简单的防数据擅改,设计了一套独立的签名协议。另外由于历史原因,卡券的JS接口先于JSSDK出现,当时的JSAPI并没有鉴权体系,所以在卡券的签名里也加上了appsecret/api_ticket这些身份信息,希望开发者理解。
卡券 api_ticket
卡券 api_ticket 是用于调用卡券相关接口的临时票据,有效期为 7200 秒,通过 access_token 来获取。这里要注意与 jsapi_ticket 区分开来。由于获取卡券 api_ticket 的 api 调用次数非常有限,频繁刷新卡券 api_ticket 会导致 api 调用受限,影响自身业务,开发者必须在自己的服务全局缓存卡券 api_ticket 。参考获取api_ticket文档
卡券扩展字段cardExt说明
cardExt本身是一个JSON字符串,是商户为该张卡券分配的唯一性信息,包含以下字段:
字段 | 是否必须 | 是否参与签名 | 说明 |
---|---|---|---|
code | 否 | 是 | 指定的卡券code码,只能被领一次。use_custom_code字段为true的卡券必须填写,非自定义code和get_custom_code_mode(预存code模式的卡券)不必填写。 |
openid | 否 | 是 | 指定领取者的openid,只有该用户能领取。bind_openid字段为true的卡券必须填写,bind_openid字段为false不必填写。 |
timestamp | 是 | 是 | 时间戳,商户生成从1970年1月1日00:00:00至今的秒数,即当前的时间,且最终需要转换为字符串形式;由商户生成后传入,不同添加请求的时间戳须动态生成,若重复将会导致领取失败!。 |
nonce_str | 否 | 是 | 随机字符串,由开发者设置传入,加强安全性(若不填写可能被重放请求)。随机字符串,不长于32位。推荐使用大小写字母和数字,不同添加请求的nonce须动态生成,若重复将会导致领取失败。 |
outer_str | 否 | 否 | 领取渠道参数,用于标识本次领取的渠道值。 |
signature | 是 | 是 | 签名,商户将接口列表中的参数按照指定方式进行签名,签名方式使用SHA1,具体签名方案参见下文;由商户按照规范签名后传入。 |
签名说明
- 将 api_ticket(特别说明:api_ticket 相较 appsecret 安全性更高,同时兼容老版本文档中使用的 appsecret 作为签名凭证。)、timestamp、card_id、code、openid、nonce_str的value值进行字符串的字典序排序。
- 将所有参数字符串拼接成一个字符串进行sha1加密,得到signature。
- signature中的timestamp,nonce字段和card_ext中的timestamp,nonce_str字段必须保持一致。
- code=jonyqin_1434008071,timestamp=1404896688,card_id=pjZ8Yt1XGILfi-FUsewpnnolGgZk, api_ticket=ojZ8YtyVyr30HheH3CM73y7h4jJE ,nonce_str=jonyqin 则signature=sha1(1404896688jonyqinjonyqin_1434008071ojZ8YtyVyr30HheH3CM73y7h4jJE pjZ8Yt1XGILfi-FUsewpnnolGgZk)=6b81fbf6af16e856334153b39737556063c82689。
强烈建议开发者使用卡券资料包中的签名工具SDK进行签名或使用debug工具进行校验:http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=cardsign
卡券签名cardSign说明
- 将 api_ticket(特别说明:api_ticket 相较 appsecret 安全性更高,同时兼容老版本文档中使用的 appsecret 作为签名凭证。)、app_id、location_id、times_tamp、nonce_str、card_id、card_type的value值进行字符串的字典序排序。
- 将所有参数字符串拼接成一个字符串进行sha1加密,得到cardSign。
附录6-常见错误及解决方法
调用config接的时候传入参数debug: true 可以开启debug模式,页面会alert出错误信息。以下为常见错误及解决方法:
- invalid url domain:当前页面所在域名与使用的corpid没有绑定(可在该企业号的应用可信域名中配置域名)。
- invalid signature签名错误:建议按如下顺序检查:
- 确认签名算法正确,可用 http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 页面工具进行校验。
- 确认config中nonceStr(js中驼峰标准大写S), timestamp与用以签名中的对应noncestr, timestamp一致。
- 确认url是页面完整的url(请在当前页面alert(location.href.split('#')[0])确认),包括'http(s)://'部分,以及'?'后面的GET参数部分,但不包括'#'hash后面的部分。
- 确认config中的appid与用来获取jsapi_ticket的corpid一致。
- 确保一定缓存access_token和jsapi_ticket。
- 确保你获取用来签名的url是动态获取的,动态页面可参见实例代码中php的实现方式。如果是html的静态页面在前端通过ajax将url传到后台签名,前端需要用js获取当前页面除去'#'hash部分的链接(可用location.href.split('#')[0]获取,而且需要encodeURIComponent),因为页面一旦分享,微信客户端会在你的链接末尾加入其它参数,如果不是动态获取当前链接,将导致分享后的页面签名失败。
- the permission value is offline verifying:这个错误是因为config没有正确执行,或者是调用的JSAPI没有传入config的jsApiList参数中。建议按如下顺序检查:
- 确认config正确通过。
- 如果是在页面加载好时就调用了JSAPI,则必须写在wx.ready的回调中。
- 确认config的jsApiList参数包含了这个JSAPI。
- permission denied:该企业号没有权限使用这个JSAPI(部分接口需要认证之后才能使用)。
- function not exist:当前客户端版本不支持该接口,请升级到新版体验。
- 为什么6.0.1版本config:ok,但是6.0.2版本之后不ok:因为6.0.2版本之前没有做权限验证,所以config都是ok,但这并不意味着你config中的签名是ok的,请在6.0.2检验是否生成正确的签名以保证config在高版本中也ok。
- 在iOS和Android都无法分享:请确认企业号已经认证,只有认证的企业号才具有分享相关接口权限,如果确实已经认证,则要检查监听接口是否在wx.ready回调函数中触发)
- 服务上线之后无法获取jsapi_ticket,自己测试时没问题:因为access_token和jsapi_ticket必须要在自己的服务器缓存,否则上线后会触发频率限制。请确保一定对token和ticket做缓存以减少服务器请求,不仅可以避免触发频率限制,还加快你们自己的服务速度。目前为了方便测试提供了1w的获取量,超过阀值后,服务将不再可用,请确保在服务上线前一定全局缓存access_token和jsapi_ticket,两者有效期均为7200秒(以返回结果中的expires_in为准),否则一旦上线触发频率限制,服务将不再可用。
- uploadImage怎么传多图:目前只支持一次上传一张,多张图片需等前一张图片上传之后再调用该接口。
- 没法对本地选择的图片进行预览:chooseImage接口本身就支持预览,不需要额外支持。
- 通过a链接(例如先通过微信授权登录)跳转到b链接,invalid signature签名失败:后台生成签名的链接为使用jssdk的当前链接,也就是跳转后的b链接,请不要用微信登录的授权链接进行签名计算,后台签名的url一定是使用jssdk的当前页面的完整url除去'#'部分。
- 出现config:fail错误:这是由于传入的config参数不全导致,请确保传入正确的appId、timestamp、nonceStr、signature和需要使用的jsApiList。
- 如何把jsapi上传到微信的多媒体资源下载到自己的服务器:请参见文档中uploadVoice和uploadImage接口的备注说明。
- Android通过jssdk上传到微信服务器,第三方再从微信下载到自己的服务器,会出现杂音:微信团队已经修复此问题,目前后台已优化上线。
- 绑定父级域名,是否其子域名也是可用的:是的,合法的子域名在绑定父域名之后是完全支持的。
- 在iOS微信6.1版本中,分享的图片外链不显示,只能显示企业号页面内链的图片或者微信服务器的图片:已在6.2中修复。
- 是否需要对低版本自己做兼容:jssdk都是兼容低版本的,不需要第三方自己额外做更多工作,但有的接口是6.0.2新引入的,只有新版才可调用。
- 使用pushState来实现web app的页面会导致签名失败:此问题已在Android6.2中修复。
- uploadImage在chooseImage的回调中有时候Android会不执行,Android6.2会解决此问题,若需支持低版本可以把调用uploadImage放在setTimeout中延迟100ms解决。
- getLocation返回的坐标在openLocation有偏差,因为getLocation返回的是gps坐标,openLocation打开的腾讯地图为火星坐标,需要第三方自己做转换,6.2版本开始已经支持直接获取火星坐标。
附录7-DEMO页面和示例代码
DEMO页面:
http://demo.open.weixin.qq.com/jssdk
示例代码:
http://demo.open.weixin.qq.com/jssdk/sample.zip
备注:链接中包含php、java、nodejs以及python的示例代码供第三方参考,第三方切记要对获取的accesstoken以及jsapi_ticket进行缓存以确保不会触发频率限制。
附录8-问题反馈
邮箱地址:weixin-open@qq.com
邮件主题:【微信JS-SDK反馈】
邮件内容说明:
用简明的语言描述问题所在,并交代清楚遇到该问题的场景,可附上截屏图片,微信团队会尽快处理你的反馈。