The custom menu of WeChat development public platform requires money to be certified to achieve. If you don’t want to spend money, you can only play with the test account, but this does not affect development. My development is based on the application development of Teacher Liu Feng’s WeChat public platform.
As long as we use the public platform to test the account, we can develop a custom menu. It is more convenient. The test account has many interfaces, which is very convenient.
When developing a custom menu, you can refer to the custom menu in the WeChat public platform developer documentation to create it.
1. Customize menu
1. Custom menus include up to 3 first-level menus, and each first-level menu contains up to 5 second-level menus.
2. The first-level menu has up to 4 Chinese characters, and the second-level menu has up to 7 Chinese characters. The extra parts will be replaced by "...".
3. After creating a custom menu, the menu refresh strategy is that when the user enters the official account session page or the official account profile page, if he finds that the last request to pull the menu was 5 minutes ago, he will pull the menu. If the menu is updated, the client's menu will be refreshed. During the test, you can try to unfollow the public account and follow it again, then
You can see the effect after creation.
The custom menu interface can implement multiple types of buttons, as follows:
1. Click: Click to push event
After the user clicks the click type button, the WeChat server will push the structure of the message type event to the developer through the message interface (refer to the message interface guide), and bring the key value filled in by the developer in the button. The developer can interact with the user through the customized key value;
2. View: Jump URL
After the user clicks the view type button, the WeChat client will open the web page URL filled in by the developer in the button, which can be combined with the web page authorization to obtain user basic information interface to obtain user basic information.
3. scancode_push: scan code to push event
After the user clicks the button, the WeChat client will call up the scan tool, and display the scan result after completing the scan operation (if it is a URL, it will enter the URL), and the scan result will be passed to the developer, and the developer can send a message.
4. scancode_waitmsg: Scan the code to push the event and the "Message Receive" prompt box pops up
After the user clicks the button, the WeChat client will call up the scan tool. After completing the scan code operation, pass the result of the scan code to the developer. At the same time, the scan tool is closed, and the "Message Receive" prompt box pops up, and then a message sent by the developer may be received.
5. pic_sysphoto: pop-up system to take photos and post pictures
After the user clicks the button, the WeChat client will adjust the system camera. After completing the photo operation, it will send the captured photos to the developer and push the event to the developer. At the same time, it will hold the system camera and then receive a message sent by the developer.
6. pic_photo_or_album: pop up and take pictures or post pictures on the album
After the user clicks the button, the WeChat client will pop up the selector for the user to select "take a photo" or "select from mobile phone album". After the user selects, he will go through the other two processes.
7. pic_weixin: Pop-up WeChat photo album sender
After the user clicks the button, the WeChat client will adjust the WeChat album. After completing the selection operation, it will send the selected photo to the developer's server and push the event to the developer. At the same time, the album will be closed. Then, a message may be received from the developer.
8. location_select: pop-up geolocation selector
After the user clicks the button, the WeChat client will call up the geolocation selection tool. After completing the selection operation, it will send the selected geographical location to the developer's server, and at the same time, it will close the location selection tool, and then receive a message sent by the developer.
9. media_id: send a message (except text message)
After the user clicks the media_id type button, the WeChat server will send the material corresponding to the permanent material id filled in by the developer to the user. The permanent material types can be pictures, audio, video, and graphic messages. Please note: The permanent material id must be the legal id obtained after uploading the "Material Management/Add permanent material" interface.
10. view_limited: jump to the URL of the text message
After the user clicks the view_limited type button, the WeChat client will open the graphic message URL corresponding to the permanent material id filled in by the developer in the button. The permanent material type only supports graphic message. Please note: The permanent material id must be the legal id obtained after uploading the "Material Management/Add permanent material" interface
2. Access the custom menu interface
1: Get access_token
The creation, query and deletion of custom menus require calling the custom menu interface open to the public platform. To call this interface, you need to obtain access_token (interface access credentials). These interfaces are all based on the https protocol, so we need to first solve the problem of how to send https requests in Java programs.
Get the interface access credentials access_token:
Getting access_token is accessed through GET:
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
There are three parameters in the link, namely grant_type, appid and secret. According to the parameter description in the figure, grant_type passes the fixed value client_credential, and appid and secret are assigned to us by WeChat after applying for the custom menu.
After the request is sent successfully, the WeChat server will return a json string, including access_token and expires_in. Among them, access_token is the credential we need in the end, and expires_in is the validity period of the credential, the unit is seconds, and 7200 seconds is 2 hours. This means that not every time you access a special interface, you need to re-acquire the access_token, and it can be used as long as the access_token is still within the validity period.
2: Menu creation
To create a menu, call the menu interface
Interface call request description
http request method: POST (please use the https protocol) https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN
In fact, it is to submit a JSON menu string in POST to the address https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN.
3. Encapsulate the general request method (custom trust manager)
To create a menu, one is to obtain the access_token interface and the other is to customize the menu interface, both are https requests.
1: Create a certificate trust manager
For https requests, we need a certificate trust manager. This manager class needs to be defined by itself, but it needs to implement the X509TrustManager interface. The code is as follows:
package org.liufeng.weixin.util; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; /** * Certificate trust manager (for https requests) * * @author liufeng * @date 2013-08-08 */ public class MyX509TrustManager implements X509TrustManager { public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } } The function of the certificate manager is to trust all certificates specified.
2: Create a universal https request
Universal https request should
1) Support HTTPS requests;
2) Support GET and POST;
3) Support parameter submission and also support no parameters;
package org.liufeng.weixin.util; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ConnectException; import java.net.URL; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import net.sf.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Public platform general interface tool class* * @author liuyq * @date 2013-08-09 */ public class WeixinUtil { private static Logger log = LoggerFactory.getLogger(WeixinUtil.class); /** * Initiate https request and get the result* * @param requestUrl Request address* @param requestMethod request method (GET, POST) * @param outputStr Submitted data* @return JSONObject(get the attribute value of the json object through JSONObject.get(key)) */ public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) { JSONObject jsonObject = null; StringBuffer buffer = new StringBuffer(); try { // Create an SSLContext object and initialize TrustManager[] tm = { new MyX509TrustManager() }; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); // Get the SSLSocketFactory object from the above SSLContext object SSLSocketFactory ssf = sslContext.getSocketFactory(); URL url = new URL(requestUrl); HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection(); httpUrlConn.setSSLSocketFactory(ssf); httpUrlConn.setDoOutput(true); httpUrlConn.setDoInput(true); httpUrlConn.setUseCaches(false); // Set request method (GET/POST) httpUrlConn.setRequestMethod(requestMethod); if ("GET".equalsIgnoreCase(requestMethod)) httpUrlConn.connect(); // When there is data that needs to be submitted if (null != outputStr) { OutputStream outputStream = httpUrlConn.getOutputStream(); // Pay attention to the encoding format to prevent Chinese garbled outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // Convert the returned input stream to a string InputStream inputStream = httpUrlConn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); // release resource inputStream.close(); inputStream = null; httpUrlConn.disconnect(); jsonObject = JSONObject.fromObject(buffer.toString()); } catch (ConnectException ce) { log.error("Weixin server connection timed out."); } catch (Exception e) { log.error("https request error:{}", e); } return jsonObject; } } 4. Encapsulate menu entity class
1: Classification of buttons
We usually regard custom menu items as buttons, and the types of buttons are divided into click (click event) and view (visit web pages).
Buttons of click type have three attributes: type, name and key, while buttons of view type have three attributes: type, name and url
2: First, after calling the access credential interface, the WeChat server will return data in json format: {"access_token":"ACCESS_TOKEN","expires_in":7200}. We encapsulate it as an AccessToken object. The object has two attributes: token and expiresIn, the code is as follows:
package org.liufeng.weixin.pojo; /** * WeChat universal interface credentials* * @author liufeng * @date 2013-08-08 */ public class AccessToken { // The obtained credentials private String token; // The validity time of the credentials, unit: seconds private int expiresIn; public String getToken() { return token; } public void setToken(String token) { this.token = token; } public int getExpiresIn() { return expiresIn; } public void setExpiresIn(int expiresIn) { this.expiresIn = expiresIn; } } Next is the encapsulation of the menu structure. Because we use object-oriented programming, the final submitted json format menu data should be directly converted from the object, rather than spelling a lot of json data into the program code. The menu structure encapsulation is based on the JSON format menu structure given in the public platform API document, as shown below:
Example of request for click and view
{ "button":[ { "type":"click", "name":"Today's song", "key":"V1001_TODAY_MUSIC" }, { "name":"menu", "sub_button":[ { "type":"view", "name":"search", "url":"http://www.soso.com/" }, { "type":"view", "name":"video", "url":"http://v.qq.com/" }, { "type":"click", "name":"like us", "key":"V1001_GOOD" }] }] } 3: Encapsulation menu structure
Each button object needs a common name attribute, so a button object base class needs to be defined, and all button objects need to inherit this class. The code of the base class is as follows:
package org.liufeng.weixin.pojo; /** * Base class of the button* * @author liufeng * @date 2013-08-08 */ public class Button { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } Next is the encapsulation of the submenu item. The submenu is defined here as follows: a menu item without a submenu may be a secondary menu item, or a primary menu that does not contain a secondary menu. This type of submenu item must contain three attributes: type, name and key. The encapsulated code is as follows:
package org.liufeng.weixin.pojo; /** * Normal button (subbutton) * * @author liufeng * @date 2013-08-08 */ public class CommonButton extends Button { private String type; private String key; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } } Definition of parent menu item: A first-level menu containing secondary menu items. This type of menu items contains two properties: name and sub_button, and sub_button is an array of submenu items. The encapsulation code of the parent menu item is as follows:
package org.liufeng.weixin.pojo; /** * Complex button (parent button) * * @author liufeng * @date 2013-08-08 */ public class ComplexButton extends Button { private Button[] sub_button; public Button[] getSub_button() { return sub_button; } public void setSub_button(Button[] sub_button) { this.sub_button = sub_button; } } Encapsulate the entire menu. The menu object contains multiple menu items (at most, there can be 3). These menu items can be submenu items (first level menu without secondary menus) or parent menu items (menu items containing secondary menus)
package org.liufeng.weixin.pojo; /** * Menu* * @author liufeng * @date 2013-08-08 */ public class Menu { private Button[] button; public Button[] getButton() { return button; } public void setButton(Button[] button) { this.button = button; } }In this way, we complete the encapsulation of the menu entity class.
How to obtain credential access_token
Continue to add the following code to the class WeixinUtil.java of the previous universal request method to obtain interface access credentials:
// Get the interface address of access_token (GET) Limited to 200 times/day public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"; /** * Get access_token * * @param appid credential* @param appsecret key* @return */ public static AccessToken getAccessToken(String appid, String appsecret) { AccessToken accessToken = null; String requestUrl = access_token_url.replace("APPID", appid).replace("APPSECRET", appsecret); JSONObject jsonObject = httpRequest(requestUrl, "GET", null); // If the request is successful if (null != jsonObject) { try { accessToken = new AccessToken(); accessToken.setToken(jsonObject.getString("access_token")); accessToken.setExpiresIn(jsonObject.getInt("expires_in")); } catch (JSONException e) { accessToken = null; // Failed to get token log.error("Failed to get token errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg")); } } return accessToken; } How to create a custom menu
Continue to add the following code to the class WeixinUtil.java of the previous universal request method to create a custom menu:
// Menu creation (POST) Limited to 100 times/day public static String menu_create_url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN"; /** * Create menu* * @param menu Menu Example* @param accessToken Valid access_token * @return 0 means success, other values indicate failure*/ public static int createMenu(Menu menu, String accessToken) { int result = 0; // Assemble the url of the creation menu String url = menu_create_url.replace("ACCESS_TOKEN", accessToken); // Convert menu object to json string String jsonMenu = JSONObject.fromObject(menu).toString(); // Call the interface to create menu JSONObject jsonObject = httpRequest(url, "POST", jsonMenu); if (null != jsonObject) { if (0 != jsonObject.getInt("errcode")) { result = jsonObject.getInt("errcode"); log.error("Create menu failed errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg")); } } return result; } Call encapsulated methods to create custom menus
package org.liufeng.weixin.main; import org.liufeng.weixin.pojo.AccessToken; import org.liufeng.weixin.pojo.Button; import org.liufeng.weixin.pojo.CommonButton; import org.liufeng.weixin.pojo.ComplexButton; import org.liufeng.weixin.pojo.Menu; import org.liufeng.weixin.util.WeixinUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Menu Manager class* * @author liufeng * @date 2013-08-08 */ public class MenuManager { private static Logger log = LoggerFactory.getLogger(MenuManager.class); public static void main(String[] args) { // Third-party user unique credential String appId = "00000000000000000"; // Third-party user unique credential key String appSecret = "0000000000000000000000000000000000000"; // Call the interface to obtain access_token AccessToken at = WeixinUtil.getAccessToken(appId, appSecret); if (null != at) { // Call the interface to create menu int result = WeixinUtil.createMenu(getMenu(), at.getToken()); // Determine the menu creation result if (0 == result) log.info("Menu creation succeeded!"); else log.info("Menu creation failed, error code: " + result); } } /** * Assemble menu data* * @return */ private static Menu getMenu() { CommonButton btn11 = new CommonButton(); btn11.setName("weather forecast"); btn11.setType("click"); btn11.setKey("11"); CommonButton btn12 = new CommonButton(); btn12.setName("Bus Query"); btn12.setType("click"); btn12.setKey("12"); CommonButton btn13 = new CommonButton(); btn13.setName("Peripheral search"); btn13.setType("click"); btn13.setKey("13"); CommonButton btn14 = new CommonButton(); btn14.setName("Today in history"); btn14.setType("click"); btn14.setKey("14"); CommonButton btn21 = new CommonButton(); btn21.setName("Song on demand"); btn21.setType("click"); btn21.setKey("21"); CommonButton btn22 = new CommonButton(); btn22.setName("Classic Game"); btn22.setType("click"); btn22.setKey("22"); CommonButton btn23 = new CommonButton(); btn23.setName("Beautiful Radio"); btn23.setType("click"); btn23.setKey("23"); CommonButton btn24 = new CommonButton(); btn24.setName("Face Recognition"); btn24.setType("click"); btn24.setKey("24"); CommonButton btn25 = new CommonButton(); btn25.setName("chat"); btn25.setType("click"); btn25.setKey("25"); CommonButton btn31 = new CommonButton(); btn31.setName("Q Friends Circle"); btn31.setType("click"); btn31.setKey("31"); CommonButton btn32 = new CommonButton(); btn32.setName("Movie Ranking"); btn32.setType("click"); btn32.setKey("32"); CommonButton btn33 = new CommonButton(); btn33.setName("Humorous Joke"); btn33.setType("click"); btn33.setKey("33"); ComplexButton mainBtn1 = new ComplexButton(); mainBtn1.setName("Life Assistant"); mainBtn1.setSub_button(new CommonButton[] { btn11, btn12, btn13, btn14 }); ComplexButton mainBtn2 = new ComplexButton(); mainBtn2.setName("Lucky Station"); mainBtn2.setSub_button(new CommonButton[] { btn21, btn22, btn23, btn24, btn25 }); ComplexButton mainBtn3 = new ComplexButton(); mainBtn3.setName("More Experience"); mainBtn3.setSub_button(new CommonButton[] { btn31, btn32, btn33 }); /** * This is the current menu structure of the official account xiaoqrobot. Each first-level menu has a second-level menu item* * If there is no second-level menu under a certain first-level menu, how should menu be defined? * For example, the third level one menu item is not "more experience", but is directly "humor joke", then menu should be defined like this: * menu.setButton(new Button[] { mainBtn1, mainBtn2, btn33 }); */ Menu menu = new Menu(); menu.setButton(new Button[] { mainBtn1, mainBtn2, mainBtn3 }); return menu; } }Note: When running the above code, you need to replace the appId and appSecret with your own official account.
Response to menu click event
package org.liufeng.course.service; import java.util.Date; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.liufeng.course.message.resp.TextMessage; import org.liufeng.course.util.MessageUtil; /** * Core service class* * @author liufeng * @date 2013-05-20 */ public class CoreService { /** * Process requests from WeChat* * @param request * @return */ public static String processRequest(HttpServletRequest request) { String respMessage = null; try { // The text message content returned by default String respContent = "Request handling exception, please try!"; // xml request parsing Map<String, String> requestMap = MessageUtil.parseXml(request); // Sender account (open_id) String fromUserName = requestMap.get("FromUserName"); // Public account String toUserName = requestMap.get("ToUserName"); // Message type String msgType = requestMap.get("MsgType"); // Reply to text message TextMessage textMessage = new TextMessage(); textMessage.setToUserName(fromUserName); textMessage.setFromUserName(toUserName); textMessage.setCreateTime(new Date().getTime()); textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT); textMessage.setFuncFlag(0); // Text message if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) { respContent = "You are sending a text message! "; } // Picture message else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)) { respContent = "You are sending an image message! "; } // Geographic location message else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LOCATION)) { respContent = "You are sending a geographic location message! "; } // Link message else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LINK)) { respContent = "You are sending a link message! "; } // Audio message else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VOICE)) { respContent = "You are sending an audio message! "; } // Event push else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) { // Event type String eventType = requestMap.get("Event"); // Subscribe if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) { respContent = "Thank you for your attention! "; } // Unsubscribe else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) { // TODO After unsubscribe, the user cannot receive the message sent by the official account, so there is no need to reply to the message} // Custom menu click event else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) { // Event KEY value corresponds to the KEY value specified when creating the custom menu String eventKey = requestMap.get("EventKey"); if (eventKey.equals("11")) { respContent = "Weather Forecast Menu Items are clicked! "; } else if (eventKey.equals("12")) { respContent = "The bus query menu item is clicked! "; } else if (eventKey.equals("13")) { respContent = "The peripheral search menu item is clicked! "; } else if (eventKey.equals("14")) { respContent = "The menu item in history was clicked! "; } else if (eventKey.equals("21")) { respContent = "The song on demand menu item is clicked! "; } else if (eventKey.equals("22")) { respContent = "The classic game menu item is clicked! "; } else if (eventKey.equals("23")) { respContent = "The beauty radio menu item was clicked! "; } else if (eventKey.equals("24")) { respContent = "The face recognition menu item is clicked! "; } else if (eventKey.equals("25")) { respContent = "The chat menu item is clicked! "; } else if (eventKey.equals("31")) { respContent = "The Q circle menu item was clicked! "; } else if (eventKey.equals("32")) { respContent = "The movie ranking menu item is clicked! "; } else if (eventKey.equals("33")) { respContent = "The Humor Joke menu item is clicked! "; } } } textMessage.setContent(respContent); respMessage = MessageUtil.textMessageToXml(textMessage); } catch (Exception e) { e.printStackTrace(); } return respMessage; } }The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.