Nodejs獲取微信簽名并使用JSSDK
1、測(cè)試號(hào)申請(qǐng)
2、獲取access_token
3、獲取 jsapi_ticket
4、獲取簽名算法
5、JSSD使用
上一篇我們講了基本的準(zhǔn)備工作,接下來(lái),進(jìn)入實(shí)戰(zhàn),由于樓主我并沒有備案過(guò)的域名(窮,沒錢,沒辦法哈),還好, 一直通不過(guò)簽名驗(yàn)證,微信比較人性化,提供測(cè)試號(hào),可以測(cè)大部分的接口,并且設(shè)置JS接口安全域名,沒有限制,可以寫任何地址,哪怕是localhost:9999也是可以的。
1、接口測(cè)試號(hào)申請(qǐng)
由于用戶體驗(yàn)和安全性方面的考慮,微信公眾號(hào)的注冊(cè)有一定門檻,某些高級(jí)接口的權(quán)限需要微信認(rèn)證后才可以獲取。
所以,為了幫助開發(fā)者快速了解和上手微信公眾號(hào)開發(fā),熟悉各個(gè)接口的調(diào)用,微信推出了微信公眾帳號(hào)測(cè)試號(hào),通過(guò)手機(jī)微信掃描二維碼即可獲得測(cè)試號(hào),在這個(gè)測(cè)試號(hào)里面可以模擬各種操作,比如分享啥的,很容易通過(guò)驗(yàn)證。
進(jìn)入微信公眾帳號(hào)測(cè)試號(hào)申請(qǐng)系統(tǒng)
體驗(yàn)接口權(quán)限表
2、獲取 access_token 訪問(wèn)令牌
access_token
(有效期7200秒,開發(fā)者必須在自己的服務(wù)全局緩存access_token`)
https請(qǐng)求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
參數(shù)說(shuō)明
| 參數(shù) | 是否必須 | 說(shuō)明 |
|---|---|---|
| grant_type | 是 | 獲取access_token填寫client_credential |
| appid | 是 | 第三方用戶唯一憑證 |
| secret | 是 | 第三方用戶唯一憑證密鑰,即appsecret |
返回說(shuō)明
正常情況下,微信會(huì)返回下述JSON數(shù)據(jù)包給公眾號(hào):
{"access_token":"ACCESS_TOKEN","expires_in":7200}
實(shí)戰(zhàn)項(xiàng)目代碼:
獲取 access_token
config/index.json:
api/accessToken.js
// 獲取 access_token
const config = require('../config/index.json'); // 配置數(shù)據(jù)
const axios = require('axios'); // 請(qǐng)求api
const CircularJSON = require('circular-json');
// (設(shè)置 | 獲取)緩存方法
const cache = require('../utils/cache');
module.exports = getAccessToken = (res) => {
const fetchUrl = `${config.getAccessToken}?grant_type=client_credential&appid=${config.appid}&secret=${config.appsecret}`;
// console.log(fetchUrl, config);
// 獲取緩存
cache.getCache('access_token', function (cacheValue) {
// 緩存存在
if (cacheValue) {
const result = CircularJSON.stringify({
access_token: cacheValue,
from: 'cache'
});
res.send(result);
} else {
// 調(diào)取微信api
axios.get(fetchUrl).then(response => {
let json = CircularJSON.stringify(response.data);
res.send(json);
// 設(shè)置緩存
if (response.data.access_token) {
cache.setCache('access_token', response.data.access_token)
}
}).catch(err => {
console.log('axios occurs ', err);
});
}
});
};
這里用的 axios請(qǐng)求微信api,獲取 access_token;
由于access_token 只有7200秒有效時(shí)間,并且限制一天最多調(diào)2000 次,所以中控服務(wù)器最好作緩存,這里使用的 node-cache,做了access_token的緩存,并且刪除的緩存的時(shí)間也設(shè)置的是 7200s,這樣在 access_token失效的時(shí)候,node緩存也會(huì)被刪除。
utils/cache.js
// node-cache 保存和獲取緩存
const NodeCache = require("node-cache");
const myCache = new NodeCache({
stdTTL: 7200, // 緩存過(guò)期時(shí)間
checkperiod: 120 // 定期檢查時(shí)間
});
// 設(shè)置緩存
var setCache = function (key, value) {
// 設(shè)置緩存
myCache.set(key, value, function (err, success) {
if (!err && success) {
console.log(key + "保存成功", value);
}
});
};
// 獲取緩存
var getCache = function (key, callback) {
// 讀取緩存
myCache.get(key, function (err, value) {
if (!err) {
if (value) {
console.log(`存在于緩存中${key}=${value}`);
callback(value);
} else {
console.log(`${key} not found in node-cache`);
callback();
}
} else {
console.log('get ' + key + ' cache occurs error =', err);
}
});
};
module.exports = {
setCache,
getCache
}
node-cache只能存活于當(dāng)前進(jìn)程里面,如果當(dāng)前node命令被重啟,將會(huì)重新去請(qǐng)求微信服務(wù)器,所以不太適合。
這里其實(shí)最好存在 redis數(shù)據(jù)庫(kù)里,
路由設(shè)置:
app.js
const express = require('express');
const api = require('./api');
const path = require('path');
const app = express();
// accessToken 獲取token
app.get('/getAccessToken', (req, res) => {
api.accessToken(res);
});
....
結(jié)果:
這是第一次請(qǐng)求,access_token從微信服務(wù)器獲取最初的數(shù)據(jù)。
接下來(lái)是第二次請(qǐng)求,access_token將會(huì)緩存中讀取。
3、獲取 jsapi_ticket臨時(shí)票據(jù)
生成簽名之前必須先了解一下jsapi_ticket,jsapi_ticket是公眾號(hào)用于調(diào)用微信JS接口的臨時(shí)票據(jù)。正常情況下,jsapi_ticket的有效期為7200秒,通過(guò)access_token來(lái)獲取。由于獲取jsapi_ticket的api調(diào)用次數(shù)非常有限,頻繁刷新jsapi_ticket會(huì)導(dǎo)致api調(diào)用受限,影響自身業(yè)務(wù),開發(fā)者必須在自己的服務(wù)全局緩存jsapi_ticket。
用上一步拿到的
access_token采用http GET方式請(qǐng)求獲得jsapi_ticket(有效期7200秒,開發(fā)者必須在自己的服務(wù)全局緩存jsapi_ticket):<https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi>
成功返回如下JSON:
{
"errcode":0,
"errmsg":"ok",
"ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd841ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUWvsdshFKA",
"expires_in":7200
}
獲得jsapi_ticket之后,就可以生成JS-SDK權(quán)限驗(yàn)證的簽名了。
回到之前說(shuō)的那個(gè)Nodejs + Express項(xiàng)目中:
api/jsapiTicket.js
// 通過(guò) access_token 獲取 jsapi_ticket 臨時(shí)票據(jù)
const axios = require('axios'); // 請(qǐng)求api
const CircularJSON = require('circular-json');
const config = require('../config/index.json');
const cache = require('../utils/cache');
module.exports = get_jsapi_ticket = (access_token, res) => {
const fetchUrl = config.getJsapiTicket + access_token;
console.log('>>>>', fetchUrl)
// 判斷是否存在于緩存中
const cacheName = "jsapi_ticket";
cache.getCache(cacheName, function (cacheValue) {
if (cacheValue) {
const result = CircularJSON.stringify({
ticket: cacheValue,
from: 'cache'
});
res.send(result);
} else {
// 調(diào)取微信api
axios.get(fetchUrl).then(response => {
let json = CircularJSON.stringify(response.data);
// promise
res.send(json);
// 設(shè)置緩存
if (response.data.ticket) {
cache.setCache(cacheName, response.data.ticket)
}
}).catch(err => {
// console.log('axios occurs ', err);
});
}
});
}
路由設(shè)置:
const express = require('express');
const api = require('./api');
const path = require('path');
const app = express();
//express請(qǐng)求別的路由中間件
require('run-middleware')(app);
// 獲取 jsapi_ticket 臨時(shí)票據(jù)
app.get('/getTicket', (req, res) => {
app.runMiddleware('/getAccessToken', function (code, body, headers) {
const result = JSON.parse(body);
console.log('User token:', result.access_token);
api.jsapiTicket(result.access_token, res);
})
});
....
這里比較特殊的地方,是用 run-middleware 這個(gè) npm package,從一個(gè)路由去直接請(qǐng)求另外一個(gè)路由的數(shù)據(jù)。
這樣避免重復(fù)很多邏輯。我們直接請(qǐng)求路由獲取上一步的 access_token;
廢話不多說(shuō),運(yùn)行一下:
第一次,是從微信服務(wù)器獲取 ticket
第二次,從緩存中:
至此,我們獲取到了 jsapi_ticker;
獲得jsapi_ticket之后,就可以生成JS-SDK權(quán)限驗(yàn)證的簽名了
4、增加簽名算法獲取微信簽名
簽名算法
簽名生成規(guī)則如下:參與簽名的字段包括
noncestr(隨機(jī)字符串), 有效的jsapi_ticket,timestamp(時(shí)間戳),url(當(dāng)前網(wǎng)頁(yè)的URL,不包含#及其后面部分) 。對(duì)所有待簽名參數(shù)按照字段名的ASCII 碼從小到大排序(字典序)后,使用URL鍵值對(duì)的格式(即key1=value1&key2=value2…)拼接成字符串string1。這里需要注意的是所有參數(shù)名均為小寫字符。對(duì)string1作sha1加密,字段名和字段值都采用原始值,不進(jìn)行URL 轉(zhuǎn)義。
說(shuō)明:
noncestr隨機(jī)字符串,一般自己生成
jsapi_ticket從微信服務(wù)器或者自己的緩存中
timestamp時(shí)間戳自己生成
url當(dāng)前頁(yè)面的url,一定要?jiǎng)討B(tài)獲取,千萬(wàn)不要hardcode然后再按照字典排序,進(jìn)行排序
jsapi_ticket&noncestr×tamp&url最后
sha1加密
即signature=sha1(string1)。 示例:
noncestr=Wm3WZYTPz0wzccnW
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3
timestamp=1414587457
url=http://mp.weixin.qq.com?params=value
步驟1. 對(duì)所有待簽名參數(shù)按照字段名的ASCII 碼從小到大排序(字典序)后,使用URL鍵值對(duì)的格式(即key1=value1&key2=value2…)拼接成字符串string1:
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW×tamp=1414587457&url=http://mp.weixin.qq.com?params=value
步驟2. 對(duì)string1進(jìn)行sha1簽名,得到signature:
0f9de62fce790f9a083d5c99e95740ceb90c27ed
注意事項(xiàng)
1.簽名用的
noncestr和timestamp必須與wx.config中的nonceStr和timestamp相同。2.簽名用的
url必須是調(diào)用JS接口頁(yè)面的完整URL。3.出于安全考慮,開發(fā)者必須在服務(wù)器端實(shí)現(xiàn)簽名的邏輯。
如出現(xiàn)
invalid signature等錯(cuò)誤詳見附錄常見錯(cuò)誤及解決辦法
代碼如下:
/**
* 獲取簽名
* @returns:
* 1. appId 必填,公眾號(hào)的唯一標(biāo)識(shí)
* 2. timestamp 必填,生成簽名的時(shí)間戳
* 3. nonceStr 必填,生成簽名的隨機(jī)串
* 4. signature 必填,簽名
*/
const crypto = require('crypto');
const config = require('../config/index.json');
// sha1加密
function sha1(str) {
let shasum = crypto.createHash("sha1")
shasum.update(str)
str = shasum.digest("hex")
return str
}
/**
* 生成簽名的時(shí)間戳
* @return {字符串}
*/
function createTimestamp() {
return parseInt(new Date().getTime() / 1000) + ''
}
/**
* 生成簽名的隨機(jī)串
* @return {字符串}
*/
function createNonceStr() {
return Math.random().toString(36).substr(2, 15)
}
/**
* 對(duì)參數(shù)對(duì)象進(jìn)行字典排序
* @param {對(duì)象} args 簽名所需參數(shù)對(duì)象
* @return {字符串} 排序后生成字符串
*/
function raw(args) {
var keys = Object.keys(args)
keys = keys.sort()
var newArgs = {}
keys.forEach(function (key) {
newArgs[key.toLowerCase()] = args[key]
})
var string = ''
for (var k in newArgs) {
string += '&' + k + '=' + newArgs[k]
}
string = string.substr(1)
return string
}
module.exports = getSign = (params, res) => {
/**
* 簽名算法
* 簽名生成規(guī)則如下:
* 參與簽名的字段包括noncestr( 隨機(jī)字符串),
* 有效的jsapi_ticket, timestamp( 時(shí)間戳),
* url( 當(dāng)前網(wǎng)頁(yè)的URL, 不包含# 及其后面部分)。
* 對(duì)所有待簽名參數(shù)按照字段名的ASCII 碼從小到大排序( 字典序) 后,
* 使用URL鍵值對(duì)的格式( 即key1 = value1 & key2 = value2…) 拼接成字符串string1。
* 這里需要注意的是所有參數(shù)名均為小寫字符。 對(duì)string1作sha1加密, 字段名和字段值都采用原始值, 不進(jìn)行URL 轉(zhuǎn)義。
*/
var ret = {
jsapi_ticket: params.ticket,
nonceStr: createNonceStr(),
timestamp: createTimestamp(),
url: params.url
};
console.log(params, ret);
var string = raw(ret)
ret.signature = sha1(string)
ret.appId = config.appid;
console.log('ret', ret)
res.send(ret);
}
路由設(shè)置:
const express = require('express');
const api = require('./api');
const path = require('path');
const app = express();
//express請(qǐng)求別的路由中間件
require('run-middleware')(app);
//獲取簽名
app.get('/sign', (req, res) => {
const params = {};
console.log(req.query)
params.url = req.query.url;
/***
* runMiddleware 請(qǐng)求別的 endPoint 獲取 jsapi_ticket
*/
app.runMiddleware('/getTicket', function (code, body, headers) {
const result = JSON.parse(body);
console.log('User ticket:', result.ticket);
params.ticket = result.ticket;
api.getSign(params, res);
});
});
....
postman 請(qǐng)求如下:
這樣我就獲取了 簽名 等一系列數(shù)據(jù)。
5、JSSDK 使用
步驟一:綁定域名
先登錄微信公眾平臺(tái)進(jìn)入“公眾號(hào)設(shè)置”的“功能設(shè)置”里填寫“JS接口安全域名”。
備注:登錄后可在“開發(fā)者中心”查看對(duì)應(yīng)的接口權(quán)限。
步驟二:引入JS文件
在需要調(diào)用JS接口的頁(yè)面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.4.0.js
如需進(jìn)一步提升服務(wù)穩(wěn)定性,當(dāng)上述資源不可訪問(wèn)時(shí),可改訪問(wèn):http://res2.wx.qq.com/open/js/jweixin-1.4.0.js (支持https)。
備注:支持使用 AMD/CMD 標(biāo)準(zhǔn)模塊加載方法加載
步驟三:通過(guò)config接口注入權(quán)限驗(yàn)證配置
所有需要使用JS-SDK的頁(yè)面必須先注入配置信息,否則將無(wú)法調(diào)用(同一個(gè)url僅需調(diào)用一次,對(duì)于變化url的SPA的web app可在每次url變化時(shí)進(jìn)行調(diào)用,目前Android微信客戶端不支持pushState的H5新特性,所以使用pushState來(lái)實(shí)現(xiàn)web app的頁(yè)面會(huì)導(dǎo)致簽名失敗,此問(wèn)題會(huì)在Android6.2中修復(fù))。
wx.config({
debug: true, // 開啟調(diào)試模式,調(diào)用的所有api的返回值會(huì)在客戶端alert出來(lái),若要查看傳入的參數(shù),可以在pc端打開,參數(shù)信息會(huì)通過(guò)log打出,僅在pc端時(shí)才會(huì)打印。
appId: '', // 必填,公眾號(hào)的唯一標(biāo)識(shí)
timestamp: , // 必填,生成簽名的時(shí)間戳
nonceStr: '', // 必填,生成簽名的隨機(jī)串
signature: '',// 必填,簽名
jsApiList: [] // 必填,需要使用的JS接口列表
});
步驟四:通過(guò)ready接口處理成功驗(yàn)證
wx.ready(function(){
// config信息驗(yàn)證后會(huì)執(zhí)行ready方法,所有接口調(diào)用都必須在config接口獲得結(jié)果之后,config是一個(gè)客戶端的異步操作,所以如果需要在頁(yè)面加載時(shí)就調(diào)用相關(guān)接口,則須把相關(guān)接口放在ready函數(shù)中調(diào)用來(lái)確保正確執(zhí)行。對(duì)于用戶觸發(fā)時(shí)才調(diào)用的接口,則可以直接調(diào)用,不需要放在ready函數(shù)中。
});
實(shí)戰(zhàn)代碼如下:
// promise
const getSignPromise = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', location.origin + '/sign?url=' + location.href, true);
xhr.send();
xhr.onload = () => {
if (xhr.readyState === xhr.DONE) {
if (xhr.status === 200) {
const result = JSON.parse(xhr.response);
console.log(result);
resolve(result);
}
}
}
});
// 分享
getSignPromise.then(res => {
getWeShare(res);
});
/***
* 微信分享
*/
const getWeShare = (params) => {
wx.config({
debug: true, // 開啟調(diào)試模式,調(diào)用的所有api的返回值會(huì)在客戶端alert出來(lái),若要查看傳入的參數(shù),可以在pc端打開,參數(shù)信息會(huì)通過(guò)log打出,僅在pc端時(shí)才會(huì)打印。
appId: params.appId, // 必填,公眾號(hào)的唯一標(biāo)識(shí)
timestamp: params.timestamp, // 必填,生成簽名的時(shí)間戳
nonceStr: params.nonceStr, // 必填,生成簽名的隨機(jī)串
signature: params.signature, // 必填,簽名
jsApiList: [
'checkJsApi',
'onMenuShareTimeline',
'onMenuShareAppMessage',
'onMenuShareQQ',
'onMenuShareWeibo',
'hideMenuItems',
'chooseImage',
'updateAppMessageShareData',
'scanQRCode'
] // 必填,需要使用的JS接口列表
});
wx.ready(function () { //需在用戶可能點(diǎn)擊分享按鈕前就先調(diào)用
const data = {
title: '測(cè)試JSSDK', // 分享標(biāo)題
desc: '后端端口簽名測(cè)試', // 分享描述
link: location.href, // 分享鏈接,該鏈接域名或路徑必須與當(dāng)前頁(yè)面對(duì)應(yīng)的公眾號(hào)JS安全域名一致
imgUrl: 'http://www.***.cf/img/share.JPG', // 分享圖標(biāo)
success: function () {
// 設(shè)置成功
}
}
wx.onMenuShareTimeline(data);
wx.onMenuShareAppMessage(data);
});
}
// 打開相冊(cè)
document.getElementById('chooseImage').addEventListener('click', function (params) {
wx.chooseImage({
count: 1, // 默認(rèn)9
sizeType: ['original', 'compressed'], // 可以指定是原圖還是壓縮圖,默認(rèn)二者都有
sourceType: ['album', 'camera'], // 可以指定來(lái)源是相冊(cè)還是相機(jī),默認(rèn)二者都有
success: function (res) {
var localIds = res.localIds; // 返回選定照片的本地ID列表,localId可以作為img標(biāo)簽的src屬性顯示圖片
console.log(localIds);
}
});
wx.scanQRCode({
needResult: 0, // 默認(rèn)為0,掃描結(jié)果由微信處理,1則直接返回掃描結(jié)果,
scanType: ["qrCode", "barCode"], // 可以指定掃二維碼還是一維碼,默認(rèn)二者都有
success: function (res) {
var result = res.resultStr; // 當(dāng)needResult 為 1 時(shí),掃碼返回的結(jié)果
}
});
})
頁(yè)面寫好之后, 我們可以在 微信開發(fā)者工具里面看到:
我們也可以打開測(cè)試公眾號(hào),在里面調(diào)試,否則,我們沒有權(quán)限調(diào)取 js 接口
至此,已經(jīng)算是成功開發(fā)了,其他接口不再做嘗試。
6、附錄
調(diào)用config接口的時(shí)候傳入?yún)?shù) debug: true 可以開啟debug模式,頁(yè)面會(huì)alert出錯(cuò)誤信息。以下為常見錯(cuò)誤及解決方法:
1.invalid url domain當(dāng)前頁(yè)面所在域名與使用的appid沒有綁定,請(qǐng)確認(rèn)正確填寫綁定的域名,僅支持80(http)和443(https)兩個(gè)端口,因此不需要填寫端口號(hào)(一個(gè)appid可以綁定三個(gè)有效域名,見 ]目錄1.1.1)。
2.·invalid signature簽名錯(cuò)誤。建議按如下順序檢查:
1.確認(rèn)簽名算法正確,可用http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 頁(yè)面工具進(jìn)行校驗(yàn)。
2.確認(rèn)
config中nonceStr(js中駝峰標(biāo)準(zhǔn)大寫S),timestamp與用以簽名中的對(duì)應(yīng)noncestr,timestamp一致。3.確認(rèn)url是頁(yè)面完整的url(請(qǐng)?jiān)诋?dāng)前頁(yè)面alert(location.href.split('#')[0])確認(rèn)),包括'http(s)://'部分,以及'?'后面的GET參數(shù)部分,但不包括'#'hash后面的部分。
4.確認(rèn) config 中的 appid 與用來(lái)獲取 jsapi_ticket 的 appid 一致。
5.確保一定緩存
access_token和jsapi_ticket。6.確保你獲取用來(lái)簽名的url是動(dòng)態(tài)獲取的,動(dòng)態(tài)頁(yè)面可參見實(shí)例代碼中php的實(shí)現(xiàn)方式。如果是html的靜態(tài)頁(yè)面在前端通過(guò)ajax將url傳到后臺(tái)簽名,前端需要用js獲取當(dāng)前頁(yè)面除去'#'hash部分的鏈接(可用location.href.split('#')[0]獲取,而且需要encodeURIComponent),因?yàn)轫?yè)面一旦分享,微信客戶端會(huì)在你的鏈接末尾加入其它參數(shù),如果不是動(dòng)態(tài)獲取當(dāng)前鏈接,將導(dǎo)致分享后的頁(yè)面簽名失敗。
3.the permission value is offline verifying這個(gè)錯(cuò)誤是因?yàn)閏onfig沒有正確執(zhí)行,或者是調(diào)用的JSAPI沒有傳入config的jsApiList參數(shù)中。建議按如下順序檢查:
1.確認(rèn)config正確通過(guò)。
2.如果是在頁(yè)面加載好時(shí)就調(diào)用了JSAPI,則必須寫在wx.ready的回調(diào)中。
3.確認(rèn)config的jsApiList參數(shù)包含了這個(gè)JSAPI。
4.permission denied該公眾號(hào)沒有權(quán)限使用這個(gè)JSAPI,或者是調(diào)用的JSAPI沒有傳入config的jsApiList參數(shù)中(部分接口需要認(rèn)證之后才能使用)。
5.function not exist當(dāng)前客戶端版本不支持該接口,請(qǐng)升級(jí)到新版體驗(yàn)。
6.為什么6.0.1版本config:ok,但是6.0.2版本之后不ok(因?yàn)?.0.2版本之前沒有做權(quán)限驗(yàn)證,所以config都是ok,但這并不意味著你config中的簽名是OK的,請(qǐng)?jiān)?.0.2檢驗(yàn)是否生成正確的簽名以保證config在高版本中也ok。)
7.在iOS和Android都無(wú)法分享(請(qǐng)確認(rèn)公眾號(hào)已經(jīng)認(rèn)證,只有認(rèn)證的公眾號(hào)才具有分享相關(guān)接口權(quán)限,如果確實(shí)已經(jīng)認(rèn)證,則要檢查監(jiān)聽接口是否在wx.ready回調(diào)函數(shù)中觸發(fā))
8.服務(wù)上線之后無(wú)法獲取jsapi_ticket,自己測(cè)試時(shí)沒問(wèn)題。(因?yàn)閍ccess_token和jsapi_ticket必須要在自己的服務(wù)器緩存,否則上線后會(huì)觸發(fā)頻率限制。請(qǐng)確保一定對(duì)token和ticket做緩存以減少2次服務(wù)器請(qǐng)求,不僅可以避免觸發(fā)頻率限制,還加快你們自己的服務(wù)速度。目前為了方便測(cè)試提供了1w的獲取量,超過(guò)閥值后,服務(wù)將不再可用,請(qǐng)確保在服務(wù)上線前一定全局緩存access_token和jsapi_ticket,兩者有效期均為7200秒,否則一旦上線觸發(fā)頻率限制,服務(wù)將不再可用)。
9.uploadImage怎么傳多圖(目前只支持一次上傳一張,多張圖片需等前一張圖片上傳之后再調(diào)用該接口)
10.沒法對(duì)本地選擇的圖片進(jìn)行預(yù)覽(chooseImage接口本身就支持預(yù)覽,不需要額外支持)
11.通過(guò)a鏈接(例如先通過(guò)微信授權(quán)登錄)跳轉(zhuǎn)到b鏈接,invalid signature簽名失敗(后臺(tái)生成簽名的鏈接為使用jssdk的當(dāng)前鏈接,也就是跳轉(zhuǎn)后的b鏈接,請(qǐng)不要用微信登錄的授權(quán)鏈接進(jìn)行簽名計(jì)算,后臺(tái)簽名的url一定是使用jssdk的當(dāng)前頁(yè)面的完整url除去'#'部分)
12.出現(xiàn)config:fail錯(cuò)誤(這是由于傳入的config參數(shù)不全導(dǎo)致,請(qǐng)確保傳入正確的appId、timestamp、nonceStr、signature和需要使用的jsApiList)
13.如何把jsapi上傳到微信的多媒體資源下載到自己的服務(wù)器(請(qǐng)參見文檔中uploadVoice和uploadImage接口的備注說(shuō)明)
14.Android通過(guò)jssdk上傳到微信服務(wù)器,第三方再?gòu)奈⑿畔螺d到自己的服務(wù)器,會(huì)出現(xiàn)雜音(微信團(tuán)隊(duì)已經(jīng)修復(fù)此問(wèn)題,目前后臺(tái)已優(yōu)化上線)
15.綁定父級(jí)域名,是否其子域名也是可用的(是的,合法的子域名在綁定父域名之后是完全支持的)
16.在iOS微信6.1版本中,分享的圖片外鏈不顯示,只能顯示公眾號(hào)頁(yè)面內(nèi)鏈的圖片或者微信服務(wù)器的圖片,已在6.2中修復(fù)
17.是否需要對(duì)低版本自己做兼容(jssdk都是兼容低版本的,不需要第三方自己額外做更多工作,但有的接口是6.0.2新引入的,只有新版才可調(diào)用)
18.該公眾號(hào)支付簽名無(wú)效,無(wú)法發(fā)起該筆交易(請(qǐng)確保你使用的jweixin.js是官方線上版本,不僅可以減少用戶流量,還有可能對(duì)某些bug進(jìn)行修復(fù),拷貝到第三方服務(wù)器中使用,官方將不對(duì)其出現(xiàn)的任何問(wèn)題提供保障,具體支付簽名算法可參考 JSSDK微信支付一欄)
19.目前Android微信客戶端不支持pushState的H5新特性,所以使用pushState來(lái)實(shí)現(xiàn)web app的頁(yè)面會(huì)導(dǎo)致簽名失敗,此問(wèn)題已在Android6.2中修復(fù)
20.uploadImage在chooseImage的回調(diào)中有時(shí)候Android會(huì)不執(zhí)行,Android6.2會(huì)解決此問(wèn)題,若需支持低版本可以把調(diào)用uploadImage放在setTimeout中延遲100ms解決
21.require subscribe錯(cuò)誤說(shuō)明你沒有訂閱該測(cè)試號(hào),該錯(cuò)誤僅測(cè)試號(hào)會(huì)出現(xiàn)
22.getLocation返回的坐標(biāo)在openLocation有偏差,因?yàn)間etLocation返回的是gps坐標(biāo),openLocation打開的騰訊地圖為火星坐標(biāo),需要第三方自己做轉(zhuǎn)換,6.2版本開始已經(jīng)支持直接獲取火星坐標(biāo)
23.查看公眾號(hào)(未添加): "menuItem:addContact"不顯示,目前僅有從公眾號(hào)傳播出去的鏈接才能顯示,來(lái)源必須是公眾號(hào)
24.ICP備案數(shù)據(jù)同步有一天延遲,所以請(qǐng)?jiān)诘诙战壎?/p>
DEMO頁(yè)面和示例代碼
DEMO頁(yè)面:
http://demo.open.weixin.qq.com/jssdk
示例代碼:
http://demo.open.weixin.qq.com/jssdk/sample.zip
備注:鏈接中包含php、java、nodejs以及python的示例代碼供第三方參考,第三方切記要對(duì)獲取的accesstoken以及jsapi_ticket進(jìn)行緩存以確保不會(huì)觸發(fā)頻率限制。