微信公眾號開發

Woolson增發表於2018-05-22

時間:2017-11-01 11:00

前面做過 HG 專案的微信端,裡面用到微信掃碼、支付、圖片選取、拍照、分享的功能。用到 weixin-js-sdk 和 WeixinJSBridge。

前端開發以Vue程式碼為例,後端以NodeJS為例。後端開發還需詳細閱讀開發者文件

1、開發準備

1.1 新建測試號

用於開發和測試環境,產品經理需準備

步驟:

登入【公眾號平臺】 → 側邊欄開發選擇【開發者工具】→ 進入【公眾平臺測試帳號】→ 掃碼確認 → 成功

==注意:測試號的配置都在此頁面==

如圖:

公眾號

1.2 配置公眾號

  • 儲存 appIDappSecret 和 測試號二維碼

    ==注意:及時儲存,以免需要的時候找不到==

  • 修改授權回撥頁面地址

    ==注意:使用者確認授權後重定向的地址==

    網頁授權

  • 修改介面配置資訊、JS介面安全域名

    ==注意:呼叫 weixin-js-sdk 功能或呼叫 WeixinJSBridge 頁面所在域名==

    修改JS安全域名:

    ​ 公眾號主頁側邊欄設定選擇【公眾號設定】→ 選擇上面第二個tab【功能設定】第三項

    安全域名

  • 如果有支付功能需要配置授權目錄

    ==注意:這個需要在商戶平臺修改,不是在公眾平臺==

    授權目錄

    舉例:

    ​ 現在需要在 http://www.behuntergatherer.com/pay/payment 頁面進行支付,就需要設定一個授權目錄為 http://www.behuntergatherer.com/pay/ 。在支付頁面路由的前一個目錄。

    建議:

    ​ 統一一個頁面進行支付。比如 http://www.behuntergatherer.com/pay/payment 是支付頁面,我在 http://www.behuntergatherer.com/vip/order 頁面需要支付,則生成好訂單後跳轉 http://www.behuntergatherer.com/pay/payment 頁面進行支付。

  • 下載安裝【Web開發者工具】

    ==注意:開發同學準備,下載地址==

    目的:

    • 頁面開發相容性、佈局和瀏覽器表現不一致
    • 微信介面除錯、呼叫介面有日誌

    注意事項:

    • 支付功能不能在本地測試,不能使用測試公眾號測試
    • 開發工具可能存在未知 bug,請保持更新

##2、簡單config封裝

2.1 開發文件

2.2 配置封裝

  • 這些介面需要配置以後才能呼叫
  • API可以配需要的幾個,亦可以全部
  • 一次配置只對當前頁面有效,跳轉頁面需要重新配置
2.2.1 JS-SDK介面配置

==注意:需要呼叫微信JSAPI介面的頁面,不能使用h5 pushState進行跳轉,會造成無效的簽名問題。請使用location.href。==

// utils.js
// 微信配置介面通用方法
const ALL_API_LIST = [
	'scanQRCode',
  	'...'
]

export function configWxApi (jsApiList = ALL_API_LIST) {
	return new Promise((resolve, reject) => {
      	// 使用當前的href獲取簽名,後端返回配置介面所需要的引數
		get('/mdm2/api/getSignature.do', {
			url: encodeURIComponent(window.location.href),
		})
			.then(data => {
				wx.config({
					debug: process.env.NODE_ENV !== 'production',
					appId: data.appId,
					timestamp: Number(data.timestamp), // 秒數,不是毫秒
					nonceStr: data.nonceStr,
					signature: data.signature,
					jsApiList,
				})

				wx.error(function (res) {
					Toast('呼叫微信jsapi返回的狀態:' + res.errMsg)
					reject()
				})

				wx.ready(resolve)
			})
	})
}
複製程式碼
2.2.2 WeixinJSBridge
// utils.js
export function checkWxBridge () {
	return new Promise((resolve, reject) => {
		if (typeof WeixinJSBridge == "undefined") {
			if (document.addEventListener) {
				document.addEventListener('WeixinJSBridgeReady', resolve, false)
			} else if (document.attachEvent){
				document.attachEvent('WeixinJSBridgeReady', resolve)
				document.attachEvent('onWeixinJSBridgeReady', resolve)
			}
		} else {
			resolve()
		}
	})
}
複製程式碼

