微信公众平台Java开发全教程
第一部分:准备工作与核心概念
在开始编码之前,我们必须先理解微信公众平台的基本概念和准备工作。

公众号类型选择
微信公众平台主要分为三种类型,你需要根据你的需求选择:
- 订阅号:主要用于信息传递,适合媒体、个人等,每天可以群发一次消息,认证后可申请微信支付接口。
- 服务号:主要为用户提供服务和功能,适合企业、政府等,每月可群发四次消息,认证后默认拥有微信支付接口。
- 小程序:一个不需要下载安装即可使用的应用,提供了丰富的UI组件和API。
本教程以 服务号 为例进行讲解,因为其接口功能最全。
申请与配置
- 注册公众号:访问 微信公众平台官网,根据指引完成注册。
- 开发者身份验证:
- 登录后台,进入「开发」->「基本配置」。
- 点击「成为开发者」,填写服务器配置(URL、Token、EncodingAESKey)。
- URL:你的Java应用在公网上的访问地址,必须以
http://或https://开头,并支持80/443端口,腾讯服务器会向这个地址发送验证请求。 - Token:可以任意填写,用作生成签名(与后面提到的
token不同,这里只是验证用的字符串)。 - EncodingAESKey:消息加解密密钥,随机生成即可。
- 消息加解密方式:选择「安全模式」或「兼容模式」,推荐「安全模式」。
- 获取凭证:
在「基本配置」页面,找到「开发者ID(AppID)」和「开发者密码(AppSecret)」,这两个是调用微信API的核心凭证,请妥善保管。
核心概念
- 开发者ID (AppID) 和 开发者密码 (AppSecret):用于获取
access_token。 - Access Token:公众号的全局唯一接口调用凭据,有效期2小时,所有API调用都需要它,你需要自己编写代码定时刷新并缓存它。
- OpenID:用户在单个公众号下的唯一标识,同一个用户在不同公众号下的OpenID是不同的。
- UnionID:如果开发者拥有多个公众号,可通过UnionID来区分用户,只要用户在公众号或小程序中授权过,UnionID在所有应用中都唯一。
第二部分:环境搭建
我们将使用 Spring Boot 来快速搭建项目,因为它极大地简化了Java Web开发的配置。

