1. 程式人生 > 實用技巧 >微信公眾號開發經驗總結

微信公眾號開發經驗總結

1. 快捷訪問

1.1 測試公眾號註冊:

http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

1.2 微信公眾號開發指南:

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1445241432

1.3 微信全域性返回碼說明:

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433747234

1.4 微信公眾號介面許可權說明:

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433401084

2 註冊一個公眾號賬戶(也可先用測試公眾號除錯環境)

2.1 賬戶註冊

2.1.1 正確填寫下列資訊

注意:

URL必須以http:// 或者https:// 開頭,分別支援80埠和443埠;

Token必須為英文或者數字,長度為3-32字元;

2.1.2 開發過程中,需要注意介面的呼叫次數,

2.1.3 注意檢查URL和JS介面安全域名,可以保持一致,也可以不一樣,個人建議最好保持一致;

3 測試開發環境

Eg:

微信網頁授權access_token和普通access_token區別

https://blog.csdn.net/benben_2015/article/details/79703508

微信獲取使用者資訊的兩個介面和兩個ACCESS_TOKEN

https://www.cnblogs.com/sxmny/articles/4969119.html

2.1 記錄全域性定量(獲取Token的兩個定量,在微信公眾號-賬戶資訊處可以查詢):

Appid 應用ID

AppSecret 應用密匙

2.2 獲取普通access_token(access_token是公眾號的全域性唯一介面呼叫憑據,公眾號呼叫各介面時都需要使用access_token)

呼叫介面:

URL: https://api.weixin.qq.com/cgi-bin/token

引數: grant_type : “client_credential”--------獲取access_token填寫client_credential Appid : Appid---------第三方使用者唯一憑證

AppSecret : AppSecret-----------第三方使用者唯一憑證金鑰,即appsecret

返回引數: access_token---------獲取到的憑證

expires_in----------憑證有效事件,單位:秒

注:公眾號對此介面的呼叫是有次數限制的,預設是2000,花錢可增加次數,獲取的access_token的有效期預設是2小時,故而,在開發過程中,可將呼叫此介面的access_token設為全域性變數,並將次介面的呼叫方法設為全域性自動呼叫,獨立出來;

Eg(將獲取access_token的介面獨立,並調整為100分鐘自動重新整理):

packagecom.gzh.api.utils;

importjava.util.Map;

importjava.util.Timer;

importjava.util.TimerTask;

importjava.util.concurrent.atomic.AtomicInteger;

importcom.alibaba.fastjson.JSONObject;

importcom.google.common.collect.Maps;

importcom.jfinal.kit.Prop;

importcom.jfinal.kit.PropKit;

importcn.hutool.http.HttpUtil;

/**

* access_token 自動重新整理

*@authorAdministrator

*

*/