2.3 配置函式使用

2.3.1 JS-SDK使用

==注意事項:==

  1. 所有呼叫 JS-SDK 的介面都必須 queryWxApi
  2. 都必須處理錯誤情況,增加使用者體驗
// 採用Promise封裝有兩種使用方法,統一選擇一個
// app.js
import { configWxApi } from 'utils'

// Promise寫法
mounted () {
  configWxApi()
    .then(res => {
    	// 配置完成
    	wx.chooseImage()
    })
    .catch(err => {
    	// 配置失敗
  	})
}

// 同步函式寫法
async mounted () {
  try {
  	const configResult = await queryWxApi(['scanQRCode'])
    // 成功處理
  } catch (err) {
  	// 錯誤處理    
  }
}
複製程式碼
2.3.2 WeixinJSBridge

==注意事項:==

  1. 所有呼叫JS-SDK的的介面都必須 checkWxBridge
  2. 都必須處理錯誤情況,增加使用者體驗
// someComponent.js
import { checkWxBridge } from 'utils'

checkWxBridge().then(() => {
 	WeixinJSBridge.call('hideOptionMenu')
  	WeixinJSBridge.invoke('getBrandWCPayRequest')
})
複製程式碼

3、後端開發示例

後端一兩個常見例子說明

3.1 Access_token

​ ==這是微信開發中最基礎的東西,詳情見官方文件== 詳細文件 => 任意門

  1. 這是什麼東西?

    access_token是公眾號的==全域性唯一==介面呼叫憑據,公眾號呼叫各介面時都需使用access_token

  2. 有什麼屬性?

    • access_token的儲存至少要保留==512個字元==空間
    • access_token的有效期目前為==2個小時(7200秒)==
    • 重複獲取將導致上次獲取的access_token==失效==
  3. 怎麼獲取?

    ​ ==呼叫介面時,請登入“微信公眾平臺-開發-基本配置”提前將伺服器IP地址新增到IP白名單中,點選檢視設定方法,否則將無法呼叫成功。==

    https請求方式: GET

    https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
    複製程式碼

    引數說明

    引數 是否必須 說明
    grant_type 獲取access_token填寫client_credential
    appid 第三方使用者唯一憑證
    secret 第三方使用者唯一憑證金鑰,即appsecret

    返回說明

    正常情況下,微信會返回下述JSON資料包給公眾號:

    {"access_token":"ACCESS_TOKEN","expires_in":7200}
    複製程式碼

開發建議:統一獲取和重新整理Access_token,不應該各自去重新整理,否則容易造成衝突,導致access_token覆蓋而影響業務。

// accessToken.js

module.exports = {
	getToken: function (callback) {
      	// 判斷token是否過期,如果過期就重取,未過期就返回
		var tokenPath = path.join(__dirname, './token.json')
		var token = JSON.parse(fs.readFileSync(tokenPath))
		var currTime = utils.getTimeStamp().second
		var isExpired = currTime > token.timestamp

		if(isExpired) fetchToken(callback)
		else callback(token.value)
	},
	updateToken: function (callback) {
		fetchToken(callback)
	},
}

function fetchToken (callback) {
	var configPath = path.join(__dirname, './config.json')
	var config = JSON.parse(fs.readFileSync(configPath))
	var param = {
		grant_type: 'client_credential',
		appid: config.appId,
		secret: config.secret,
	}
	var paramStr = utils.obj2Params(param)
	var url = `https://api.weixin.qq.com/cgi-bin/token?${paramStr}`

	request(url, function(err, res, body) {
		// 記錄獲取token時間,兩小時後過期重取
      	try {
        	var resReult = JSON.parse(body);
            var newToken = {
                value: resReult.access_token,
                timestamp: utils.getTimeStamp().second + 7200,
            }
            var filePath = path.join(__dirname, 'token.json')
            fs.writeFileSync(filePath, JSON.stringify(newToken))
            callback(newToken.value)
        } catch (e) {
          	console.log('獲取token出錯,', e)
          	callback()
        }
	})
}

// somejs.js
import { getToken } from 'accessToken'

getToken(function(token) {
	// 其他業務
})
複製程式碼

3.2 獲取使用者openid