创建Spring Boot项目
使用 Spring Initializr 快速创建项目。
- Project: Maven Project
- Language: Java
- Spring Boot: 选择一个稳定版本 (如 2.7.x 或 3.x.x)
- Project Metadata:
- Group:
com.example - Artifact:
wechat-demo - Name:
wechat-demo - Packaging: Jar
- Java: 11 或更高版本
- Group:
- Dependencies:
Spring Web: 用于构建Web应用。Lombok(可选): 简化Java代码。Spring Data Redis(推荐): 用于缓存access_token。
项目结构
创建一个基本的项目结构,用于存放不同功能的代码。
src/main/java/com/example/wechatdemo/
├── config/ // 配置类
├── controller/ // 控制器,处理HTTP请求
├── service/ // 业务逻辑层
│ ├── impl/ // 服务实现
│ └── WeChatApiService.java
├── utils/ // 工具类
│ └── WeChatUtils.java
└── WeChatDemoApplication.java
第三部分:接入与验证
这是开发的第一步,验证你的服务器是否有效。
控制器代码
创建一个控制器来处理来自微信服务器的验证请求。
src/main/java/com/example/wechatdemo/controller/WeChatController.java
package com.example.wechatdemo.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
@RestController
@RequestMapping("/wechat")
public class WeChatController {
// 从application.properties中读取配置
@Value("${wechat.token}")
private String token;
@GetMapping
public String validate(@RequestParam("signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam("echostr") String echostr) {
// 1. 将token, timestamp, nonce三个参数进行字典序排序
String[] arr = new String[]{token, timestamp, nonce};
Arrays.sort(arr);
// 2. 将三个参数字符串拼接成一个字符串进行sha1加密
StringBuilder content = new StringBuilder();
for (String s : arr) {
content.append(s);
}
String temp = sha1(content.toString());
// 3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
if (temp.equals(signature)) {
// 验证成功,原样返回echostr
return echostr;
} else {
// 验证失败
return "error";
}
}
/**
* SHA1加密算法
*/
private String sha1(String str) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(str.getBytes());
byte[] messageDigest = digest.digest();
StringBuilder hexStr = new StringBuilder();
for (byte b : messageDigest) {
String shaHex = Integer.toHexString(b & 0xFF);
if (shaHex.length() < 2) {
hexStr.append(0);
}
hexStr.append(shaHex);
}
return hexStr.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
}
配置文件
在 src/main/resources/application.properties 中添加你的配置。
# 服务器端口 server.port=8080 # 微信公众号配置 # 这里的token必须和公众号后台设置的完全一致 wechat.token=your_wechat_token wechat.appId=your_app_id wechat.appSecret=your_app_secret
本地测试与上线
-
本地测试:由于你的电脑没有公网IP,微信服务器无法访问,你需要使用内网穿透工具,如 ngrok。
- 下载并安装ngrok。
- 在命令行运行
ngrok http 8080,它会生成一个公网URL,如https://abcdefg1234.ngrok.io。 - 将这个URL(
https://abcdefg1234.ngrok.io/wechat)填写到公众号后台的URL地址栏。 - 点击提交,如果提示成功,说明你的Java服务器已经成功接入微信公众平台。
-
上线部署:将你的Spring Boot应用打包成jar包,上传到云服务器(如阿里云、腾讯云)上运行,确保云服务器的80/443端口开放,并将公网IP和路径配置到公众号后台。
第四部分:接收与处理用户消息
接入成功后,用户向公众号发送消息、点击菜单等事件,微信服务器都会通过你配置的URL以POST请求推送过来。
消息与事件格式
微信推送的消息是XML格式的,我们需要解析这个XML。
- 文本消息示例:
<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>
- 关注事件示例:
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>1348831860</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[subscribe]]></Event> </xml>
使用XStream解析XML
为了避免手动解析XML的繁琐,我们可以使用XStream库。
在pom.xml中添加依赖:
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.19</version>
</dependency>
创建消息实体类
创建Java类来映射XML结构。
src/main/java/com/example/wechatdemo/model/WeChatMessage.java
package com.example.wechatdemo.model;
import lombok.Data;
import com.thoughtworks.xstream.annotations.XStreamAlias;
@Data
public class WeChatMessage {
@XStreamAlias("ToUserName")
private String toUserName;
@XStreamAlias("FromUserName")
private String fromUserName;
@XStreamAlias("CreateTime")
private Long createTime;
@XStreamAlias("MsgType")
private String msgType;
// ... 其他根据消息类型添加字段
}
src/main/java/com/example/wechatdemo/model/TextMessage.java
package com.example.wechatdemo.model;
import lombok.Data;
import com.thoughtworks.xstream.annotations.XStreamAlias;
@Data
@XStreamAlias("xml")
public class TextMessage extends WeChatMessage {
@XStreamAlias("Content")
private String content;
// 可以添加回复文本消息需要的字段
@XStreamAlias("Content")
private String replyContent;
}
更新控制器处理POST请求
修改WeChatController,增加处理POST请求的逻辑。
src/main/java/com/example/wechatdemo/controller/WeChatController.java
package com.example.wechatdemo.controller;
// ... imports
import org.springframework.web.bind.annotation.PostMapping;
import com.example.wechatdemo.model.TextMessage;
import com.example.wechatdemo.model.WeChatMessage;
import com.thoughtworks.xstream.XStream;
import java.io.InputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
@RestController
@RequestMapping("/wechat")
public class WeChatController {
// ... 前面的GET方法用于验证
@PostMapping
public String handlePost(HttpServletRequest request) {
try {
// 1. 获取输入流
InputStream inputStream = request.getInputStream();
// 2. 读取输入流
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
String xml = sb.toString();
System.out.println("收到消息: " + xml);
// 3. 使用XStream解析XML
XStream xstream = new XStream();
xstream.processAnnotations(WeChatMessage.class);
xstream.processAnnotations(TextMessage.class);
// 解决XML标签问题
xstream.alias("xml", WeChatMessage.class);
WeChatMessage message = (WeChatMessage) xstream.fromXML(xml);
// 4. 根据消息类型处理
if ("text".equals(message.getMsgType())) {
TextMessage textMessage = (TextMessage) message;
// 构造回复消息
TextReply reply = new TextReply();
reply.setToUserName(textMessage.getFromUserName());
reply.setFromUserName(textMessage.getToUserName());
reply.setCreateTime(System.currentTimeMillis() / 1000);
reply.setMsgType("text");
reply.setContent("你发送的是: " + textMessage.getContent());
// 将回复消息对象转换为XML
return xstream.toXML(reply);
} else if ("event".equals(message.getMsgType())) {
if ("subscribe".equals(((TextMessage)message).getEvent())) {
// 用户关注,返回欢迎消息
TextReply reply = new TextReply();
reply.setToUserName(message.getFromUserName());
reply.setFromUserName(message.getToUserName());
reply.setCreateTime(System.currentTimeMillis() / 1000);
reply.setMsgType("text");
reply.setContent("感谢关注!");
return xstream.toXML(reply);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return "success"; // 无论处理成功与否,都必须返回"success",否则微信会认为消息处理失败
}
}
// 新增一个用于回复的实体类
class TextReply extends WeChatMessage {
private String content;
// getters and setters
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
}
重启你的应用,当你关注公众号或向公众号发送文本消息时,它会给你相应的回复。
第五部分:调用微信API
接收消息只是第一步,更重要的是主动调用微信API来获取数据或执行操作。
获取Access Token
这是所有API调用前必须做的步骤,我们需要一个服务来定时获取并缓存它。
src/main/java/com/example/wechatdemo/service/WeChatApiService.java
package com.example.wechatdemo.service;
public interface WeChatApiService {
String getAccessToken();
}
src/main/java/com/example/wechatdemo/service/impl/WeChatApiServiceImpl.java
package com.example.wechatdemo.service.impl;
import com.example.wechatdemo.service.WeChatApiService;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Service
public class WeChatApiServiceImpl implements WeChatApiService {
@Value("${wechat.appId}")
private String appId;
@Value("${wechat.appSecret}")
private String appSecret;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private RestTemplate restTemplate; // 需要配置RestTemplate Bean
private static final String ACCESS_TOKEN_KEY = "wechat:access_token";
private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
@Override
public String getAccessToken() {
// 1. 先从Redis缓存中获取
String token = stringRedisTemplate.opsForValue().get(ACCESS_TOKEN_KEY);
if (token != null) {
return token;
}
// 2. 缓存中没有,则从微信服务器获取
String url = String.format(ACCESS_TOKEN_URL, appId, appSecret);
String response = restTemplate.getForObject(url, String.class);
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(response);
token = rootNode.get("access_token").asText();
long expiresIn = rootNode.get("expires_in").asLong(); // 通常是7200秒
// 3. 将token存入Redis,并设置过期时间(比实际过期时间短一点,比如7000秒)
stringRedisTemplate.opsForValue().set(ACCESS_TOKEN_KEY, token, expiresIn - 200, TimeUnit.SECONDS);
return token;
} catch (Exception e) {
// 处理异常,比如打印日志
e.printStackTrace();
return null;
}
}
}
注意:你需要在启动类或配置类中配置 RestTemplate 和 RedisTemplate。
获取用户信息
假设我们要获取刚刚关注用户的昵称和头像。
修改 WeChatController 中的关注事件处理逻辑。
src/main/java/com/example/wechatdemo/controller/WeChatController.java
// ... imports
import com.example.wechatdemo.service.WeChatApiService;
import org.springframework.beans.factory.annotation.Autowired;
@RestController
@RequestMapping("/wechat")
public class WeChatController {
@Autowired
private WeChatApiService weChatApiService;
// ... 其他代码
@PostMapping
public String handlePost(HttpServletRequest request) {
// ... 前面的XML解析代码
if ("event".equals(message.getMsgType())) {
if ("subscribe".equals(((TextMessage)message).getEvent())) {
String openId = message.getFromUserName();
// 1. 获取access_token
String accessToken = weChatApiService.getAccessToken();
if (accessToken == null) {
return "success"; // 获取token失败,无法回复
}
// 2. 调用获取用户信息API
String userInfoUrl = String.format("https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=zh_CN", accessToken, openId);
String userInfoResponse = restTemplate.getForObject(userInfoUrl, String.class);
// 3. 解析用户信息并回复
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode userNode = mapper.readTree(userInfoResponse);
String nickname = userNode.get("nickname").asText();
String headImgUrl = userNode.get("headimgurl").asText();
// 4. 构造回复消息
TextReply reply = new TextReply();
reply.setToUserName(openId);
reply.setFromUserName(message.getToUserName());
reply.setCreateTime(System.currentTimeMillis() / 1000);
reply.setMsgType("text");
reply.setContent("欢迎你," + nickname + "!\n你的头像链接是:" + headImgUrl);
XStream xstream = new XStream();
xstream.alias("xml", TextReply.class);
return xstream.toXML(reply);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return "success";
}
// ... 其他代码
}
当你再次关注公众号时,它会回复你的昵称和头像链接。
第六部分:高级功能示例
发送模板消息
模板消息用于向用户发送重要的服务通知,如订单确认、物流更新等。
步骤:
- 在公众号后台获取模板ID:登录公众号后台,「模板消息」->「我的模板」,选择一个模板并复制其
template_id。 - 编写发送代码:
// 在 WeChatApiService 中添加方法
public void sendTemplateMessage(String openId, String templateId, String url, Map<String, TemplateData> dataMap) {
String accessToken = getAccessToken();
if (accessToken == null) {
return;
}
String sendUrl = String.format("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s", accessToken);
// 构造请求体
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("touser", openId);
requestBody.put("template_id", templateId);
requestBody.put("url", url); // 点击模板消息后跳转的链接
requestBody.put("data", dataMap);
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
String response = restTemplate.postForObject(sendUrl, entity, String.class);
System.out.println("发送模板消息响应: " + response);
// 处理响应...
}
TemplateData 类:
@Data
public class TemplateData {
private String value;
private String color;
}
调用示例:
// 在某个业务逻辑中调用
Map<String, TemplateData> data = new HashMap<>();
data.put("first", new TemplateData("您的订单已支付成功!", "#173177"));
data.put("keyword1", new TemplateData("202510271234", "#173177"));
data.put("keyword2", new TemplateData("¥99.00", "#173177"));
data.put("remark", new TemplateData("感谢您的惠顾!", "#173177"));
weChatApiService.sendTemplateMessage("用户的OpenID", "模板ID", "https://www.your-site.com/order", data);
微信支付
微信支付是一个相对复杂的功能,涉及流程如下:
- 配置支付目录:在公众号后台「微信支付」->「开发配置」中,配置你的支付授权目录。
- 获取OpenID:用户必须在公众号内完成OAuth2.0授权,才能获取到用户的OpenID,这通常通过引导用户点击一个链接完成。
- 统一下单:你的服务器调用微信支付的「统一下单」API (
https://api.mch.weixin.qq.com/pay/unifiedorder),生成预支付交易会话标识 (prepay_id)。 - 生成支付参数:你的服务器使用
prepay_id、AppID、时间戳、随机数等再次签名,生成JSAPI支付所需参数。 - 前端调起支付:将生成的参数传递给前端(网页或小程序),前端调用微信JS-SDK的
chooseWXPay方法调起支付收银台。
由于微信支付涉及签名、证书、回调处理等多个复杂环节,强烈建议使用成熟的第三方SDK,如 Weixin-Java-Tools,它极大地简化了支付流程。
总结与建议
- 官方文档是第一手资料:遇到任何问题,首先查阅 微信公众平台官方文档。
- 善用工具:使用Postman等工具来测试API,比直接在代码中调试更方便。
- 安全性:注意保护你的AppSecret,对API调用进行签名验证,防止恶意请求。
- 异步处理:对于耗时操作(如调用某些API),考虑使用消息队列(如RabbitMQ, Kafka)进行异步处理,避免用户等待超时。
- 使用成熟框架:对于支付等复杂功能,不要重复造轮子,使用像WxJava这样经过大量项目验证的框架可以节省你大量的时间和精力。
这份教程为你提供了一个完整的开发路径,从最基础的接入,到消息处理,再到API调用,最后是高级功能,希望它能帮助你顺利开展微信公众平台的后端开发工作。
