微信公眾平臺(tái)開發(fā)教程 php微信公眾號(hào)開發(fā),開發(fā)什么?備忘。。微信公眾平臺(tái)開發(fā) php
2022-06-22
這太令人沮喪,太難以理解。也太坑了。
以下是這幾天微信公眾號(hào)相關(guān)工作的總結(jié)。不全面,只是作為初學(xué)者的記錄,僅供備忘。
一、微信公眾號(hào)開發(fā),開發(fā)什么?
公眾號(hào)不同于小程序。小程序類似于手機(jī)APP,自主開發(fā),微信只提供入口;而公眾號(hào)基本在微信的框架內(nèi)。微信公眾號(hào)本質(zhì)上是用戶的一個(gè)聯(lián)系人,但只是一個(gè)特殊的聯(lián)系人。通過(guò)微信提供的公眾號(hào)管理后臺(tái),無(wú)需任何編程,即可快速搭建像樣的公眾號(hào),菜單、機(jī)器人客服、文章更新一應(yīng)俱全。
但是,如果你想要更強(qiáng)的功能,你需要開發(fā)。比如機(jī)器人客服。通過(guò)公眾號(hào)管理后臺(tái),可以定義一些自動(dòng)回復(fù)語(yǔ)句,但畢竟還不夠智能。這時(shí)候,我們可以在互聯(lián)網(wǎng)上搭建一個(gè)服務(wù)器,提供相應(yīng)的服務(wù)。當(dāng)然,這需要準(zhǔn)備好 URL 和域名。
第二,菜單。點(diǎn)擊公眾號(hào)的菜單項(xiàng)后,可以回復(fù)一些消息,跳轉(zhuǎn)到小程序,或者打開網(wǎng)頁(yè)。如果您打開網(wǎng)頁(yè),如果是未經(jīng)驗(yàn)證的公眾號(hào),則只能打開公眾號(hào)中的素材,或當(dāng)前公眾號(hào)已發(fā)表的文章或圖片、視頻等;和經(jīng)過(guò)驗(yàn)證的公眾號(hào),可以直接打開任意網(wǎng)址。這些網(wǎng)頁(yè)通常是部署在互聯(lián)網(wǎng)上的所謂微信網(wǎng)頁(yè)。他們使用微信JS-SDK,上面有各種微信元素,比如掃描、分享到朋友圈等等。當(dāng)然,這部分需要開發(fā)。
還有向追隨者發(fā)送消息。我認(rèn)為這是微信公眾號(hào)最大的賣點(diǎn)。比如我關(guān)注了某個(gè)公眾號(hào),通過(guò)這個(gè)公眾號(hào)的菜單打開相關(guān)小程序做事,當(dāng)事情進(jìn)展的時(shí)候,系統(tǒng)可以通過(guò)這個(gè)公眾號(hào)給我發(fā)消息提醒我當(dāng)前工作進(jìn)展。我認(rèn)為這是公眾號(hào)開發(fā)中最有價(jià)值的工作。
當(dāng)然,也有可以自動(dòng)在公眾號(hào)上發(fā)表文章的程序。不過(guò)這種事情也可以在公眾號(hào)管理后臺(tái)手動(dòng)完成,無(wú)非就是動(dòng)手。
二、發(fā)展鋪墊
在開發(fā)之前,有必要了解相關(guān)規(guī)范。建議同時(shí)閱讀開發(fā)文檔的開頭:微信公眾平臺(tái)開發(fā)概述
1、公眾號(hào)分類
衣著三色,食分五等。所謂微信公眾號(hào)分為訂閱號(hào)和服務(wù)號(hào)。個(gè)人只能申請(qǐng)訂閱號(hào),企業(yè)可以申請(qǐng)訂閱號(hào)和服務(wù)號(hào)。然后公眾號(hào)分為認(rèn)證和未認(rèn)證。公眾號(hào)的類型,是否經(jīng)過(guò)認(rèn)證,決定了是否可以調(diào)用很多微信服務(wù)。沒(méi)有認(rèn)證,基本沒(méi)什么玩的。而且很遺憾,個(gè)人申請(qǐng)的訂閱號(hào)根本無(wú)法通過(guò)微信認(rèn)證,直接擋住了門。
如何開發(fā)它?微信還非?!百N心”地提供了測(cè)試號(hào)機(jī)制。不用申請(qǐng)公眾號(hào),我們可以先申請(qǐng)一個(gè)測(cè)試號(hào),用這個(gè)號(hào)來(lái)測(cè)試微信服務(wù)接口。測(cè)試號(hào)所有微信服務(wù)接口均可訪問(wèn)。當(dāng)然是鵝!和微信網(wǎng)頁(yè)一樣,需要在手機(jī)上運(yùn)行才能看到效果,如果使用測(cè)試號(hào),有些東西是無(wú)法渲染的。比如所謂的微信開放標(biāo)簽(即微信定義的標(biāo)簽,類似于HTML)。
訂閱賬戶和服務(wù)賬戶有不同的側(cè)重點(diǎn)。據(jù)我了解,訂閱號(hào)側(cè)重于發(fā)布文章,而服務(wù)號(hào)側(cè)重于發(fā)送有針對(duì)性的通知。一般來(lái)說(shuō),服務(wù)帳戶比訂閱帳戶強(qiáng)大得多。
從表面上看,訂閱號(hào)每天可以發(fā)送1條消息,而服務(wù)號(hào)每個(gè)月只能發(fā)送4條消息,訂閱號(hào)更強(qiáng)。問(wèn)題是,群發(fā)消息有什么用?當(dāng)我們?cè)诰W(wǎng)上做事時(shí),我們想要的是對(duì)我來(lái)說(shuō)是新聞。只有服務(wù)帳戶可以發(fā)送此有針對(duì)性的通知消息。
在文檔中,這種消息稱為模板消息。為什么叫模板消息?這是因?yàn)檫@種消息是結(jié)合模板生成的。就像我們的手機(jī)短信一樣。做過(guò)手機(jī)短信開發(fā)的都知道,手機(jī)短信是不能隨便發(fā)的。因?yàn)楸娝苤脑?,必須有所謂的模版,就是怕非法和無(wú)恥的內(nèi)容,就是短信的格式是固定的,而且很多字也是固定的,我們只需要填寫每次我們發(fā)送一些內(nèi)容。此模板必須事先創(chuàng)建并獲得電信運(yùn)營(yíng)商的批準(zhǔn)。微信消息也使用模板。調(diào)用發(fā)送接口時(shí),需要將模板ID作為參數(shù)傳入。
目前小程序的模板消息功能已經(jīng)廢棄,取而代之的是所謂的“統(tǒng)一服務(wù)消息”,實(shí)際上是通過(guò)服務(wù)號(hào)發(fā)送的。也就是說(shuō),小程序要給用戶發(fā)送通知,就必須對(duì)應(yīng)一個(gè)服務(wù)號(hào)。
但是世界上還有一種叫做訂閱消息的東西。公眾號(hào)叫訂閱通知,小程序叫訂閱消息。有兩種類型:一次性和長(zhǎng)期。訂閱消息需要用戶主動(dòng)訂閱。例如,使用麥當(dāng)勞小程序點(diǎn)餐時(shí),每次付款后,都會(huì)詢問(wèn)您是否接受取餐通知。長(zhǎng)期只對(duì)部分民生和醫(yī)院公眾賬號(hào)開放。這是在申請(qǐng)公共帳戶時(shí)給出的。不要冒險(xiǎn),低估微信折騰人的能力。否則,發(fā)送時(shí)對(duì)方永遠(yuǎn)收不到,也可能沒(méi)有錯(cuò)誤信息;
模板消息和訂閱消息有什么區(qū)別?訂閱新聞不僅僅是要求用戶手動(dòng)訂閱,也沒(méi)什么。問(wèn)題是,這必須用手機(jī)來(lái)完成。如果我通過(guò) PC 做事并想在手機(jī)上接收提醒怎么辦?訂閱新聞就完成了。
2、公眾號(hào)調(diào)試工具
它是微信開發(fā)者工具。請(qǐng)注意,它是開發(fā)人員工具,而不是開發(fā)工具。這個(gè)工具,對(duì)于小程序來(lái)說(shuō),確實(shí)是一個(gè)開發(fā)工具;對(duì)于公眾號(hào),它只是一個(gè)調(diào)試工具,無(wú)法通過(guò)它輸入任何代碼;它只是微信網(wǎng)頁(yè)的調(diào)試工具。此時(shí)它只是一個(gè)微信瀏覽器。方法是在微信開發(fā)者工具頂部輸入微信網(wǎng)頁(yè)地址進(jìn)行瀏覽調(diào)試微信公眾平臺(tái)開發(fā)教程 php,類似普通瀏覽器按F12。
3、查看微信網(wǎng)頁(yè)運(yùn)行結(jié)果
公眾號(hào)可以通過(guò)微信客戶端看到。這里所說(shuō)的查看結(jié)果是指查看微信網(wǎng)頁(yè)的運(yùn)行結(jié)果。雖然有“網(wǎng)頁(yè)”二字,但這不是一個(gè)普通的網(wǎng)頁(yè)。如果用普通瀏覽器訪問(wèn),雖然沒(méi)有報(bào)錯(cuò),但是看不到效果。它應(yīng)該通過(guò)微信瀏覽器或微信開發(fā)者工具運(yùn)行。需要注意的是,手機(jī)上沒(méi)有微信瀏覽器這個(gè)app,是微信暗示的。如何召喚它?你可以把微信網(wǎng)頁(yè)的地址發(fā)給微信上的朋友,比如“文件傳輸助手”,然后在聊天記錄里點(diǎn)擊這個(gè)網(wǎng)址,就會(huì)用微信瀏覽器打開。絕對(duì)給力的是微信瀏覽器,QQ瀏覽器不好用。
4、開發(fā)文檔
基于微信進(jìn)行二次開發(fā),上網(wǎng)查資料基本沒(méi)用。最好老老實(shí)實(shí)閱讀官方微信開發(fā)文檔。
公眾號(hào)開發(fā)文檔在公眾號(hào)管理后臺(tái)-設(shè)置與開發(fā)-開發(fā)者工具-開發(fā)文檔中打開。
微信有兩個(gè)平臺(tái),小程序叫“微信開發(fā)開放平臺(tái)”,公眾號(hào)叫“微信開發(fā)公共平臺(tái)”。
5、一些術(shù)語(yǔ)
1)管理員和操作員
在開發(fā)過(guò)程中,不可避免地會(huì)訪問(wèn)微信公眾號(hào)管理后臺(tái)修改或設(shè)置一些設(shè)置,但要更改設(shè)置,必須掃描二維碼進(jìn)行身份認(rèn)證。這很尷尬。申請(qǐng)公眾號(hào)的人是管理員,但不一定參與開發(fā)。提醒大老板掃描二維碼會(huì)很不方便網(wǎng)站制作,甚至是不可能的。您可以將開發(fā)人員添加到操作員列表并自行掃描代碼。運(yùn)營(yíng)商分為長(zhǎng)期和短期兩種。他們應(yīng)該是具有足夠權(quán)限的長(zhǎng)期經(jīng)營(yíng)者。
可以在微信公眾號(hào)管理后臺(tái)-設(shè)置與開發(fā)-人員設(shè)置中進(jìn)行設(shè)置。
2)IP 白名單
在開發(fā)過(guò)程中,我們需要訪問(wèn)微信服務(wù)器,比如獲取。發(fā)出請(qǐng)求的 IP 需要在白名單中。這個(gè)IP是指互聯(lián)網(wǎng)IP。如果我們?cè)隍v云網(wǎng)內(nèi)部開發(fā),那么這個(gè)IP就是騰云網(wǎng)的IP,用于上網(wǎng)。問(wèn)題是微信公眾平臺(tái)開發(fā)教程 php網(wǎng)站優(yōu)化,這個(gè)IP經(jīng)常變化。我不知道這樣做的好方法,所以基本上每天更改一次白名單。
IP白名單是供我們?cè)诒镜卣{(diào)試微信開發(fā)者工具的。不需要手機(jī)操作。
3)開發(fā)者微信ID
公眾號(hào)管理后臺(tái)-設(shè)置與開發(fā)-網(wǎng)頁(yè)開發(fā)者工具,添加我們開發(fā)者的微信賬號(hào)。這是用來(lái)開發(fā)微信網(wǎng)頁(yè)的。因?yàn)槲⑿砰_發(fā)者工具需要微信登錄。
4)JS接口安全域名
公眾號(hào)管理后臺(tái)-設(shè)置與開發(fā)-公眾號(hào)設(shè)置-功能設(shè)置。
微信網(wǎng)頁(yè)開發(fā)也需要它。我們的頁(yè)面需要放在這個(gè)域名下,才能使用微信的js-sdk。
填寫這個(gè)域名的時(shí)候,需要下載一個(gè)txt文件放到域名下,微信可以驗(yàn)證域名的真實(shí)性后再保存。但是填完之后,我們?cè)诒镜亻_發(fā)的時(shí)候,可以修改host文件,把本地ip映射到域名。畢竟是前端的東西。js-sdk本身需要微信瀏覽器的支持,它無(wú)法判斷請(qǐng)求來(lái)自哪個(gè)IP,我們傳給它什么都會(huì)相信。
5)微信打開標(biāo)簽
類似于HTML,微信獨(dú)有的標(biāo)簽。喜歡
跳轉(zhuǎn)小程序:<wx-open-launch-weapp>
跳轉(zhuǎn)App:<wx-open-launch-app>
服務(wù)號(hào)訂閱通知:<wx-open-subscribe>
音頻播放:<wx-open-audio>
三、微信網(wǎng)頁(yè)開發(fā)
1、概覽
微信網(wǎng)頁(yè)實(shí)際上只是網(wǎng)頁(yè),只不過(guò)它引用了微信提供的JS庫(kù),可能會(huì)使用微信獨(dú)有的所謂開放標(biāo)簽,類似于html。而這個(gè)微信網(wǎng)頁(yè)似乎是在一個(gè)普通的瀏覽器上運(yùn)行的。雖然沒(méi)有報(bào)錯(cuò),但是好像沒(méi)什么效果。它只能在微信瀏覽器或微信開發(fā)者工具上運(yùn)行。
下面是一個(gè)微信網(wǎng)頁(yè)(boot下,合并)
2、驗(yàn)證
微信網(wǎng)頁(yè)在運(yùn)行時(shí),必須先通過(guò)微信驗(yàn)證,然后才能正常使用微信的各種功能。驗(yàn)證過(guò)程是,
1)微信服務(wù)器獲取并訪問(wèn)
2)訪問(wèn)微信服務(wù)器獲取
3)使用,隨機(jī)字符串、時(shí)間戳、當(dāng)前頁(yè)面地址依次組成字符串,然后對(duì)字符串進(jìn)行sha hash運(yùn)算得到摘要
4)使用摘要訪問(wèn)微信服務(wù)器獲取簽名
5)注冊(cè)、時(shí)間戳、隨機(jī)字符串、簽名、本頁(yè)要使用的微信功能、微信打開標(biāo)簽到微信
在上面的例子中,
wx.config({
debug: true,
appId: /*[[${wc.appId}]]*/'',
timestamp: /*[[${wc.timestamp}]]*/'',//注意是秒,不是毫秒
nonceStr: /*[[${wc.nonceStr}]]*/'',//隨機(jī)串
signature: /*[[${wc.signature}]]*/'',//關(guān)鍵所在
jsApiList: ['chooseImage','scanQRCode','updateAppMessageShareData','updateTimelineShareData'],//需要使用的微信js-sdk函數(shù)列表
openTagList: ['wx-open-subscribe']//開放標(biāo)簽列表
});
要得到這個(gè) json 對(duì)象并不容易。可以在微信公眾號(hào)管理后臺(tái)獲取,每個(gè)公眾號(hào)都有唯一的;時(shí)間戳也很容易獲得;隨機(jī)字符串由自己決定,比較容易;最麻煩的是簽名,我調(diào)試了一天左右,總的意思是非法簽名。
獲取此 json 對(duì)象的 java 代碼:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import javax.annotation.PostConstruct;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;
@Service
public class WxServiceImpl implements WxService {
@Override
public WxConfig getWxConfig(String url) {//url是微信網(wǎng)頁(yè)地址
WxConfig wc = new WxConfig();//這個(gè)是自定義的對(duì)象,不必深究
wc.setAppId(APPID);
wc.setNonceStr(getNonceStr());//隨機(jī)串
wc.setTimestamp((long) (new Date()).getTime() / 1000);//時(shí)間戳
wc.setSignature(getSignature(wc.getNonceStr(), wc.getTimestamp(), url));//簽名
return wc;
}
@PostConstruct
void init() {
/*
由于從微信服務(wù)器獲取token和ticket的函數(shù)有調(diào)用次數(shù)限制(每天<=2000),因此用redis將它們緩存起來(lái)
*/
this.jedis = new Jedis(redis的IP, redis端口號(hào));
}
private String getSignature(String nonceStr, long timestamp, String url) {//獲取簽名
String signature = null;
String ticket = getTicket();
if (ticket != null) {
String string1 = String.format("jsapi_ticket=%s&noncestr=%s×tamp=%d&url=%s",
ticket,
nonceStr,
timestamp,
url);
signature = getSha1(string1);
}
return signature;
}
//redis對(duì)象
private Jedis jedis;
//除了redis緩存,也用靜態(tài)變量保存一份。不過(guò),應(yīng)用程序重啟它們就消失了,并且不會(huì)自動(dòng)過(guò)期
//而從微信獲取到的token和ticket有效期是7200秒
private String _ticket = null;
private String _token = null;
//鎖。為避免并發(fā),使用鎖機(jī)制,不要大家都去獲取token和ticket
private ReentrantLock lockTok = new ReentrantLock();
private ReentrantLock lockTik = new ReentrantLock();
private String getTicket() {
String ticket = null;
String key = "ticket";
ticket = getKey(key, this._ticket);
if (ticket == null) {
//鎖定。
lockTik.lock();
ticket = getKey(key, this._ticket);//再努力一把
if (ticket == null) {
String token = getToken();
if (token != null) {
ticket = callGet(String.format("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi",
token), "ticket");
if (ticket != null) {
this._ticket = ticket;
setKey(key, ticket);
}
}
}
//解鎖
lockTik.unlock();
}
return ticket;
}
private String getToken() {
String token = null;
String key = "token";
token = getKey(key, this._token);
if (token == null) {
lockTok.lock();
token = getKey(key, this._token);
if (token == null) {
token = callGet(String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",APPID,AppSecret),
"access_token");
if (token != null) {
this._token = token;
setKey(key, token);
}
}
lockTok.unlock();
}
return token;
}
final static int EXPIRTED = 7200;
private String getKey(String key, String v) {
String value = null;
try {
value = jedis.get(key);
} catch (Exception ex) {
value = v;//如果無(wú)法從redis中讀取則將候補(bǔ)變量值返回。但變量值可能有過(guò)期的問(wèn)題
System.err.println(ex.getMessage());
}
return value;
}
private void setKey(String key, String value) {
try {
jedis.set(key, value);
jedis.expire(key, EXPIRTED);
} catch (Exception ex) {
System.err.println(ex.getMessage());
}
}
final static int NONCESTR = 16;//隨機(jī)串的長(zhǎng)度為16。這個(gè)數(shù)值是自己定的
private String getNonceStr() {// 生成隨機(jī)字符串noncestr
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
StringBuffer noncestr = new StringBuffer();
int limit = chars.length() - 1;
for (int i = 0; i < NONCESTR; i++) {
Random r = new Random();
int j = r.nextInt(limit);
noncestr.append(chars.substring(j, j + 1));
}
return noncestr.toString();
}
private static String getSha1(String string1) {
MessageDigest sha = null; // 此處的sha代表sha1
try {
sha = MessageDigest.getInstance("SHA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
byte[] md5Bytes = new byte[0];
try {
md5Bytes = sha.digest(string1.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
StringBuffer hexValue = new StringBuffer();
for (int i = 0; i < md5Bytes.length; i++) {
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16) {
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString();
}
public static String callGet(String api, String key) {//get的方式訪問(wèn)微信api
String re = null;
System.out.println(String.format("正在獲取 %s : %s", key, api));
try {
URL url = new URL(api);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
BufferedReader br = new BufferedReader(
new InputStreamReader(connection.getInputStream(), "UTF-8"));
String line;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
br.close();
connection.disconnect();
JSONObject json = JSON.parseObject(sb.toString());
if (json.containsKey(key)) {
re = json.getString(key);
}
System.out.println(re);
} catch (Exception ex) {
ex.printStackTrace();
System.out.println(String.format("訪問(wèn)接口%s失敗", api));
}
return re;
}
}
3、調(diào)試
在微信開發(fā)者工具上,輸入微信網(wǎng)頁(yè)地址,運(yùn)行,得到很多提示。對(duì)于微信公眾號(hào)來(lái)說(shuō),微信開發(fā)者工具是一個(gè)調(diào)試工具,一個(gè)開啟調(diào)試模式的瀏覽器。但一開始,它總是失敗?,F(xiàn)在,我在示例中聲明需要使用以下微信功能:
wx.config({
debug: true,
appId: /*[[${wc.appId}]]*/'',
timestamp: /*[[${wc.timestamp}]]*/'',//注意是秒,不是毫秒
nonceStr: /*[[${wc.nonceStr}]]*/'',//隨機(jī)串
signature: /*[[${wc.signature}]]*/'',//關(guān)鍵所在
jsApiList: ['chooseImage','scanQRCode','updateAppMessageShareData','updateTimelineShareData'],//需要使用的微信js-sdk函數(shù)列表
openTagList: ['wx-open-subscribe']//開放標(biāo)簽列表
});
結(jié)果,從返回的信息來(lái)看,我可以訪問(wèn)的函數(shù)是0。除此之外,到處都是“失敗,是”之類的提示。
開啟微信開發(fā)者工具的調(diào)試模式:在微信開發(fā)者工具中,頂部菜單“微信開發(fā)者工具”-調(diào)試-“調(diào)試微信開發(fā)者工具”-,看這個(gè)?_r=…的返回信息,結(jié)果是 {: , : ” ”}!
簽名算法有問(wèn)題嗎?我用的是微信公眾號(hào)管理后臺(tái)提供的微信JS接口簽名驗(yàn)證工具,結(jié)果完全一致,所以不存在算法錯(cuò)誤的問(wèn)題。
4、填坑
后來(lái)發(fā)現(xiàn)是網(wǎng)址有問(wèn)題。在生成簽名的過(guò)程中,需要傳遞當(dāng)前微信網(wǎng)頁(yè)的URL參與構(gòu)造字符串,然后對(duì)字符串進(jìn)行哈希處理,傳遞給微信服務(wù)器獲取簽名。問(wèn)題是,這個(gè)URL應(yīng)該怎么寫?由于我的微信頁(yè)面名為.html,所以我在訪問(wèn)的時(shí)候一般在瀏覽器上輸入地址是這樣的: 所以我也用這個(gè)地址參與了簽名的構(gòu)建,結(jié)果總是提示簽名是無(wú)效的。后來(lái)我把地址全寫了:,一下子就OK了??。
但是官方開發(fā)文檔里寫的是什么呢?
不認(rèn)真檢查問(wèn)題會(huì)害死人。