公眾號開發中常見的需求 詳細文件 => 任意門

  1. openid是啥?

    是加密後的微訊號,==每個使用者==對==每個公眾號==的OpenID是唯一的。對於不同公眾號,同一使用者的openid不同。

  2. openid能幹啥?

    公眾號可通過本介面來根據OpenID獲取使用者基本資訊,包括暱稱頭像性別所在城市語言關注時間

    提示:

    ​ 使用者頭像連結http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0 修改連結最後一個數字也是就是0,能獲取不同尺寸的頭像,(有0、46、64、96、132數值可選,0代表640*640正方形頭像)。

    {
    	"subscribe": 1, 			// 使用者是否訂閱該公眾號 0:未關注 1:已關注
      	"openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M", 
       	"nickname": "Band",			// 使用者的暱稱
       	"sex": 1,     				// 性別,1:男性,2:女性,0:未知
       	"language": "zh_CN",    	// 使用者的語言,簡體中文為zh_CN
       	"city": "廣州", 
       	"province": "廣東", 
       	"country": "中國", 
       	"headimgurl": "LINK_URL",	// 使用者頭像圖片連結
      	"subscribe_time": 1382694957,// 關注公眾號時間
      	"unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL", // 
      	"remark": "",				// 公眾號運營者對粉絲的備註
      	"groupid": 0,				// 使用者所在的分組ID(相容舊的使用者分組介面)
      	"tagid_list":[128,2]		// 使用者被打上的標籤ID列表
    }
    複製程式碼
  3. 怎麼拿到openid?

    兩種獲取的方法(相同介面所帶引數不同而已):

    1. 靜默授權 snsapi_base,==體驗較好,獲取到的資料較少==
    2. 使用者授權 snsapi_userinfo,==體驗較差,使用者可能拒絕,獲取到的資料多==

    ​==注意:對於已關注公眾號的使用者,如果使用者從公眾號的會話或者自定義選單進入本公眾號的網頁授權頁,即使是scope為snsapi_userinfo,也是靜默授權,使用者無感知。==

3.3 獲取簽名

==簽名在微信開發過程中扮演了一個非常重要的角色,很容易出錯== 詳細文件 => 任意門

  1. 簽名是啥?

    公眾號用於呼叫微信介面的臨時票據

  2. 怎麼獲取?

    官方文件說明 詳細文件 => 任意門

3.4 支付功能實現

​ 官方詳細文件 => 任意門

4、前端開發示例

4.1 關注快捷入口

​ 微信是反對引導關注的,但是,為了能留住訪客。we have to。使用的連結是公眾號詳情頁面下【歷史訊息】頁面。可以點進頁面,然後右上角【點點點】點選然後複製連結獲取。

關注展示

4.2 介面使用【支付】(WeixinJSBridge)

​ 官方詳細文件 => 任意門

4.2.1 功能開發

