diff --git a/pom.xml b/pom.xml
index 8367f9f4..e83c2c1b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -106,6 +106,7 @@
2.5
1.10
9.3.0.RC0
+ 2.9.0
@@ -193,6 +194,11 @@
guava
${guava.version}
+
+ redis.clients
+ jedis
+ ${jedis.version}
+
diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml
index 62ef79e0..32f04f25 100644
--- a/weixin-java-mp/pom.xml
+++ b/weixin-java-mp/pom.xml
@@ -44,6 +44,10 @@
2.9.4
test
+
+ redis.clients
+ jedis
+
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java
new file mode 100644
index 00000000..89648268
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java
@@ -0,0 +1,309 @@
+package me.chanjar.weixin.mp.api;
+
+import me.chanjar.weixin.common.bean.WxAccessToken;
+import me.chanjar.weixin.common.util.ToStringUtils;
+import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder;
+import redis.clients.jedis.Jedis;
+
+import javax.net.ssl.SSLContext;
+import java.io.File;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * 基于Redis的微信配置provider
+ *
+ * @author lly835
+ */
+public class WxMpInRedisConfigStorage implements WxMpConfigStorage {
+
+ private final static String ACCESS_TOKEN_KEY = "wechat_access_token_";
+
+ private final static String JSAPI_TICKET_KEY = "wechat_jsapi_ticket_";
+
+ private final static String CARDAPI_TICKET_KEY = "wechat_cardapi_ticket_";
+
+ protected volatile String appId;
+ protected volatile String secret;
+ protected volatile String partnerId;
+ protected volatile String partnerKey;
+ protected volatile String notifyURL;
+ protected volatile String tradeType;
+ protected volatile String token;
+ protected volatile String accessToken;
+ protected volatile String aesKey;
+ protected volatile long expiresTime;
+
+ protected volatile String oauth2redirectUri;
+
+ protected volatile String httpProxyHost;
+ protected volatile int httpProxyPort;
+ protected volatile String httpProxyUsername;
+ protected volatile String httpProxyPassword;
+
+ protected volatile String jsapiTicket;
+ protected volatile long jsapiTicketExpiresTime;
+
+ protected volatile String cardApiTicket;
+ protected volatile long cardApiTicketExpiresTime;
+
+ protected Lock accessTokenLock = new ReentrantLock();
+ protected Lock jsapiTicketLock = new ReentrantLock();
+ protected Lock cardApiTicketLock = new ReentrantLock();
+
+ /**
+ * 临时文件目录
+ */
+ protected volatile File tmpDirFile;
+
+ protected volatile SSLContext sslContext;
+
+ protected volatile ApacheHttpClientBuilder apacheHttpClientBuilder;
+
+ protected Jedis jedis;
+
+ @Override
+ public String getAccessToken() {
+ return jedis.get(ACCESS_TOKEN_KEY.concat(appId));
+ }
+
+ @Override
+ public Lock getAccessTokenLock() {
+ return this.accessTokenLock;
+ }
+
+ @Override
+ public boolean isAccessTokenExpired() {
+ return getAccessToken() == null ? true : false;
+ }
+
+ @Override
+ public synchronized void updateAccessToken(WxAccessToken accessToken) {
+ updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
+ }
+
+ @Override
+ public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
+ this.accessToken = accessToken;
+ this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
+ jedis.set(ACCESS_TOKEN_KEY.concat(appId), accessToken);
+ jedis.expire(ACCESS_TOKEN_KEY.concat(appId), expiresInSeconds - 200);
+ }
+
+ @Override
+ public void expireAccessToken() {
+ this.expiresTime = 0;
+ }
+
+ @Override
+ public String getJsapiTicket() {
+ return this.jsapiTicket;
+ }
+
+ public void setJsapiTicket(String jsapiTicket) {
+ this.jsapiTicket = jsapiTicket;
+ }
+
+ @Override
+ public Lock getJsapiTicketLock() {
+ return this.jsapiTicketLock;
+ }
+
+ @Override
+ public boolean isJsapiTicketExpired() {
+ return getJsapiTicket() == null ? true : false;
+ }
+
+ @Override
+ public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) {
+ this.jsapiTicket = jsapiTicket;
+ // 预留200秒的时间
+ this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
+ jedis.set(JSAPI_TICKET_KEY.concat(appId), accessToken);
+ jedis.expire(JSAPI_TICKET_KEY.concat(appId), expiresInSeconds - 200);
+ }
+
+ @Override
+ public void expireJsapiTicket() {
+ this.jsapiTicketExpiresTime = 0;
+ }
+
+ /**
+ * 卡券api_ticket
+ */
+ @Override
+ public String getCardApiTicket() {
+ return this.cardApiTicket;
+ }
+
+ @Override
+ public Lock getCardApiTicketLock() {
+ return this.cardApiTicketLock;
+ }
+
+ @Override
+ public boolean isCardApiTicketExpired() {
+ return System.currentTimeMillis() > this.cardApiTicketExpiresTime;
+ }
+
+ @Override
+ public synchronized void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) {
+ this.cardApiTicket = cardApiTicket;
+ // 预留200秒的时间
+ this.cardApiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
+ jedis.set(CARDAPI_TICKET_KEY.concat(appId), accessToken);
+ jedis.expire(CARDAPI_TICKET_KEY.concat(appId), expiresInSeconds - 200);
+ }
+
+ @Override
+ public void expireCardApiTicket() {
+ this.cardApiTicketExpiresTime = 0;
+ }
+
+ @Override
+ public String getAppId() {
+ return this.appId;
+ }
+
+ public void setAppId(String appId) {
+ this.appId = appId;
+ }
+
+ @Override
+ public String getSecret() {
+ return this.secret;
+ }
+
+ public void setSecret(String secret) {
+ this.secret = secret;
+ }
+
+ @Override
+ public String getToken() {
+ return this.token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ @Override
+ public long getExpiresTime() {
+ return this.expiresTime;
+ }
+
+ public void setExpiresTime(long expiresTime) {
+ this.expiresTime = expiresTime;
+ }
+
+ @Override
+ public String getAesKey() {
+ return this.aesKey;
+ }
+
+ public void setAesKey(String aesKey) {
+ this.aesKey = aesKey;
+ }
+
+ @Override
+ public String getOauth2redirectUri() {
+ return this.oauth2redirectUri;
+ }
+
+ public void setOauth2redirectUri(String oauth2redirectUri) {
+ this.oauth2redirectUri = oauth2redirectUri;
+ }
+
+ @Override
+ public String getHttpProxyHost() {
+ return this.httpProxyHost;
+ }
+
+ public void setHttpProxyHost(String httpProxyHost) {
+ this.httpProxyHost = httpProxyHost;
+ }
+
+ @Override
+ public int getHttpProxyPort() {
+ return this.httpProxyPort;
+ }
+
+ public void setHttpProxyPort(int httpProxyPort) {
+ this.httpProxyPort = httpProxyPort;
+ }
+
+ @Override
+ public String getHttpProxyUsername() {
+ return this.httpProxyUsername;
+ }
+
+ public void setHttpProxyUsername(String httpProxyUsername) {
+ this.httpProxyUsername = httpProxyUsername;
+ }
+
+ @Override
+ public String getHttpProxyPassword() {
+ return this.httpProxyPassword;
+ }
+
+ public void setHttpProxyPassword(String httpProxyPassword) {
+ this.httpProxyPassword = httpProxyPassword;
+ }
+
+ @Override
+ public String toString() {
+ return ToStringUtils.toSimpleString(this);
+ }
+
+ public void setPartnerId(String partnerId) {
+ this.partnerId = partnerId;
+ }
+
+ public void setPartnerKey(String partnerKey) {
+ this.partnerKey = partnerKey;
+ }
+
+
+ public String getNotifyURL() {
+ return notifyURL;
+ }
+
+ public void setNotifyURL(String notifyURL) {
+ this.notifyURL = notifyURL;
+ }
+
+ public String getTradeType() {
+ return tradeType;
+ }
+
+ public void setTradeType(String tradeType) {
+ this.tradeType = tradeType;
+ }
+
+ @Override
+ public File getTmpDirFile() {
+ return this.tmpDirFile;
+ }
+
+ public void setTmpDirFile(File tmpDirFile) {
+ this.tmpDirFile = tmpDirFile;
+ }
+
+ @Override
+ public ApacheHttpClientBuilder getApacheHttpClientBuilder() {
+ return this.apacheHttpClientBuilder;
+ }
+
+ public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) {
+ this.apacheHttpClientBuilder = apacheHttpClientBuilder;
+ }
+
+ @Override
+ public boolean autoRefreshToken() {
+ return true;
+ }
+
+ public void setJedis(Jedis jedis) {
+ this.jedis = jedis;
+ }
+}