微信公眾號開發(fā)
?前面做過 HG 項(xiàng)目的微信端瓦戚,里面用到微信掃碼团驱、支付摸吠、圖片選取、拍照嚎花、分享的功能寸痢。用到 weixin-js-sdk 和 WeixinJSBridge。
?前端開發(fā)以Vue代碼為例紊选,后端以NodeJS為例啼止。后端開發(fā)還需詳細(xì)閱讀開發(fā)者文檔。
用于開發(fā)和測試環(huán)境献烦,產(chǎn)品經(jīng)理需準(zhǔn)備
步驟:
登錄【公眾號平臺】 → 側(cè)邊欄開發(fā)選擇【開發(fā)者工具】→ 進(jìn)入【公眾平臺測試帳號】→ 掃碼確認(rèn) → 成功
注意:測試號的配置都在此頁面
如圖:
保存?appID?、appSecret?和 測試號二維碼
注意:及時保存卖词,以免需要的時候找不到
修改授權(quán)回調(diào)頁面地址
注意:用戶確認(rèn)授權(quán)后重定向的地址
修改接口配置信息巩那、JS接口安全域名
注意:調(diào)用?weixin-js-sdk?功能或調(diào)用?WeixinJSBridge?頁面所在域名
修改JS安全域名:
?公眾號主頁側(cè)邊欄設(shè)置選擇【公眾號設(shè)置】→ 選擇上面第二個tab【功能設(shè)置】第三項(xiàng)
?
如果有支付功能需要配置授權(quán)目錄
注意:這個需要在商戶平臺修改,不是在公眾平臺
?
舉例:
?現(xiàn)在需要在?http://www.behuntergatherer.com/pay/payment?頁面進(jìn)行支付姚炕,就需要設(shè)置一個授權(quán)目錄為?http://www.behuntergatherer.com/pay/?歧焦。在支付頁面路由的前一個目錄优妙。
?
建議:
?統(tǒng)一一個頁面進(jìn)行支付哩俭。比如?http://www.behuntergatherer.com/pay/payment?是支付頁面石窑,我在?http://www.behuntergatherer.com/vip/order?頁面需要支付窿春,則生成好訂單后跳轉(zhuǎn)?http://www.behuntergatherer.com/pay/payment?頁面進(jìn)行支付得问。
?
下載安裝【W(wǎng)eb開發(fā)者工具】
注意:開發(fā)同學(xué)準(zhǔn)備砍聊,下載地址
目的:
頁面開發(fā)兼容性页藻、布局和瀏覽器表現(xiàn)不一致
微信接口調(diào)試桨嫁、調(diào)用接口有日志
注意事項(xiàng):
支付功能不能在本地測試,不能使用測試公眾號測試
開發(fā)工具可能存在未知 bug份帐,請保持更新
##2瞧甩、簡單config封裝
示例?用手機(jī)端微信打開體驗(yàn)
微信JS接口?微信接口詳細(xì)文檔
微信JS接口調(diào)試工具?可以在線上測試接口返回的數(shù)據(jù)結(jié)構(gòu)
這些接口需要配置以后才能調(diào)用
API可以配需要的幾個,亦可以全部
一次配置只對當(dāng)前頁面有效弥鹦,跳轉(zhuǎn)頁面需要重新配置
注意:需要調(diào)用微信JSAPI接口的頁面肚逸,不能使用h5 pushState進(jìn)行跳轉(zhuǎn),會造成無效的簽名問題彬坏。請使用location.href朦促。
// utils.js//微信配置接口通用方法constALL_API_LIST = ['scanQRCode','...']exportfunctionconfigWxApi(jsApiList = ALL_API_LIST){returnnewPromise((resolve, reject) =>{//使用當(dāng)前的href獲取簽名,后端返回配置接口所需要的參數(shù)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),//秒數(shù)栓始,不是毫秒nonceStr: data.nonceStr,signature: data.signature,jsApiList,})wx.error(function(res){Toast('調(diào)用微信jsapi返回的狀態(tài):'+ res.errMsg)reject()})wx.ready(resolve)})})}
// utils.jsexportfunctioncheckWxBridge(){returnnewPromise((resolve, reject) =>{if(typeofWeixinJSBridge =="undefined") {if(document.addEventListener) {document.addEventListener('WeixinJSBridgeReady', resolve,false)}elseif(document.attachEvent){document.attachEvent('WeixinJSBridgeReady', resolve)document.attachEvent('onWeixinJSBridgeReady', resolve)}}else{resolve()}})}
注意事項(xiàng):
所有調(diào)用 JS-SDK 的接口都必須 queryWxApi
都必須處理錯誤情況务冕,增加用戶體驗(yàn)
//采用Promise封裝有兩種使用方法,統(tǒng)一選擇一個// app.jsimport{ configWxApi }from'utils'// Promise寫法mounted () {? configWxApi()? ? .then(res=>{//配置完成wx.chooseImage()? ? })? ? .catch(err=>{//配置失敗})}//同步函數(shù)寫法asyncmounted () {try{constconfigResult =awaitqueryWxApi(['scanQRCode'])//成功處理}catch(err) {//錯誤處理}}
注意事項(xiàng):
所有調(diào)用JS-SDK的的接口都必須?checkWxBridge
都必須處理錯誤情況幻赚,增加用戶體驗(yàn)
// someComponent.jsimport{ checkWxBridge }from'utils'checkWxBridge().then(()=>{ WeixinJSBridge.call('hideOptionMenu')? WeixinJSBridge.invoke('getBrandWCPayRequest')})
?后端一兩個常見例子說明
?這是微信開發(fā)中最基礎(chǔ)的東西,詳情見官方文檔?詳細(xì)文檔 =>?任意門
這是什么東西落恼?
access_token是公眾號的全局唯一接口調(diào)用憑據(jù)箩退,公眾號調(diào)用各接口時都需使用access_token
有什么屬性?
access_token的存儲至少要保留512個字符空間
access_token的有效期目前為2個小時(7200秒)
重復(fù)獲取將導(dǎo)致上次獲取的access_token失效
怎么獲燃亚戴涝?
?調(diào)用接口時,請登錄“微信公眾平臺-開發(fā)-基本配置”提前將服務(wù)器IP地址添加到IP白名單中钻蔑,點(diǎn)擊查看設(shè)置方法啥刻,否則將無法調(diào)用成功。
https請求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
參數(shù)說明
參數(shù)是否必須說明
grant_type是獲取access_token填寫client_credential
appid是第三方用戶唯一憑證
secret是第三方用戶唯一憑證密鑰咪笑,即appsecret
返回說明
正常情況下可帽,微信會返回下述JSON數(shù)據(jù)包給公眾號:
{"access_token":"ACCESS_TOKEN","expires_in":7200}
開發(fā)建議:統(tǒng)一獲取和刷新Access_token,不應(yīng)該各自去刷新窗怒,否則容易造成沖突映跟,導(dǎo)致access_token覆蓋而影響業(yè)務(wù)钝满。
// accessToken.jsmodule.exports = {getToken:function(callback){//判斷token是否過期,如果過期就重取申窘,未過期就返回vartokenPath = path.join(__dirname,'./token.json')vartoken =JSON.parse(fs.readFileSync(tokenPath))varcurrTime = utils.getTimeStamp().secondvarisExpired = currTime > token.timestampif(isExpired) fetchToken(callback)elsecallback(token.value)},updateToken:function(callback){fetchToken(callback)},}functionfetchToken(callback){varconfigPath = path.join(__dirname,'./config.json')varconfig =JSON.parse(fs.readFileSync(configPath))varparam = {grant_type:'client_credential',appid: config.appId,secret: config.secret,}varparamStr = utils.obj2Params(param)varurl =`https://api.weixin.qq.com/cgi-bin/token?${paramStr}`request(url,function(err, res, body){//記錄獲取token時間弯蚜,兩小時后過期重取try{varresReult =JSON.parse(body);varnewToken = {value: resReult.access_token,timestamp: utils.getTimeStamp().second +7200,? ? ? ? ? ? }varfilePath = path.join(__dirname,'token.json')? ? ? ? ? ? fs.writeFileSync(filePath,JSON.stringify(newToken))? ? ? ? ? ? callback(newToken.value)? ? ? ? }catch(e) {console.log('獲取token出錯,', e)? ? ? ? ? callback()? ? ? ? }})}// somejs.jsimport{ getToken }from'accessToken'getToken(function(token){//其他業(yè)務(wù)})
?公眾號開發(fā)中常見的需求?詳細(xì)文檔 =>?任意門
openid是啥剃法?
是加密后的微信號碎捺,每個用戶對每個公眾號的OpenID是唯一的。對于不同公眾號贷洲,同一用戶的openid不同收厨。
openid能干啥?
公眾號可通過本接口來根據(jù)OpenID獲取用戶基本信息优构,包括昵稱诵叁、頭像、性別钦椭、所在城市拧额、語言和關(guān)注時間
提示:
?用戶頭像鏈接http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0?修改鏈接最后一個數(shù)字也是就是0,能獲取不同尺寸的頭像彪腔,(有0侥锦、46、64德挣、96恭垦、132數(shù)值可選,0代表640*640正方形頭像)格嗅。
{"subscribe": 1, //用戶是否訂閱該公眾號0:未關(guān)注1:已關(guān)注"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,//關(guān)注公眾號時間"unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL", //? "remark": "",//公眾號運(yùn)營者對粉絲的備注"groupid": 0,//用戶所在的分組ID(兼容舊的用戶分組接口)"tagid_list":[128,2]//用戶被打上的標(biāo)簽ID列表}
怎么拿到openid?
兩種獲取的方法(相同接口所帶參數(shù)不同而已):
靜默授權(quán)?snsapi_base懂扼,體驗(yàn)較好禁荸,獲取到的數(shù)據(jù)較少
用戶授權(quán)?snsapi_userinfo右蒲,體驗(yàn)較差阀湿,用戶可能拒絕,獲取到的數(shù)據(jù)多
?注意:對于已關(guān)注公眾號的用戶瑰妄,如果用戶從公眾號的會話或者自定義菜單進(jìn)入本公眾號的網(wǎng)頁授權(quán)頁陷嘴,即使是scope為snsapi_userinfo,也是靜默授權(quán)间坐,用戶無感知灾挨。
?
簽名在微信開發(fā)過程中扮演了一個非常重要的角色邑退,很容易出錯?詳細(xì)文檔 =>?任意門
簽名是啥?
公眾號用于調(diào)用微信接口的臨時票據(jù)
怎么獲壤统巍地技?
官方文檔說明 詳細(xì)文檔 =>?任意門
?官方詳細(xì)文檔 =>?任意門
? 微信是反對引導(dǎo)關(guān)注的秒拔,但是莫矗,為了能留住訪客。we have to砂缩。使用的鏈接是公眾號詳情頁面下【歷史消息】頁面作谚。可以點(diǎn)進(jìn)頁面庵芭,然后右上角【點(diǎn)點(diǎn)點(diǎn)】點(diǎn)擊然后復(fù)制鏈接獲取妹懒。
?官方詳細(xì)文檔 =>?任意門
注意事項(xiàng)
時間戳為秒數(shù),部分系統(tǒng)取到的值為毫秒級双吆,需要轉(zhuǎn)換成秒(10位數(shù)字)
package?內(nèi)容是"prepay_id=" 是預(yù)訂單id(統(tǒng)一下單任意門)
paySign?是?params?所有參數(shù)根據(jù)微信簽名規(guī)則生成
import{ checkWxBridge }from'utils'someFunction () {get('/wx/fetchSign').then(data=>{// data需要的數(shù)據(jù)// appId, timeStamp, nonceStr, package, paySignconstparams =Object.assign(data, {signType:'MD5'})? ? ? ? checkWxBridge().then(()=>{? ? ? ? ? WeixinJSBridge.invoke('getBrandWCPayRequest', params, res => {// res數(shù)據(jù)結(jié)構(gòu){err_msg: 'get_brand_wcpay_request:ok'}// err_msg可能的值ok眨唬、fail、cancel})? ? ? ? })? ? })}
測試號是沒有支付功能好乐,需要用正式的公眾號单绑,而且需要有支付權(quán)限的公眾號(個人公眾號不可支付)。在測試環(huán)境使用正式環(huán)境的openid去實(shí)現(xiàn)支付曹宴。
varopenIdMap = {'測試公眾號openId':'正式公眾號openId','正式公眾號openId':'測試公眾號openId',}//測試環(huán)境1.【前端】購買請求2.【后端】用戶購買(測試公眾號openId)3.【后端】統(tǒng)一下單用(openIdMap[測試公眾號openId])4.【前端】拿到prepay_id搂橙,WeixinJSBridge.invoke('getBrandWCPayRequest')支付5.【前端】用戶支付請求=>微信6.【后端】收到支付成功或回調(diào)(可異步或同步),通知前端7.【后端】保存相應(yīng)數(shù)據(jù)到(openIdMap[正式公眾號openId])
CRMWeb 項(xiàng)目使用的 VueJS笛坦,使用的是npm相關(guān)包 vue-croppa
vue-croppa 直接選擇圖片
輸出裁剪后的圖片
頁面簽名
import{ queryWxApi }from'utils'mounted () {queryWxApi().then(res=>this.wxConfSucc =true)}
選擇本地圖片或拍照区转,獲取到 localId
wx.chooseImage({count:1,//可選擇張數(shù)sizeType: ['original','compressed'],sourceType: ['album',//從相冊選'camera',//拍照],success:(res) =>{letlocalId = res.localIds[0]? ? },})
通過 localId 上傳到微信服務(wù)器拿到 serverId(只能保存3天)
wx.uploadImage({localId: localId,//本地IDisShowProgressTips:1,//是否顯示上傳進(jìn)度success:res=>{constserverId = res.serverId? ? },})
服務(wù)端通過 serverId 下載圖片到自己服務(wù)器,并返回圖片鏈接給前端
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")
?調(diào)用 config 接的時候傳入?yún)?shù) debug: true 可以開啟 debug 模式废离,頁面會 alert 出錯誤信息。以下為常見錯誤及解決方法:
?當(dāng)前頁面所在域名與使用的 corpid 沒有綁定(可在該企業(yè)號的應(yīng)用可信域名中配置域名)礁芦。
?簽名錯誤蜻韭,建議按如下順序檢查:
確認(rèn)簽名算法正確,可用?http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign?頁面工具進(jìn)行校驗(yàn)柿扣。
確認(rèn) config 中 nonceStr, timestamp 與用以簽名中的對應(yīng) noncestr, timestamp一致肖方。
確認(rèn) url 是頁面完整的 url (請?jiān)诋?dāng)前頁面 alert(location.href.split('#')[0]) 確認(rèn)),包括 http(s):// 部分未状,以及'俯画?'后面的 GET 參數(shù)部分,但不包括 '#'hash 后面的部分。
確認(rèn) config 中的 appid 與用來獲取 jsapi_ticket 的 corpid 一致司草。
確保一定緩存 access_token 和 jsapi_ticket艰垂。
可能獲取 access_token 次數(shù)超過一天限制的數(shù)量泡仗。
使用 pushState 來實(shí)現(xiàn) web app 的頁面會導(dǎo)致簽名失敗,改用 location.href猜憎。
確保你獲取用來簽名的 url 是動態(tài)獲取的娩怎,動態(tài)頁面可參見實(shí)例代碼中 php 的實(shí)現(xiàn)方式。如果是 html 的靜態(tài)頁面在前端通過 ajax 將 url 傳到后臺簽名胰柑,前端需要用js獲取當(dāng)前頁面除去'#'hash部分的鏈接(可用 location.href.split('#')[0] 獲取,而且需要encodeURIComponent)峦树,因?yàn)轫撁嬉坏┓窒恚⑿趴蛻舳藭谀愕逆溄幽┪布尤肫渌鼌?shù)旦事,如果不是動態(tài)獲取當(dāng)前鏈接魁巩,將導(dǎo)致分享后的頁面簽名失敗。
5.3?the permission value is offline verifying
?這個錯誤是因?yàn)?config 沒有正確執(zhí)行姐浮,或者是調(diào)用的 JSAPI 沒有傳入 config 的 jsApiList 參數(shù)中谷遂。建議按如下順序檢查:
確認(rèn) config 正確通過。
如果是在頁面加載好時就調(diào)用了 JSAPI卖鲤,則必須寫在 wx.ready 的回調(diào)中肾扰。
確認(rèn) config 的 jsApiList 參數(shù)包含了這個 JSAPI。
? 該企業(yè)號沒有權(quán)限使用這個 JSAPI(部分接口需要認(rèn)證之后才能使用)
? 當(dāng)前客戶端版本不支持該接口蛋逾,請升級到新版體驗(yàn)集晚。
5.6 6.0.1版本config:ok,6.0.2版本之后不ok
? 因?yàn)?.0.2版本之前沒有做權(quán)限驗(yàn)證区匣,所以 config 都是ok偷拔,但這并不意味著你 config 中的簽名是 ok 的,請?jiān)?.0.2檢驗(yàn)是否生成正確的簽名以保證 config 在高版本中也 ok亏钩。
?請確認(rèn)企業(yè)號已經(jīng)認(rèn)證莲绰,只有認(rèn)證的企業(yè)號才具有分享相關(guān)接口權(quán)限,如果確實(shí)已經(jīng)認(rèn)證姑丑,則要檢查監(jiān)聽接口是否在wx.ready回調(diào)函數(shù)中觸發(fā))
5.8 服務(wù)上線之后無法獲取jsapi_ticket
?因?yàn)?access_token 和 jsapi_ticket 必須要在自己的服務(wù)器緩存蛤签,否則上線后會觸發(fā)頻率限制。請確保一定對token 和 ticket 做緩存以減少服務(wù)器請求栅哀,不僅可以避免觸發(fā)頻率限制震肮,還加快你們自己的服務(wù)速度。目前為了方便測試提供了1w的獲取量留拾,超過閥值后戳晌,服務(wù)將不再可用,請確保在服務(wù)上線前一定全局緩存 access_toke和 jsapi_ticket间驮,兩者有效期均為7200秒(以返回結(jié)果中的 expires_in 為準(zhǔn))躬厌,否則一旦上線觸發(fā)頻率限制,服務(wù)將不再可用竞帽。
? 目前只支持一次上傳一張扛施,多張圖片需等前一張圖片上傳之后再調(diào)用該接口。
? chooseImage 接口本身就支持預(yù)覽屹篓,不需要額外支持疙渣。
? 這是由于傳入的 config 參數(shù)不全導(dǎo)致,請確保傳入正確 appId堆巧、timestamp妄荔、nonceStr、signature 和需要使用的 jsApiList谍肤。
5.12 使用pushState來實(shí)現(xiàn)web app的頁面會導(dǎo)致簽名失敗
? 此問題已在Android6.2中修復(fù)啦租。在IOS中此問題依然存在。
5.13 Android uploadImage在chooseImage的回調(diào)中有時候會不執(zhí)行
? Android6.2 會解決此問題荒揣,若需支持低版本可以把調(diào)用 uploadImage 放在 setTimeout 中延遲 100ms 解決篷角。
5.14 getLocation坐標(biāo)在openLocation有偏差
? 因?yàn)?getLocation 返回的是gps坐標(biāo),openLocation 打開的騰訊地圖為火星坐標(biāo)系任,需要第三方自己做轉(zhuǎn)換恳蹲,6.2 版本開始已經(jīng)支持直接獲取火星坐標(biāo)。
? 通過 snsapi_base 方式獲取未關(guān)注公眾號的用戶 openid俩滥。測試號是不可以的嘉蕾。
['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',]
[//類型一'hideOptionMenu',//隱藏右上角按鈕'showOptionMenu',//顯示右上角按鈕'hideToolbar',//隱藏底部工具欄'showToolbar',//顯示底部工具欄//類型二'getNetworkType',//獲取網(wǎng)絡(luò)狀態(tài)'getBrandWCPayRequest',//調(diào)用支付]//類型一WeixinJSBridge.call('hideToolbar')//類型二WeixinJSBridge.invoke('getBrandWCPayRequest')
[//基本類'menuItem:exposeArticle'//舉報(bào)'menuItem:setFont'//調(diào)整字體'menuItem:dayMode'//日間模式'menuItem:nightMode'//夜間模式'menuItem:refresh'//刷新'menuItem:profile'//查看公眾號(已添加)'menuItem:addContact'//查看公眾號(未添加)//傳播類'menuItem:share:appMessage'//發(fā)送給朋友'menuItem:share:timeline'//分享到朋友圈'menuItem:share:qq'//分享到QQ'menuItem:share:weiboApp'//分享到Weibo'menuItem:favorite'//收藏'menuItem:share:facebook'//分享到FB//保護(hù)類'menuItem:jsDebug'//調(diào)試'menuItem:editTag'//編輯標(biāo)簽'menuItem:delete'//刪除'menuItem:copyUrl'//復(fù)制鏈接'menuItem:originPage'//原網(wǎng)頁'menuItem:readMode'//閱讀模式'menuItem:openWithQQBrowser'//在QQ瀏覽器中打開'menuItem:openWithSafari'//在Safari中打開'menuItem:share:email'//郵件'menuItem:share:brand'//一些特殊公眾號]