Nodejs獲取微信簽名并使用JSSDK

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)證。

微信JSSDK開發(fā)文檔

進(jìn)入微信公眾帳號(hào)測(cè)試號(hào)申請(qǐng)系統(tǒng)

image

體驗(yàn)接口權(quán)限表

類目 功能 接口 每日調(diào)用上限/次 操作
對(duì)話服務(wù) 基礎(chǔ)支持 獲取access_token 2000
獲取微信服務(wù)器IP地址 無(wú)上限
接收消息 驗(yàn)證消息真實(shí)性 無(wú)上限
接收普通消息 無(wú)上限
接收事件推送 無(wú)上限
接收語(yǔ)音識(shí)別結(jié)果 無(wú)上限 [關(guān)閉](javascript:void(0);)
發(fā)送消息 自動(dòng)回復(fù) 無(wú)上限
客服接口 500000
群發(fā)接口 詳情
模板消息(業(yè)務(wù)通知) 100000
用戶管理 用戶分組管理 詳情
設(shè)置用戶備注名 10000
獲取用戶基本信息 500000
獲取用戶列表 500
獲取用戶地理位置 無(wú)上限 [關(guān)閉](javascript:void(0);)
推廣支持 生成帶參數(shù)二維碼 100000
長(zhǎng)鏈接轉(zhuǎn)短鏈接接口 1000
界面豐富 自定義菜單 詳情
素材管理 素材管理接口 詳情
功能服務(wù) 智能接口 語(yǔ)義理解接口 1000
設(shè)備功能 設(shè)備功能接口 無(wú)上限 設(shè)置 [關(guān)閉](javascript:void(0);)
多客服 獲取客服聊天記錄 5000
客服管理 詳情
會(huì)話控制 詳情
網(wǎng)頁(yè)服務(wù) 網(wǎng)頁(yè)帳號(hào) 網(wǎng)頁(yè)授權(quán)獲取用戶基本信息 無(wú)上限 [修改](javascript:void(0);)
基礎(chǔ)接口 判斷當(dāng)前客戶端版本是否支持指定JS接口 無(wú)上限
分享接口 獲取“分享到朋友圈”按鈕點(diǎn)擊狀態(tài)及自定義分享內(nèi)容接口 無(wú)上限
獲取“分享給朋友”按鈕點(diǎn)擊狀態(tài)及自定義分享內(nèi)容接口 無(wú)上限
獲取“分享到QQ”按鈕點(diǎn)擊狀態(tài)及自定義分享內(nèi)容接口 無(wú)上限
獲取“分享到騰訊微博”按鈕點(diǎn)擊狀態(tài)及自定義分享內(nèi)容接口 無(wú)上限
圖像接口 拍照或從手機(jī)相冊(cè)中選圖接口 無(wú)上限
預(yù)覽圖片接口 無(wú)上限
上傳圖片接口 無(wú)上限
下載圖片接口 無(wú)上限
音頻接口 開始錄音接口 無(wú)上限
停止錄音接口 無(wú)上限
播放語(yǔ)音接口 無(wú)上限
暫停播放接口 無(wú)上限
停止播放接口 無(wú)上限
上傳語(yǔ)音接口 無(wú)上限
下載語(yǔ)音接口 無(wú)上限
智能接口 識(shí)別音頻并返回識(shí)別結(jié)果接口 無(wú)上限
設(shè)備信息 獲取網(wǎng)絡(luò)狀態(tài)接口 無(wú)上限
地理位置 使用微信內(nèi)置地圖查看位置接口 無(wú)上限
獲取地理位置接口 無(wú)上限
界面操作 隱藏右上角菜單接口 無(wú)上限
顯示右上角菜單接口 無(wú)上限
關(guān)閉當(dāng)前網(wǎng)頁(yè)窗口接口 無(wú)上限
批量隱藏功能按鈕接口 無(wú)上限
批量顯示功能按鈕接口 無(wú)上限
隱藏所有非基礎(chǔ)按鈕接口 無(wú)上限
顯示所有功能按鈕接口 無(wú)上限

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:

image

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é)果:

image

這是第一次請(qǐng)求,access_token從微信服務(wù)器獲取最初的數(shù)據(jù)。

接下來(lái)是第二次請(qǐng)求,access_token將會(huì)緩存中讀取。

image

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

image

第二次,從緩存中:

image

至此,我們獲取到了 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ì)string1sha1加密,字段名和字段值都采用原始值,不進(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&timestamp&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&timestamp=1414587457&url=http://mp.weixin.qq.com?params=value

步驟2. 對(duì)string1進(jìn)行sha1簽名,得到signature

0f9de62fce790f9a083d5c99e95740ceb90c27ed

注意事項(xiàng)

1.簽名用的noncestrtimestamp必須與wx.config中的nonceStrtimestamp相同。

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)求如下:

image

這樣我就獲取了 簽名 等一系列數(shù)據(jù)。

5、JSSDK 使用

微信JS-SDK說(shuō)明文檔

步驟一:綁定域名

先登錄微信公眾平臺(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ā)者工具里面看到:

image
image

我們也可以打開測(cè)試公眾號(hào),在里面調(diào)試,否則,我們沒有權(quán)限調(diào)取 js 接口

image
image
image

至此,已經(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)confignonceStr(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_tokenjsapi_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

image

示例代碼:

http://demo.open.weixin.qq.com/jssdk/sample.zip

備注:鏈接中包含php、java、nodejs以及python的示例代碼供第三方參考,第三方切記要對(duì)獲取的accesstoken以及jsapi_ticket進(jìn)行緩存以確保不會(huì)觸發(fā)頻率限制。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 先引入JS 文件 this.wxShare() 在created里調(diào)用 首先登陸微信公眾號(hào) JSSDK使用步驟 步...
    寄魚予海與你閱讀 6,957評(píng)論 1 3
  • 1. 準(zhǔn)備工作 1.1 查看公眾號(hào)分享接口權(quán)限 要使用微信SDK必須要有經(jīng)過(guò)微信認(rèn)證的非個(gè)人服務(wù)號(hào) 登陸服務(wù)號(hào)后,...
    sxplus閱讀 6,719評(píng)論 0 2
  • 微信服務(wù)號(hào)開發(fā) 整體流程 域名報(bào)備,服務(wù)器搭建 Python開發(fā)環(huán)境和項(xiàng)目的初始化搭建; 微信公眾號(hào)注冊(cè)及開發(fā)模式...
    飛行員suke閱讀 4,693評(píng)論 0 14
  • 每天晚上開一會(huì)兒空調(diào)的并不太涼快的時(shí)候也是很難得的 真是涼風(fēng)有興咧 然而中午最熱的時(shí)候沒有空調(diào)關(guān)門關(guān)窗風(fēng)扇一檔真是...
    蔣一句閱讀 214評(píng)論 0 0
  • 李國(guó)陽(yáng)閱讀 187評(píng)論 0 0

友情鏈接更多精彩內(nèi)容