上一篇《微信開發(fā)—微信開發(fā)環(huán)境搭建》我們已經(jīng)完成了微信開發(fā)的準(zhǔn)備工作,準(zhǔn)備工作完成之後,就要開始步入正題了。
一、微信公眾平臺的基本原理
在開始做之前,先簡單介紹了微信公眾平臺的基本原理。
微信伺服器就相當(dāng)於一個轉(zhuǎn)發(fā)伺服器,終端機(jī)(手機(jī)、Pad等)發(fā)起請求至微信伺服器,微信伺服器然後將請求轉(zhuǎn)發(fā)給我們的應(yīng)用伺服器。應(yīng)用程式伺服器處理完畢後,將回應(yīng)資料回傳給微信伺服器,微信伺服器再將特定回應(yīng)訊息回覆至微信App終端。
通訊協(xié)定為:HTTP
資料傳輸格式為:XML
具體的流程如下圖所示:
# 來一張更直觀的圖吧:
# 我們需要做的事情,就是對微信伺服器轉(zhuǎn)發(fā)的HTTP請求做出回應(yīng)。具體的請求內(nèi)容,我們依照特定的XML格式去解析,處理完畢後,也要依照特定的XML格式回傳。
二、微信公眾號接入
在微信公眾平臺開發(fā)者文件上,關(guān)於公眾號接入這一節(jié)內(nèi)容在接入指南上寫的比較詳細(xì)的,文檔中說接取公眾號需要3個步驟,分別是:
1、填入伺服器設(shè)定
2、驗證伺服器位址的有效性
3、依據(jù)介面文件實作業(yè)務(wù)邏輯
其實,第3步已經(jīng)不能算做公眾號接入的步驟,而是接入之後,開發(fā)人員可以根據(jù)微信公眾號提供的介面所能做的一些開發(fā)。
第1步驟中伺服器設(shè)定包含伺服器位址(URL)、Token和EncodingAESKey。
伺服器位址即公眾號後臺提供業(yè)務(wù)邏輯的入口位址,目前只支援80端口,之後包括接入驗證以及任何其它的操作的請求(例如訊息的發(fā)送、選單管理、素材管理等)都要從這個地址進(jìn)入。存取驗證和其它請求的區(qū)別就是,存取驗證時是get請求,其它時候是post請求;
Token可由開發(fā)者可以任意填寫,用作生成簽名(該Token會和接口URL中包含的Token進(jìn)行比對,從而驗證安全性);
EncodingAESKey由開發(fā)者手動填寫或隨機(jī)生成,將用作訊息體加解密金鑰。 本例中全部以未加密的明文訊息方式,不涉及此組態(tài)項目。
第2步,驗證伺服器位址的有效性,當(dāng)點擊「提交」按鈕後,微信伺服器將發(fā)送一個http的get請求到剛剛填寫的伺服器位址,並且攜帶四個參數(shù):
# 接到請求後,我們需要做如下三步,若確認(rèn)此次GET請求來自微信伺服器,原樣傳回echostr參數(shù)內(nèi)容,則存取生效,否則存取失敗。
1. 將token、timestamp、nonce三個參數(shù)進(jìn)行字典序排序
2. 將三個參數(shù)字串拼接成一個字串進(jìn)行sha1加密
3. 開發(fā)者取得加密後的字串可與signature對比,標(biāo)識該請求來自微信
下面我們用Java代碼來演示一下這個驗證過程
使用IDE(Eclipse或者IntelliJ IDEA)創(chuàng)建一個JavaWeb項目,這裡我使用的是IntelliJ IDEA,專案目錄結(jié)構(gòu)如下圖所示:
編寫一個servlevt,在其中的doGet方法中定義校驗方法,具體程式碼如下:
package me.gacl.wx.web.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; /** * Created by xdp on 2016/1/25. * 使用@WebServlet注解配置WxServlet,urlPatterns屬性指明了WxServlet的訪問路徑 */ @WebServlet(urlPatterns="/WxServlet") public class WxServlet extends HttpServlet { /** * Token可由開發(fā)者可以任意填寫,用作生成簽名(該Token會和接口URL中包含的Token進(jìn)行比對,從而驗證安全性) * 比如這里我將Token設(shè)置為gacl */ private final String TOKEN = "gacl"; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("開始校驗簽名"); /** * 接收微信服務(wù)器發(fā)送請求時傳遞過來的4個參數(shù) */ String signature = request.getParameter("signature");//微信加密簽名signature結(jié)合了開發(fā)者填寫的token參數(shù)和請求中的timestamp參數(shù)、nonce參數(shù)。 String timestamp = request.getParameter("timestamp");//時間戳 String nonce = request.getParameter("nonce");//隨機(jī)數(shù) String echostr = request.getParameter("echostr");//隨機(jī)字符串 //排序 String sortString = sort(TOKEN, timestamp, nonce); //加密 String mySignature = sha1(sortString); //校驗簽名 if (mySignature != null && mySignature != "" && mySignature.equals(signature)) { System.out.println("簽名校驗通過。"); //如果檢驗成功輸出echostr,微信服務(wù)器接收到此輸出,才會確認(rèn)檢驗完成。 //response.getWriter().println(echostr); response.getWriter().write(echostr); } else { System.out.println("簽名校驗失敗."); } } /** * 排序方法 * * @param token * @param timestamp * @param nonce * @return */ public String sort(String token, String timestamp, String nonce) { String[] strArray = {token, timestamp, nonce}; Arrays.sort(strArray); StringBuilder sb = new StringBuilder(); for (String str : strArray) { sb.append(str); } return sb.toString(); } /** * 將字符串進(jìn)行sha1加密 * * @param str 需要加密的字符串 * @return 加密后的內(nèi)容 */ public String sha1(String str) { try { MessageDigest digest = MessageDigest.getInstance("SHA-1"); digest.update(str.getBytes()); byte messageDigest[] = digest.digest(); // Create Hex String StringBuffer hexString = new StringBuffer(); // 字節(jié)數(shù)組轉(zhuǎn)換為 十六進(jìn)制 數(shù) for (int i = 0; i < messageDigest.length; i++) { String shaHex = Integer.toHexString(messageDigest[i] & 0xFF); if (shaHex.length() < 2) { hexString.append(0); } hexString.append(shaHex); } return hexString.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return ""; } }
我在這裡用的Servlet3.0,使用Servlet3.0的好處就是可以直接使用@WebServlet註解映射Servlet的存取路徑,不再需要在web.xml檔案中進(jìn)行配置.
將WxStudy項目部署到Tomcat伺服器中運行,直接啟動項目,然後用ngrok將本地8080端口映射到外網(wǎng)(如何使用ngrok請參考博客《微信開發(fā)—微信開發(fā)環(huán)境搭建》) 。如下圖所示:
測試是否可以通過http://xdp.ngrok.natapp.cn地址正常訪問,測試結(jié)果如下:
可以看到,我們的項目已經(jīng)可以被外網(wǎng)正常訪問到了。
進(jìn)入微信測試公眾號管理界面,在接口配置信息中填入映射的外網(wǎng)地址和token,如下圖所示:
點擊提交按鈕,頁面會提示配置成功,
IDE的控制臺中輸出了校驗通過的信息,如下圖所示:
到此,我們的公眾號應(yīng)用已經(jīng)能夠和微信服務(wù)器正常通信了,也就是說我們的公眾號已經(jīng)接入到微信公眾平臺了。
三、access_token管理
3.1、access_token介紹
我們的公眾號和微信服務(wù)器對接成功之后,接下來要做的就是根據(jù)我們的業(yè)務(wù)需求調(diào)用微信公眾號提供的接口來實現(xiàn)相應(yīng)的邏輯了。在使用微信公眾號接口中都需要一個access_token。
關(guān)于access_token,在微信公眾平臺開發(fā)者文檔上的獲取接口調(diào)用憑據(jù)有比較詳細(xì)的介紹:access_token是公眾號的全局唯一票據(jù),公眾號調(diào)用各接口時都需使用access_token,開發(fā)者需要妥善保存access_token的存儲至少要保留512個字符空間。access_token的有效期目前為2個小時,需定時刷新,重復(fù)獲取將導(dǎo)致上次獲取的access_token失效。并且每天調(diào)用獲取access_token接口的上限是2000次。
總結(jié)以上說明,access_token需要做到以下兩點:
1.因為access_token有2個小時的時效性,要有一個機(jī)制保證最長2個小時重新獲取一次。
2.因為接口調(diào)用上限每天2000次,所以不能調(diào)用太頻繁。
3.2、微信公眾平臺提供的獲取access_token的接口
關(guān)于access_token的獲取方式,在微信公眾平臺開發(fā)者文檔上有說明,公眾號可以調(diào)用一個叫"獲取access token"的接口來獲取access_token。
獲取access token接口調(diào)用請求說明
http請求方式: GET
請求的URL地址:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
我們可以看到,調(diào)用過程中需要傳遞appID和AppSecret,appID和AppSecret是在申請公眾號的時候自動分配給公眾號的,相當(dāng)于公眾號的身份標(biāo)示,使用微信公眾號的注冊帳號登錄到騰訊提供的微信公眾號管理后臺就可以看到自己申請的公眾號的AppID和AppSecret,如下圖所示:
這是我申請公眾號測試帳號時分配到的AppID和AppSecret。
3.3、獲取access_token方案以及具體實現(xiàn)
這里采用的方案是這樣的,定義一個默認(rèn)啟動的servlet,在init方法中啟動一個Thread,這個進(jìn)程中定義一個無限循環(huán)的方法,用來獲取access_token,當(dāng)獲取成功后,此進(jìn)程休眠7000秒(7000秒=1.944444444444444小時),否則休眠3秒鐘繼續(xù)獲取。流程圖如下:
下面正式開始在工程中實現(xiàn)以上思路,因為返回的數(shù)據(jù)都是json格式,這里會用到阿里的fastjson庫,為構(gòu)造請求和處理請求后的數(shù)據(jù)序列化和反序列化提供支持。
1.定義一個AccessToken實體類
package me.gacl.wx.entry; /** * AccessToken的數(shù)據(jù)模型 * Created by xdp on 2016/1/25. */ public class AccessToken { //獲取到的憑證 private String accessToken; //憑證有效時間,單位:秒 private int expiresin; public String getAccessToken() { return accessToken; } public void setAccessToken(String accessToken) { this.accessToken = accessToken; } public int getExpiresin() { return expiresin; } public void setExpiresin(int expiresin) { this.expiresin = expiresin; } }
2.定義一個AccessTokenInfo類,用于存放獲取到的AccessToken,代碼如下:
package me.gacl.wx.Common; import me.gacl.wx.entry.AccessToken; /** * Created by xdp on 2016/1/25. */ public class AccessTokenInfo { //注意是靜態(tài)的 public static AccessToken accessToken = null; }
3.編寫一個用于發(fā)起https請求的工具類NetWorkHelper,代碼如下:
package me.gacl.wx.util; import javax.net.ssl.*; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; /** * 訪問網(wǎng)絡(luò)用到的工具類 */ public class NetWorkHelper { /** * 發(fā)起Https請求 * @param reqUrl 請求的URL地址 * @param requestMethod * @return 響應(yīng)后的字符串 */ public String getHttpsResponse(String reqUrl, String requestMethod) { URL url; InputStream is; String resultData = ""; try { url = new URL(reqUrl); HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); TrustManager[] tm = {xtm}; SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, tm, null); con.setSSLSocketFactory(ctx.getSocketFactory()); con.setHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String arg0, SSLSession arg1) { return true; } }); con.setDoInput(true); //允許輸入流,即允許下載 //在android中必須將此項設(shè)置為false con.setDoOutput(false); //允許輸出流,即允許上傳 con.setUseCaches(false); //不使用緩沖 if (null != requestMethod && !requestMethod.equals("")) { con.setRequestMethod(requestMethod); //使用指定的方式 } else { con.setRequestMethod("GET"); //使用get請求 } is = con.getInputStream(); //獲取輸入流,此時才真正建立鏈接 InputStreamReader isr = new InputStreamReader(is); BufferedReader bufferReader = new BufferedReader(isr); String inputLine; while ((inputLine = bufferReader.readLine()) != null) { resultData += inputLine + "\n"; } System.out.println(resultData); } catch (Exception e) { e.printStackTrace(); } return resultData; } X509TrustManager xtm = new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } @Override public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } }; }
getHttpsResponse方法是請求一個https地址,參數(shù)requestMethod為字符串“GET”或者“POST”,傳null或者“”默認(rèn)為get方式。
4.定義一個默認(rèn)啟動的servlet,在init方法中啟動一個新的線程去獲取accessToken
package me.gacl.wx.web.servlet; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import me.gacl.wx.Common.AccessTokenInfo; import me.gacl.wx.entry.AccessToken; import me.gacl.wx.util.NetWorkHelper; import javax.servlet.ServletException; import javax.servlet.annotation.WebInitParam; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; /** * 用于獲取accessToken的Servlet * Created by xdp on 2016/1/25. */ @WebServlet( name = "AccessTokenServlet", urlPatterns = {"/AccessTokenServlet"}, loadOnStartup = 1, initParams = { @WebInitParam(name = "appId", value = "wxbe4d433e857e8bb1"), @WebInitParam(name = "appSecret", value = "ccbc82d560876711027b3d43a6f2ebda") }) public class AccessTokenServlet extends HttpServlet { @Override public void init() throws ServletException { System.out.println("啟動WebServlet"); super.init(); final String appId = getInitParameter("appId"); final String appSecret = getInitParameter("appSecret"); //開啟一個新的線程 new Thread(new Runnable() { @Override public void run() { while (true) { try { //獲取accessToken AccessTokenInfo.accessToken = getAccessToken(appId, appSecret); //獲取成功 if (AccessTokenInfo.accessToken != null) { //獲取到access_token 休眠7000秒,大約2個小時左右 Thread.sleep(7000 * 1000); //Thread.sleep(10 * 1000);//10秒鐘獲取一次 } else { //獲取失敗 Thread.sleep(1000 * 3); //獲取的access_token為空 休眠3秒 } } catch (Exception e) { System.out.println("發(fā)生異常:" + e.getMessage()); e.printStackTrace(); try { Thread.sleep(1000 * 10); //發(fā)生異常休眠1秒 } catch (Exception e1) { } } } } }).start(); } /** * 獲取access_token * * @return AccessToken */ private AccessToken getAccessToken(String appId, String appSecret) { NetWorkHelper netHelper = new NetWorkHelper(); /** * 接口地址為https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET,其中g(shù)rant_type固定寫為client_credential即可。 */ String Url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", appId, appSecret); //此請求為https的get請求,返回的數(shù)據(jù)格式為{"access_token":"ACCESS_TOKEN","expires_in":7200} String result = netHelper.getHttpsResponse(Url, ""); System.out.println("獲取到的access_token="+result); //使用FastJson將Json字符串解析成Json對象 JSONObject json = JSON.parseObject(result); AccessToken token = new AccessToken(); token.setAccessToken(json.getString("access_token")); token.setExpiresin(json.getInteger("expires_in")); return token; } }
AccessTokenServlet采用注解的方式進(jìn)行配置
至此代碼實現(xiàn)完畢,將項目部署,看到控制臺輸出如下:
為了方便看效果,可以把休眠時間設(shè)置短一點,比如10秒獲取一次,然后將access_token輸出。
下面做一個測試jsp頁面,并把休眠時間設(shè)置為10秒,這樣過10秒刷新頁面,就可以看到變化
<%-- Created by IntelliJ IDEA. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="me.gacl.wx.Common.AccessTokenInfo"%> <html> <head> <title></title> </head> <body> 微信學(xué)習(xí) <hr/> access_token為:<%=AccessTokenInfo.accessToken.getAccessToken()%> </body> </html>
10秒鐘后刷新頁面,access_token變了,如下圖所示:
四、接收微信服務(wù)器發(fā)送的消息并做出響應(yīng)
經(jīng)過上述的三步,我們開發(fā)前的準(zhǔn)備工作已經(jīng)完成了,接下來要做的就是接收微信服務(wù)器發(fā)送的消息并做出響應(yīng)
從微信公眾平臺接口消息指南中可以了解到,當(dāng)用戶向公眾帳號發(fā)消息時,微信服務(wù)器會將消息通過POST方式提交給我們在接口配置信息中填寫的URL,而我們就需要在URL所指向的請求處理類WxServlet的doPost方法中接收消息、處理消息和響應(yīng)消息。
4.1.編寫一個用于處理消息的工具類
編寫處理消息的工具欄,工具類代碼如下:
package me.gacl.wx.util; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import javax.servlet.http.HttpServletRequest; import java.io.InputStream; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 消息處理工具類 * Created by xdp on 2016/1/26. */ public class MessageHandlerUtil { /** * 解析微信發(fā)來的請求(XML) * @param request * @return map * @throws Exception */ public static Map<String,String> parseXml(HttpServletRequest request) throws Exception { // 將解析結(jié)果存儲在HashMap中 Map<String,String> map = new HashMap(); // 從request中取得輸入流 InputStream inputStream = request.getInputStream(); System.out.println("獲取輸入流"); // 讀取輸入流 SAXReader reader = new SAXReader(); Document document = reader.read(inputStream); // 得到xml根元素 Element root = document.getRootElement(); // 得到根元素的所有子節(jié)點 List<Element> elementList = root.elements(); // 遍歷所有子節(jié)點 for (Element e : elementList) { System.out.println(e.getName() + "|" + e.getText()); map.put(e.getName(), e.getText()); } // 釋放資源 inputStream.close(); inputStream = null; return map; } // 根據(jù)消息類型 構(gòu)造返回消息 public static String buildXml(Map<String,String> map) { String result; String msgType = map.get("MsgType").toString(); System.out.println("MsgType:" + msgType); if(msgType.toUpperCase().equals("TEXT")){ result = buildTextMessage(map, "孤傲蒼狼在學(xué)習(xí)和總結(jié)微信開發(fā)了,構(gòu)建一條文本消息:Hello World!"); }else{ String fromUserName = map.get("FromUserName"); // 開發(fā)者微信號 String toUserName = map.get("ToUserName"); result = String .format( "<xml>" + "<ToUserName><![CDATA[%s]]></ToUserName>" + "<FromUserName><![CDATA[%s]]></FromUserName>" + "<CreateTime>%s</CreateTime>" + "<MsgType><![CDATA[text]]></MsgType>" + "<Content><![CDATA[%s]]></Content>" + "</xml>", fromUserName, toUserName, getUtcTime(), "請回復(fù)如下關(guān)鍵詞:\n文本\n圖片\n語音\n視頻\n音樂\n圖文"); } return result; } /** * 構(gòu)造文本消息 * * @param map * @param content * @return */ private static String buildTextMessage(Map<String,String> map, String content) { //發(fā)送方帳號 String fromUserName = map.get("FromUserName"); // 開發(fā)者微信號 String toUserName = map.get("ToUserName"); /** * 文本消息XML數(shù)據(jù)格式 * <xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>1348831860</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[this is a test]]></Content> <MsgId>1234567890123456</MsgId> </xml> */ return String.format( "<xml>" + "<ToUserName><![CDATA[%s]]></ToUserName>" + "<FromUserName><![CDATA[%s]]></FromUserName>" + "<CreateTime>%s</CreateTime>" + "<MsgType><![CDATA[text]]></MsgType>" + "<Content><![CDATA[%s]]></Content>" + "</xml>", fromUserName, toUserName, getUtcTime(), content); } private static String getUtcTime() { Date dt = new Date();// 如果不需要格式,可直接用dt,dt就是當(dāng)前系統(tǒng)時間 DateFormat df = new SimpleDateFormat("yyyyMMddhhmm");// 設(shè)置顯示格式 String nowTime = df.format(dt); long dd = (long) 0; try { dd = df.parse(nowTime).getTime(); } catch (Exception e) { } return String.valueOf(dd); } }
為了方便解析微信服務(wù)器發(fā)送給我們的xml格式的數(shù)據(jù),這里我們借助于開源框架dom4j去解析xml(這里使用的是dom4j-2.0.0-RC1.jar)
4.2.在WxServlet的doPost方法中處理請求
WxServlet的doPost方法的代碼如下:
/** * 處理微信服務(wù)器發(fā)來的消息 */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO 接收、處理、響應(yīng)由微信服務(wù)器轉(zhuǎn)發(fā)的用戶發(fā)送給公眾帳號的消息 // 將請求、響應(yīng)的編碼均設(shè)置為UTF-8(防止中文亂碼) request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); System.out.println("請求進(jìn)入"); String result = ""; try { Map<String,String> map = MessageHandlerUtil.parseXml(request); System.out.println("開始構(gòu)造消息"); result = MessageHandlerUtil.buildXml(map); System.out.println(result); if(result.equals("")){ result = "未正確響應(yīng)"; } } catch (Exception e) { e.printStackTrace(); System.out.println("發(fā)生異常:"+ e.getMessage()); } response.getWriter().println(result); }
到此,我們的WxServlet已經(jīng)可以正常處理用戶的請求并做出響應(yīng)了.接下來我們測試一下我們開發(fā)好的公眾號應(yīng)用是否可以正常和微信用戶交互
將WxStudy部署到Tomcat服務(wù)器,啟動服務(wù)器,記得使用ngrok將本地Tomcat服務(wù)器的8080端口映射到外網(wǎng),保證接口配置信息的URL地址:http://xdp.ngrok.natapp.cn/WxServlet可以正常與微信服務(wù)器通信
登錄到我們的測試公眾號的管理后臺,然后用微信掃描一下測試號的二維碼,如下圖所示:
關(guān)注成功后,我們開發(fā)好的公眾號應(yīng)用會先給用戶發(fā)一條提示用戶操作的文本消息,微信用戶根據(jù)提示操作輸入"文本",我們的公眾號應(yīng)用接收到用戶請求后就給用戶回復(fù)了一條我們自己構(gòu)建好的文本消息,如下圖所示:
我們的公眾號應(yīng)用響應(yīng)給微信用戶的文本消息的XML數(shù)據(jù)如下:
<xml> <ToUserName><![CDATA[ojADgs0eDaqh7XkTM9GvDmdYPoDw]]></ToUserName> <FromUserName><![CDATA[gh_43df3882c452]]></FromUserName> <CreateTime>1453755900000</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[孤傲蒼狼在學(xué)習(xí)和總結(jié)微信開發(fā)了,構(gòu)建一條文本消息:Hello World!]]></Content> </xml>
測試公眾號的管理后臺也可以看到關(guān)注測試號的用戶列表,如下圖所示:
通過這個簡單的入門程序,我們揭開了微信開發(fā)的神秘面紗了.
更多微信開發(fā)入門學(xué)習(xí)總結(jié)相關(guān)文章請關(guān)注PHP中文網(wǎng)!

熱AI工具

Undress AI Tool
免費脫衣圖片

Undresser.AI Undress
人工智慧驅(qū)動的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強(qiáng)大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6
視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

PHP是一種開源的腳本語言,廣泛應(yīng)用於網(wǎng)頁開發(fā)和伺服器端編程,尤其在微信開發(fā)中得到了廣泛的應(yīng)用。如今,越來越多的企業(yè)和開發(fā)者開始使用PHP進(jìn)行微信開發(fā),因為它成為了真正的易學(xué)易用的開發(fā)語言。在微信開發(fā)中,訊息的加密和解密是一個非常重要的問題,因為它們涉及資料的安全性。對於沒有加密和解密方式的消息,駭客可以輕鬆取得其中的數(shù)據(jù),對用戶造成威脅

在微信公眾號開發(fā)中,投票功能經(jīng)常被運用。投票功能是讓使用者快速參與互動的好方式,也是舉辦活動和調(diào)查意見的重要工具。本文將為您介紹如何使用PHP實作微信投票功能。在取得微信公眾號授權(quán)首先,你需要取得微信公眾號的授權(quán)。在微信公眾平臺上,你需要設(shè)定微信公眾號碼的api地址、官方帳號和公眾號碼對應(yīng)的token。在我們使用PHP語言開發(fā)的過程中,我們需要使用微信官方提供的PH

隨著微信的普及,越來越多的企業(yè)開始將其作為行銷工具。而微信群發(fā)功能,則是企業(yè)進(jìn)行微信行銷的重要手段之一。但是,如果只依靠手動發(fā)送,對於行銷人員來說是一件極為費時費力的工作。所以,開發(fā)一款微信群發(fā)工具就顯得格外重要。本文將介紹如何使用PHP開發(fā)微信群發(fā)工具。一、準(zhǔn)備工作開發(fā)微信群發(fā)工具,我們需要掌握以下幾個技術(shù)點:PHP基礎(chǔ)知識微信公眾平臺開發(fā)開發(fā)工具:Sub

微信是目前全球用戶規(guī)模最大的社群平臺之一,隨著行動網(wǎng)路的普及,越來越多的企業(yè)開始意識到微信行銷的重要性。在進(jìn)行微信行銷時,客服服務(wù)是至關(guān)重要的一環(huán)。為了更好地管理客服聊天窗口,我們可以藉助PHP語言進(jìn)行微信開發(fā)。一、PHP微信開發(fā)簡介PHP是一種開源的伺服器端腳本語言,廣泛用於Web開發(fā)領(lǐng)域。結(jié)合微信公眾平臺提供的開發(fā)接口,我們可以使用PHP語言進(jìn)行微信

在微信公眾號開發(fā)中,使用者標(biāo)籤管理是一個非常重要的功能,可以讓開發(fā)者更了解和管理自己的使用者。本篇文章將介紹如何使用PHP實作微信使用者標(biāo)籤管理功能。一、取得微信用戶openid在使用微信用戶標(biāo)籤管理功能之前,我們首先需要取得用戶的openid。在微信公眾號開發(fā)中,透過使用者授權(quán)的方式取得openid是比較常見的做法。在使用者授權(quán)完成後,我們可以透過以下程式碼取得用

隨著微信成為了人們生活中越來越重要的通訊工具,其敏捷的訊息傳遞功能迅速受到廣大企業(yè)和個人的青睞。對企業(yè)而言,將微信發(fā)展為一個行銷平臺已經(jīng)成為趨勢,而微信開發(fā)的重要性也逐漸凸顯。在其中,群發(fā)功能更是被廣泛使用,那麼,作為PHP程式設(shè)計師,如何實現(xiàn)群發(fā)訊息發(fā)送記錄呢?以下將為大家簡單介紹一下。 1.了解微信公眾號相關(guān)開發(fā)知識在了解如何實現(xiàn)群發(fā)訊息發(fā)送記錄之前,我

如何使用PHP實現(xiàn)微信公眾號開發(fā)微信公眾號已經(jīng)成為了許多企業(yè)推廣和互動的重要管道,而PHP作為常用的Web語言,也可以用來進(jìn)行微信公眾號的開發(fā)。本文將介紹使用PHP實現(xiàn)微信公眾號開發(fā)的具體步驟。第一步:取得微信公眾號的開發(fā)者帳號在開始微信公眾號開發(fā)之前,需要先去申請一個微信公眾號的開發(fā)者帳號。具體的註冊流程可參考微信公眾平臺的官方網(wǎng)

隨著網(wǎng)路和行動智慧型裝置的發(fā)展,微信成為了社交和行銷領(lǐng)域不可或缺的一部分。在這個越來越數(shù)位化的時代,如何使用PHP進(jìn)行微信開發(fā)已經(jīng)成為了許多開發(fā)者的關(guān)注點。本文主要介紹如何使用PHP進(jìn)行微信發(fā)展的相關(guān)知識點,以及其中的一些技巧和注意事項。一、開發(fā)環(huán)境準(zhǔn)備在進(jìn)行微信開發(fā)之前,首先需要準(zhǔn)備好對應(yīng)的開發(fā)環(huán)境。具體來說,需要安裝PHP的運作環(huán)境,以及微信公眾平臺提