publicclassWxToken {

publicstaticStringglobalTokenObject= "";//調取介面的返回值引數(access_token、expires_in)

publicstaticStringglobalToken= "";// access_token

publicstaticAtomicIntegeratomicInteger=newAtomicInteger();//計數器,統計介面呼叫的次數

publicstaticString appid = "**************";

publicstaticString appsecret = "*************";

static{

newTimer().schedule(newTimerTask() {

@Override

publicvoidrun() {

Map<String,Object> paramMap = Maps.newHashMap();

paramMap.put("appid", appid);

paramMap.put("secret", appsecret);

paramMap.put("grant_type", "client_credential");

globalTokenObject= HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token", paramMap);

JSONObject wxToken = JSONObject.parseObject(globalTokenObject);

globalToken= HttpUtil.get("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + wxToken.getString("access_token") + "&type=jsapi");

atomicInteger.incrementAndGet();

}

}, 0, 2*50*60*1000);

}

}

2.3 獲取使用者Code/微信訪問公眾號地址

使用微信開啟如下地址https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

引數:appid---------appid

redirect_uri-------系統路徑,需與微信公中號繫結路徑一致

response_type-------“code”字串

scope------“snsapi_userinfo”字串

state------“1”字串

(注意這個介面中有個引數scope 預設有2個值snsapi_base和snsapi_userinfo,這個介面會根據scope 來生成不同的code並且獲取不同作用的access_token,不管scope傳什麼值都能在得到對應access_token的同時得到open_id, 如果你只需要得到opend_id 那使用snsapi_base引數到此結束了,如果需要獲取使用者的其他資訊比如 暱稱 地址 就要snsapi_userinfo 會彈出授權)

eg:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx97967746637ee7c4&redirect_uri=http://www.HuaLuo.com/gzh/gzh/app.html&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect

直接在微信中訪問,系統中用如下方法接收:

//獲取使用者code --var code = getCode(‘code’);

Function getCode(name){

var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");

var r = window.location.search.substr(1).match(reg);

if (r != null) return unescape(r[2]); return null;

}

即可拿到使用者Code;

2.4 獲取openid

簡單的實現微信獲取openid

https://blog.csdn.net/z880698223/article/details/78485243/

1.呼叫https://open.weixin.qq.com/connect/oauth2/authorize介面獲取到code

《引數2.3》

2.得到code作為一個引數呼叫https://api.weixin.qq.com/sns/oauth2/access_token介面獲取到openid 《參照2.5》

2.5 獲取微信網頁授權access_token

呼叫介面:

URL: https://api.weixin.qq.com/sns/oauth2/access_token

引數:

grant_type : “authorization_code”--------填寫client_credential

Appid : Appid---------第三方使用者唯一憑證

AppSecret : AppSecret-----------第三方使用者唯一憑證金鑰,即appsecret

Code:Code----------標識不同的使用者code 《參照2.3》

返回引數:

Openid

access_token

2.6 獲取微信使用者資訊

EG:

https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

呼叫介面:

URL: https://api.weixin.qq.com/sns/userinfo

引數:

Openid-----《參照2.5》

Lang----“zh_CN”字串

access_token------《參照2》>(注:此access_token網上資料說需要普通access_token,及呼叫《2.2》介面返回的普通access_token,但是實際中發現呼叫《2.5》介面返回的網頁授權access_token也可以拿到使用者資訊,故而實際開發中可換著呼叫除錯,個人建議使用普通access_token,因為開文提出將此引數定義為全域性變數,故而對程式碼一致性維護較方便)

返回引數:

Subscribe--------使用者是否訂閱該公眾號標識,值為0時,代表此使用者沒有關注該公眾號,拉取不到其餘資訊

Openid----------使用者的標識,對當前公眾號唯一

Nickname-----------使用者的暱稱

Sex----------使用者的性別,值為1時是男性,值為2時是女性,值為0時是未知

City----------使用者所在城市

Country-------------使用者所在國家

Province-----------使用者所在省份

Language---------使用者的語言,簡體中文為zh_CN

Headimgurl-----------使用者頭像,最後一個數值代表正方形頭像大小(有0、46、64、96、132數值可選,0代表640*640正方形頭像),使用者沒有頭像時該項為空。若使用者更換頭像,原有頭像URL將失效

subscribe_time-------------使用者關注時間,為時間戳。如果使用者曾多次關注,則取最後關注時間

unionid------------只有在使用者將公眾號繫結到微信開放平臺帳號後,才會出現該欄位

remark-----------公眾號運營者對粉絲的備註,公眾號運營者可在微信公眾平臺使用者管理介面對粉絲新增備註

groupid ------------- 使用者所在的分組ID(相容舊的使用者分組介面)

tagid_list ------------使用者被打上的標籤ID列表

subscribe_scene----------- 返回使用者關注的渠道來源,ADD_SCENE_SEARCH 公眾號搜尋,ADD_SCENE_ACCOUNT_MIGRATION 公眾號遷移,ADD_SCENE_PROFILE_CARD 名片分享,ADD_SCENE_QR_CODE 掃描二維碼,ADD_SCENEPROFILE LINK 圖文頁內名稱點選,ADD_SCENE_PROFILE_ITEM 圖文頁右上角選單,ADD_SCENE_PAID 支付後關注,ADD_SCENE_OTHERS 其他

qr_scene ------------二維碼掃碼場景(開發者自定義)

qr_scene_str------------ 二維碼掃碼場景描述(開發者自定義)

2.7 呼叫微信JS-SDK,獲取jsapi_ticket

微信公眾平臺jsapi開發教程(1)獲取:

https://blog.csdn.net/linfanhehe/article/details/52354848

生成簽名之前必須先了解一下jsapi_ticket,jsapi_ticket是公眾號用於呼叫微信JS介面的臨時票據。正常情況下,jsapi_ticket的有效期為7200秒,通過access_token來獲取。由於獲取jsapi_ticket的api呼叫次數非常有限,頻繁重新整理jsapi_ticket會導致api呼叫受限,影響自身業務,開發者必須在自己的服務全域性快取jsapi_ticket 。《參照2.2》和我們獲取普通access_token類似,由於有訪問次數的限制,我們將次引數調為全域性自動觸發;

介面:

URL:https://api.weixin.qq.com/cgi-bin/ticket/getticket

引數: access_token-----《參照2.2》

Type----“jsapi”字串

返回引數:ticket------------呼叫微信jsapi的憑證票

2.8 初始化微信config配置

所有需要使用JS-SDK的頁面必須先注入配置資訊,否則將無法呼叫(同一個url僅需呼叫一次,對於變化url的SPA的web app可在每次url變化時進行呼叫,目前Android微信客戶端不支援pushState的H5新特性,所以使用pushState來實現web app的頁面會導致簽名失敗,此問題會在Android6.2中修復)。

微信調取音訊、視訊、分享等介面前,需要先初始化驗證

JS:

app.ajax('/wxInitConfig.action',{'url':location.href},function(configDate){

wx.config({

debug:true, // 開啟除錯模式,呼叫的所有api的返回值會在客戶端alert出來,若要檢視傳入的引數,可以在pc端開啟,引數資訊會通過log打出,僅在pc端時才會列印。

appId: configDate.appid, // 必填,公眾號的唯一標識

timestamp: configDate.timestamp, // 必填,生成簽名的時間戳

nonceStr: configDate.nonceStr, // 必填,生成簽名的隨機串

signature: configDate.signature,// 必填,簽名,見附錄1

jsApiList: ['onMenuShareQQ', 'onMenuShareWeibo', 'onMenuShareTimeline','onMenuShareAppMessage','startRecord','stopRecord','onVoiceRecordEnd','playVoice','stopVoice','onVoicePlayEnd','uploadVoice','onMenuShareQZone'] // 必填,需要使用的JS介面列表,所有JS介面列表見附錄2 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115

});

});

ACTION:

publicvoidwxInitConfig(){

JSONObject wxToken = JSONObject.parseObject(WxToken.globalToken);《參照2.2程式碼,獲取全域性ticket》

String jsapi_ticket = wxToken.getString("ticket");

//此處如果沒有正常獲取到jsapi_ticket,請檢視返回碼,在微信公眾號中查詢錯誤資訊

//前臺傳過來的當前URL訪問路徑

String url = Dto.getParam().getString("url");

//演算法生成時間戳、簽名等資訊,見下列程式碼,直接附原始碼,不多說

Map<String, String> ret = Sign.sign(jsapi_ticket, url);

for(@SuppressWarnings("rawtypes") Map.Entry entry : ret.entrySet()) {

System.err.println(entry.getKey().toString() + " : " + entry.getValue());

Dto.getResult().set(entry.getKey().toString(), entry.getValue());

}

Dto.getResult().set("appid",wxProp.get("appid"));

this.renderJson(Dto.getResult().getData());

}

///////////////演算法生成時間戳、簽名等資訊/////////////////////////////

////過程中可能呼叫MessageDigest包,請自行Baidu下載////////////////

packagecom.gzh.api.utils;

importjava.util.UUID;

importjava.util.Map;

importjava.util.HashMap;

importjava.util.Formatter;

importjava.security.MessageDigest;

importjava.security.NoSuchAlgorithmException;

importjava.io.UnsupportedEncodingException;

publicclassSign {

publicstaticvoidmain(String[] args) {

String jsapi_ticket = "jsapi_ticket";

// 注意 URL 一定要動態獲取,不能hardcode

String url = "http://example.com";

Map<String, String> ret =sign(jsapi_ticket, url);

for(Map.Entryentry : ret.entrySet()) {

System.out.println(entry.getKey() + ", " + entry.getValue());

}

};

publicstaticMap<String, String> sign(String jsapi_ticket, String url) {

Map<String, String> ret =newHashMap<String, String>();

String nonce_str =create_nonce_str();

String timestamp =create_timestamp();

String string1;

String signature = "";

//注意這裡引數名必須全部小寫,且必須有序

string1 = "jsapi_ticket=" + jsapi_ticket +

"&noncestr=" + nonce_str +

"&timestamp=" + timestamp +

"&url=" + url;

System.out.println(string1);

try

{

MessageDigest crypt = MessageDigest.getInstance("SHA-1");

crypt.reset();

crypt.update(string1.getBytes("UTF-8"));

signature =byteToHex(crypt.digest());

}

catch(NoSuchAlgorithmException e)

{

e.printStackTrace();

}

catch(UnsupportedEncodingException e)

{

e.printStackTrace();

}

ret.put("url", url);

ret.put("jsapi_ticket", jsapi_ticket);

ret.put("nonceStr", nonce_str);

ret.put("timestamp", timestamp);

ret.put("signature", signature);

returnret;

}

privatestaticString byteToHex(finalbyte[] hash) {

Formatter formatter =newFormatter();

for(byteb : hash)

{

formatter.format("%02x", b);

}

String result = formatter.toString();

formatter.close();

returnresult;

}

privatestaticString create_nonce_str() {

returnUUID.randomUUID().toString();

}

privatestaticString create_timestamp() {

returnLong.toString(System.currentTimeMillis() / 1000);

}

}

2.9 呼叫微信JS-SDK例項(以錄音為例);

參考微信開發說明文件

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115

/* 開啟錄音*/

wx.startRecord({

success:function() {

alert(“介面呼叫成功”);

},

Fail:function(){

alert(“介面呼叫失敗”);//可以檢查access_token許可權或者彈出除錯,具體問題具體分析《參照2.8》,開啟除錯模式

},

Cancel:function(){

alert(“使用者拒絕提供錄音許可權”);

}

});

/* 停止錄音*/

wx.stopRecord({


success: function (res) {


var localId = res.localId;


}
});

/* 監聽錄音自動停止(由於微信有錄音時限限制,一次錄音最大時長為1分鐘,若超過此時=時限未停止錄音,則執行此介面方法)*/

wx.onVoiceRecordEnd({
// 錄音時間超過一分鐘沒有停止的時候會執行 complete 回撥
complete: function (res) {
var localId = res.localId;
}
});

注:我在此處有一個錄音時長大於一分鐘的問題,目前沒有好的解決方案,樓主在此處的解決方案是:當執行上述大於一分鐘回撥時,重新開啟錄音介面,直至手動關閉錄音;將幾個錄音的localId存起來,一併發至後臺獲取錄音檔案,合成一份音訊檔案,合成程式碼網上找,但這個方法有一個Bug,在重新開啟錄音,即轉換的這個時間段,有大概1~2秒的錄音失效期;

/* 播放錄音 */
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,參考文件 .目前多媒體檔案下載介面的頻率限制為10000次/天,如需要調高頻率,請登入微信公眾平臺,在開發 - 介面許可權的列表中,申請提高臨時上限。

根據serverId獲取微信伺服器上的錄音檔案;
呼叫介面:
URL: http://file.api.weixin.qq.com/cgi-bin/media/get
引數: access_token------參照《2.2》
media_id----------serverId
返回引數是檔案流,需自行解析;附例項;
Eg:

/* (non-Javadoc)

* @see com.gzh.api.service.ReciteContentService#saveRecite(com.sdp.core.dt.DtoParam)

*/

@Override

publicDtoResult saveRecite(DtoParam param)throwsException{

/** 引數接收*/

String contentName = Dto.getParam().getString("contentName");//詩詞名稱

String contentId = Dto.getParam().getString("contentId");//詩詞ID

String fansId = Dto.getParam().getString("fansId");//粉絲ID

String serverId = Dto.getParam().getString("serverId");

Record isFans = api.findFirst("select * from gzh_personalcenter where loginid = ?", fansId);

if(isFans !=null&& isFans.get("department") !=null&& !isFans.get("department").equals("")){

String[] serverIds = Str.isNotAnyEmpty(serverId)?serverId.split(","):null;/* 當傳過來的serverId有效時執行*/

if(serverIds !=null&& serverIds.length > 0){

System.err.println("正在上傳錄音檔案...");

longtime1 = System.currentTimeMillis();

/* 刪除舊錄音檔案及資料*/

List<Record> deleteList = api.find("select * from gzh_recitecontent where fansid = ? and contentid = ?", fansId, contentId);

for(Record re : deleteList){

File file =newFile(fileRoot + re.getStr("path"));

if(file.exists()) file.delete();

api.deleteById("gzh_recitecontent", re.getStr("id"));

}

//String token = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token", paramMap);

JSONObject wxToken = JSONObject.parseObject(WxToken.globalTokenObject);

List<String> tempRecites =newArrayList<>();/*amr檔案集合*/

List<String> newRecites =newArrayList<>();/* mp3檔案集合*/

/* 進行臨時目錄建立*/

String tempResPath = fileRoot + tempPath;

if(!newFile(tempResPath).exists())newFile(tempResPath).mkdirs();

/* 進行錄音檔案目錄建立*/

String realResPath = fileRoot + fileStore + recitePath + "/" + contentId + "/" + fansId;//檔案實際儲存目錄

if(!newFile(realResPath).exists())newFile(realResPath).mkdirs();//先進行目錄建立操作

intsize = 0;/* 記錄所有檔案輸入流大小*/

/* 定義輸出流寫入引數*/

FileOutputStream fileOutputStream =null;

InputStream inputStream =null;

String fileName = ID.get();/* 檔名稱*/

/* 將檔案分批取出並儲存為MP3檔案*/

for(inti = 0; i < serverIds.length; i++){

try{

/* 從微信伺服器獲取錄音檔案,格式為amr*/

String url = "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token="

+ wxToken.getString("access_token") + "&media_id=" + serverIds[i];

URL urlGet =newURL(url);

HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();

http.setRequestMethod("GET"); // 必須是get方式請求

http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

http.setDoOutput(true);

http.setDoInput(true);

// System.setProperty("sun.net.client.defaultConnectTimeout", "3000");// 連線超時30秒

// System.setProperty("sun.net.client.defaultReadTimeout", "3000"); // 讀取超時30秒

http.connect();

// 獲取檔案轉化為byte流

inputStream = http.getInputStream();

size += inputStream.available();

String tempFileName = tempResPath + "/" + fileName + (i+1) + ".amr";

String recitFileName = tempResPath + "/" + fileName + (i+1) + ".mp3";

if(serverIds.length == 1){

recitFileName = realResPath + "/" + fileName + ".mp3";

}

tempRecites.add(tempFileName);

newRecites.add(recitFileName);

byte[] data =newbyte[1024 * 1024];

intlen = 0;

fileOutputStream =newFileOutputStream(tempFileName);

while((len = inputStream.read(data)) != -1) {

fileOutputStream.write(data, 0, len);

}

/* 關閉流資源*/

if(inputStream !=null) inputStream.close();

if(fileOutputStream !=null) fileOutputStream.close();

/*amr格式轉換成mp3格式*/

File source =newFile(tempFileName);

File target =newFile(recitFileName);

AudioAttributes audio =newAudioAttributes();

Encoder encoder =newEncoder();

audio.setCodec("libmp3lame");

EncodingAttributes attrs =newEncodingAttributes();

attrs.setFormat("mp3");

attrs.setAudioAttributes(audio);

encoder.encode(source, target, attrs);/* 執行轉換*/

}catch(Exception e) {

System.err.println(e.getMessage());

}

}

System.err.println("輸入流大小:" + size);

File file =newFile(realResPath + "/" + fileName + ".mp3");

if(newRecites.size() > 1){

try{

combine(file.getPath(), newRecites);

}catch(Exception e) {

System.err.println(e.getMessage());

}

}

System.err.println("音訊大小:" + file.length()/1024 + "KB");

//資訊儲存資料庫表

Record record =newRecord();

record.set("id", ID.get());

record.set("contentname", contentName);//詩詞名稱

record.set("contentid", contentId);//詩詞ID

record.set("fansid", fansId);//粉絲ID

record.set("path", fileStore + recitePath + "/" + contentId + "/" + fansId + "/" + fileName + ".mp3");//吟誦錄音路徑

record.set("uploaddate",newTimestamp(newjava.util.Date().getTime()));//吟誦時間

api.save(getTableName(), record);

longtime2 = System.currentTimeMillis();

System.out.println("上傳時間:" + (time2 - time1)/1000 + "s");

/* 刪除臨時檔案*/

for(inti = 0; i < tempRecites.size(); i++){

File tempFile =newFile(tempRecites.get(i));

if(tempFile.exists()){

System.out.println("刪除AMR臨時檔案結果[" + tempRecites.get(i) + ":" + tempFile.delete() + "]");

}

if(tempRecites.size() > 1){

tempFile =newFile(newRecites.get(i));

if(tempFile.exists()){

System.out.println("刪除MP3臨時檔案結果[" + newRecites.get(i) + ":" + tempFile.delete() + "]");

}

}

}

}

}else{

Dto.getResult().setSuccessStatus("請完善個人資訊");

}

returnDto.getResult();

}

/*將多個MP3檔案合併*/

privatestaticbooleancombine(String outFile, List<String> inFiles)throwsException{

File[] files =newFile[inFiles.size()];

for(inti = 0; i < files.length; i++){

files[i] =newFile(inFiles.get(i));

}

FileInputStream fis =null;

FileOutputStream fos =newFileOutputStream(outFile,true);//合併其實就是檔案的續寫,寫成true

for(inti = 0; i < files.length; i++){

fis =newFileInputStream(files[i]);

intlen = 0;

for(byte[] buf =newbyte[1024 * 1024]; (len = fis.read(buf)) != -1;){

fos.write(buf, 0, len);

}

fis.close();

}

fos.close();

returntrue;

 }

/* 下載錄音介面 */
wx.downloadVoice({
serverId: '', // 需要下載的音訊的伺服器端ID,由uploadVoice介面獲得
isShowProgressTips: 1, // 預設為1,顯示進度提示
success: function (res) {
var localId = res.localId; // 返回音訊的本地ID
}
});
/* 此下載介面只是返回微信伺服器端的檔案localId ,然後呼叫播放介面讀取localId ,但是微信伺服器端的錄音檔案只保留3天,請知曉!*/