==注意事項==

  1. 時間戳為秒數,部分系統取到的值為毫秒級,需要轉換成秒(10位數字)
  2. package 內容是"prepay_id=" 是預訂單id(統一下單任意門
  3. paySignparams 所有引數根據微信簽名規則生成
import { checkWxBridge } from 'utils'

someFunction () {
	get('/wx/fetchSign').then(data => {
      	// data 需要的資料
      	// appId, timeStamp, nonceStr, package, paySign
        const params = Object.assign(data, {signType: 'MD5'})

        checkWxBridge().then(() => {
          	WeixinJSBridge.invoke('getBrandWCPayRequest', params, res => {
            	// res 資料結構 {err_msg: 'get_brand_wcpay_request:ok'}
              	// err_msg 可能的值 ok、fail、cancel
	        })
        })
    })
}
複製程式碼
4.2.2 測試號測試支付

測試號是沒有支付功能,需要用正式的公眾號,而且需要有支付許可權的公眾號(個人公眾號不可支付)。在測試環境使用正式環境的openid去實現支付。

var openIdMap = {
 	'測試公眾號openId': '正式公眾號openId',
 	'正式公眾號openId': '測試公眾號openId',
}

// 測試環境
1. 【前端】購買請求
2. 【後端】使用者購買(測試公眾號openId)
3. 【後端】統一下單用(openIdMap[測試公眾號openId])
4. 【前端】拿到prepay_id,WeixinJSBridge.invoke('getBrandWCPayRequest')支付
5. 【前端】使用者支付請求 => 微信
6. 【後端】收到支付成功或回撥(可非同步或同步),通知前端
7. 【後端】儲存相應資料到(openIdMap[正式公眾號openId])
複製程式碼

4.3 頭像剪下功能(JS-SDK)

CRMWeb 專案使用的 VueJS,使用的是npm相關包 vue-croppa

4.3.1 通過Input選擇
  1. vue-croppa 直接選擇圖片
  2. 輸出裁剪後的圖片
4.3.2 通過weixin-js-sdk選擇
  1. 頁面簽名

    import { queryWxApi } from 'utils' 
    
    mounted () {
    	queryWxApi().then(res => this.wxConfSucc = true)
    }
    複製程式碼
  2. 選擇本地圖片或拍照,獲取到 localId

    wx.chooseImage({
    	count: 1,	 // 可選擇張數
        sizeType: ['original', 'compressed'],
        sourceType: [
        	'album', // 從相簿選
          	'camera',// 拍照
        ],
        success: (res) => {
            let localId = res.localIds[0]
        },
    })
    複製程式碼
  3. 通過 localId 上傳到微信伺服器拿到 serverId(只能儲存3天)

    wx.uploadImage({
        localId: localId,      // 本地ID
        isShowProgressTips: 1, // 是否顯示上傳進度
        success: res => {
          	const serverId = res.serverId
        },
    })
    複製程式碼
  4. 服務端通過 serverId 下載圖片到自己伺服器,並返回圖片連結給前端

    croppa(
        v-model="croppa",
        :width="coroppaStyle.width"
        :height="coroppaStyle.height"
        :disable-click-to-choose="false"
        :show-remove-button="false"
        :initial-position="position"
    )
        img(
        	:src="showImgUrl"
        	slot="initial"
        )
    複製程式碼

5、常見錯誤

呼叫 config 接的時候傳入引數 debug: true 可以開啟 debug 模式,頁面會 alert 出錯誤資訊。以下為常見錯誤及解決方法:

5.1 invalid url domain

當前頁面所在域名與使用的 corpid 沒有繫結(可在該企業號的應用可信域名中配置域名)。

5.2 ==invalid signature==

簽名錯誤,建議按如下順序檢查:

  1. 確認簽名演算法正確,可用 mp.weixin.qq.com/debug/cgi-b… 頁面工具進行校驗。
  2. 確認 config 中 nonceStr, timestamp 與用以簽名中的對應 noncestr, timestamp一致。
  3. 確認 url 是頁面完整的 url (請在當前頁面 alert(location.href.split('#')[0]) 確認),包括 http(s):// 部分,以及'?'後面的 GET 引數部分,但不包括 '#'hash 後面的部分。
  4. 確認 config 中的 appid 與用來獲取 jsapi_ticket 的 corpid 一致。
  5. 確保一定快取 access_token 和 jsapi_ticket。
  6. 可能獲取 access_token 次數超過一天限制的數量。
  7. 使用 pushState 來實現 web app 的頁面會導致簽名失敗,改用 location.href。
  8. 確保你獲取用來簽名的 url 是動態獲取的,動態頁面可參見例項程式碼中 php 的實現方式。如果是 html 的靜態頁面在前端通過 ajax 將 url 傳到後臺簽名,前端需要用js獲取當前頁面除去'#'hash部分的連結(可用 location.href.split('#')[0] 獲取,而且需要==encodeURIComponent==),因為頁面一旦分享,微信客戶端會在你的連結末尾加入其它引數,如果不是動態獲取當前連結,將導致分享後的頁面簽名失敗。

5.3 ==the permission value is offline verifying==

這個錯誤是因為 config 沒有正確執行,或者是呼叫的 JSAPI 沒有傳入 config 的 jsApiList 引數中。建議按如下順序檢查:

  1. 確認 config 正確通過。
  2. 如果是在頁面載入好時就呼叫了 JSAPI,則必須寫在 wx.ready 的回撥中。
  3. 確認 config 的 jsApiList 引數包含了這個 JSAPI。

5.4 permission denied

​ 該企業號沒有許可權使用這個 JSAPI(部分介面需要認證之後才能使用)

5.5 function not exist

​ 當前客戶端版本不支援該介面,請升級到新版體驗。

5.6 6.0.1版本config:ok,6.0.2版本之後不ok

​ 因為6.0.2版本之前沒有做許可權驗證,所以 config 都是ok,但這並不意味著你 config 中的簽名是 ok 的,請在6.0.2檢驗是否生成正確的簽名以保證 config 在高版本中也 ok。

5.7 ==在iOS和Android都無法分享==

​ 請確認企業號已經認證,只有認證的企業號才具有分享相關介面許可權,如果確實已經認證,則要檢查監聽介面是否在wx.ready回撥函式中觸發

5.8 服務上線之後無法獲取jsapi_ticket

​ 因為 access_token 和 jsapi_ticket 必須要在自己的伺服器快取,否則上線後會觸發頻率限制。請確保一定對token 和 ticket 做快取以減少伺服器請求,不僅可以避免觸發頻率限制,還加快你們自己的服務速度。目前為了方便測試提供了1w的獲取量,超過閥值後,服務將不再可用,請確保在服務上線前一定全域性快取 access_toke和 jsapi_ticket,兩者有效期均為7200秒(以返回結果中的 expires_in 為準),否則一旦上線觸發頻率限制,服務將不再可用

5.9 uploadImage傳多圖

​ 目前只支援一次上傳一張,多張圖片需等前一張圖片上傳之後再呼叫該介面。

5.10 對本地選擇的圖片進行預覽

​ chooseImage 介面本身就支援預覽,不需要額外支援。

5.11 出現config:fail錯誤

​ 這是由於傳入的 config 引數不全導致,請確保傳入正確 appId、timestamp、nonceStr、signature 和需要使用的 jsApiList。

5.12 使用pushState來實現web app的頁面會導致簽名失敗

​ 此問題已在Android6.2中修復。在IOS中此問題依然存在。

5.13 Android uploadImage在chooseImage的回撥中有時候會不執行

​ Android6.2 會解決此問題,若需支援低版本可以把呼叫 uploadImage 放在 setTimeout 中延遲 100ms 解決。

5.14 getLocation座標在openLocation有偏差

​ 因為 getLocation 返回的是gps座標,openLocation 開啟的騰訊地圖為火星座標,需要第三方自己做轉換,6.2 版本開始已經支援直接獲取火星座標。

5.15 ==未關注測試號==

​ 通過 snsapi_base 方式獲取未關注公眾號的使用者 openid。測試號是不可以的。

6、附錄

6.1 微信JSAPI列表

[
  	'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',
]
複製程式碼

6.2 WeixinJSBridge 介面列表

[
  	// 型別一
	'hideOptionMenu',       // 隱藏右上角按鈕
  	'showOptionMenu',       // 顯示右上角按鈕
  	'hideToolbar',          // 隱藏底部工具欄
  	'showToolbar',          // 顯示底部工具欄
  	// 型別二
  	'getNetworkType',       // 獲取網路狀態
  	'getBrandWCPayRequest', // 呼叫支付
]

// 型別一
WeixinJSBridge.call('hideToolbar')
// 型別二
WeixinJSBridge.invoke('getBrandWCPayRequest')
複製程式碼

6.3 所有選單項列表

[
  	// 基本類
    'menuItem:exposeArticle' // 舉報
    'menuItem:setFont' // 調整字型
    'menuItem:dayMode' // 日間模式
    'menuItem:nightMode' // 夜間模式
    'menuItem:refresh' // 重新整理
    'menuItem:profile' // 檢視公眾號(已新增)
    'menuItem:addContact' // 檢視公眾號(未新增)
  	// 傳播類
    'menuItem:share:appMessage' // 傳送給朋友
    'menuItem:share:timeline' // 分享到朋友圈
    'menuItem:share:qq' // 分享到QQ
    'menuItem:share:weiboApp' // 分享到Weibo
    'menuItem:favorite' // 收藏
    'menuItem:share:facebook' // 分享到FB
  	// 保護類
    'menuItem:jsDebug' // 除錯
    'menuItem:editTag' // 編輯標籤
    'menuItem:delete' // 刪除
    'menuItem:copyUrl' // 複製連結
    'menuItem:originPage' // 原網頁
    'menuItem:readMode' // 閱讀模式
    'menuItem:openWithQQBrowser' // 在QQ瀏覽器中開啟
    'menuItem:openWithSafari' // 在Safari中開啟
    'menuItem:share:email' // 郵件
    'menuItem:share:brand' // 一些特殊公眾號
]
複製程式碼

相關文章