微信第三方平台业务流程图
这里对接微信开放平台使用了开源框架
依赖:
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-open</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>com.github.jedis-lock</groupId>
<artifactId>jedis-lock</artifactId>
<version>1.0.0</version>
</dependency>
配置文件:
wechat:
open:
#微信第三方平台对应APPID
componentAppId: ""
#微信第三方平台对应APPSECRET
componentSecret: ""
#微信第三方平台消息校验Token
componentToken: ""
#微信第三方平台消息加解密Key
componentAesKey: ""
# 微信open专用 redis
redis:
database:
host:
port:
timeout: # 连接超时时长(毫秒)
jedis:
pool:
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 10 # 连接池中的最大空闲连接
min-idle: 5 # 连接池中的最小空闲连接
基础配置类(通用):
(读取配置文件生成access_token)
package com.eaton.guan.wx.program.cms.config;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author hfbin
* @email huangfubin00@gmail.com
* @date 2019-10-12 00:17
* @description:
*/
@ConfigurationProperties(prefix = "wechat.open")
public class WechatOpenProperties {
/**
* 设置微信三方平台的appid
*/
private String componentAppId;
/**
* 设置微信三方平台的app secret
*/
private String componentSecret;
/**
* 设置微信三方平台的token
*/
private String componentToken;
/**
* 设置微信三方平台的EncodingAESKey
*/
private String componentAesKey;
public String getComponentAppId() {
return componentAppId;
}
public void setComponentAppId(String componentAppId) {
this.componentAppId = componentAppId;
}
public String getComponentSecret() {
return componentSecret;
}
public void setComponentSecret(String componentSecret) {
this.componentSecret = componentSecret;
}
public String getComponentToken() {
return componentToken;
}
public void setComponentToken(String componentToken) {
this.componentToken = componentToken;
}
public String getComponentAesKey() {
return componentAesKey;
}
public void setComponentAesKey(String componentAesKey) {
this.componentAesKey = componentAesKey;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this,
ToStringStyle.MULTI_LINE_STYLE);
}
}
(redis读取(存储access_token))
package com.eaton.guan.wx.program.cms.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Protocol;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocketFactory;
/**
* @author hfbin
* @email huangfubin00@gmail.com
* @date 2019-10-12 00:17
* @description:
*/
@ConfigurationProperties(prefix = "wechat.redis")
public class RedisProperies extends JedisPoolConfig {
private String host = Protocol.DEFAULT_HOST;
private int port = Protocol.DEFAULT_PORT;
private String password;
private int database = 1;
private int connectionTimeout = Protocol.DEFAULT_TIMEOUT;
private int soTimeout = Protocol.DEFAULT_TIMEOUT;
private String clientName;
private boolean ssl;
private SSLSocketFactory sslSocketFactory;
private SSLParameters sslParameters;
private HostnameVerifier hostnameVerifier;
public boolean isSsl() {
return ssl;
}
public void setSsl(boolean ssl) {
this.ssl = ssl;
}
public SSLSocketFactory getSslSocketFactory() {
return sslSocketFactory;
}
public void setSslSocketFactory(SSLSocketFactory sslSocketFactory) {
this.sslSocketFactory = sslSocketFactory;
}
public SSLParameters getSslParameters() {
return sslParameters;
}
public void setSslParameters(SSLParameters sslParameters) {
this.sslParameters = sslParameters;
}
public HostnameVerifier getHostnameVerifier() {
return hostnameVerifier;
}
public void setHostnameVerifier(HostnameVerifier hostnameVerifier) {
this.hostnameVerifier = hostnameVerifier;
}
public String getHost() {
return host;
}
public void setHost(String host) {
if (host == null || "".equals(host)) {
host = Protocol.DEFAULT_HOST;
}
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
if ("".equals(password)) {
password = null;
}
this.password = password;
}
public int getDatabase() {
return database;
}
public void setDatabase(int database) {
this.database = database;
}
public String getClientName() {
return clientName;
}
public void setClientName(String clientName) {
if ("".equals(clientName)) {
clientName = null;
}
this.clientName = clientName;
}
public int getConnectionTimeout() {
return connectionTimeout;
}
public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
public int getSoTimeout() {
return soTimeout;
}
public void setSoTimeout(int soTimeout) {
this.soTimeout = soTimeout;
}
}
第三方平台审批时需要使用的方法(通用):
(一个授权事件接收URL,一个消息与事件接收URL)
package com.github.binarywang.demo.wx.open.controller;
import com.github.binarywang.demo.wx.open.service.WxOpenServiceDemo;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @author <a href="https://github.com/007gzs">007</a>
*/
@RestController
@RequestMapping("/notify")
public class WechatNotifyController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
protected WxOpenServiceDemo wxOpenService;
@RequestMapping("/receive_ticket")
public Object receiveTicket(@RequestBody(required = false) String requestBody, @RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce, @RequestParam("signature") String signature,
@RequestParam(name = "encrypt_type", required = false) String encType,
@RequestParam(name = "msg_signature", required = false) String msgSignature) {
this.logger.info(
"\n接收微信请求:[signature=[{}], encType=[{}], msgSignature=[{}],"
+ " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
signature, encType, msgSignature, timestamp, nonce, requestBody);
if (!StringUtils.equalsIgnoreCase("aes", encType)
|| !wxOpenService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) {
throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
}
// aes加密的消息
WxOpenXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedXml(requestBody,
wxOpenService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
this.logger.debug("\n消息解密后内容为:\n{} ", inMessage.toString());
try {
String out = wxOpenService.getWxOpenComponentService().route(inMessage);
this.logger.debug("\n组装回复信息:{}", out);
} catch (WxErrorException e) {
this.logger.error("receive_ticket", e);
}
return "success";
}
@RequestMapping("{appId}/callback")
public Object callback(@RequestBody(required = false) String requestBody,
@PathVariable("appId") String appId,
@RequestParam("signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam("openid") String openid,
@RequestParam("encrypt_type") String encType,
@RequestParam("msg_signature") String msgSignature) {
this.logger.info(
"\n接收微信请求:[appId=[{}], openid=[{}], signature=[{}], encType=[{}], msgSignature=[{}],"
+ " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
appId, openid, signature, encType, msgSignature, timestamp, nonce, requestBody);
if (!StringUtils.equalsIgnoreCase("aes", encType)
|| !wxOpenService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) {
throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
}
String out = "";
// aes加密的消息
WxMpXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedMpXml(requestBody,
wxOpenService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
this.logger.debug("\n消息解密后内容为:\n{} ", inMessage.toString());
// 全网发布测试用例
if (StringUtils.equalsAnyIgnoreCase(appId, "wxd101a85aa106f53e", "wx570bc396a51b8ff8")) {
try {
if (StringUtils.equals(inMessage.getMsgType(), "text")) {
if (StringUtils.equals(inMessage.getContent(), "TESTCOMPONENT_MSG_TYPE_TEXT")) {
out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml(
WxMpXmlOutMessage.TEXT().content("TESTCOMPONENT_MSG_TYPE_TEXT_callback")
.fromUser(inMessage.getToUser())
.toUser(inMessage.getFromUser())
.build(),
wxOpenService.getWxOpenConfigStorage()
);
} else if (StringUtils.startsWith(inMessage.getContent(), "QUERY_AUTH_CODE:")) {
String msg = inMessage.getContent().replace("QUERY_AUTH_CODE:", "") + "_from_api";
WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(msg).toUser(inMessage.getFromUser()).build();
wxOpenService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage);
}
} else if (StringUtils.equals(inMessage.getMsgType(), "event")) {
WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(inMessage.getEvent() + "from_callback").toUser(inMessage.getFromUser()).build();
wxOpenService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage);
}
} catch (WxErrorException e) {
logger.error("callback", e);
}
}else{
WxMpXmlOutMessage outMessage = wxOpenService.getWxOpenMessageRouter().route(inMessage, appId);
if(outMessage != null){
out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml(outMessage, wxOpenService.getWxOpenConfigStorage());
}
}
return out;
}
}
(调用第三方平台统一入口,在需要调用的地方注入这个service)
(可以直接使用,框架会在调用的时候自动加上access_token)
package com.github.binarywang.demo.wx.open.service;
import com.github.binarywang.demo.wx.open.config.RedisProperies;
import com.github.binarywang.demo.wx.open.config.WechatOpenProperties;
import me.chanjar.weixin.open.api.impl.WxOpenInRedisConfigStorage;
import me.chanjar.weixin.open.api.impl.WxOpenMessageRouter;
import me.chanjar.weixin.open.api.impl.WxOpenServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;
import redis.clients.jedis.JedisPool;
import javax.annotation.PostConstruct;
/**
* @author <a href="https://github.com/007gzs">007</a>
*/
@Service
@EnableConfigurationProperties({WechatOpenProperties.class, RedisProperies.class})
public class WxOpenServiceDemo extends WxOpenServiceImpl {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private WechatOpenProperties wechatOpenProperties;
@Autowired
private RedisProperies redisProperies;
private volatile static JedisPool pool;
private WxOpenMessageRouter wxOpenMessageRouter;
@PostConstruct
public void init() {
WxOpenInRedisConfigStorage inRedisConfigStorage = new WxOpenInRedisConfigStorage(getJedisPool());
inRedisConfigStorage.setComponentAppId(wechatOpenProperties.getComponentAppId());
inRedisConfigStorage.setComponentAppSecret(wechatOpenProperties.getComponentSecret());
inRedisConfigStorage.setComponentToken(wechatOpenProperties.getComponentToken());
inRedisConfigStorage.setComponentAesKey(wechatOpenProperties.getComponentAesKey());
setWxOpenConfigStorage(inRedisConfigStorage);
wxOpenMessageRouter = new WxOpenMessageRouter(this);
wxOpenMessageRouter.rule().handler((wxMpXmlMessage, map, wxMpService, wxSessionManager) -> {
logger.info("\n接收到 {} 公众号请求消息,内容:{}", wxMpService.getWxMpConfigStorage().getAppId(), wxMpXmlMessage);
return null;
}).next();
}
public WxOpenMessageRouter getWxOpenMessageRouter(){
return wxOpenMessageRouter;
}
private JedisPool getJedisPool() {
if (pool == null) {
synchronized (WxOpenServiceDemo.class) {
if (pool == null) {
pool = new JedisPool(redisProperies, redisProperies.getHost(),
redisProperies.getPort(), redisProperies.getConnectionTimeout(),
redisProperies.getSoTimeout(), redisProperies.getPassword(),
redisProperies.getDatabase(), redisProperies.getClientName(),
redisProperies.isSsl(), redisProperies.getSslSocketFactory(),
redisProperies.getSslParameters(), redisProperies.getHostnameVerifier());
}
}
}
return pool;
}
}
查询可调用方法demo(这里使用的是idea的全局搜索):
上传代码:
在分发模板库代码的时候官方会让你在小程序代码上上传ext.json(这个可以不用上传只需要在page里填写对应的首页地址就行)