1、第一步首先去natapp去注册和下载内网穿透工具,有免费的可以使用,具体操作看NatApp的操作文档。
2.下载解压后,配置一个环境变量,natapp的环境变量
3.搞好后通过内网击穿搞一个域名,authtoken免费在网站获取,将
natapp -authtoken yourtoken
放入natapp.exe程序中即可获取域名(authtoken换成自己的)
4.你有了这个域名就可以在微信配置你的公众号信息了,首先你去微信公众号创建一个公众号,再通过查看官方文档了解微信公众号开发,主要是配置token,服务器地址(带上项目启动链接微信的访问路径)
5.配置测试号的一些信息appid,appsecret,url和token(和前面配置的token一样),[去这配置],(https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index)
6.你提交微信配置的时候要先把项目写好,先说项目内容,写好后再提交配置,下面是项目启动时访问微信服务器,利用appid和appsecret去生成accesToken(每个请求都需要携带这个token才能与微信服务器交互),先把所有用到的jar包放出来,然后是连接微信服务器的类
a.项目所需要的jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>3.1.270</version><!-- 注:这里只是示例版本号(可直接使用),可获取并替换为 最新的版本号,注意不要使用4.0.x版本(非最新版本) -->
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.3.1</version>
</dependency>
b.需要用到的工具类
import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
* @author gewenbo
* @date 2022/6/21 17:35
* 用于发送http请求的工具类(向微信发送http请求,获取access_token)
*/
public class NetWorkUtil {
/**
* 发起HTTPS请求
* @param reqUrl
* @param requestMethod 请求方式,传null的话默认是get请求
* @return 相应字符串
*/
public String getHttpsResponse(String reqUrl, String requestMethod) {
URL url;
InputStream is;
String result ="";
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中必须将此项设置为false,允许输出流,即允许上传
con.setDoOutput(false);
//不使用缓冲
con.setUseCaches(false);
if (null != requestMethod && !requestMethod.equals("")) {
//使用指定的方式
con.setRequestMethod(requestMethod);
} else {
//使用get请求
con.setRequestMethod("GET");
}
//获取输入流,此时才真正建立链接
is = con.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader bufferReader = new BufferedReader(isr);
String inputLine;
while ((inputLine = bufferReader.readLine()) != null) {
result += inputLine + "\n";
}
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
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 {
}
};
// 以post方式将data数据发送到 Url
public static String post(String url,String data){//data 是要post方式发送出去的json格式数据
try{
URL urlobj = new URL(url);
URLConnection connection = urlobj.openConnection();
//要发送数据出去,必须要设置为可发送数据状态
connection.setDoOutput(true);
//获取数据流
OutputStream os = connection.getOutputStream();
//写出数据
os.write(data.getBytes());
os.close();
//获取输入流
InputStream is = connection.getInputStream();
byte[] b = new byte[1024];
int len;
StringBuilder sb = new StringBuilder();
while ((len=is.read(b))!=-1){
sb.append(new String(b,0,len));
}
return sb.toString();
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
import com.longming.tengxun1.entity.AccessToken;
import com.longming.tengxun1.service.StartService;
import com.longming.tengxun1.common.AccessTokenInfo;
/**
* @author gewenbo
* @date 2022/6/23 16:46
* 获取accesToken的工具类,方便直接获取这个token
*/
public class TokenUtil {
public String getTokenName(){
StartService startService = new StartService();
AccessToken accessToken = startService.getAccessToken(AccessTokenInfo.APP_ID, AccessTokenInfo.APP_SECRET);
String tokenName = accessToken.getTokenName();
return tokenName;
}
}
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
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;
/**
* @author gewenbo
* @date 2022/6/21 19:53
* 处理公众号发来的消息(XML格式) 将解析结果存储在HashMap中
*/
public class MessageHandlerUtils {
private static Logger logger = LoggerFactory.getLogger(MessageHandlerUtils.class);
/**
* 获取微信公众号里发送过来的消息
* @param request
* @return
*/
public static Map<String,String> getMsgFromClient(HttpServletRequest request){
logger.info("获取输入流,开始处理消息");
// 将解析结果存储在HashMap中
Map<String,String> map = new HashMap();
InputStream inputStream=null;
try {
inputStream = request.getInputStream();
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List<Element> elementList = root.elements();
// 遍历所有子节点,解析打印微信发来的消息
for (Element e : elementList) {
logger.info(e.getName() + "|" + e.getText());
map.put(e.getName(), e.getText());
}
} catch (Exception e) {
e.printStackTrace();
}finally {
// 释放资源
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return map;
}
/**
* 根据消息类型 构造返回消息
*/
public static String buildXml(Map<String,String> map) {
String result;
String msgType = map.get("MsgType").toString();
logger.info("消息类型:"+map.get("MsgType").toString());
if(msgType.toUpperCase().equals("TEXT")){
result = buildTextMessage(map, "来了老表?");
}else if(msgType.toUpperCase().equals("EVENT")){
// 事件KEY值,与创建自定义菜单时指定的KEY值对应
String eventKey = map.get("EventKey");
if (eventKey.equals("1")){
result = buildImageMessage(map);
}else {
result = buildTextMessage(map, "微信客服:xxxxxxxxxxx 邮 箱:xxxxxxxx 电 话:xxxxx 地 址:xxxxxx ");
}
}else if(msgType.toUpperCase().equals("IMAGE")){
result = buildImageMessage(map);
} else{
String fromUserName = map.get("FromUserName");
// 开发者微信号
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(),
"请点击您想操作的菜单业务");
}
return result;
}
/**
* 构造文本消息
* @param map
* @param content
* @return
*/
private static String buildTextMessage(Map<String,String> map, String content) {
//发送方帐号
String fromUserName = map.get("FromUserName");
// 开发者微信号
String toUserName = map.get("ToUserName");
/**
* 文本消息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);
}
/**
* 获取当前时间
* @return
*/
private static String getUtcTime() {
// 如果不需要格式,可直接用dt,dt就是当前系统时间
Date dt = new Date();
// 设置显示格式
DateFormat df = new SimpleDateFormat("yyyyMMddhhmm");
String nowTime = df.format(dt);
long dd = (long) 0;
try {
dd = df.parse(nowTime).getTime();
} catch (Exception e) {
}
logger.info("当前时间:"+String.valueOf(dd));
return String.valueOf(dd);
}
/**
* 返回图片给用户
* @param map
* @return
*/
private static String buildImageMessage(Map<String, String> map) {
String fromUserName = map.get("FromUserName");
String toUserName = map.get("ToUserName");
/*返回用户发过来的图片*/
String media_id = "dP7kdl73YYqcOjogBOdFCM31LKOEVegCC1Z5p33WjLpxP0v7suVJR12MgBgUQCFd";
return String.format(
"<xml>" +
"<ToUserName><![CDATA[%s]]></ToUserName>" +
"<FromUserName><![CDATA[%s]]></FromUserName>" +
"<CreateTime>%s</CreateTime>" +
"<MsgType><![CDATA[image]]></MsgType>" +
"<Image>" +
" <MediaId><![CDATA[%s]]></MediaId>" +
"</Image>" +
"</xml>",
fromUserName, toUserName, getUtcTime(), media_id
);
}
}
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.Reader;
/**
* @author gewenbo
* @date 2022/6/23 18:28
* 将数据转换由json转换为字符串
*/
public class JsonUtil {
/**
* 读取JSON文件转换为字符串
* @return
*/
public static String readJsonFile() {
String jsonStr = "";
try {
//获取json文件的相对路径
String s = String.valueOf(JsonUtil.class.getResource("/static/Menu.json"));
s = s.substring(s.indexOf("/")+1);
File jsonFile = new File(s);
Reader reader = new InputStreamReader(new FileInputStream(jsonFile), "utf-8");
int ch = 0;
StringBuffer sb = new StringBuffer();
while ((ch = reader.read()) != -1) {
sb.append((char) ch);
}
reader.close();
jsonStr = sb.toString();
return jsonStr;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
}
c.实体类和静态文件
import lombok.Data;
/**
* @author gewenbo
* @date 2022/6/21 17:28
*/
@Data
public class AccessToken {
/**
* 获取到的凭证
*/
private String tokenName;
/**
* 凭证有效时间 单位:秒
*/
private int expireSecond;
}
{
"button": [
{
"name": "热门活动",
"sub_button": [
{
"type": "view",
"name": "我的key",
"url": "https://www.longming.com/",
"sub_button": [ ]
},
{
"type": "view",
"name": "vpn购买",
"url": "https://www.longming.com/",
"sub_button": [ ]
},
{
"type": "view",
"name": "AI快速注册",
"url": "https://www.longming.com/",
"sub_button": [ ]
},
{
"type": "view",
"name": "我爱你购买",
"url": "https://www.longming.com/",
"sub_button": [ ]
},
{
"type": "view",
"name": ".cn仅17元",
"url": "https://www.longming.com/",
"sub_button": [ ]
}
]
},
{
"name": "产品服务",
"sub_button": [
{
"type": "view",
"name": "产品首页",
"url": "https://www.longming.com/",
"sub_button": [ ]
},
{
"type": "view",
"name": "个人中心",
"url": "https://www.longming.com/",
"sub_button": [ ]
},
{
"type": "view",
"name": "域名注册",
"url": "https://www.longming.com/",
"sub_button": [ ]
},
{
"type": "view",
"name": "我的域名",
"url": "https://www.longming.com/",
"sub_button": [ ]
},
{
"type": "view",
"name": "whoid查询",
"url": "https://www.longming.com/",
"sub_button": [ ]
}
]
},
{
"name": "关于我们",
"sub_button": [
{
"type": "click",
"name": "公司介绍",
"key": "1",
"sub_button": [ ]
},
{
"type": "click",
"name": "联系我们",
"key": "2",
"sub_button": [ ]
}
]
}
]
}
d.本地项目连接微信服务器的类,只有这个通过才可以进行下面的操作。
import com.longming.tengxun1.util.NetWorkUtil;
import com.longming.tengxun1.entity.AccessToken;
import com.longming.tengxun1.common.AccessTokenInfo;
import org.springframework.boot.ApplicationRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.boot.ApplicationArguments;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* @author gewenbo
* @date 2022/6/21 17:38
* 默认启动项目的时候就启动该类,用来向微信后台定期获取access_token值
*继承ApplicationRunner接口的话,项目启动时就会执行里边的run方法
*/
//@Order定义组件加载顺序
@Order(value = 1)
@Component
public class StartService implements ApplicationRunner {
static Logger logger = LoggerFactory.getLogger(StartService.class);
public static AccessToken accessToken = null;
@Override
public void run(ApplicationArguments args) throws Exception {
logger.info("开始获取微信里的access_token");
//获取accessToken
accessToken = getAccessToken(AccessTokenInfo.APP_ID, AccessTokenInfo.APP_SECRET);
}
public AccessToken getAccessToken(String appId, String appSecret) {
NetWorkUtil netHelper = new NetWorkUtil();
//利用format方法生成我们需要的路径
String Url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", appId, appSecret);
//此请求为https的get请求,返回的数据格式为{"access_token":"ACCESS_TOKEN","expires_in":7200}
String result = netHelper.getHttpsResponse(Url, "");
logger.info("获取到的access_token="+result);
//使用FastJson将Json字符串解析成Json对象
JSONObject json = JSON.parseObject(result);
AccessToken token = new AccessToken();
token.setTokenName(json.getString("access_token"));
token.setExpireSecond(json.getInteger("expires_in"));
return token;
}
}
7.然后就是微信服务器去连接本地项目,这个路径就是你在测试号和微信公众号里面配置的路径,注意带上这个方法。
import com.longming.tengxun1.util.MessageHandlerUtils;
import com.longming.tengxun1.common.AccessTokenInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Map;
/**
* @author gewenbo
* @date 2022/6/21 17:47
* 公众号入口
*/
@RequestMapping("/gzh")
@Controller
public class GzhController {
static Logger logger = LoggerFactory.getLogger(GzhController.class);
@RequestMapping("link")
public void checkSignature(HttpServletRequest request, HttpServletResponse response) {
logger.info("校验签名start");
/**
* 接收微信服务器发送请求时传递过来的参数
*/
//签名
String signature = request.getParameter("signature");
//时间戳
String timestamp = request.getParameter("timestamp");
//随机数
String nonce = request.getParameter("nonce");
//随机字符串
String echostr = request.getParameter("echostr");
String method = request.getMethod();
if(method.equals("GET")){
//get请求,说明是在配置微信后台的url过来的请求
/**
* 将token、timestamp、nonce三个参数进行字典序排序
* 并拼接为一个字符串
*/
String sortStr = this.sort(AccessTokenInfo.TOKEN, timestamp, nonce);
/**
* 对排序后的sortStr进行shal加密
*/
String mySignature = shal(sortStr);
/**
* 校验"微信服务器传递过来的签名"和"加密后的字符串"是否一致, 如果一致则签名通过,否则不通过
* 每次刚启动项目后,把下边的注释打开,与微信基本配置里的URL进行交互
* 配置完毕后把下边代码注释掉即可
*/
if (!"".equals(signature) && !"".equals(mySignature) && signature.equals(mySignature)) {
logger.info("签名校验通过");
try {
//必须响应给微信,不然会提示"token校验失败"
if(echostr!=null&&echostr!=""){
response.getWriter().write(echostr);
}
} catch (IOException e) {
e.printStackTrace();
}
} else {
logger.info("校验签名失败");
}
}else{
//post请求,说明是微信公众号里来的请求
try {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
Map<String, String> map = MessageHandlerUtils.getMsgFromClient(request);
System.out.println("开始构造消息");
String result = "";
result = MessageHandlerUtils.buildXml(map);
if (result.equals("")) {
result = "未正确响应";
}
try {
response.getWriter().write(result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 参数排序
*
* @param token
* @param timestamp
* @param nonce
* @return
*/
public String sort(String token, String timestamp, String nonce) {
String[] strArray = {token, timestamp, nonce};
if (strArray.length>=0){
Arrays.sort(strArray);
}
StringBuilder sb = new StringBuilder();
for (String str : strArray) {
sb.append(str);
}
return sb.toString();
}
/**
* 字符串进行shal加密
*
* @param str
* @return
*/
public String shal(String str) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(str.getBytes());
byte messageDigest[] = digest.digest();
StringBuffer hexString = new StringBuffer();
// 字节数组转换为 十六进制 数
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 "";
}
}
8.创建删除查询菜单的实现类
import com.alibaba.fastjson.JSONObject;
import com.longming.tengxun1.util.JsonUtil;
import com.longming.tengxun1.util.NetWorkUtil;
import com.longming.tengxun1.util.TokenUtil;
import com.longming.tengxun1.common.WechatURL;
import org.springframework.stereotype.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author gewenbo
* @date 2022/6/22 11:51
*/
@Service
public class MenuService {
static Logger logger = LoggerFactory.getLogger(MenuService.class);
/*
* 创建菜单
* @param menu 菜单实例
* @param accessToken 有效凭证
*/
public static void createMenu(){
TokenUtil tokenUtil = new TokenUtil();
String jsonData = JsonUtil.readJsonFile();
logger.info(jsonData);
//替换 "ACCESS_TOKEN" 为我们自己获取得到的ACCESS_TOKEN
String url = WechatURL.CREATE_MENU_URL.replace("ACCESS_TOKEN",tokenUtil.getTokenName());
//发送请求
NetWorkUtil netWorkUtil = new NetWorkUtil();
String result = netWorkUtil.post(url,jsonData);
JSONObject resultDate = JSONObject.parseObject(result);
if (resultDate.getString("errmsg").equals("ok")){
logger.info("创建菜单成功:"+resultDate);
}else {
logger.info("创建菜单失败,原因:"+resultDate.getString("errmsg"));
}
}
/*
* 查询菜单数据
* @param accessToken 有效凭证
* @return
* @throws Exception JSONObject*/
public static JSONObject getMenu()
{
//1.获取请求url
TokenUtil tokenUtil = new TokenUtil();
String url = WechatURL.GET_MENU_URL.replace("ACCESS_TOKEN", tokenUtil.getTokenName());
NetWorkUtil netWorkUtil = new NetWorkUtil();
//2.发起GET请求,获取返回结果
String result = netWorkUtil.getHttpsResponse(url,"");
JSONObject jsonObject = JSONObject.parseObject(result);
if (jsonObject.get("errmsg")==null){
logger.info("查询菜单成功:"+jsonObject);
return jsonObject;
}else {
logger.info("查询菜单失败,原因:"+jsonObject.getString("errmsg"));
return null;
}
}
/*
* 删除菜单
* @param accessToken 有效凭证
* @throws Exception void
*/
public static void deleteMenu() {
//1.获取请求url
TokenUtil tokenUtil = new TokenUtil();
String url = WechatURL.DELETE_MENU_URL.replace("ACCESS_TOKEN", tokenUtil.getTokenName());
//2.发起GET请求,获取返回结果
NetWorkUtil netWorkUtil = new NetWorkUtil();
String result = netWorkUtil.getHttpsResponse(url, "");
JSONObject jsonObject = JSONObject.parseObject(result);
if (jsonObject.getString("errmsg").equals("ok")) {
logger.info("删除菜单成功:" + jsonObject);
} else {
logger.info("删除菜单失败,原因:" + jsonObject.getString("errmsg"));
}
}
}
9.需要的公共参数类
/**
* @author gewenbo
* @date 2022/6/21 17:33
* 获取token需要的信息
*/
public class AccessTokenInfo {
//获取accesToken需要的appid和appsecret
public static final String APP_ID = "你的appid";
public static final String APP_SECRET = "你的appSecret";
/**
* 这里是自定义的token,需和你微信配置界面提交的token完全一致
*/
public static final String TOKEN = "你的公众号Token";
}
/**
* @author gewenbo
* @date 2022/6/23 13:19
* 创建删除查询的微信服务器路径
*/
public class WechatURL {
//1.菜单创建(POST) 限100(次/天)
public static final String CREATE_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
//2.查询菜单数据
public static final String GET_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN";
//3.删除菜单
public static final String DELETE_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN";
}
10.搞好后启动项目,你的微信测试账号下面有一个测试二维码,关注这个微信公众号,即可看到你创建的菜单信息啦。
11.文字消息回复问题可以参考官方文档开发