| @@ -26,6 +26,7 @@ test-config.xml | |||||
| /gradle/ | /gradle/ | ||||
| *.bat | *.bat | ||||
| /gradlew | /gradlew | ||||
| **/build/ | |||||
| # OSX | # OSX | ||||
| # Icon must end with two \r | # Icon must end with two \r | ||||
| @@ -2,7 +2,7 @@ allprojects { | |||||
| apply plugin: 'maven' | apply plugin: 'maven' | ||||
| group = 'com.github.binarywang' | group = 'com.github.binarywang' | ||||
| version = '2.3.0' | |||||
| version = '2.4.0-SNAPSHOT' | |||||
| } | } | ||||
| subprojects { | subprojects { | ||||
| @@ -21,6 +21,7 @@ subprojects { | |||||
| dependencies { | dependencies { | ||||
| compile group: 'org.slf4j', name: 'slf4j-api', version:'1.7.10' | compile group: 'org.slf4j', name: 'slf4j-api', version:'1.7.10' | ||||
| compile group: 'org.apache.httpcomponents', name: 'httpmime', version:'4.5' | compile group: 'org.apache.httpcomponents', name: 'httpmime', version:'4.5' | ||||
| compile group: 'org.apache.httpcomponents', name: 'httpclient', version:'4.5' | |||||
| compile group: 'com.google.code.gson', name: 'gson', version:'2.7' | compile group: 'com.google.code.gson', name: 'gson', version:'2.7' | ||||
| compile group: 'com.google.guava', name: 'guava', version:'19.0' | compile group: 'com.google.guava', name: 'guava', version:'19.0' | ||||
| compile group: 'commons-codec', name: 'commons-codec', version:'1.10' | compile group: 'commons-codec', name: 'commons-codec', version:'1.10' | ||||
| @@ -5,7 +5,7 @@ | |||||
| <modelVersion>4.0.0</modelVersion> | <modelVersion>4.0.0</modelVersion> | ||||
| <groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
| <artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
| <version>2.3.0</version> | |||||
| <version>2.4.0</version> | |||||
| <packaging>pom</packaging> | <packaging>pom</packaging> | ||||
| <name>WeiXin Java Tools - Parent</name> | <name>WeiXin Java Tools - Parent</name> | ||||
| <description>微信公众号、企业号上级POM</description> | <description>微信公众号、企业号上级POM</description> | ||||
| @@ -54,6 +54,21 @@ | |||||
| <email>aimilin@yeah.net</email> | <email>aimilin@yeah.net</email> | ||||
| <url>https://github.com/aimilin6688</url> | <url>https://github.com/aimilin6688</url> | ||||
| </developer> | </developer> | ||||
| <developer> | |||||
| <name>Eric.Tsai</name> | |||||
| <email>xiaodong.cai.ks@gmail.com</email> | |||||
| <url>https://github.com/iwareserictsai</url> | |||||
| </developer> | |||||
| <developer> | |||||
| <name>withinthefog</name> | |||||
| <email>withinthefog@gmail.com</email> | |||||
| <url>https://github.com/withinthefog</url> | |||||
| </developer> | |||||
| <developer> | |||||
| <name>Keung</name> | |||||
| <email>dongfuqiang1988@163.com</email> | |||||
| <url>https://github.com/johnnytung</url> | |||||
| </developer> | |||||
| </developers> | </developers> | ||||
| <scm> | <scm> | ||||
| @@ -70,6 +85,9 @@ | |||||
| </modules> | </modules> | ||||
| <properties> | <properties> | ||||
| <maven.compiler.source>1.7</maven.compiler.source> | |||||
| <maven.compiler.target>1.7</maven.compiler.target> | |||||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||||
| <downloadJavadocs>true</downloadJavadocs> | <downloadJavadocs>true</downloadJavadocs> | ||||
| <downloadSources>true</downloadSources> | <downloadSources>true</downloadSources> | ||||
| @@ -97,6 +115,11 @@ | |||||
| <version>${logback.version}</version> | <version>${logback.version}</version> | ||||
| <scope>test</scope> | <scope>test</scope> | ||||
| </dependency> | </dependency> | ||||
| <dependency> | |||||
| <groupId>org.apache.httpcomponents</groupId> | |||||
| <artifactId>httpclient</artifactId> | |||||
| <version>${httpclient.version}</version> | |||||
| </dependency> | |||||
| <dependency> | <dependency> | ||||
| <groupId>org.apache.httpcomponents</groupId> | <groupId>org.apache.httpcomponents</groupId> | ||||
| <artifactId>httpmime</artifactId> | <artifactId>httpmime</artifactId> | ||||
| @@ -296,10 +319,8 @@ | |||||
| <plugin> | <plugin> | ||||
| <groupId>org.apache.maven.plugins</groupId> | <groupId>org.apache.maven.plugins</groupId> | ||||
| <artifactId>maven-compiler-plugin</artifactId> | <artifactId>maven-compiler-plugin</artifactId> | ||||
| <version>2.3.2</version> | |||||
| <version>3.6.0</version> | |||||
| <configuration> | <configuration> | ||||
| <source>1.7</source> | |||||
| <target>1.7</target> | |||||
| <encoding>UTF-8</encoding> | <encoding>UTF-8</encoding> | ||||
| </configuration> | </configuration> | ||||
| </plugin> | </plugin> | ||||
| @@ -6,7 +6,7 @@ dependencies { | |||||
| testCompile group: 'org.testng', name: 'testng', version:'6.8.7' | testCompile group: 'org.testng', name: 'testng', version:'6.8.7' | ||||
| testCompile group: 'org.mockito', name: 'mockito-all', version:'1.9.5' | testCompile group: 'org.mockito', name: 'mockito-all', version:'1.9.5' | ||||
| testCompile group: 'com.google.inject', name: 'guice', version:'3.0' | testCompile group: 'com.google.inject', name: 'guice', version:'3.0' | ||||
| testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version:'9.3.0.M0' | |||||
| testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version:'9.3.0.M0' | |||||
| testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version:'9.3.0.RC0' | |||||
| testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version:'9.3.0.RC0' | |||||
| } | } | ||||
| test.useTestNG() | test.useTestNG() | ||||
| @@ -6,7 +6,7 @@ | |||||
| <parent> | <parent> | ||||
| <groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
| <artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
| <version>2.3.0</version> | |||||
| <version>2.4.0</version> | |||||
| </parent> | </parent> | ||||
| <artifactId>weixin-java-common</artifactId> | <artifactId>weixin-java-common</artifactId> | ||||
| @@ -18,8 +18,13 @@ public class WxConsts { | |||||
| public static final String XML_MSG_LOCATION = "location"; | public static final String XML_MSG_LOCATION = "location"; | ||||
| public static final String XML_MSG_LINK = "link"; | public static final String XML_MSG_LINK = "link"; | ||||
| public static final String XML_MSG_EVENT = "event"; | public static final String XML_MSG_EVENT = "event"; | ||||
| public static final String XML_MSG_DEVICE_TEXT = "device_text"; | |||||
| public static final String XML_MSG_DEVICE_EVENT = "device_event"; | |||||
| public static final String XML_MSG_DEVICE_STATUS = "device_status"; | |||||
| public static final String XML_MSG_HARDWARE = "hardware"; | |||||
| public static final String XML_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; | public static final String XML_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; | ||||
| /////////////////////// | /////////////////////// | ||||
| // 主动发送消息(即客服消息)的消息类型 | // 主动发送消息(即客服消息)的消息类型 | ||||
| /////////////////////// | /////////////////////// | ||||
| @@ -1,5 +1,8 @@ | |||||
| package me.chanjar.weixin.common.bean.menu; | package me.chanjar.weixin.common.bean.menu; | ||||
| import me.chanjar.weixin.common.util.ToStringUtils; | |||||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||||
| import java.io.InputStream; | import java.io.InputStream; | ||||
| import java.io.InputStreamReader; | import java.io.InputStreamReader; | ||||
| import java.io.Serializable; | import java.io.Serializable; | ||||
| @@ -7,11 +10,8 @@ import java.nio.charset.StandardCharsets; | |||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.List; | import java.util.List; | ||||
| import me.chanjar.weixin.common.bean.menu.WxMenuButton; | |||||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||||
| /** | /** | ||||
| * 企业号菜单 | |||||
| * 菜单(公众号和企业号共用的) | |||||
| * | * | ||||
| * @author Daniel Qian | * @author Daniel Qian | ||||
| */ | */ | ||||
| @@ -61,9 +61,7 @@ public class WxMenu implements Serializable { | |||||
| @Override | @Override | ||||
| public String toString() { | public String toString() { | ||||
| return "WxMenu{" + | |||||
| "buttons=" + this.buttons + | |||||
| '}'; | |||||
| return ToStringUtils.toSimpleString(this); | |||||
| } | } | ||||
| } | } | ||||
| @@ -6,7 +6,7 @@ dependencies { | |||||
| testCompile group: 'org.testng', name: 'testng', version:'6.8.7' | testCompile group: 'org.testng', name: 'testng', version:'6.8.7' | ||||
| testCompile group: 'org.mockito', name: 'mockito-all', version:'1.9.5' | testCompile group: 'org.mockito', name: 'mockito-all', version:'1.9.5' | ||||
| testCompile group: 'com.google.inject', name: 'guice', version:'3.0' | testCompile group: 'com.google.inject', name: 'guice', version:'3.0' | ||||
| testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version:'9.3.0.M0' | |||||
| testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version:'9.3.0.M0' | |||||
| testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version:'9.3.0.RC0' | |||||
| testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version:'9.3.0.RC0' | |||||
| } | } | ||||
| test.useTestNG() | test.useTestNG() | ||||
| @@ -6,7 +6,7 @@ | |||||
| <parent> | <parent> | ||||
| <groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
| <artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
| <version>2.3.0</version> | |||||
| <version>2.4.0</version> | |||||
| </parent> | </parent> | ||||
| <artifactId>weixin-java-cp</artifactId> | <artifactId>weixin-java-cp</artifactId> | ||||
| @@ -45,7 +45,7 @@ public interface WxCpConfigStorage { | |||||
| String getCorpSecret(); | String getCorpSecret(); | ||||
| String getAgentId(); | |||||
| Integer getAgentId(); | |||||
| String getToken(); | String getToken(); | ||||
| @@ -19,7 +19,7 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
| protected volatile String token; | protected volatile String token; | ||||
| protected volatile String accessToken; | protected volatile String accessToken; | ||||
| protected volatile String aesKey; | protected volatile String aesKey; | ||||
| protected volatile String agentId; | |||||
| protected volatile Integer agentId; | |||||
| protected volatile long expiresTime; | protected volatile long expiresTime; | ||||
| protected volatile String oauth2redirectUri; | protected volatile String oauth2redirectUri; | ||||
| @@ -146,11 +146,11 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public String getAgentId() { | |||||
| public Integer getAgentId() { | |||||
| return this.agentId; | return this.agentId; | ||||
| } | } | ||||
| public void setAgentId(String agentId) { | |||||
| public void setAgentId(Integer agentId) { | |||||
| this.agentId = agentId; | this.agentId = agentId; | ||||
| } | } | ||||
| @@ -1,267 +1,259 @@ | |||||
| package me.chanjar.weixin.cp.api; | |||||
| import java.io.File; | |||||
| import me.chanjar.weixin.common.bean.WxAccessToken; | |||||
| import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder; | |||||
| import redis.clients.jedis.Jedis; | |||||
| import redis.clients.jedis.JedisPool; | |||||
| /** | |||||
| * Jedis client implementor for wechat config storage | |||||
| * | |||||
| * @author gaigeshen | |||||
| */ | |||||
| public class WxCpJedisConfigStorage implements WxCpConfigStorage { | |||||
| /* Redis keys here */ | |||||
| private static final String ACCESS_TOKEN_KEY = "WX_CP_ACCESS_TOKEN"; | |||||
| private static final String ACCESS_TOKEN_EXPIRES_TIME_KEY = "WX_CP_ACCESS_TOKEN_EXPIRES_TIME"; | |||||
| private static final String JS_API_TICKET_KEY = "WX_CP_JS_API_TICKET"; | |||||
| private static final String JS_API_TICKET_EXPIRES_TIME_KEY = "WX_CP_JS_API_TICKET_EXPIRES_TIME"; | |||||
| private volatile String corpId; | |||||
| private volatile String corpSecret; | |||||
| private volatile String token; | |||||
| private volatile String aesKey; | |||||
| private volatile String agentId; | |||||
| private volatile String oauth2redirectUri; | |||||
| private volatile String httpProxyHost; | |||||
| private volatile int httpProxyPort; | |||||
| private volatile String httpProxyUsername; | |||||
| private volatile String httpProxyPassword; | |||||
| private volatile File tmpDirFile; | |||||
| private volatile ApacheHttpClientBuilder apacheHttpClientBuilder; | |||||
| /* Redis clients pool */ | |||||
| private final JedisPool jedisPool; | |||||
| public WxCpJedisConfigStorage(String host, int port) { | |||||
| this.jedisPool = new JedisPool(host, port); | |||||
| } | |||||
| /** | |||||
| * | |||||
| * This method will be destroy jedis pool | |||||
| */ | |||||
| public void destroy() { | |||||
| this.jedisPool.destroy(); | |||||
| } | |||||
| @Override | |||||
| public String getAccessToken() { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| return jedis.get(ACCESS_TOKEN_KEY); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public boolean isAccessTokenExpired() { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY); | |||||
| if (expiresTimeStr != null) { | |||||
| Long expiresTime = Long.parseLong(expiresTimeStr); | |||||
| return System.currentTimeMillis() > expiresTime; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void expireAccessToken() { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| jedis.set(ACCESS_TOKEN_EXPIRES_TIME_KEY, "0"); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public synchronized void updateAccessToken(WxAccessToken accessToken) { | |||||
| this.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | |||||
| } | |||||
| @Override | |||||
| public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| jedis.set(ACCESS_TOKEN_KEY, accessToken); | |||||
| jedis.set(ACCESS_TOKEN_EXPIRES_TIME_KEY, | |||||
| (System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L) + ""); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public String getJsapiTicket() { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| return jedis.get(JS_API_TICKET_KEY); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public boolean isJsapiTicketExpired() { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| String expiresTimeStr = jedis.get(JS_API_TICKET_EXPIRES_TIME_KEY); | |||||
| if (expiresTimeStr != null) { | |||||
| Long expiresTime = Long.parseLong(expiresTimeStr); | |||||
| return System.currentTimeMillis() > expiresTime; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void expireJsapiTicket() { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY, "0"); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| jedis.set(JS_API_TICKET_KEY, jsapiTicket); | |||||
| jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY, | |||||
| (System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L + "")); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public String getCorpId() { | |||||
| return this.corpId; | |||||
| } | |||||
| @Override | |||||
| public String getCorpSecret() { | |||||
| return this.corpSecret; | |||||
| } | |||||
| @Override | |||||
| public String getAgentId() { | |||||
| return this.agentId; | |||||
| } | |||||
| @Override | |||||
| public String getToken() { | |||||
| return this.token; | |||||
| } | |||||
| @Override | |||||
| public String getAesKey() { | |||||
| return this.aesKey; | |||||
| } | |||||
| @Override | |||||
| public long getExpiresTime() { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY); | |||||
| if (expiresTimeStr != null) { | |||||
| Long expiresTime = Long.parseLong(expiresTimeStr); | |||||
| return expiresTime; | |||||
| } | |||||
| return 0L; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public String getOauth2redirectUri() { | |||||
| return this.oauth2redirectUri; | |||||
| } | |||||
| @Override | |||||
| public String getHttpProxyHost() { | |||||
| return this.httpProxyHost; | |||||
| } | |||||
| @Override | |||||
| public int getHttpProxyPort() { | |||||
| return this.httpProxyPort; | |||||
| } | |||||
| @Override | |||||
| public String getHttpProxyUsername() { | |||||
| return this.httpProxyUsername; | |||||
| } | |||||
| @Override | |||||
| public String getHttpProxyPassword() { | |||||
| return this.httpProxyPassword; | |||||
| } | |||||
| @Override | |||||
| public File getTmpDirFile() { | |||||
| return this.tmpDirFile; | |||||
| } | |||||
| @Override | |||||
| public ApacheHttpClientBuilder getApacheHttpClientBuilder() { | |||||
| return this.apacheHttpClientBuilder; | |||||
| } | |||||
| public void setCorpId(String corpId) { | |||||
| this.corpId = corpId; | |||||
| } | |||||
| public void setCorpSecret(String corpSecret) { | |||||
| this.corpSecret = corpSecret; | |||||
| } | |||||
| public void setToken(String token) { | |||||
| this.token = token; | |||||
| } | |||||
| public void setAesKey(String aesKey) { | |||||
| this.aesKey = aesKey; | |||||
| } | |||||
| public void setAgentId(String agentId) { | |||||
| this.agentId = agentId; | |||||
| } | |||||
| // ============================ Setters below | |||||
| public void setOauth2redirectUri(String oauth2redirectUri) { | |||||
| this.oauth2redirectUri = oauth2redirectUri; | |||||
| } | |||||
| public void setHttpProxyHost(String httpProxyHost) { | |||||
| this.httpProxyHost = httpProxyHost; | |||||
| } | |||||
| public void setHttpProxyPort(int httpProxyPort) { | |||||
| this.httpProxyPort = httpProxyPort; | |||||
| } | |||||
| public void setHttpProxyUsername(String httpProxyUsername) { | |||||
| this.httpProxyUsername = httpProxyUsername; | |||||
| } | |||||
| public void setHttpProxyPassword(String httpProxyPassword) { | |||||
| this.httpProxyPassword = httpProxyPassword; | |||||
| } | |||||
| public void setTmpDirFile(File tmpDirFile) { | |||||
| this.tmpDirFile = tmpDirFile; | |||||
| } | |||||
| public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) { | |||||
| this.apacheHttpClientBuilder = apacheHttpClientBuilder; | |||||
| } | |||||
| } | |||||
| package me.chanjar.weixin.cp.api; | |||||
| import me.chanjar.weixin.common.bean.WxAccessToken; | |||||
| import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder; | |||||
| import redis.clients.jedis.Jedis; | |||||
| import redis.clients.jedis.JedisPool; | |||||
| import java.io.File; | |||||
| /** | |||||
| * Jedis client implementor for wechat config storage | |||||
| * | |||||
| * @author gaigeshen | |||||
| */ | |||||
| public class WxCpJedisConfigStorage implements WxCpConfigStorage { | |||||
| /* Redis keys here */ | |||||
| private static final String ACCESS_TOKEN_KEY = "WX_CP_ACCESS_TOKEN"; | |||||
| private static final String ACCESS_TOKEN_EXPIRES_TIME_KEY = "WX_CP_ACCESS_TOKEN_EXPIRES_TIME"; | |||||
| private static final String JS_API_TICKET_KEY = "WX_CP_JS_API_TICKET"; | |||||
| private static final String JS_API_TICKET_EXPIRES_TIME_KEY = "WX_CP_JS_API_TICKET_EXPIRES_TIME"; | |||||
| /* Redis clients pool */ | |||||
| private final JedisPool jedisPool; | |||||
| private volatile String corpId; | |||||
| private volatile String corpSecret; | |||||
| private volatile String token; | |||||
| private volatile String aesKey; | |||||
| private volatile Integer agentId; | |||||
| private volatile String oauth2redirectUri; | |||||
| private volatile String httpProxyHost; | |||||
| private volatile int httpProxyPort; | |||||
| private volatile String httpProxyUsername; | |||||
| private volatile String httpProxyPassword; | |||||
| private volatile File tmpDirFile; | |||||
| private volatile ApacheHttpClientBuilder apacheHttpClientBuilder; | |||||
| public WxCpJedisConfigStorage(String host, int port) { | |||||
| this.jedisPool = new JedisPool(host, port); | |||||
| } | |||||
| /** | |||||
| * This method will be destroy jedis pool | |||||
| */ | |||||
| public void destroy() { | |||||
| this.jedisPool.destroy(); | |||||
| } | |||||
| @Override | |||||
| public String getAccessToken() { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| return jedis.get(ACCESS_TOKEN_KEY); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public boolean isAccessTokenExpired() { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY); | |||||
| if (expiresTimeStr != null) { | |||||
| Long expiresTime = Long.parseLong(expiresTimeStr); | |||||
| return System.currentTimeMillis() > expiresTime; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void expireAccessToken() { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| jedis.set(ACCESS_TOKEN_EXPIRES_TIME_KEY, "0"); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public synchronized void updateAccessToken(WxAccessToken accessToken) { | |||||
| this.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | |||||
| } | |||||
| @Override | |||||
| public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| jedis.set(ACCESS_TOKEN_KEY, accessToken); | |||||
| jedis.set(ACCESS_TOKEN_EXPIRES_TIME_KEY, | |||||
| (System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L) + ""); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public String getJsapiTicket() { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| return jedis.get(JS_API_TICKET_KEY); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public boolean isJsapiTicketExpired() { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| String expiresTimeStr = jedis.get(JS_API_TICKET_EXPIRES_TIME_KEY); | |||||
| if (expiresTimeStr != null) { | |||||
| Long expiresTime = Long.parseLong(expiresTimeStr); | |||||
| return System.currentTimeMillis() > expiresTime; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void expireJsapiTicket() { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY, "0"); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| jedis.set(JS_API_TICKET_KEY, jsapiTicket); | |||||
| jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY, | |||||
| (System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L + "")); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public String getCorpId() { | |||||
| return this.corpId; | |||||
| } | |||||
| public void setCorpId(String corpId) { | |||||
| this.corpId = corpId; | |||||
| } | |||||
| @Override | |||||
| public String getCorpSecret() { | |||||
| return this.corpSecret; | |||||
| } | |||||
| public void setCorpSecret(String corpSecret) { | |||||
| this.corpSecret = corpSecret; | |||||
| } | |||||
| @Override | |||||
| public Integer getAgentId() { | |||||
| return this.agentId; | |||||
| } | |||||
| public void setAgentId(Integer agentId) { | |||||
| this.agentId = agentId; | |||||
| } | |||||
| @Override | |||||
| public String getToken() { | |||||
| return this.token; | |||||
| } | |||||
| public void setToken(String token) { | |||||
| this.token = token; | |||||
| } | |||||
| @Override | |||||
| public String getAesKey() { | |||||
| return this.aesKey; | |||||
| } | |||||
| public void setAesKey(String aesKey) { | |||||
| this.aesKey = aesKey; | |||||
| } | |||||
| @Override | |||||
| public long getExpiresTime() { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY); | |||||
| if (expiresTimeStr != null) { | |||||
| Long expiresTime = Long.parseLong(expiresTimeStr); | |||||
| return expiresTime; | |||||
| } | |||||
| return 0L; | |||||
| } | |||||
| } | |||||
| @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; | |||||
| } | |||||
| // ============================ Setters below | |||||
| 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 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; | |||||
| } | |||||
| } | |||||
| @@ -163,12 +163,12 @@ public class WxCpMessageRouter { | |||||
| // 返回最后一个非异步的rule的执行结果 | // 返回最后一个非异步的rule的执行结果 | ||||
| if (rule.isAsync()) { | if (rule.isAsync()) { | ||||
| futures.add( | futures.add( | ||||
| this.executorService.submit(new Runnable() { | |||||
| @Override | |||||
| public void run() { | |||||
| rule.service(wxMessage, WxCpMessageRouter.this.wxCpService, WxCpMessageRouter.this.sessionManager, WxCpMessageRouter.this.exceptionHandler); | |||||
| } | |||||
| }) | |||||
| this.executorService.submit(new Runnable() { | |||||
| @Override | |||||
| public void run() { | |||||
| rule.service(wxMessage, WxCpMessageRouter.this.wxCpService, WxCpMessageRouter.this.sessionManager, WxCpMessageRouter.this.exceptionHandler); | |||||
| } | |||||
| }) | |||||
| ); | ); | ||||
| } else { | } else { | ||||
| res = rule.service(wxMessage, this.wxCpService, this.sessionManager, this.exceptionHandler); | res = rule.service(wxMessage, this.wxCpService, this.sessionManager, this.exceptionHandler); | ||||
| @@ -205,10 +205,10 @@ public class WxCpMessageRouter { | |||||
| String messageId = ""; | String messageId = ""; | ||||
| if (wxMessage.getMsgId() == null) { | if (wxMessage.getMsgId() == null) { | ||||
| messageId = String.valueOf(wxMessage.getCreateTime()) | messageId = String.valueOf(wxMessage.getCreateTime()) | ||||
| + "-" + String.valueOf(wxMessage.getAgentId() == null ? "" : wxMessage.getAgentId()) | |||||
| + "-" + wxMessage.getFromUserName() | |||||
| + "-" + String.valueOf(wxMessage.getEventKey() == null ? "" : wxMessage.getEventKey()) | |||||
| + "-" + String.valueOf(wxMessage.getEvent() == null ? "" : wxMessage.getEvent()) | |||||
| + "-" + String.valueOf(wxMessage.getAgentId() == null ? "" : wxMessage.getAgentId()) | |||||
| + "-" + wxMessage.getFromUserName() | |||||
| + "-" + String.valueOf(wxMessage.getEventKey() == null ? "" : wxMessage.getEventKey()) | |||||
| + "-" + String.valueOf(wxMessage.getEvent() == null ? "" : wxMessage.getEvent()) | |||||
| ; | ; | ||||
| } else { | } else { | ||||
| messageId = String.valueOf(wxMessage.getMsgId()); | messageId = String.valueOf(wxMessage.getMsgId()); | ||||
| @@ -202,24 +202,24 @@ public class WxCpMessageRouterRule { | |||||
| protected boolean test(WxCpXmlMessage wxMessage) { | protected boolean test(WxCpXmlMessage wxMessage) { | ||||
| return | return | ||||
| (this.fromUser == null || this.fromUser.equals(wxMessage.getFromUserName())) | |||||
| && | |||||
| (this.agentId == null || this.agentId.equals(wxMessage.getAgentId())) | |||||
| && | |||||
| (this.msgType == null || this.msgType.equals(wxMessage.getMsgType())) | |||||
| && | |||||
| (this.event == null || this.event.equals(wxMessage.getEvent())) | |||||
| && | |||||
| (this.eventKey == null || this.eventKey.equals(wxMessage.getEventKey())) | |||||
| && | |||||
| (this.content == null || this.content | |||||
| .equals(wxMessage.getContent() == null ? null : wxMessage.getContent().trim())) | |||||
| && | |||||
| (this.rContent == null || Pattern | |||||
| .matches(this.rContent, wxMessage.getContent() == null ? "" : wxMessage.getContent().trim())) | |||||
| && | |||||
| (this.matcher == null || this.matcher.match(wxMessage)) | |||||
| ; | |||||
| (this.fromUser == null || this.fromUser.equals(wxMessage.getFromUserName())) | |||||
| && | |||||
| (this.agentId == null || this.agentId.equals(wxMessage.getAgentId())) | |||||
| && | |||||
| (this.msgType == null || this.msgType.equals(wxMessage.getMsgType())) | |||||
| && | |||||
| (this.event == null || this.event.equals(wxMessage.getEvent())) | |||||
| && | |||||
| (this.eventKey == null || this.eventKey.equals(wxMessage.getEventKey())) | |||||
| && | |||||
| (this.content == null || this.content | |||||
| .equals(wxMessage.getContent() == null ? null : wxMessage.getContent().trim())) | |||||
| && | |||||
| (this.rContent == null || Pattern | |||||
| .matches(this.rContent, wxMessage.getContent() == null ? "" : wxMessage.getContent().trim())) | |||||
| && | |||||
| (this.matcher == null || this.matcher.match(wxMessage)) | |||||
| ; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -160,7 +160,7 @@ public interface WxCpService { | |||||
| * | * | ||||
| * @param menu | * @param menu | ||||
| * @throws WxErrorException | * @throws WxErrorException | ||||
| * @see #menuCreate(String, me.chanjar.weixin.common.bean.menu.WxMenu) | |||||
| * @see #menuCreate(Integer, WxMenu) | |||||
| */ | */ | ||||
| void menuCreate(WxMenu menu) throws WxErrorException; | void menuCreate(WxMenu menu) throws WxErrorException; | ||||
| @@ -177,7 +177,7 @@ public interface WxCpService { | |||||
| * @throws WxErrorException | * @throws WxErrorException | ||||
| * @see #menuCreate(me.chanjar.weixin.common.bean.menu.WxMenu) | * @see #menuCreate(me.chanjar.weixin.common.bean.menu.WxMenu) | ||||
| */ | */ | ||||
| void menuCreate(String agentId, WxMenu menu) throws WxErrorException; | |||||
| void menuCreate(Integer agentId, WxMenu menu) throws WxErrorException; | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| @@ -188,7 +188,7 @@ public interface WxCpService { | |||||
| * </pre> | * </pre> | ||||
| * | * | ||||
| * @throws WxErrorException | * @throws WxErrorException | ||||
| * @see #menuDelete(String) | |||||
| * @see #menuDelete(Integer) | |||||
| */ | */ | ||||
| void menuDelete() throws WxErrorException; | void menuDelete() throws WxErrorException; | ||||
| @@ -204,7 +204,7 @@ public interface WxCpService { | |||||
| * @throws WxErrorException | * @throws WxErrorException | ||||
| * @see #menuDelete() | * @see #menuDelete() | ||||
| */ | */ | ||||
| void menuDelete(String agentId) throws WxErrorException; | |||||
| void menuDelete(Integer agentId) throws WxErrorException; | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| @@ -215,7 +215,7 @@ public interface WxCpService { | |||||
| * </pre> | * </pre> | ||||
| * | * | ||||
| * @throws WxErrorException | * @throws WxErrorException | ||||
| * @see #menuGet(String) | |||||
| * @see #menuGet(Integer) | |||||
| */ | */ | ||||
| WxMenu menuGet() throws WxErrorException; | WxMenu menuGet() throws WxErrorException; | ||||
| @@ -231,7 +231,7 @@ public interface WxCpService { | |||||
| * @throws WxErrorException | * @throws WxErrorException | ||||
| * @see #menuGet() | * @see #menuGet() | ||||
| */ | */ | ||||
| WxMenu menuGet(String agentId) throws WxErrorException; | |||||
| WxMenu menuGet(Integer agentId) throws WxErrorException; | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| @@ -396,12 +396,12 @@ public interface WxCpService { | |||||
| * <pre> | * <pre> | ||||
| * 构造oauth2授权的url连接 | * 构造oauth2授权的url连接 | ||||
| * </pre> | * </pre> | ||||
| * | |||||
| * | |||||
| * @param state | * @param state | ||||
| * @return url | * @return url | ||||
| */ | */ | ||||
| String oauth2buildAuthorizationUrl(String state); | String oauth2buildAuthorizationUrl(String state); | ||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| * 构造oauth2授权的url连接 | * 构造oauth2授权的url连接 | ||||
| @@ -425,7 +425,7 @@ public interface WxCpService { | |||||
| * | * | ||||
| * @param code | * @param code | ||||
| * @return [userid, deviceid] | * @return [userid, deviceid] | ||||
| * @see #oauth2getUserInfo(String, String) | |||||
| * @see #oauth2getUserInfo(Integer, String) | |||||
| */ | */ | ||||
| String[] oauth2getUserInfo(String code) throws WxErrorException; | String[] oauth2getUserInfo(String code) throws WxErrorException; | ||||
| @@ -443,7 +443,7 @@ public interface WxCpService { | |||||
| * @return [userid, deviceid] | * @return [userid, deviceid] | ||||
| * @see #oauth2getUserInfo(String) | * @see #oauth2getUserInfo(String) | ||||
| */ | */ | ||||
| String[] oauth2getUserInfo(String agentId, String code) throws WxErrorException; | |||||
| String[] oauth2getUserInfo(Integer agentId, String code) throws WxErrorException; | |||||
| /** | /** | ||||
| @@ -23,7 +23,6 @@ import me.chanjar.weixin.cp.bean.WxCpUser; | |||||
| import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
| import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
| import org.apache.http.HttpHost; | import org.apache.http.HttpHost; | ||||
| import org.apache.http.client.ClientProtocolException; | |||||
| import org.apache.http.client.config.RequestConfig; | import org.apache.http.client.config.RequestConfig; | ||||
| import org.apache.http.client.methods.CloseableHttpResponse; | import org.apache.http.client.methods.CloseableHttpResponse; | ||||
| import org.apache.http.client.methods.HttpGet; | import org.apache.http.client.methods.HttpGet; | ||||
| @@ -69,7 +68,7 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| public boolean checkSignature(String msgSignature, String timestamp, String nonce, String data) { | public boolean checkSignature(String msgSignature, String timestamp, String nonce, String data) { | ||||
| try { | try { | ||||
| return SHA1.gen(this.configStorage.getToken(), timestamp, nonce, data) | return SHA1.gen(this.configStorage.getToken(), timestamp, nonce, data) | ||||
| .equals(msgSignature); | |||||
| .equals(msgSignature); | |||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -95,18 +94,18 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| synchronized (this.globalAccessTokenRefreshLock) { | synchronized (this.globalAccessTokenRefreshLock) { | ||||
| if (this.configStorage.isAccessTokenExpired()) { | if (this.configStorage.isAccessTokenExpired()) { | ||||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" | String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" | ||||
| + "&corpid=" + this.configStorage.getCorpId() | |||||
| + "&corpsecret=" + this.configStorage.getCorpSecret(); | |||||
| + "&corpid=" + this.configStorage.getCorpId() | |||||
| + "&corpsecret=" + this.configStorage.getCorpSecret(); | |||||
| try { | try { | ||||
| HttpGet httpGet = new HttpGet(url); | HttpGet httpGet = new HttpGet(url); | ||||
| if (this.httpProxy != null) { | if (this.httpProxy != null) { | ||||
| RequestConfig config = RequestConfig.custom() | RequestConfig config = RequestConfig.custom() | ||||
| .setProxy(this.httpProxy).build(); | |||||
| .setProxy(this.httpProxy).build(); | |||||
| httpGet.setConfig(config); | httpGet.setConfig(config); | ||||
| } | } | ||||
| String resultContent = null; | String resultContent = null; | ||||
| try (CloseableHttpClient httpclient = getHttpclient(); | try (CloseableHttpClient httpclient = getHttpclient(); | ||||
| CloseableHttpResponse response = httpclient.execute(httpGet)) { | |||||
| CloseableHttpResponse response = httpclient.execute(httpGet)) { | |||||
| resultContent = new BasicResponseHandler().handleResponse(response); | resultContent = new BasicResponseHandler().handleResponse(response); | ||||
| } finally { | } finally { | ||||
| httpGet.releaseConnection(); | httpGet.releaseConnection(); | ||||
| @@ -117,9 +116,7 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| } | } | ||||
| WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | ||||
| this.configStorage.updateAccessToken( | this.configStorage.updateAccessToken( | ||||
| accessToken.getAccessToken(), accessToken.getExpiresIn()); | |||||
| } catch (ClientProtocolException e) { | |||||
| throw new RuntimeException(e); | |||||
| accessToken.getAccessToken(), accessToken.getExpiresIn()); | |||||
| } catch (IOException e) { | } catch (IOException e) { | ||||
| throw new RuntimeException(e); | throw new RuntimeException(e); | ||||
| } | } | ||||
| @@ -149,7 +146,7 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); | String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); | ||||
| int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); | int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); | ||||
| this.configStorage.updateJsapiTicket(jsapiTicket, | this.configStorage.updateJsapiTicket(jsapiTicket, | ||||
| expiresInSeconds); | |||||
| expiresInSeconds); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -162,10 +159,10 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| String noncestr = RandomUtils.getRandomStr(); | String noncestr = RandomUtils.getRandomStr(); | ||||
| String jsapiTicket = getJsapiTicket(false); | String jsapiTicket = getJsapiTicket(false); | ||||
| String signature = SHA1.genWithAmple( | String signature = SHA1.genWithAmple( | ||||
| "jsapi_ticket=" + jsapiTicket, | |||||
| "noncestr=" + noncestr, | |||||
| "timestamp=" + timestamp, | |||||
| "url=" + url | |||||
| "jsapi_ticket=" + jsapiTicket, | |||||
| "noncestr=" + noncestr, | |||||
| "timestamp=" + timestamp, | |||||
| "url=" + url | |||||
| ); | ); | ||||
| WxJsapiSignature jsapiSignature = new WxJsapiSignature(); | WxJsapiSignature jsapiSignature = new WxJsapiSignature(); | ||||
| jsapiSignature.setTimestamp(timestamp); | jsapiSignature.setTimestamp(timestamp); | ||||
| @@ -191,9 +188,9 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public void menuCreate(String agentId, WxMenu menu) throws WxErrorException { | |||||
| public void menuCreate(Integer agentId, WxMenu menu) throws WxErrorException { | |||||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/create?agentid=" | String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/create?agentid=" | ||||
| + this.configStorage.getAgentId(); | |||||
| + this.configStorage.getAgentId(); | |||||
| post(url, menu.toJson()); | post(url, menu.toJson()); | ||||
| } | } | ||||
| @@ -203,7 +200,7 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public void menuDelete(String agentId) throws WxErrorException { | |||||
| public void menuDelete(Integer agentId) throws WxErrorException { | |||||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/delete?agentid=" + agentId; | String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/delete?agentid=" + agentId; | ||||
| get(url, null); | get(url, null); | ||||
| } | } | ||||
| @@ -214,7 +211,7 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public WxMenu menuGet(String agentId) throws WxErrorException { | |||||
| public WxMenu menuGet(Integer agentId) throws WxErrorException { | |||||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/get?agentid=" + agentId; | String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/get?agentid=" + agentId; | ||||
| try { | try { | ||||
| String resultContent = get(url, null); | String resultContent = get(url, null); | ||||
| @@ -230,7 +227,7 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| @Override | @Override | ||||
| public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) | public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) | ||||
| throws WxErrorException, IOException { | |||||
| throws WxErrorException, IOException { | |||||
| return mediaUpload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType)); | return mediaUpload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType)); | ||||
| } | } | ||||
| @@ -244,9 +241,9 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| public File mediaDownload(String media_id) throws WxErrorException { | public File mediaDownload(String media_id) throws WxErrorException { | ||||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/media/get"; | String url = "https://qyapi.weixin.qq.com/cgi-bin/media/get"; | ||||
| return execute( | return execute( | ||||
| new MediaDownloadRequestExecutor( | |||||
| this.configStorage.getTmpDirFile()), | |||||
| url, "media_id=" + media_id); | |||||
| new MediaDownloadRequestExecutor( | |||||
| this.configStorage.getTmpDirFile()), | |||||
| url, "media_id=" + media_id); | |||||
| } | } | ||||
| @@ -254,9 +251,9 @@ this.configStorage.getTmpDirFile()), | |||||
| public Integer departCreate(WxCpDepart depart) throws WxErrorException { | public Integer departCreate(WxCpDepart depart) throws WxErrorException { | ||||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create"; | String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create"; | ||||
| String responseContent = execute( | String responseContent = execute( | ||||
| new SimplePostRequestExecutor(), | |||||
| url, | |||||
| depart.toJson()); | |||||
| new SimplePostRequestExecutor(), | |||||
| url, | |||||
| depart.toJson()); | |||||
| JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
| return GsonHelper.getAsInteger(tmpJsonElement.getAsJsonObject().get("id")); | return GsonHelper.getAsInteger(tmpJsonElement.getAsJsonObject().get("id")); | ||||
| } | } | ||||
| @@ -283,11 +280,11 @@ this.configStorage.getTmpDirFile()), | |||||
| */ | */ | ||||
| JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
| return WxCpGsonBuilder.INSTANCE.create() | return WxCpGsonBuilder.INSTANCE.create() | ||||
| .fromJson( | |||||
| tmpJsonElement.getAsJsonObject().get("department"), | |||||
| new TypeToken<List<WxCpDepart>>() { | |||||
| }.getType() | |||||
| ); | |||||
| .fromJson( | |||||
| tmpJsonElement.getAsJsonObject().get("department"), | |||||
| new TypeToken<List<WxCpDepart>>() { | |||||
| }.getType() | |||||
| ); | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -313,8 +310,8 @@ this.configStorage.getTmpDirFile()), | |||||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/user/batchdelete"; | String url = "https://qyapi.weixin.qq.com/cgi-bin/user/batchdelete"; | ||||
| JsonObject jsonObject = new JsonObject(); | JsonObject jsonObject = new JsonObject(); | ||||
| JsonArray jsonArray = new JsonArray(); | JsonArray jsonArray = new JsonArray(); | ||||
| for (int i = 0; i < userids.length; i++) { | |||||
| jsonArray.add(new JsonPrimitive(userids[i])); | |||||
| for (String userid : userids) { | |||||
| jsonArray.add(new JsonPrimitive(userid)); | |||||
| } | } | ||||
| jsonObject.add("useridlist", jsonArray); | jsonObject.add("useridlist", jsonArray); | ||||
| post(url, jsonObject.toString()); | post(url, jsonObject.toString()); | ||||
| @@ -343,11 +340,11 @@ this.configStorage.getTmpDirFile()), | |||||
| String responseContent = get(url, params); | String responseContent = get(url, params); | ||||
| JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
| return WxCpGsonBuilder.INSTANCE.create() | return WxCpGsonBuilder.INSTANCE.create() | ||||
| .fromJson( | |||||
| tmpJsonElement.getAsJsonObject().get("userlist"), | |||||
| new TypeToken<List<WxCpUser>>() { | |||||
| }.getType() | |||||
| ); | |||||
| .fromJson( | |||||
| tmpJsonElement.getAsJsonObject().get("userlist"), | |||||
| new TypeToken<List<WxCpUser>>() { | |||||
| }.getType() | |||||
| ); | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -366,11 +363,11 @@ this.configStorage.getTmpDirFile()), | |||||
| String responseContent = get(url, params); | String responseContent = get(url, params); | ||||
| JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
| return WxCpGsonBuilder.INSTANCE.create() | return WxCpGsonBuilder.INSTANCE.create() | ||||
| .fromJson( | |||||
| tmpJsonElement.getAsJsonObject().get("userlist"), | |||||
| new TypeToken<List<WxCpUser>>() { | |||||
| }.getType() | |||||
| ); | |||||
| .fromJson( | |||||
| tmpJsonElement.getAsJsonObject().get("userlist"), | |||||
| new TypeToken<List<WxCpUser>>() { | |||||
| }.getType() | |||||
| ); | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -404,11 +401,11 @@ this.configStorage.getTmpDirFile()), | |||||
| String responseContent = get(url, null); | String responseContent = get(url, null); | ||||
| JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
| return WxCpGsonBuilder.INSTANCE.create() | return WxCpGsonBuilder.INSTANCE.create() | ||||
| .fromJson( | |||||
| tmpJsonElement.getAsJsonObject().get("taglist"), | |||||
| new TypeToken<List<WxCpTag>>() { | |||||
| }.getType() | |||||
| ); | |||||
| .fromJson( | |||||
| tmpJsonElement.getAsJsonObject().get("taglist"), | |||||
| new TypeToken<List<WxCpTag>>() { | |||||
| }.getType() | |||||
| ); | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -417,11 +414,11 @@ this.configStorage.getTmpDirFile()), | |||||
| String responseContent = get(url, null); | String responseContent = get(url, null); | ||||
| JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
| return WxCpGsonBuilder.INSTANCE.create() | return WxCpGsonBuilder.INSTANCE.create() | ||||
| .fromJson( | |||||
| tmpJsonElement.getAsJsonObject().get("userlist"), | |||||
| new TypeToken<List<WxCpUser>>() { | |||||
| }.getType() | |||||
| ); | |||||
| .fromJson( | |||||
| tmpJsonElement.getAsJsonObject().get("userlist"), | |||||
| new TypeToken<List<WxCpUser>>() { | |||||
| }.getType() | |||||
| ); | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -460,14 +457,14 @@ this.configStorage.getTmpDirFile()), | |||||
| } | } | ||||
| @Override | @Override | ||||
| public String oauth2buildAuthorizationUrl(String state) { | |||||
| return this.oauth2buildAuthorizationUrl( | |||||
| this.configStorage.getOauth2redirectUri(), | |||||
| state | |||||
| ); | |||||
| } | |||||
| public String oauth2buildAuthorizationUrl(String state) { | |||||
| return this.oauth2buildAuthorizationUrl( | |||||
| this.configStorage.getOauth2redirectUri(), | |||||
| state | |||||
| ); | |||||
| } | |||||
| @Override | |||||
| @Override | |||||
| public String oauth2buildAuthorizationUrl(String redirectUri, String state) { | public String oauth2buildAuthorizationUrl(String redirectUri, String state) { | ||||
| String url = "https://open.weixin.qq.com/connect/oauth2/authorize?"; | String url = "https://open.weixin.qq.com/connect/oauth2/authorize?"; | ||||
| url += "appid=" + this.configStorage.getCorpId(); | url += "appid=" + this.configStorage.getCorpId(); | ||||
| @@ -487,10 +484,10 @@ this.configStorage.getOauth2redirectUri(), | |||||
| } | } | ||||
| @Override | @Override | ||||
| public String[] oauth2getUserInfo(String agentId, String code) throws WxErrorException { | |||||
| public String[] oauth2getUserInfo(Integer agentId, String code) throws WxErrorException { | |||||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?" | String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?" | ||||
| + "code=" + code | |||||
| + "&agentid=" + agentId; | |||||
| + "code=" + code | |||||
| + "&agentid=" + agentId; | |||||
| String responseText = get(url, null); | String responseText = get(url, null); | ||||
| JsonElement je = new JsonParser().parse(responseText); | JsonElement je = new JsonParser().parse(responseText); | ||||
| JsonObject jo = je.getAsJsonObject(); | JsonObject jo = je.getAsJsonObject(); | ||||
| @@ -544,14 +541,14 @@ this.configStorage.getOauth2redirectUri(), | |||||
| return executeInternal(executor, uri, data); | return executeInternal(executor, uri, data); | ||||
| } catch (WxErrorException e) { | } catch (WxErrorException e) { | ||||
| WxError error = e.getError(); | WxError error = e.getError(); | ||||
| /** | |||||
| /* | |||||
| * -1 系统繁忙, 1000ms后重试 | * -1 系统繁忙, 1000ms后重试 | ||||
| */ | */ | ||||
| if (error.getErrorCode() == -1) { | if (error.getErrorCode() == -1) { | ||||
| int sleepMillis = this.retrySleepMillis * (1 << retryTimes); | int sleepMillis = this.retrySleepMillis * (1 << retryTimes); | ||||
| try { | try { | ||||
| this.log.debug("微信系统繁忙,{}ms 后重试(第{}次)", sleepMillis, | this.log.debug("微信系统繁忙,{}ms 后重试(第{}次)", sleepMillis, | ||||
| retryTimes + 1); | |||||
| retryTimes + 1); | |||||
| Thread.sleep(sleepMillis); | Thread.sleep(sleepMillis); | ||||
| } catch (InterruptedException e1) { | } catch (InterruptedException e1) { | ||||
| throw new RuntimeException(e1); | throw new RuntimeException(e1); | ||||
| @@ -566,7 +563,7 @@ this.configStorage.getOauth2redirectUri(), | |||||
| } | } | ||||
| protected synchronized <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException { | protected synchronized <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException { | ||||
| if (uri.indexOf("access_token=") != -1) { | |||||
| if (uri.contains("access_token=")) { | |||||
| throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri); | throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri); | ||||
| } | } | ||||
| String accessToken = getAccessToken(false); | String accessToken = getAccessToken(false); | ||||
| @@ -576,7 +573,7 @@ this.configStorage.getOauth2redirectUri(), | |||||
| try { | try { | ||||
| return executor.execute(getHttpclient(), this.httpProxy, | return executor.execute(getHttpclient(), this.httpProxy, | ||||
| uriWithAccessToken, data); | |||||
| uriWithAccessToken, data); | |||||
| } catch (WxErrorException e) { | } catch (WxErrorException e) { | ||||
| WxError error = e.getError(); | WxError error = e.getError(); | ||||
| /* | /* | ||||
| @@ -593,8 +590,6 @@ this.configStorage.getOauth2redirectUri(), | |||||
| throw new WxErrorException(error); | throw new WxErrorException(error); | ||||
| } | } | ||||
| return null; | return null; | ||||
| } catch (ClientProtocolException e) { | |||||
| throw new RuntimeException(e); | |||||
| } catch (IOException e) { | } catch (IOException e) { | ||||
| throw new RuntimeException(e); | throw new RuntimeException(e); | ||||
| } | } | ||||
| @@ -608,15 +603,15 @@ this.configStorage.getOauth2redirectUri(), | |||||
| public void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider) { | public void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider) { | ||||
| this.configStorage = wxConfigProvider; | this.configStorage = wxConfigProvider; | ||||
| ApacheHttpClientBuilder apacheHttpClientBuilder = this.configStorage | ApacheHttpClientBuilder apacheHttpClientBuilder = this.configStorage | ||||
| .getApacheHttpClientBuilder(); | |||||
| .getApacheHttpClientBuilder(); | |||||
| if (null == apacheHttpClientBuilder) { | if (null == apacheHttpClientBuilder) { | ||||
| apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); | apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); | ||||
| } | } | ||||
| apacheHttpClientBuilder.httpProxyHost(this.configStorage.getHttpProxyHost()) | apacheHttpClientBuilder.httpProxyHost(this.configStorage.getHttpProxyHost()) | ||||
| .httpProxyPort(this.configStorage.getHttpProxyPort()) | |||||
| .httpProxyUsername(this.configStorage.getHttpProxyUsername()) | |||||
| .httpProxyPassword(this.configStorage.getHttpProxyPassword()); | |||||
| .httpProxyPort(this.configStorage.getHttpProxyPort()) | |||||
| .httpProxyUsername(this.configStorage.getHttpProxyUsername()) | |||||
| .httpProxyPassword(this.configStorage.getHttpProxyPassword()); | |||||
| if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) { | if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) { | ||||
| this.httpProxy = new HttpHost(this.configStorage.getHttpProxyHost(), this.configStorage.getHttpProxyPort()); | this.httpProxy = new HttpHost(this.configStorage.getHttpProxyHost(), this.configStorage.getHttpProxyPort()); | ||||
| @@ -1,17 +1,12 @@ | |||||
| package me.chanjar.weixin.cp.bean; | package me.chanjar.weixin.cp.bean; | ||||
| import me.chanjar.weixin.cp.bean.messagebuilder.*; | |||||
| import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||||
| import java.io.Serializable; | import java.io.Serializable; | ||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.List; | import java.util.List; | ||||
| import me.chanjar.weixin.cp.bean.messagebuilder.FileBuilder; | |||||
| import me.chanjar.weixin.cp.bean.messagebuilder.ImageBuilder; | |||||
| import me.chanjar.weixin.cp.bean.messagebuilder.NewsBuilder; | |||||
| import me.chanjar.weixin.cp.bean.messagebuilder.TextBuilder; | |||||
| import me.chanjar.weixin.cp.bean.messagebuilder.VideoBuilder; | |||||
| import me.chanjar.weixin.cp.bean.messagebuilder.VoiceBuilder; | |||||
| import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||||
| /** | /** | ||||
| * 消息 | * 消息 | ||||
| * | * | ||||
| @@ -23,7 +18,7 @@ public class WxCpMessage implements Serializable { | |||||
| private String toUser; | private String toUser; | ||||
| private String toParty; | private String toParty; | ||||
| private String toTag; | private String toTag; | ||||
| private String agentId; | |||||
| private Integer agentId; | |||||
| private String msgType; | private String msgType; | ||||
| private String content; | private String content; | ||||
| private String mediaId; | private String mediaId; | ||||
| @@ -101,11 +96,11 @@ public class WxCpMessage implements Serializable { | |||||
| this.toTag = toTag; | this.toTag = toTag; | ||||
| } | } | ||||
| public String getAgentId() { | |||||
| public Integer getAgentId() { | |||||
| return this.agentId; | return this.agentId; | ||||
| } | } | ||||
| public void setAgentId(String agentId) { | |||||
| public void setAgentId(Integer agentId) { | |||||
| this.agentId = agentId; | this.agentId = agentId; | ||||
| } | } | ||||
| @@ -5,13 +5,13 @@ import me.chanjar.weixin.cp.bean.WxCpMessage; | |||||
| public class BaseBuilder<T> { | public class BaseBuilder<T> { | ||||
| protected String msgType; | protected String msgType; | ||||
| protected String agentId; | |||||
| protected Integer agentId; | |||||
| protected String toUser; | protected String toUser; | ||||
| protected String toParty; | protected String toParty; | ||||
| protected String toTag; | protected String toTag; | ||||
| protected String safe; | protected String safe; | ||||
| public T agentId(String agentId) { | |||||
| public T agentId(Integer agentId) { | |||||
| this.agentId = agentId; | this.agentId = agentId; | ||||
| return (T) this; | return (T) this; | ||||
| } | } | ||||
| @@ -6,8 +6,8 @@ dependencies { | |||||
| testCompile group: 'org.testng', name: 'testng', version:'6.8.7' | testCompile group: 'org.testng', name: 'testng', version:'6.8.7' | ||||
| testCompile group: 'org.mockito', name: 'mockito-all', version:'1.9.5' | testCompile group: 'org.mockito', name: 'mockito-all', version:'1.9.5' | ||||
| testCompile group: 'com.google.inject', name: 'guice', version:'3.0' | testCompile group: 'com.google.inject', name: 'guice', version:'3.0' | ||||
| testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version:'9.3.0.M0' | |||||
| testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version:'9.3.0.M0' | |||||
| testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version:'9.3.0.RC0' | |||||
| testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version:'9.3.0.RC0' | |||||
| testCompile group: 'joda-time', name: 'joda-time', version:'2.9.4' | testCompile group: 'joda-time', name: 'joda-time', version:'2.9.4' | ||||
| } | } | ||||
| test.useTestNG() | test.useTestNG() | ||||
| @@ -6,7 +6,7 @@ | |||||
| <parent> | <parent> | ||||
| <groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
| <artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
| <version>2.3.0</version> | |||||
| <version>2.4.0</version> | |||||
| </parent> | </parent> | ||||
| <artifactId>weixin-java-mp</artifactId> | <artifactId>weixin-java-mp</artifactId> | ||||
| <name>WeiXin Java Tools - MP</name> | <name>WeiXin Java Tools - MP</name> | ||||
| @@ -5,6 +5,7 @@ import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder; | |||||
| import javax.net.ssl.SSLContext; | import javax.net.ssl.SSLContext; | ||||
| import java.io.File; | import java.io.File; | ||||
| import java.util.concurrent.locks.Lock; | |||||
| /** | /** | ||||
| * 微信客户端配置存储 | * 微信客户端配置存储 | ||||
| @@ -15,6 +16,8 @@ public interface WxMpConfigStorage { | |||||
| String getAccessToken(); | String getAccessToken(); | ||||
| Lock getAccessTokenLock(); | |||||
| boolean isAccessTokenExpired(); | boolean isAccessTokenExpired(); | ||||
| /** | /** | ||||
| @@ -24,19 +27,21 @@ public interface WxMpConfigStorage { | |||||
| /** | /** | ||||
| * 应该是线程安全的 | * 应该是线程安全的 | ||||
| * @param accessToken | |||||
| * @param accessToken 要更新的WxAccessToken对象 | |||||
| */ | */ | ||||
| void updateAccessToken(WxAccessToken accessToken); | void updateAccessToken(WxAccessToken accessToken); | ||||
| /** | /** | ||||
| * 应该是线程安全的 | * 应该是线程安全的 | ||||
| * @param accessToken | |||||
| * @param expiresIn | |||||
| * @param accessToken 新的accessToken值 | |||||
| * @param expiresInSeconds 过期时间,以秒为单位 | |||||
| */ | */ | ||||
| void updateAccessToken(String accessToken, int expiresIn); | |||||
| void updateAccessToken(String accessToken, int expiresInSeconds); | |||||
| String getJsapiTicket(); | String getJsapiTicket(); | ||||
| Lock getJsapiTicketLock(); | |||||
| boolean isJsapiTicketExpired(); | boolean isJsapiTicketExpired(); | ||||
| /** | /** | ||||
| @@ -46,12 +51,15 @@ public interface WxMpConfigStorage { | |||||
| /** | /** | ||||
| * 应该是线程安全的 | * 应该是线程安全的 | ||||
| * @param jsapiTicket | |||||
| * @param jsapiTicket 新的jsapi ticket值 | |||||
| * @param expiresInSeconds 过期时间,以秒为单位 | |||||
| */ | */ | ||||
| void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); | void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); | ||||
| String getCardApiTicket(); | String getCardApiTicket(); | ||||
| Lock getCardApiTicketLock(); | |||||
| boolean isCardApiTicketExpired(); | boolean isCardApiTicketExpired(); | ||||
| /** | /** | ||||
| @@ -61,7 +69,8 @@ public interface WxMpConfigStorage { | |||||
| /** | /** | ||||
| * 应该是线程安全的 | * 应该是线程安全的 | ||||
| * @param cardApiTicket | |||||
| * @param cardApiTicket 新的cardApi ticket值 | |||||
| * @param expiresInSeconds 过期时间,以秒为单位 | |||||
| */ | */ | ||||
| void updateCardApiTicket(String cardApiTicket, int expiresInSeconds); | void updateCardApiTicket(String cardApiTicket, int expiresInSeconds); | ||||
| @@ -70,7 +79,7 @@ public interface WxMpConfigStorage { | |||||
| String getSecret(); | String getSecret(); | ||||
| String getPartnerId(); | String getPartnerId(); | ||||
| String getPartnerKey(); | String getPartnerKey(); | ||||
| String getToken(); | String getToken(); | ||||
| @@ -88,7 +97,7 @@ public interface WxMpConfigStorage { | |||||
| String getHttpProxyUsername(); | String getHttpProxyUsername(); | ||||
| String getHttpProxyPassword(); | String getHttpProxyPassword(); | ||||
| File getTmpDirFile(); | File getTmpDirFile(); | ||||
| SSLContext getSSLContext(); | SSLContext getSSLContext(); | ||||
| @@ -98,4 +107,9 @@ public interface WxMpConfigStorage { | |||||
| * @return ApacheHttpClientBuilder | * @return ApacheHttpClientBuilder | ||||
| */ | */ | ||||
| ApacheHttpClientBuilder getApacheHttpClientBuilder(); | ApacheHttpClientBuilder getApacheHttpClientBuilder(); | ||||
| /** | |||||
| * 是否自动刷新token | |||||
| */ | |||||
| boolean autoRefreshToken(); | |||||
| } | } | ||||
| @@ -6,6 +6,8 @@ import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder; | |||||
| import javax.net.ssl.SSLContext; | import javax.net.ssl.SSLContext; | ||||
| import java.io.File; | import java.io.File; | ||||
| import java.util.concurrent.locks.Lock; | |||||
| import java.util.concurrent.locks.ReentrantLock; | |||||
| /** | /** | ||||
| * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 | * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 | ||||
| @@ -36,6 +38,10 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
| protected volatile String cardApiTicket; | protected volatile String cardApiTicket; | ||||
| protected volatile long cardApiTicketExpiresTime; | protected volatile long cardApiTicketExpiresTime; | ||||
| protected Lock accessTokenLock = new ReentrantLock(); | |||||
| protected Lock jsapiTicketLock = new ReentrantLock(); | |||||
| protected Lock cardApiTicketLock = new ReentrantLock(); | |||||
| /** | /** | ||||
| * 临时文件目录 | * 临时文件目录 | ||||
| */ | */ | ||||
| @@ -50,6 +56,11 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
| return this.accessToken; | return this.accessToken; | ||||
| } | } | ||||
| @Override | |||||
| public Lock getAccessTokenLock() { | |||||
| return this.accessTokenLock; | |||||
| } | |||||
| @Override | @Override | ||||
| public boolean isAccessTokenExpired() { | public boolean isAccessTokenExpired() { | ||||
| return System.currentTimeMillis() > this.expiresTime; | return System.currentTimeMillis() > this.expiresTime; | ||||
| @@ -63,7 +74,7 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
| @Override | @Override | ||||
| public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { | public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { | ||||
| this.accessToken = accessToken; | this.accessToken = accessToken; | ||||
| this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; | |||||
| this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -76,6 +87,11 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
| return this.jsapiTicket; | return this.jsapiTicket; | ||||
| } | } | ||||
| @Override | |||||
| public Lock getJsapiTicketLock() { | |||||
| return this.jsapiTicketLock; | |||||
| } | |||||
| public void setJsapiTicket(String jsapiTicket) { | public void setJsapiTicket(String jsapiTicket) { | ||||
| this.jsapiTicket = jsapiTicket; | this.jsapiTicket = jsapiTicket; | ||||
| } | } | ||||
| @@ -97,7 +113,7 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
| public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { | public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { | ||||
| this.jsapiTicket = jsapiTicket; | this.jsapiTicket = jsapiTicket; | ||||
| // 预留200秒的时间 | // 预留200秒的时间 | ||||
| this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; | |||||
| this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -113,6 +129,11 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
| return this.cardApiTicket; | return this.cardApiTicket; | ||||
| } | } | ||||
| @Override | |||||
| public Lock getCardApiTicketLock() { | |||||
| return this.cardApiTicketLock; | |||||
| } | |||||
| @Override | @Override | ||||
| public boolean isCardApiTicketExpired() { | public boolean isCardApiTicketExpired() { | ||||
| return System.currentTimeMillis() > this.cardApiTicketExpiresTime; | return System.currentTimeMillis() > this.cardApiTicketExpiresTime; | ||||
| @@ -122,7 +143,7 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
| public synchronized void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) { | public synchronized void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) { | ||||
| this.cardApiTicket = cardApiTicket; | this.cardApiTicket = cardApiTicket; | ||||
| // 预留200秒的时间 | // 预留200秒的时间 | ||||
| this.cardApiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; | |||||
| this.cardApiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -270,6 +291,11 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
| return this.apacheHttpClientBuilder; | return this.apacheHttpClientBuilder; | ||||
| } | } | ||||
| @Override | |||||
| public boolean autoRefreshToken() { | |||||
| return true; | |||||
| } | |||||
| public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) { | public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) { | ||||
| this.apacheHttpClientBuilder = apacheHttpClientBuilder; | this.apacheHttpClientBuilder = apacheHttpClientBuilder; | ||||
| } | } | ||||
| @@ -2,6 +2,7 @@ package me.chanjar.weixin.mp.api; | |||||
| import me.chanjar.weixin.common.bean.menu.WxMenu; | import me.chanjar.weixin.common.bean.menu.WxMenu; | ||||
| import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
| import me.chanjar.weixin.mp.bean.menu.WxMpGetSelfMenuInfoResult; | |||||
| /** | /** | ||||
| * 菜单相关操作接口 | * 菜单相关操作接口 | ||||
| @@ -56,4 +57,20 @@ public interface WxMpMenuService { | |||||
| */ | */ | ||||
| WxMenu menuTryMatch(String userid) throws WxErrorException; | WxMenu menuTryMatch(String userid) throws WxErrorException; | ||||
| /** | |||||
| * <pre> | |||||
| * 获取自定义菜单配置接口 | |||||
| * 本接口将会提供公众号当前使用的自定义菜单的配置,如果公众号是通过API调用设置的菜单,则返回菜单的开发配置,而如果公众号是在公众平台官网通过网站功能发布菜单,则本接口返回运营者设置的菜单配置。 | |||||
| 请注意: | |||||
| 1、第三方平台开发者可以通过本接口,在旗下公众号将业务授权给你后,立即通过本接口检测公众号的自定义菜单配置,并通过接口再次给公众号设置好自动回复规则,以提升公众号运营者的业务体验。 | |||||
| 2、本接口与自定义菜单查询接口的不同之处在于,本接口无论公众号的接口是如何设置的,都能查询到接口,而自定义菜单查询接口则仅能查询到使用API设置的菜单配置。 | |||||
| 3、认证/未认证的服务号/订阅号,以及接口测试号,均拥有该接口权限。 | |||||
| 4、从第三方平台的公众号登录授权机制上来说,该接口从属于消息与菜单权限集。 | |||||
| 5、本接口中返回的图片/语音/视频为临时素材(临时素材每次获取都不同,3天内有效,通过素材管理-获取临时素材接口来获取这些素材),本接口返回的图文消息为永久素材素材(通过素材管理-获取永久素材接口来获取这些素材)。 | |||||
| * 接口调用请求说明: | |||||
| http请求方式: GET(请使用https协议) | |||||
| https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=ACCESS_TOKEN | |||||
| *</pre> | |||||
| */ | |||||
| WxMpGetSelfMenuInfoResult getSelfMenuInfo() throws WxErrorException; | |||||
| } | } | ||||
| @@ -2,7 +2,6 @@ package me.chanjar.weixin.mp.api; | |||||
| import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
| import me.chanjar.weixin.mp.bean.pay.WxPayJsSDKCallback; | import me.chanjar.weixin.mp.bean.pay.WxPayJsSDKCallback; | ||||
| import me.chanjar.weixin.mp.bean.pay.result.WxPayOrderCloseResult; | |||||
| import me.chanjar.weixin.mp.bean.pay.request.WxEntPayRequest; | import me.chanjar.weixin.mp.bean.pay.request.WxEntPayRequest; | ||||
| import me.chanjar.weixin.mp.bean.pay.request.WxPayRefundRequest; | import me.chanjar.weixin.mp.bean.pay.request.WxPayRefundRequest; | ||||
| import me.chanjar.weixin.mp.bean.pay.request.WxPaySendRedpackRequest; | import me.chanjar.weixin.mp.bean.pay.request.WxPaySendRedpackRequest; | ||||
| @@ -13,8 +12,9 @@ import java.io.File; | |||||
| import java.util.Map; | import java.util.Map; | ||||
| /** | /** | ||||
| * 微信支付相关接口 | |||||
| * Created by Binary Wang on 2016/7/28. | |||||
| * 微信支付相关接口 | |||||
| * Created by Binary Wang on 2016/7/28. | |||||
| * | |||||
| * @author binarywang (https://github.com/binarywang) | * @author binarywang (https://github.com/binarywang) | ||||
| */ | */ | ||||
| public interface WxMpPayService { | public interface WxMpPayService { | ||||
| @@ -24,14 +24,15 @@ public interface WxMpPayService { | |||||
| * 查询订单(详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2) | * 查询订单(详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2) | ||||
| * 该接口提供所有微信支付订单的查询,商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。 | * 该接口提供所有微信支付订单的查询,商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。 | ||||
| * 需要调用查询接口的情况: | * 需要调用查询接口的情况: | ||||
| ◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知; | |||||
| ◆ 调用支付接口后,返回系统错误或未知交易状态情况; | |||||
| ◆ 调用被扫支付API,返回USERPAYING的状态; | |||||
| ◆ 调用关单或撤销接口API之前,需确认支付状态; | |||||
| * ◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知; | |||||
| * ◆ 调用支付接口后,返回系统错误或未知交易状态情况; | |||||
| * ◆ 调用被扫支付API,返回USERPAYING的状态; | |||||
| * ◆ 调用关单或撤销接口API之前,需确认支付状态; | |||||
| * 接口地址:https://api.mch.weixin.qq.com/pay/orderquery | * 接口地址:https://api.mch.weixin.qq.com/pay/orderquery | ||||
| * </pre> | * </pre> | ||||
| * | |||||
| * @param transactionId 微信支付分配的商户号 | * @param transactionId 微信支付分配的商户号 | ||||
| * @param outTradeNo 商户系统内部的订单号,当没提供transaction_id时需要传这个。 | |||||
| * @param outTradeNo 商户系统内部的订单号,当没提供transaction_id时需要传这个。 | |||||
| * @throws WxErrorException | * @throws WxErrorException | ||||
| */ | */ | ||||
| WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException; | WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException; | ||||
| @@ -47,6 +48,7 @@ public interface WxMpPayService { | |||||
| * 接口地址:https://api.mch.weixin.qq.com/pay/closeorder | * 接口地址:https://api.mch.weixin.qq.com/pay/closeorder | ||||
| * 是否需要证书: 不需要。 | * 是否需要证书: 不需要。 | ||||
| * </pre> | * </pre> | ||||
| * | |||||
| * @param outTradeNo 商户系统内部的订单号,当没提供transaction_id时需要传这个。 | * @param outTradeNo 商户系统内部的订单号,当没提供transaction_id时需要传这个。 | ||||
| * @throws WxErrorException | * @throws WxErrorException | ||||
| */ | */ | ||||
| @@ -56,16 +58,18 @@ public interface WxMpPayService { | |||||
| * 统一下单(详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1) | * 统一下单(详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1) | ||||
| * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识" | * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识" | ||||
| * 接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder | * 接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder | ||||
| * @throws WxErrorException | |||||
| * @param request 请求对象 | |||||
| * | * | ||||
| * @param request 请求对象,注意一些参数如appid、mchid等不用设置,方法内会自动从配置对象中获取到(前提是对应配置中已经设置) | |||||
| * @throws WxErrorException | |||||
| */ | */ | ||||
| WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws WxErrorException; | WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws WxErrorException; | ||||
| /** | /** | ||||
| * 该接口调用“统一下单”接口,并拼装发起支付请求需要的参数 | * 该接口调用“统一下单”接口,并拼装发起支付请求需要的参数 | ||||
| * 详见http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN | * 详见http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN | ||||
| * @param request 请求对象 | |||||
| * | |||||
| * @param request 请求对象,注意一些参数如appid、mchid等不用设置,方法内会自动从配置对象中获取到(前提是对应配置中已经设置) | |||||
| * @throws WxErrorException | |||||
| */ | */ | ||||
| Map<String, String> getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException; | Map<String, String> getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException; | ||||
| @@ -75,16 +79,33 @@ public interface WxMpPayService { | |||||
| * 详见 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4 | * 详见 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4 | ||||
| * 接口链接:https://api.mch.weixin.qq.com/secapi/pay/refund | * 接口链接:https://api.mch.weixin.qq.com/secapi/pay/refund | ||||
| * </pre> | * </pre> | ||||
| * | |||||
| * @param request 请求对象 | * @param request 请求对象 | ||||
| * @param keyFile 证书文件对象 | |||||
| * @param keyFile 证书文件对象 | |||||
| * @return 退款操作结果 | * @return 退款操作结果 | ||||
| */ | */ | ||||
| WxPayRefundResult refund(WxPayRefundRequest request, File keyFile) throws WxErrorException; | WxPayRefundResult refund(WxPayRefundRequest request, File keyFile) throws WxErrorException; | ||||
| /** | |||||
| * <pre> | |||||
| * 微信支付-查询退款 | |||||
| * 应用场景: | |||||
| * 提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。 | |||||
| * 详见 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5 | |||||
| * 接口链接:https://api.mch.weixin.qq.com/pay/refundquery | |||||
| * </pre> | |||||
| * 以下四个参数四选一 | |||||
| * @param transactionId 微信订单号 | |||||
| * @param outTradeNo 商户订单号 | |||||
| * @param outRefundNo 商户退款单号 | |||||
| * @param refundId 微信退款单号 | |||||
| * @return 退款信息 | |||||
| */ | |||||
| WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) throws WxErrorException; | |||||
| /** | /** | ||||
| * 读取支付结果通知 | * 读取支付结果通知 | ||||
| * 详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 | * 详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 | ||||
| * | |||||
| */ | */ | ||||
| WxPayJsSDKCallback getJSSDKCallbackData(String xmlData) throws WxErrorException; | WxPayJsSDKCallback getJSSDKCallbackData(String xmlData) throws WxErrorException; | ||||
| @@ -93,7 +114,6 @@ public interface WxMpPayService { | |||||
| * 计算Map键值对是否和签名相符, | * 计算Map键值对是否和签名相符, | ||||
| * 按照字段名的 ASCII 码从小到大排序(字典序)后,使用 URL 键值对的 格式(即 key1=value1&key2=value2...)拼接成字符串 | * 按照字段名的 ASCII 码从小到大排序(字典序)后,使用 URL 键值对的 格式(即 key1=value1&key2=value2...)拼接成字符串 | ||||
| * </pre> | * </pre> | ||||
| * | |||||
| */ | */ | ||||
| boolean checkJSSDKCallbackDataSignature(Map<String, String> kvm, String signature); | boolean checkJSSDKCallbackDataSignature(Map<String, String> kvm, String signature); | ||||
| @@ -102,13 +122,29 @@ public interface WxMpPayService { | |||||
| * <pre> | * <pre> | ||||
| * 文档详见: | * 文档详见: | ||||
| * 发送普通红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3 | * 发送普通红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3 | ||||
| * 接口地址:https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack | |||||
| * 发送裂变红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_5&index=4 | * 发送裂变红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_5&index=4 | ||||
| * 接口地址:https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack | |||||
| * </pre> | * </pre> | ||||
| * | |||||
| * @param request 请求对象 | * @param request 请求对象 | ||||
| * @param keyFile 证书文件对象 | |||||
| * @param keyFile 证书文件对象 | |||||
| */ | */ | ||||
| WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request, File keyFile) throws WxErrorException; | WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request, File keyFile) throws WxErrorException; | ||||
| /** | |||||
| * <pre> | |||||
| * 查询红包记录 | |||||
| * 用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包。 | |||||
| * 请求Url https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo | |||||
| * 是否需要证书 是(证书及使用说明详见商户证书) | |||||
| * 请求方式 POST | |||||
| * </pre> | |||||
| * @param mchBillNo 商户发放红包的商户订单号,比如10000098201411111234567890 | |||||
| * @param keyFile 证书文件对象 | |||||
| */ | |||||
| WxPayRedpackQueryResult queryRedpack(String mchBillNo, File keyFile) throws WxErrorException; | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| * 企业付款业务是基于微信支付商户平台的资金管理能力,为了协助商户方便地实现企业向个人付款,针对部分有开发能力的商户,提供通过API完成企业付款的功能。 | * 企业付款业务是基于微信支付商户平台的资金管理能力,为了协助商户方便地实现企业向个人付款,针对部分有开发能力的商户,提供通过API完成企业付款的功能。 | ||||
| @@ -118,8 +154,9 @@ public interface WxMpPayService { | |||||
| * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2 | * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2 | ||||
| * 接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers | * 接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers | ||||
| * </pre> | * </pre> | ||||
| * | |||||
| * @param request 请求对象 | * @param request 请求对象 | ||||
| * @param keyFile 证书文件对象 | |||||
| * @param keyFile 证书文件对象 | |||||
| */ | */ | ||||
| WxEntPayResult entPay(WxEntPayRequest request, File keyFile) throws WxErrorException; | WxEntPayResult entPay(WxEntPayRequest request, File keyFile) throws WxErrorException; | ||||
| @@ -130,8 +167,9 @@ public interface WxMpPayService { | |||||
| * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_3 | * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_3 | ||||
| * 接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo | * 接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo | ||||
| * </pre> | * </pre> | ||||
| * | |||||
| * @param partnerTradeNo 商户订单号 | * @param partnerTradeNo 商户订单号 | ||||
| * @param keyFile 证书文件对象 | |||||
| * @param keyFile 证书文件对象 | |||||
| */ | */ | ||||
| WxEntPayQueryResult queryEntPay(String partnerTradeNo, File keyFile) throws WxErrorException; | WxEntPayQueryResult queryEntPay(String partnerTradeNo, File keyFile) throws WxErrorException; | ||||
| @@ -14,8 +14,8 @@ public interface WxMpService { | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| * 验证推送过来的消息的正确性 | |||||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=验证消息真实性 | |||||
| * 验证消息的确来自微信服务器 | |||||
| * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN | |||||
| * </pre> | * </pre> | ||||
| */ | */ | ||||
| boolean checkSignature(String timestamp, String nonce, String signature); | boolean checkSignature(String timestamp, String nonce, String signature); | ||||
| @@ -36,7 +36,7 @@ public interface WxMpService { | |||||
| * | * | ||||
| * 程序员在非必要情况下尽量不要主动调用此方法 | * 程序员在非必要情况下尽量不要主动调用此方法 | ||||
| * | * | ||||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=获取access_token | |||||
| * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183&token=&lang=zh_CN | |||||
| * </pre> | * </pre> | ||||
| * | * | ||||
| * @param forceRefresh 强制刷新 | * @param forceRefresh 强制刷新 | ||||
| @@ -55,7 +55,7 @@ public interface WxMpService { | |||||
| * 获得jsapi_ticket | * 获得jsapi_ticket | ||||
| * 获得时会检查jsapiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干 | * 获得时会检查jsapiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干 | ||||
| * | * | ||||
| * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95 | |||||
| * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN | |||||
| * </pre> | * </pre> | ||||
| * | * | ||||
| * @param forceRefresh 强制刷新 | * @param forceRefresh 强制刷新 | ||||
| @@ -66,7 +66,7 @@ public interface WxMpService { | |||||
| * <pre> | * <pre> | ||||
| * 创建调用jsapi时所需要的签名 | * 创建调用jsapi时所需要的签名 | ||||
| * | * | ||||
| * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95 | |||||
| * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN | |||||
| * </pre> | * </pre> | ||||
| */ | */ | ||||
| WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; | WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; | ||||
| @@ -75,7 +75,7 @@ public interface WxMpService { | |||||
| * <pre> | * <pre> | ||||
| * 上传群发用的图文消息,上传后才能群发图文消息 | * 上传群发用的图文消息,上传后才能群发图文消息 | ||||
| * | * | ||||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=高级群发接口 | |||||
| * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN | |||||
| * </pre> | * </pre> | ||||
| * | * | ||||
| * @see #massGroupMessageSend(me.chanjar.weixin.mp.bean.WxMpMassTagMessage) | * @see #massGroupMessageSend(me.chanjar.weixin.mp.bean.WxMpMassTagMessage) | ||||
| @@ -86,7 +86,7 @@ public interface WxMpService { | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| * 上传群发用的视频,上传后才能群发视频消息 | * 上传群发用的视频,上传后才能群发视频消息 | ||||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=高级群发接口 | |||||
| * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN | |||||
| * </pre> | * </pre> | ||||
| * | * | ||||
| * @see #massGroupMessageSend(me.chanjar.weixin.mp.bean.WxMpMassTagMessage) | * @see #massGroupMessageSend(me.chanjar.weixin.mp.bean.WxMpMassTagMessage) | ||||
| @@ -99,7 +99,7 @@ public interface WxMpService { | |||||
| * 分组群发消息 | * 分组群发消息 | ||||
| * 如果发送图文消息,必须先使用 {@link #massNewsUpload(me.chanjar.weixin.mp.bean.WxMpMassNews)} 获得media_id,然后再发送 | * 如果发送图文消息,必须先使用 {@link #massNewsUpload(me.chanjar.weixin.mp.bean.WxMpMassNews)} 获得media_id,然后再发送 | ||||
| * 如果发送视频消息,必须先使用 {@link #massVideoUpload(me.chanjar.weixin.mp.bean.WxMpMassVideo)} 获得media_id,然后再发送 | * 如果发送视频消息,必须先使用 {@link #massVideoUpload(me.chanjar.weixin.mp.bean.WxMpMassVideo)} 获得media_id,然后再发送 | ||||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=高级群发接口 | |||||
| * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN | |||||
| * </pre> | * </pre> | ||||
| */ | */ | ||||
| WxMpMassSendResult massGroupMessageSend(WxMpMassTagMessage message) throws WxErrorException; | WxMpMassSendResult massGroupMessageSend(WxMpMassTagMessage message) throws WxErrorException; | ||||
| @@ -109,7 +109,7 @@ public interface WxMpService { | |||||
| * 按openId列表群发消息 | * 按openId列表群发消息 | ||||
| * 如果发送图文消息,必须先使用 {@link #massNewsUpload(me.chanjar.weixin.mp.bean.WxMpMassNews)} 获得media_id,然后再发送 | * 如果发送图文消息,必须先使用 {@link #massNewsUpload(me.chanjar.weixin.mp.bean.WxMpMassNews)} 获得media_id,然后再发送 | ||||
| * 如果发送视频消息,必须先使用 {@link #massVideoUpload(me.chanjar.weixin.mp.bean.WxMpMassVideo)} 获得media_id,然后再发送 | * 如果发送视频消息,必须先使用 {@link #massVideoUpload(me.chanjar.weixin.mp.bean.WxMpMassVideo)} 获得media_id,然后再发送 | ||||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=高级群发接口 | |||||
| * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN | |||||
| * </pre> | * </pre> | ||||
| */ | */ | ||||
| WxMpMassSendResult massOpenIdsMessageSend(WxMpMassOpenIdsMessage message) throws WxErrorException; | WxMpMassSendResult massOpenIdsMessageSend(WxMpMassOpenIdsMessage message) throws WxErrorException; | ||||
| @@ -263,84 +263,84 @@ public interface WxMpService { | |||||
| WxMpConfigStorage getWxMpConfigStorage(); | WxMpConfigStorage getWxMpConfigStorage(); | ||||
| /** | /** | ||||
| * 返回客服接口方法实现类,以方便调用个其各种接口 | |||||
| * 返回客服接口方法实现类,以方便调用其各个接口 | |||||
| * | * | ||||
| * @return WxMpKefuService | * @return WxMpKefuService | ||||
| */ | */ | ||||
| WxMpKefuService getKefuService(); | WxMpKefuService getKefuService(); | ||||
| /** | /** | ||||
| * 返回素材相关接口方法的实现类对象,以方便调用个其各种接口 | |||||
| * 返回素材相关接口方法的实现类对象,以方便调用其各个接口 | |||||
| * | * | ||||
| * @return WxMpMaterialService | * @return WxMpMaterialService | ||||
| */ | */ | ||||
| WxMpMaterialService getMaterialService(); | WxMpMaterialService getMaterialService(); | ||||
| /** | /** | ||||
| * 返回菜单相关接口方法的实现类对象,以方便调用个其各种接口 | |||||
| * 返回菜单相关接口方法的实现类对象,以方便调用其各个接口 | |||||
| * | * | ||||
| * @return WxMpMenuService | * @return WxMpMenuService | ||||
| */ | */ | ||||
| WxMpMenuService getMenuService(); | WxMpMenuService getMenuService(); | ||||
| /** | /** | ||||
| * 返回用户相关接口方法的实现类对象,以方便调用个其各种接口 | |||||
| * 返回用户相关接口方法的实现类对象,以方便调用其各个接口 | |||||
| * | * | ||||
| * @return WxMpUserService | * @return WxMpUserService | ||||
| */ | */ | ||||
| WxMpUserService getUserService(); | WxMpUserService getUserService(); | ||||
| /** | /** | ||||
| * 返回用户标签相关接口方法的实现类对象,以方便调用个其各种接口 | |||||
| * 返回用户标签相关接口方法的实现类对象,以方便调用其各个接口 | |||||
| * | * | ||||
| * @return WxMpUserTagService | * @return WxMpUserTagService | ||||
| */ | */ | ||||
| WxMpUserTagService getUserTagService(); | WxMpUserTagService getUserTagService(); | ||||
| /** | /** | ||||
| * 返回二维码相关接口方法的实现类对象,以方便调用个其各种接口 | |||||
| * 返回二维码相关接口方法的实现类对象,以方便调用其各个接口 | |||||
| * | * | ||||
| * @return WxMpQrcodeService | * @return WxMpQrcodeService | ||||
| */ | */ | ||||
| WxMpQrcodeService getQrcodeService(); | WxMpQrcodeService getQrcodeService(); | ||||
| /** | /** | ||||
| * 返回卡券相关接口方法的实现类对象,以方便调用个其各种接口 | |||||
| * 返回卡券相关接口方法的实现类对象,以方便调用其各个接口 | |||||
| * | * | ||||
| * @return WxMpCardService | * @return WxMpCardService | ||||
| */ | */ | ||||
| WxMpCardService getCardService(); | WxMpCardService getCardService(); | ||||
| /** | /** | ||||
| * 返回微信支付相关接口方法的实现类对象,以方便调用个其各种接口 | |||||
| * 返回微信支付相关接口方法的实现类对象,以方便调用其各个接口 | |||||
| * | * | ||||
| * @return WxMpPayService | * @return WxMpPayService | ||||
| */ | */ | ||||
| WxMpPayService getPayService(); | WxMpPayService getPayService(); | ||||
| /** | /** | ||||
| * 返回数据分析统计相关接口方法的实现类对象,以方便调用个其各种接口 | |||||
| * 返回数据分析统计相关接口方法的实现类对象,以方便调用其各个接口 | |||||
| * | * | ||||
| * @return WxMpDataCubeService | * @return WxMpDataCubeService | ||||
| */ | */ | ||||
| WxMpDataCubeService getDataCubeService(); | WxMpDataCubeService getDataCubeService(); | ||||
| /** | /** | ||||
| * 返回用户黑名单管理相关接口方法的实现类对象,以方便调用其各种接口 | |||||
| * 返回用户黑名单管理相关接口方法的实现类对象,以方便调用其各个接口 | |||||
| * | * | ||||
| * @return WxMpUserBlacklistService | * @return WxMpUserBlacklistService | ||||
| */ | */ | ||||
| WxMpUserBlacklistService getBlackListService(); | WxMpUserBlacklistService getBlackListService(); | ||||
| /** | /** | ||||
| * 返回门店管理相关接口方法的实现类对象,以方便调用其各种接口 | |||||
| * 返回门店管理相关接口方法的实现类对象,以方便调用其各个接口 | |||||
| * | * | ||||
| * @return WxMpStoreService | * @return WxMpStoreService | ||||
| */ | */ | ||||
| WxMpStoreService getStoreService(); | WxMpStoreService getStoreService(); | ||||
| /** | /** | ||||
| * 返回模板消息相关接口方法的实现类对象,以方便调用其各种接口 | |||||
| * 返回模板消息相关接口方法的实现类对象,以方便调用其各个接口 | |||||
| * | * | ||||
| * @return WxMpTemplateMsgService | * @return WxMpTemplateMsgService | ||||
| */ | */ | ||||
| @@ -1,6 +1,7 @@ | |||||
| package me.chanjar.weixin.mp.api.impl; | package me.chanjar.weixin.mp.api.impl; | ||||
| import java.util.Arrays; | import java.util.Arrays; | ||||
| import java.util.concurrent.locks.Lock; | |||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
| @@ -29,11 +30,6 @@ public class WxMpCardServiceImpl implements WxMpCardService { | |||||
| private final Logger log = LoggerFactory.getLogger(WxMpCardServiceImpl.class); | private final Logger log = LoggerFactory.getLogger(WxMpCardServiceImpl.class); | ||||
| /** | |||||
| * 全局的是否正在刷新卡券api_ticket的锁 | |||||
| */ | |||||
| private final Object globalCardApiTicketRefreshLock = new Object(); | |||||
| private WxMpService wxMpService; | private WxMpService wxMpService; | ||||
| public WxMpCardServiceImpl(WxMpService wxMpService) { | public WxMpCardServiceImpl(WxMpService wxMpService) { | ||||
| @@ -66,21 +62,25 @@ public class WxMpCardServiceImpl implements WxMpCardService { | |||||
| */ | */ | ||||
| @Override | @Override | ||||
| public String getCardApiTicket(boolean forceRefresh) throws WxErrorException { | public String getCardApiTicket(boolean forceRefresh) throws WxErrorException { | ||||
| if (forceRefresh) { | |||||
| this.wxMpService.getWxMpConfigStorage().expireCardApiTicket(); | |||||
| } | |||||
| if (this.wxMpService.getWxMpConfigStorage().isCardApiTicketExpired()) { | |||||
| synchronized (this.globalCardApiTicketRefreshLock) { | |||||
| if (this.wxMpService.getWxMpConfigStorage().isCardApiTicketExpired()) { | |||||
| String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card"; | |||||
| String responseContent = this.wxMpService.execute(new SimpleGetRequestExecutor(), url, null); | |||||
| JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||||
| JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); | |||||
| String cardApiTicket = tmpJsonObject.get("ticket").getAsString(); | |||||
| int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); | |||||
| this.wxMpService.getWxMpConfigStorage().updateCardApiTicket(cardApiTicket, expiresInSeconds); | |||||
| } | |||||
| Lock lock = wxMpService.getWxMpConfigStorage().getCardApiTicketLock(); | |||||
| try { | |||||
| lock.lock(); | |||||
| if (forceRefresh) { | |||||
| this.wxMpService.getWxMpConfigStorage().expireCardApiTicket(); | |||||
| } | |||||
| if (this.wxMpService.getWxMpConfigStorage().isCardApiTicketExpired()) { | |||||
| String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card"; | |||||
| String responseContent = this.wxMpService.execute(new SimpleGetRequestExecutor(), url, null); | |||||
| JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||||
| JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); | |||||
| String cardApiTicket = tmpJsonObject.get("ticket").getAsString(); | |||||
| int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); | |||||
| this.wxMpService.getWxMpConfigStorage().updateCardApiTicket(cardApiTicket, expiresInSeconds); | |||||
| } | } | ||||
| } finally { | |||||
| lock.unlock(); | |||||
| } | } | ||||
| return this.wxMpService.getWxMpConfigStorage().getCardApiTicket(); | return this.wxMpService.getWxMpConfigStorage().getCardApiTicket(); | ||||
| } | } | ||||
| @@ -1,12 +1,13 @@ | |||||
| package me.chanjar.weixin.mp.api.impl; | package me.chanjar.weixin.mp.api.impl; | ||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import me.chanjar.weixin.common.bean.menu.WxMenu; | import me.chanjar.weixin.common.bean.menu.WxMenu; | ||||
| import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
| import me.chanjar.weixin.mp.api.WxMpMenuService; | import me.chanjar.weixin.mp.api.WxMpMenuService; | ||||
| import me.chanjar.weixin.mp.api.WxMpService; | import me.chanjar.weixin.mp.api.WxMpService; | ||||
| import me.chanjar.weixin.mp.bean.menu.WxMpGetSelfMenuInfoResult; | |||||
| import me.chanjar.weixin.mp.bean.menu.WxMpSelfMenuInfo; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| /** | /** | ||||
| * Created by Binary Wang on 2016/7/21. | * Created by Binary Wang on 2016/7/21. | ||||
| @@ -14,7 +15,7 @@ import me.chanjar.weixin.mp.api.WxMpService; | |||||
| public class WxMpMenuServiceImpl implements WxMpMenuService { | public class WxMpMenuServiceImpl implements WxMpMenuService { | ||||
| private static final String API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/menu"; | private static final String API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/menu"; | ||||
| private static Logger log = LoggerFactory | private static Logger log = LoggerFactory | ||||
| .getLogger(WxMpMenuServiceImpl.class); | |||||
| .getLogger(WxMpMenuServiceImpl.class); | |||||
| private WxMpService wxMpService; | private WxMpService wxMpService; | ||||
| @@ -74,10 +75,17 @@ public class WxMpMenuServiceImpl implements WxMpMenuService { | |||||
| } catch (WxErrorException e) { | } catch (WxErrorException e) { | ||||
| // 46003 不存在的菜单数据 46002 不存在的菜单版本 | // 46003 不存在的菜单数据 46002 不存在的菜单版本 | ||||
| if (e.getError().getErrorCode() == 46003 | if (e.getError().getErrorCode() == 46003 | ||||
| || e.getError().getErrorCode() == 46002) { | |||||
| || e.getError().getErrorCode() == 46002) { | |||||
| return null; | return null; | ||||
| } | } | ||||
| throw e; | throw e; | ||||
| } | } | ||||
| } | } | ||||
| @Override | |||||
| public WxMpGetSelfMenuInfoResult getSelfMenuInfo() throws WxErrorException { | |||||
| String url = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info"; | |||||
| String resultContent = this.wxMpService.get(url, null); | |||||
| return WxMpGetSelfMenuInfoResult.fromJson(resultContent); | |||||
| } | |||||
| } | } | ||||
| @@ -8,7 +8,6 @@ import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||||
| import me.chanjar.weixin.mp.api.WxMpPayService; | import me.chanjar.weixin.mp.api.WxMpPayService; | ||||
| import me.chanjar.weixin.mp.api.WxMpService; | import me.chanjar.weixin.mp.api.WxMpService; | ||||
| import me.chanjar.weixin.mp.bean.pay.WxPayJsSDKCallback; | import me.chanjar.weixin.mp.bean.pay.WxPayJsSDKCallback; | ||||
| import me.chanjar.weixin.mp.bean.pay.result.WxPayOrderCloseResult; | |||||
| import me.chanjar.weixin.mp.bean.pay.request.*; | import me.chanjar.weixin.mp.bean.pay.request.*; | ||||
| import me.chanjar.weixin.mp.bean.pay.result.*; | import me.chanjar.weixin.mp.bean.pay.result.*; | ||||
| import org.apache.commons.codec.digest.DigestUtils; | import org.apache.commons.codec.digest.DigestUtils; | ||||
| @@ -42,13 +41,11 @@ import java.util.*; | |||||
| */ | */ | ||||
| public class WxMpPayServiceImpl implements WxMpPayService { | public class WxMpPayServiceImpl implements WxMpPayService { | ||||
| protected final Logger log = LoggerFactory.getLogger(this.getClass()); | |||||
| private static final String PAY_BASE_URL = "https://api.mch.weixin.qq.com"; | private static final String PAY_BASE_URL = "https://api.mch.weixin.qq.com"; | ||||
| private static final String[] TRADE_TYPES = new String[]{"JSAPI","NATIVE", "APP"}; | |||||
| private static final String[] REFUND_ACCOUNT = new String[]{"REFUND_SOURCE_RECHARGE_FUNDS", | |||||
| private static final String[] TRADE_TYPES = new String[]{"JSAPI", "NATIVE", "APP"}; | |||||
| private static final String[] REFUND_ACCOUNT = new String[]{"REFUND_SOURCE_RECHARGE_FUNDS", | |||||
| "REFUND_SOURCE_UNSETTLED_FUNDS"}; | "REFUND_SOURCE_UNSETTLED_FUNDS"}; | ||||
| protected final Logger log = LoggerFactory.getLogger(this.getClass()); | |||||
| private WxMpService wxMpService; | private WxMpService wxMpService; | ||||
| public WxMpPayServiceImpl(WxMpService wxMpService) { | public WxMpPayServiceImpl(WxMpService wxMpService) { | ||||
| @@ -57,7 +54,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
| @Override | @Override | ||||
| public WxPayRefundResult refund(WxPayRefundRequest request, File keyFile) | public WxPayRefundResult refund(WxPayRefundRequest request, File keyFile) | ||||
| throws WxErrorException { | |||||
| throws WxErrorException { | |||||
| checkParameters(request); | checkParameters(request); | ||||
| XStream xstream = XStreamInitializer.getInstance(); | XStream xstream = XStreamInitializer.getInstance(); | ||||
| @@ -67,7 +64,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
| request.setAppid(this.wxMpService.getWxMpConfigStorage().getAppId()); | request.setAppid(this.wxMpService.getWxMpConfigStorage().getAppId()); | ||||
| String partnerId = this.wxMpService.getWxMpConfigStorage().getPartnerId(); | String partnerId = this.wxMpService.getWxMpConfigStorage().getPartnerId(); | ||||
| request.setMchId(partnerId); | request.setMchId(partnerId); | ||||
| request.setNonceStr( System.currentTimeMillis() + ""); | |||||
| request.setNonceStr(System.currentTimeMillis() + ""); | |||||
| request.setOpUserId(partnerId); | request.setOpUserId(partnerId); | ||||
| String sign = this.createSign(BeanUtils.xmlBean2Map(request), this.wxMpService.getWxMpConfigStorage().getPartnerKey()); | String sign = this.createSign(BeanUtils.xmlBean2Map(request), this.wxMpService.getWxMpConfigStorage().getPartnerKey()); | ||||
| request.setSign(sign); | request.setSign(sign); | ||||
| @@ -79,11 +76,45 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
| return result; | return result; | ||||
| } | } | ||||
| @Override | |||||
| public WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) throws WxErrorException { | |||||
| if ((StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo) && StringUtils.isBlank(outRefundNo) && StringUtils.isBlank(refundId)) || | |||||
| (StringUtils.isNotBlank(transactionId) && StringUtils.isNotBlank(outTradeNo) && StringUtils.isNotBlank(outRefundNo) && StringUtils.isNotBlank(refundId))) { | |||||
| throw new IllegalArgumentException("transaction_id , out_trade_no,out_refund_no, refund_id 必须四选一"); | |||||
| } | |||||
| XStream xstream = XStreamInitializer.getInstance(); | |||||
| xstream.processAnnotations(WxPayRefundQueryRequest.class); | |||||
| xstream.processAnnotations(WxPayRefundQueryResult.class); | |||||
| WxPayRefundQueryRequest request = new WxPayRefundQueryRequest(); | |||||
| request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); | |||||
| request.setTransactionId(StringUtils.trimToNull(transactionId)); | |||||
| request.setOutRefundNo(StringUtils.trimToNull(outRefundNo)); | |||||
| request.setRefundId(StringUtils.trimToNull(refundId)); | |||||
| request.setAppid(this.wxMpService.getWxMpConfigStorage().getAppId()); | |||||
| request.setMchId(this.wxMpService.getWxMpConfigStorage().getPartnerId()); | |||||
| request.setNonceStr(System.currentTimeMillis() + ""); | |||||
| String sign = this.createSign(BeanUtils.xmlBean2Map(request), | |||||
| this.wxMpService.getWxMpConfigStorage().getPartnerKey()); | |||||
| request.setSign(sign); | |||||
| String url = PAY_BASE_URL + "/pay/refundquery"; | |||||
| String responseContent = this.executeRequest(url, xstream.toXML(request)); | |||||
| WxPayRefundQueryResult result = (WxPayRefundQueryResult) xstream.fromXML(responseContent); | |||||
| result.composeRefundRecords(responseContent); | |||||
| this.checkResult(result); | |||||
| return result; | |||||
| } | |||||
| private void checkResult(WxPayBaseResult result) throws WxErrorException { | private void checkResult(WxPayBaseResult result) throws WxErrorException { | ||||
| if (!"SUCCESS".equalsIgnoreCase(result.getReturnCode()) | if (!"SUCCESS".equalsIgnoreCase(result.getReturnCode()) | ||||
| || !"SUCCESS".equalsIgnoreCase(result.getResultCode())) { | || !"SUCCESS".equalsIgnoreCase(result.getResultCode())) { | ||||
| throw new WxErrorException(WxError.newBuilder().setErrorCode(-1) | throw new WxErrorException(WxError.newBuilder().setErrorCode(-1) | ||||
| .setErrorMsg("返回代码:" + result.getReturnCode() + ", 返回信息: " | |||||
| .setErrorMsg("返回代码: " + result.getReturnCode() + ", 返回信息: " | |||||
| + result.getReturnMsg() + ", 结果代码: " + result.getResultCode() + ", 错误代码: " | + result.getReturnMsg() + ", 结果代码: " + result.getResultCode() + ", 错误代码: " | ||||
| + result.getErrCode() + ", 错误详情: " + result.getErrCodeDes()) | + result.getErrCode() + ", 错误详情: " + result.getErrCodeDes()) | ||||
| .build()); | .build()); | ||||
| @@ -94,7 +125,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
| BeanUtils.checkRequiredFields(request); | BeanUtils.checkRequiredFields(request); | ||||
| if (StringUtils.isNotBlank(request.getRefundAccount())) { | if (StringUtils.isNotBlank(request.getRefundAccount())) { | ||||
| if(!ArrayUtils.contains(REFUND_ACCOUNT, request.getRefundAccount())){ | |||||
| if (!ArrayUtils.contains(REFUND_ACCOUNT, request.getRefundAccount())) { | |||||
| throw new IllegalArgumentException("refund_account目前必须为" + Arrays.toString(REFUND_ACCOUNT) + "其中之一"); | throw new IllegalArgumentException("refund_account目前必须为" + Arrays.toString(REFUND_ACCOUNT) + "其中之一"); | ||||
| } | } | ||||
| } | } | ||||
| @@ -118,14 +149,14 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
| @Override | @Override | ||||
| public boolean checkJSSDKCallbackDataSignature(Map<String, String> kvm, | public boolean checkJSSDKCallbackDataSignature(Map<String, String> kvm, | ||||
| String signature) { | |||||
| String signature) { | |||||
| return signature.equals(this.createSign(kvm, | return signature.equals(this.createSign(kvm, | ||||
| this.wxMpService.getWxMpConfigStorage().getPartnerKey())); | |||||
| this.wxMpService.getWxMpConfigStorage().getPartnerKey())); | |||||
| } | } | ||||
| @Override | @Override | ||||
| public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request, File keyFile) | public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request, File keyFile) | ||||
| throws WxErrorException { | |||||
| throws WxErrorException { | |||||
| XStream xstream = XStreamInitializer.getInstance(); | XStream xstream = XStreamInitializer.getInstance(); | ||||
| xstream.processAnnotations(WxPaySendRedpackRequest.class); | xstream.processAnnotations(WxPaySendRedpackRequest.class); | ||||
| xstream.processAnnotations(WxPaySendRedpackResult.class); | xstream.processAnnotations(WxPaySendRedpackResult.class); | ||||
| @@ -136,7 +167,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
| request.setNonceStr(System.currentTimeMillis() + ""); | request.setNonceStr(System.currentTimeMillis() + ""); | ||||
| String sign = this.createSign(BeanUtils.xmlBean2Map(request), | String sign = this.createSign(BeanUtils.xmlBean2Map(request), | ||||
| this.wxMpService.getWxMpConfigStorage().getPartnerKey()); | |||||
| this.wxMpService.getWxMpConfigStorage().getPartnerKey()); | |||||
| request.setSign(sign); | request.setSign(sign); | ||||
| String url = PAY_BASE_URL + "/mmpaymkttransfers/sendredpack"; | String url = PAY_BASE_URL + "/mmpaymkttransfers/sendredpack"; | ||||
| @@ -147,25 +178,52 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
| String responseContent = this.executeRequestWithKeyFile(url, keyFile, xstream.toXML(request), mchId); | String responseContent = this.executeRequestWithKeyFile(url, keyFile, xstream.toXML(request), mchId); | ||||
| WxPaySendRedpackResult result = (WxPaySendRedpackResult) xstream | WxPaySendRedpackResult result = (WxPaySendRedpackResult) xstream | ||||
| .fromXML(responseContent); | |||||
| .fromXML(responseContent); | |||||
| this.checkResult(result); | |||||
| return result; | |||||
| } | |||||
| @Override | |||||
| public WxPayRedpackQueryResult queryRedpack(String mchBillNo, File keyFile) throws WxErrorException { | |||||
| XStream xstream = XStreamInitializer.getInstance(); | |||||
| xstream.processAnnotations(WxPayRedpackQueryRequest.class); | |||||
| xstream.processAnnotations(WxPayRedpackQueryResult.class); | |||||
| WxPayRedpackQueryRequest request = new WxPayRedpackQueryRequest(); | |||||
| request.setMchBillNo(mchBillNo); | |||||
| request.setBillType("MCHT"); | |||||
| request.setAppid(this.wxMpService.getWxMpConfigStorage().getAppId()); | |||||
| String mchId = this.wxMpService.getWxMpConfigStorage().getPartnerId(); | |||||
| request.setMchId(mchId); | |||||
| request.setNonceStr(System.currentTimeMillis() + ""); | |||||
| String sign = this.createSign(BeanUtils.xmlBean2Map(request), | |||||
| this.wxMpService.getWxMpConfigStorage().getPartnerKey()); | |||||
| request.setSign(sign); | |||||
| String url = PAY_BASE_URL + "/mmpaymkttransfers/gethbinfo"; | |||||
| String responseContent = this.executeRequestWithKeyFile(url, keyFile, xstream.toXML(request), mchId); | |||||
| WxPayRedpackQueryResult result = (WxPayRedpackQueryResult) xstream.fromXML(responseContent); | |||||
| this.checkResult(result); | this.checkResult(result); | ||||
| return result; | return result; | ||||
| } | } | ||||
| /** | /** | ||||
| * 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) | * 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) | ||||
| * | |||||
| * @param packageParams 原始参数 | * @param packageParams 原始参数 | ||||
| * @param signKey 加密Key(即 商户Key) | |||||
| * @param signKey 加密Key(即 商户Key) | |||||
| * @return 签名字符串 | * @return 签名字符串 | ||||
| */ | */ | ||||
| private String createSign(Map<String, String> packageParams, String signKey) { | private String createSign(Map<String, String> packageParams, String signKey) { | ||||
| SortedMap<String, String> sortedMap = new TreeMap<>(packageParams); | SortedMap<String, String> sortedMap = new TreeMap<>(packageParams); | ||||
| StringBuffer toSign = new StringBuffer(); | |||||
| StringBuilder toSign = new StringBuilder(); | |||||
| for (String key : sortedMap.keySet()) { | for (String key : sortedMap.keySet()) { | ||||
| String value = packageParams.get(key); | String value = packageParams.get(key); | ||||
| if (null != value && !"".equals(value) && !"sign".equals(key) | if (null != value && !"".equals(value) && !"sign".equals(key) | ||||
| && !"key".equals(key)) { | |||||
| && !"key".equals(key)) { | |||||
| toSign.append(key + "=" + value + "&"); | toSign.append(key + "=" + value + "&"); | ||||
| } | } | ||||
| } | } | ||||
| @@ -237,7 +295,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
| @Override | @Override | ||||
| public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) | public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) | ||||
| throws WxErrorException { | |||||
| throws WxErrorException { | |||||
| checkParameters(request); | checkParameters(request); | ||||
| XStream xstream = XStreamInitializer.getInstance(); | XStream xstream = XStreamInitializer.getInstance(); | ||||
| @@ -249,14 +307,14 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
| request.setNonceStr(System.currentTimeMillis() + ""); | request.setNonceStr(System.currentTimeMillis() + ""); | ||||
| String sign = this.createSign(BeanUtils.xmlBean2Map(request), | String sign = this.createSign(BeanUtils.xmlBean2Map(request), | ||||
| this.wxMpService.getWxMpConfigStorage().getPartnerKey()); | |||||
| this.wxMpService.getWxMpConfigStorage().getPartnerKey()); | |||||
| request.setSign(sign); | request.setSign(sign); | ||||
| String url = PAY_BASE_URL + "/pay/unifiedorder"; | String url = PAY_BASE_URL + "/pay/unifiedorder"; | ||||
| String responseContent = this.executeRequest(url, xstream.toXML(request)); | String responseContent = this.executeRequest(url, xstream.toXML(request)); | ||||
| WxPayUnifiedOrderResult result = (WxPayUnifiedOrderResult) xstream | WxPayUnifiedOrderResult result = (WxPayUnifiedOrderResult) xstream | ||||
| .fromXML(responseContent); | |||||
| .fromXML(responseContent); | |||||
| this.checkResult(result); | this.checkResult(result); | ||||
| return result; | return result; | ||||
| } | } | ||||
| @@ -264,7 +322,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
| private void checkParameters(WxPayUnifiedOrderRequest request) throws WxErrorException { | private void checkParameters(WxPayUnifiedOrderRequest request) throws WxErrorException { | ||||
| BeanUtils.checkRequiredFields(request); | BeanUtils.checkRequiredFields(request); | ||||
| if (! ArrayUtils.contains(TRADE_TYPES, request.getTradeType())) { | |||||
| if (!ArrayUtils.contains(TRADE_TYPES, request.getTradeType())) { | |||||
| throw new IllegalArgumentException("trade_type目前必须为" + Arrays.toString(TRADE_TYPES) + "其中之一"); | throw new IllegalArgumentException("trade_type目前必须为" + Arrays.toString(TRADE_TYPES) + "其中之一"); | ||||
| } | } | ||||
| @@ -283,7 +341,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
| String prepayId = unifiedOrderResult.getPrepayId(); | String prepayId = unifiedOrderResult.getPrepayId(); | ||||
| if (StringUtils.isBlank(prepayId)) { | if (StringUtils.isBlank(prepayId)) { | ||||
| throw new RuntimeException(String.format("Failed to get prepay id due to error code '%s'(%s).", | throw new RuntimeException(String.format("Failed to get prepay id due to error code '%s'(%s).", | ||||
| unifiedOrderResult.getErrCode(), unifiedOrderResult.getErrCodeDes())); | |||||
| unifiedOrderResult.getErrCode(), unifiedOrderResult.getErrCodeDes())); | |||||
| } | } | ||||
| Map<String, String> payInfo = new HashMap<>(); | Map<String, String> payInfo = new HashMap<>(); | ||||
| @@ -347,7 +405,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
| return result; | return result; | ||||
| } | } | ||||
| private String executeRequest( String url, String requestStr) throws WxErrorException { | |||||
| private String executeRequest(String url, String requestStr) throws WxErrorException { | |||||
| HttpPost httpPost = new HttpPost(url); | HttpPost httpPost = new HttpPost(url); | ||||
| if (this.wxMpService.getHttpProxy() != null) { | if (this.wxMpService.getHttpProxy() != null) { | ||||
| httpPost.setConfig(RequestConfig.custom().setProxy(this.wxMpService.getHttpProxy()).build()); | httpPost.setConfig(RequestConfig.custom().setProxy(this.wxMpService.getHttpProxy()).build()); | ||||
| @@ -358,25 +416,25 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
| try (CloseableHttpResponse response = httpclient.execute(httpPost)) { | try (CloseableHttpResponse response = httpclient.execute(httpPost)) { | ||||
| String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8); | String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8); | ||||
| this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}",url, requestStr, result); | |||||
| this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, requestStr, result); | |||||
| return result; | return result; | ||||
| } | } | ||||
| } catch (IOException e) { | } catch (IOException e) { | ||||
| this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", url, requestStr, e.getMessage()); | this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", url, requestStr, e.getMessage()); | ||||
| throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg(e.getMessage()).build(), e); | throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg(e.getMessage()).build(), e); | ||||
| }finally { | |||||
| } finally { | |||||
| httpPost.releaseConnection(); | httpPost.releaseConnection(); | ||||
| } | } | ||||
| } | } | ||||
| private String executeRequestWithKeyFile( String url, File keyFile, String requestStr, String mchId) throws WxErrorException { | |||||
| private String executeRequestWithKeyFile(String url, File keyFile, String requestStr, String mchId) throws WxErrorException { | |||||
| try (FileInputStream inputStream = new FileInputStream(keyFile)) { | try (FileInputStream inputStream = new FileInputStream(keyFile)) { | ||||
| KeyStore keyStore = KeyStore.getInstance("PKCS12"); | KeyStore keyStore = KeyStore.getInstance("PKCS12"); | ||||
| keyStore.load(inputStream, mchId.toCharArray()); | keyStore.load(inputStream, mchId.toCharArray()); | ||||
| SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build(); | SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build(); | ||||
| SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null, | |||||
| new DefaultHostnameVerifier()); | |||||
| SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, | |||||
| new DefaultHostnameVerifier()); | |||||
| HttpPost httpPost = new HttpPost(url); | HttpPost httpPost = new HttpPost(url); | ||||
| if (this.wxMpService.getHttpProxy() != null) { | if (this.wxMpService.getHttpProxy() != null) { | ||||
| @@ -387,10 +445,10 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
| httpPost.setEntity(new StringEntity(new String(requestStr.getBytes("UTF-8"), "ISO-8859-1"))); | httpPost.setEntity(new StringEntity(new String(requestStr.getBytes("UTF-8"), "ISO-8859-1"))); | ||||
| try (CloseableHttpResponse response = httpclient.execute(httpPost)) { | try (CloseableHttpResponse response = httpclient.execute(httpPost)) { | ||||
| String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8); | String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8); | ||||
| this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}",url, requestStr, result); | |||||
| this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, requestStr, result); | |||||
| return result; | return result; | ||||
| } | } | ||||
| }finally { | |||||
| } finally { | |||||
| httpPost.releaseConnection(); | httpPost.releaseConnection(); | ||||
| } | } | ||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| @@ -28,6 +28,7 @@ import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
| import java.io.IOException; | import java.io.IOException; | ||||
| import java.util.concurrent.locks.Lock; | |||||
| public class WxMpServiceImpl implements WxMpService { | public class WxMpServiceImpl implements WxMpService { | ||||
| @@ -35,16 +36,6 @@ public class WxMpServiceImpl implements WxMpService { | |||||
| protected final Logger log = LoggerFactory.getLogger(this.getClass()); | protected final Logger log = LoggerFactory.getLogger(this.getClass()); | ||||
| /** | |||||
| * 全局的是否正在刷新access token的锁 | |||||
| */ | |||||
| private final Object globalAccessTokenRefreshLock = new Object(); | |||||
| /** | |||||
| * 全局的是否正在刷新jsapi_ticket的锁 | |||||
| */ | |||||
| private final Object globalJsapiTicketRefreshLock = new Object(); | |||||
| private WxMpConfigStorage configStorage; | private WxMpConfigStorage configStorage; | ||||
| private WxMpKefuService kefuService = new WxMpKefuServiceImpl(this); | private WxMpKefuService kefuService = new WxMpKefuServiceImpl(this); | ||||
| @@ -98,38 +89,42 @@ public class WxMpServiceImpl implements WxMpService { | |||||
| @Override | @Override | ||||
| public String getAccessToken(boolean forceRefresh) throws WxErrorException { | public String getAccessToken(boolean forceRefresh) throws WxErrorException { | ||||
| if (forceRefresh) { | |||||
| this.configStorage.expireAccessToken(); | |||||
| } | |||||
| if (this.configStorage.isAccessTokenExpired()) { | |||||
| synchronized (this.globalAccessTokenRefreshLock) { | |||||
| if (this.configStorage.isAccessTokenExpired()) { | |||||
| String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" + | |||||
| "&appid=" + this.configStorage.getAppId() + "&secret=" | |||||
| + this.configStorage.getSecret(); | |||||
| try { | |||||
| HttpGet httpGet = new HttpGet(url); | |||||
| if (this.httpProxy != null) { | |||||
| RequestConfig config = RequestConfig.custom().setProxy(this.httpProxy).build(); | |||||
| httpGet.setConfig(config); | |||||
| } | |||||
| try (CloseableHttpResponse response = getHttpclient().execute(httpGet)) { | |||||
| String resultContent = new BasicResponseHandler().handleResponse(response); | |||||
| WxError error = WxError.fromJson(resultContent); | |||||
| if (error.getErrorCode() != 0) { | |||||
| throw new WxErrorException(error); | |||||
| } | |||||
| WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | |||||
| this.configStorage.updateAccessToken(accessToken.getAccessToken(), | |||||
| accessToken.getExpiresIn()); | |||||
| }finally { | |||||
| httpGet.releaseConnection(); | |||||
| Lock lock = this.configStorage.getAccessTokenLock(); | |||||
| try { | |||||
| lock.lock(); | |||||
| if (forceRefresh) { | |||||
| this.configStorage.expireAccessToken(); | |||||
| } | |||||
| if (this.configStorage.isAccessTokenExpired()) { | |||||
| String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" + | |||||
| "&appid=" + this.configStorage.getAppId() + "&secret=" | |||||
| + this.configStorage.getSecret(); | |||||
| try { | |||||
| HttpGet httpGet = new HttpGet(url); | |||||
| if (this.httpProxy != null) { | |||||
| RequestConfig config = RequestConfig.custom().setProxy(this.httpProxy).build(); | |||||
| httpGet.setConfig(config); | |||||
| } | |||||
| try (CloseableHttpResponse response = getHttpclient().execute(httpGet)) { | |||||
| String resultContent = new BasicResponseHandler().handleResponse(response); | |||||
| WxError error = WxError.fromJson(resultContent); | |||||
| if (error.getErrorCode() != 0) { | |||||
| throw new WxErrorException(error); | |||||
| } | } | ||||
| } catch (IOException e) { | |||||
| throw new RuntimeException(e); | |||||
| WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | |||||
| this.configStorage.updateAccessToken(accessToken.getAccessToken(), | |||||
| accessToken.getExpiresIn()); | |||||
| }finally { | |||||
| httpGet.releaseConnection(); | |||||
| } | } | ||||
| } catch (IOException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | } | ||||
| } | } | ||||
| } finally { | |||||
| lock.unlock(); | |||||
| } | } | ||||
| return this.configStorage.getAccessToken(); | return this.configStorage.getAccessToken(); | ||||
| } | } | ||||
| @@ -141,22 +136,25 @@ public class WxMpServiceImpl implements WxMpService { | |||||
| @Override | @Override | ||||
| public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { | public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { | ||||
| if (forceRefresh) { | |||||
| this.configStorage.expireJsapiTicket(); | |||||
| } | |||||
| Lock lock = this.configStorage.getJsapiTicketLock(); | |||||
| try { | |||||
| lock.lock(); | |||||
| if (this.configStorage.isJsapiTicketExpired()) { | |||||
| synchronized (this.globalJsapiTicketRefreshLock) { | |||||
| if (this.configStorage.isJsapiTicketExpired()) { | |||||
| String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi"; | |||||
| String responseContent = execute(new SimpleGetRequestExecutor(), url, null); | |||||
| JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); | |||||
| JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); | |||||
| String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); | |||||
| int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); | |||||
| this.configStorage.updateJsapiTicket(jsapiTicket, expiresInSeconds); | |||||
| } | |||||
| if (forceRefresh) { | |||||
| this.configStorage.expireJsapiTicket(); | |||||
| } | |||||
| if (this.configStorage.isJsapiTicketExpired()) { | |||||
| String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi"; | |||||
| String responseContent = execute(new SimpleGetRequestExecutor(), url, null); | |||||
| JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); | |||||
| JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); | |||||
| String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); | |||||
| int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); | |||||
| this.configStorage.updateJsapiTicket(jsapiTicket, expiresInSeconds); | |||||
| } | } | ||||
| } finally { | |||||
| lock.unlock(); | |||||
| } | } | ||||
| return this.configStorage.getJsapiTicket(); | return this.configStorage.getJsapiTicket(); | ||||
| } | } | ||||
| @@ -370,9 +368,7 @@ public class WxMpServiceImpl implements WxMpService { | |||||
| return result; | return result; | ||||
| } catch (WxErrorException e) { | } catch (WxErrorException e) { | ||||
| WxError error = e.getError(); | WxError error = e.getError(); | ||||
| /** | |||||
| * -1 系统繁忙, 1000ms后重试 | |||||
| */ | |||||
| // -1 系统繁忙, 1000ms后重试 | |||||
| if (error.getErrorCode() == -1) { | if (error.getErrorCode() == -1) { | ||||
| int sleepMillis = this.retrySleepMillis * (1 << retryTimes); | int sleepMillis = this.retrySleepMillis * (1 << retryTimes); | ||||
| try { | try { | ||||
| @@ -411,8 +407,11 @@ public class WxMpServiceImpl implements WxMpService { | |||||
| if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) { | if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) { | ||||
| // 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token | // 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token | ||||
| this.configStorage.expireAccessToken(); | this.configStorage.expireAccessToken(); | ||||
| return this.execute(executor, uri, data); | |||||
| if(this.configStorage.autoRefreshToken()){ | |||||
| return this.execute(executor, uri, data); | |||||
| } | |||||
| } | } | ||||
| if (error.getErrorCode() != 0) { | if (error.getErrorCode() != 0) { | ||||
| this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uri, data, | this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uri, data, | ||||
| error); | error); | ||||
| @@ -1,13 +1,13 @@ | |||||
| package me.chanjar.weixin.mp.bean; | package me.chanjar.weixin.mp.bean; | ||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||||
| import java.io.Serializable; | import java.io.Serializable; | ||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.List; | import java.util.List; | ||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||||
| /** | /** | ||||
| * OpenId列表群发的消息 | |||||
| * openid列表群发的消息 | |||||
| * | * | ||||
| * @author chanjarster | * @author chanjarster | ||||
| */ | */ | ||||
| @@ -64,17 +64,26 @@ public class WxMpMassOpenIdsMessage implements Serializable { | |||||
| } | } | ||||
| /** | /** | ||||
| * OpenId列表,最多支持10,000个 | |||||
| * openid列表,最多支持10,000个 | |||||
| */ | */ | ||||
| public List<String> getToUsers() { | public List<String> getToUsers() { | ||||
| return this.toUsers; | return this.toUsers; | ||||
| } | } | ||||
| /** | /** | ||||
| * 添加OpenId,最多支持10,000个 | |||||
| * @param openId | |||||
| * 添加openid,最多支持10,000个 | |||||
| * @param openid | |||||
| */ | */ | ||||
| public void addUser(String openId) { | |||||
| this.toUsers.add(openId); | |||||
| public void addUser(String openid) { | |||||
| this.toUsers.add(openid); | |||||
| } | } | ||||
| /** | |||||
| * 提供set方法,方便客户端直接设置所有群发对象的openid列表 | |||||
| * @param toUsers | |||||
| */ | |||||
| public void setToUsers(List<String> toUsers) { | |||||
| this.toUsers = toUsers; | |||||
| } | |||||
| } | } | ||||
| @@ -14,7 +14,7 @@ public class WxDataCubeArticleTotalDetail { | |||||
| * 统计的日期,在getarticletotal接口中,ref_date指的是文章群发出日期, 而stat_date是数据统计日期 | * 统计的日期,在getarticletotal接口中,ref_date指的是文章群发出日期, 而stat_date是数据统计日期 | ||||
| */ | */ | ||||
| @SerializedName("stat_date") | @SerializedName("stat_date") | ||||
| private Integer statDate; | |||||
| private String statDate; | |||||
| /** | /** | ||||
| * target_user | * target_user | ||||
| @@ -191,11 +191,11 @@ public class WxDataCubeArticleTotalDetail { | |||||
| @SerializedName("feed_share_from_other_cnt") | @SerializedName("feed_share_from_other_cnt") | ||||
| private Integer feedShareFromOtherCnt; | private Integer feedShareFromOtherCnt; | ||||
| public Integer getStatDate() { | |||||
| public String getStatDate() { | |||||
| return this.statDate; | return this.statDate; | ||||
| } | } | ||||
| public void setStatDate(Integer statDate) { | |||||
| public void setStatDate(String statDate) { | |||||
| this.statDate = statDate; | this.statDate = statDate; | ||||
| } | } | ||||
| @@ -31,7 +31,7 @@ public class WxMpMaterialNews implements Serializable { | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| * 群发图文消息article | * 群发图文消息article | ||||
| * 1. thumbMediaId (必填) 图文消息缩略图的media_id,可以在基础支持-上传多媒体文件接口中获得 | |||||
| * 1. thumbMediaId (必填) 图文消息的封面图片素材id(必须是永久mediaID) | |||||
| * 2. author 图文消息的作者 | * 2. author 图文消息的作者 | ||||
| * 3. title (必填) 图文消息的标题 | * 3. title (必填) 图文消息的标题 | ||||
| * 4. contentSourceUrl 在图文消息页面点击“阅读原文”后的页面链接 | * 4. contentSourceUrl 在图文消息页面点击“阅读原文”后的页面链接 | ||||
| @@ -0,0 +1,44 @@ | |||||
| package me.chanjar.weixin.mp.bean.menu; | |||||
| import com.google.gson.annotations.SerializedName; | |||||
| import me.chanjar.weixin.common.util.ToStringUtils; | |||||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||||
| /** | |||||
| * <pre> | |||||
| * Created by Binary Wang on 2016-11-25. | |||||
| * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
| * </pre> | |||||
| */ | |||||
| public class WxMpGetSelfMenuInfoResult { | |||||
| @SerializedName("selfmenu_info") | |||||
| private WxMpSelfMenuInfo selfMenuInfo; | |||||
| @SerializedName("is_menu_open") | |||||
| private Integer isMenuOpen; | |||||
| public static WxMpGetSelfMenuInfoResult fromJson(String json) { | |||||
| return WxGsonBuilder.create().fromJson(json, WxMpGetSelfMenuInfoResult.class); | |||||
| } | |||||
| @Override | |||||
| public String toString() { | |||||
| return ToStringUtils.toSimpleString(this); | |||||
| } | |||||
| public WxMpSelfMenuInfo getSelfMenuInfo() { | |||||
| return selfMenuInfo; | |||||
| } | |||||
| public void setSelfMenuInfo(WxMpSelfMenuInfo selfMenuInfo) { | |||||
| this.selfMenuInfo = selfMenuInfo; | |||||
| } | |||||
| public Integer getIsMenuOpen() { | |||||
| return isMenuOpen; | |||||
| } | |||||
| public void setIsMenuOpen(Integer isMenuOpen) { | |||||
| this.isMenuOpen = isMenuOpen; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,288 @@ | |||||
| package me.chanjar.weixin.mp.bean.menu; | |||||
| import com.google.gson.annotations.SerializedName; | |||||
| import me.chanjar.weixin.common.util.ToStringUtils; | |||||
| import java.util.ArrayList; | |||||
| import java.util.List; | |||||
| /** | |||||
| * <pre> | |||||
| * Created by Binary Wang on 2016-11-25. | |||||
| * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
| * </pre> | |||||
| */ | |||||
| public class WxMpSelfMenuInfo { | |||||
| /** | |||||
| * 菜单按钮 | |||||
| */ | |||||
| @SerializedName("button") | |||||
| private List<WxMpSelfMenuButton> buttons; | |||||
| @Override | |||||
| public String toString() { | |||||
| return ToStringUtils.toSimpleString(this); | |||||
| } | |||||
| public static class WxMpSelfMenuButton { | |||||
| @Override | |||||
| public String toString() { | |||||
| return ToStringUtils.toSimpleString(this); | |||||
| } | |||||
| /** | |||||
| * <pre> | |||||
| * 菜单的类型,公众平台官网上能够设置的菜单类型有view(跳转网页)、text(返回文本,下同)、img、photo、video、voice。 | |||||
| * 使用API设置的则有8种,详见<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141013&token=&lang=zh_CN">《自定义菜单创建接口》</a> | |||||
| * </pre> | |||||
| */ | |||||
| @SerializedName("type") | |||||
| private String type; | |||||
| /** | |||||
| * 菜单名称 | |||||
| */ | |||||
| @SerializedName("name") | |||||
| private String name; | |||||
| /** | |||||
| * <pre> | |||||
| * 对于不同的菜单类型,value的值意义不同。 | |||||
| * 官网上设置的自定义菜单: | |||||
| * <li>Text:保存文字到value; | |||||
| * <li>Img、voice:保存mediaID到value; | |||||
| * <li>Video:保存视频下载链接到value; | |||||
| * <li>News:保存图文消息到news_info,同时保存mediaID到value; | |||||
| * <li>View:保存链接到url。</li> | |||||
| * | |||||
| * 使用API设置的自定义菜单: | |||||
| * <li>click、scancode_push、scancode_waitmsg、pic_sysphoto、pic_photo_or_album、 pic_weixin、location_select:保存值到key; | |||||
| * <li>view:保存链接到url | |||||
| * </pre> | |||||
| */ | |||||
| @SerializedName("key") | |||||
| private String key; | |||||
| /** | |||||
| * @see #key | |||||
| */ | |||||
| @SerializedName("url") | |||||
| private String url; | |||||
| /** | |||||
| * @see #key | |||||
| */ | |||||
| @SerializedName("value") | |||||
| private String value; | |||||
| /** | |||||
| * 子菜单信息 | |||||
| */ | |||||
| @SerializedName("sub_button") | |||||
| private SubButtons subButtons; | |||||
| public SubButtons getSubButtons() { | |||||
| return subButtons; | |||||
| } | |||||
| public void setSubButtons(SubButtons subButtons) { | |||||
| this.subButtons = subButtons; | |||||
| } | |||||
| public static class SubButtons { | |||||
| @Override | |||||
| public String toString() { | |||||
| return ToStringUtils.toSimpleString(this); | |||||
| } | |||||
| @SerializedName("list") | |||||
| private List<WxMpSelfMenuButton> subButtons = new ArrayList<>(); | |||||
| public List<WxMpSelfMenuButton> getSubButtons() { | |||||
| return subButtons; | |||||
| } | |||||
| public void setSubButtons(List<WxMpSelfMenuButton> subButtons) { | |||||
| this.subButtons = subButtons; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * 图文消息的信息 | |||||
| */ | |||||
| @SerializedName("news_info") | |||||
| private NewsInfo newsInfo; | |||||
| public String getType() { | |||||
| return type; | |||||
| } | |||||
| public void setType(String type) { | |||||
| this.type = type; | |||||
| } | |||||
| public String getName() { | |||||
| return name; | |||||
| } | |||||
| public void setName(String name) { | |||||
| this.name = name; | |||||
| } | |||||
| public String getKey() { | |||||
| return key; | |||||
| } | |||||
| public void setKey(String key) { | |||||
| this.key = key; | |||||
| } | |||||
| public String getUrl() { | |||||
| return url; | |||||
| } | |||||
| public void setUrl(String url) { | |||||
| this.url = url; | |||||
| } | |||||
| public String getValue() { | |||||
| return value; | |||||
| } | |||||
| public void setValue(String value) { | |||||
| this.value = value; | |||||
| } | |||||
| public NewsInfo getNewsInfo() { | |||||
| return newsInfo; | |||||
| } | |||||
| public void setNewsInfo(NewsInfo newsInfo) { | |||||
| this.newsInfo = newsInfo; | |||||
| } | |||||
| public static class NewsInfo { | |||||
| @Override | |||||
| public String toString() { | |||||
| return ToStringUtils.toSimpleString(this); | |||||
| } | |||||
| @SerializedName("list") | |||||
| private List<NewsInButton> news = new ArrayList<>(); | |||||
| public List<NewsInButton> getNews() { | |||||
| return news; | |||||
| } | |||||
| public void setNews(List<NewsInButton> news) { | |||||
| this.news = news; | |||||
| } | |||||
| public static class NewsInButton { | |||||
| @Override | |||||
| public String toString() { | |||||
| return ToStringUtils.toSimpleString(this); | |||||
| } | |||||
| /** | |||||
| * 图文消息的标题 | |||||
| */ | |||||
| @SerializedName("title") | |||||
| private String title; | |||||
| /** | |||||
| * 摘要 | |||||
| */ | |||||
| @SerializedName("digest") | |||||
| private String digest; | |||||
| /** | |||||
| * 作者 | |||||
| */ | |||||
| @SerializedName("author") | |||||
| private String author; | |||||
| /** | |||||
| * show_cover | |||||
| * 是否显示封面,0为不显示,1为显示 | |||||
| */ | |||||
| @SerializedName("show_cover") | |||||
| private Integer showCover; | |||||
| /** | |||||
| * 封面图片的URL | |||||
| */ | |||||
| @SerializedName("cover_url") | |||||
| private String coverUrl; | |||||
| /** | |||||
| * 正文的URL | |||||
| */ | |||||
| @SerializedName("content_url") | |||||
| private String contentUrl; | |||||
| /** | |||||
| * 原文的URL,若置空则无查看原文入口 | |||||
| */ | |||||
| @SerializedName("source_url") | |||||
| private String sourceUrl; | |||||
| public String getTitle() { | |||||
| return title; | |||||
| } | |||||
| public void setTitle(String title) { | |||||
| this.title = title; | |||||
| } | |||||
| public String getDigest() { | |||||
| return digest; | |||||
| } | |||||
| public void setDigest(String digest) { | |||||
| this.digest = digest; | |||||
| } | |||||
| public String getAuthor() { | |||||
| return author; | |||||
| } | |||||
| public void setAuthor(String author) { | |||||
| this.author = author; | |||||
| } | |||||
| public Integer getShowCover() { | |||||
| return showCover; | |||||
| } | |||||
| public void setShowCover(Integer showCover) { | |||||
| this.showCover = showCover; | |||||
| } | |||||
| public String getCoverUrl() { | |||||
| return coverUrl; | |||||
| } | |||||
| public void setCoverUrl(String coverUrl) { | |||||
| this.coverUrl = coverUrl; | |||||
| } | |||||
| public String getContentUrl() { | |||||
| return contentUrl; | |||||
| } | |||||
| public void setContentUrl(String contentUrl) { | |||||
| this.contentUrl = contentUrl; | |||||
| } | |||||
| public String getSourceUrl() { | |||||
| return sourceUrl; | |||||
| } | |||||
| public void setSourceUrl(String sourceUrl) { | |||||
| this.sourceUrl = sourceUrl; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -263,6 +263,118 @@ public class WxMpXmlMessage implements Serializable { | |||||
| @XStreamAlias("FailReason") | @XStreamAlias("FailReason") | ||||
| private String failReason; | private String failReason; | ||||
| /////////////////////////////////////// | |||||
| // 微信硬件平台相关事件推送 | |||||
| /////////////////////////////////////// | |||||
| /** | |||||
| * 设备类型,目前为"公众账号原始ID" | |||||
| */ | |||||
| @XStreamAlias("DeviceType") | |||||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||||
| private String deviceType; | |||||
| /** | |||||
| * 设备ID,第三方提供 | |||||
| */ | |||||
| @XStreamAlias("DeviceID") | |||||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||||
| private String deviceId; | |||||
| @XStreamAlias("HardWare") | |||||
| private HardWare hardWare = new HardWare(); | |||||
| /** | |||||
| * 请求类型:0:退订设备状态;1:心跳;(心跳的处理方式跟订阅一样)2:订阅设备状态 | |||||
| */ | |||||
| @XStreamAlias("OpType") | |||||
| private Integer opType; | |||||
| /** | |||||
| * 设备状态:0:未连接;1:已连接 | |||||
| */ | |||||
| @XStreamAlias("DeviceStatus") | |||||
| private Integer deviceStatus; | |||||
| public static WxMpXmlMessage fromXml(String xml) { | |||||
| return XStreamTransformer.fromXml(WxMpXmlMessage.class, xml); | |||||
| } | |||||
| public static WxMpXmlMessage fromXml(InputStream is) { | |||||
| return XStreamTransformer.fromXml(WxMpXmlMessage.class, is); | |||||
| } | |||||
| /** | |||||
| * 从加密字符串转换 | |||||
| * | |||||
| * @param encryptedXml | |||||
| * @param wxMpConfigStorage | |||||
| * @param timestamp | |||||
| * @param nonce | |||||
| * @param msgSignature | |||||
| */ | |||||
| public static WxMpXmlMessage fromEncryptedXml(String encryptedXml, | |||||
| WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce, | |||||
| String msgSignature) { | |||||
| WxMpCryptUtil cryptUtil = new WxMpCryptUtil(wxMpConfigStorage); | |||||
| String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, | |||||
| encryptedXml); | |||||
| return fromXml(plainText); | |||||
| } | |||||
| public static WxMpXmlMessage fromEncryptedXml(InputStream is, | |||||
| WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce, | |||||
| String msgSignature) { | |||||
| try { | |||||
| return fromEncryptedXml(IOUtils.toString(is, "UTF-8"), wxMpConfigStorage, | |||||
| timestamp, nonce, msgSignature); | |||||
| } catch (IOException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| public Integer getOpType() { | |||||
| return opType; | |||||
| } | |||||
| public void setOpType(Integer opType) { | |||||
| this.opType = opType; | |||||
| } | |||||
| public Integer getDeviceStatus() { | |||||
| return deviceStatus; | |||||
| } | |||||
| public void setDeviceStatus(Integer deviceStatus) { | |||||
| this.deviceStatus = deviceStatus; | |||||
| } | |||||
| public HardWare getHardWare() { | |||||
| return hardWare; | |||||
| } | |||||
| public void setHardWare(HardWare hardWare) { | |||||
| this.hardWare = hardWare; | |||||
| } | |||||
| public String getDeviceType() { | |||||
| return deviceType; | |||||
| } | |||||
| public void setDeviceType(String deviceType) { | |||||
| this.deviceType = deviceType; | |||||
| } | |||||
| public String getDeviceId() { | |||||
| return deviceId; | |||||
| } | |||||
| public void setDeviceId(String deviceId) { | |||||
| this.deviceId = deviceId; | |||||
| } | |||||
| public Long getExpiredTime() { | public Long getExpiredTime() { | ||||
| return this.expiredTime; | return this.expiredTime; | ||||
| } | } | ||||
| @@ -346,7 +458,6 @@ public class WxMpXmlMessage implements Serializable { | |||||
| * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_LINK} | * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_LINK} | ||||
| * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_EVENT} | * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_EVENT} | ||||
| * </pre> | * </pre> | ||||
| * | |||||
| */ | */ | ||||
| public String getMsgType() { | public String getMsgType() { | ||||
| return this.msgType; | return this.msgType; | ||||
| @@ -537,43 +648,6 @@ public class WxMpXmlMessage implements Serializable { | |||||
| this.fromUser = fromUser; | this.fromUser = fromUser; | ||||
| } | } | ||||
| public static WxMpXmlMessage fromXml(String xml) { | |||||
| return XStreamTransformer.fromXml(WxMpXmlMessage.class, xml); | |||||
| } | |||||
| public static WxMpXmlMessage fromXml(InputStream is) { | |||||
| return XStreamTransformer.fromXml(WxMpXmlMessage.class, is); | |||||
| } | |||||
| /** | |||||
| * 从加密字符串转换 | |||||
| * | |||||
| * @param encryptedXml | |||||
| * @param wxMpConfigStorage | |||||
| * @param timestamp | |||||
| * @param nonce | |||||
| * @param msgSignature | |||||
| */ | |||||
| public static WxMpXmlMessage fromEncryptedXml(String encryptedXml, | |||||
| WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce, | |||||
| String msgSignature) { | |||||
| WxMpCryptUtil cryptUtil = new WxMpCryptUtil(wxMpConfigStorage); | |||||
| String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, | |||||
| encryptedXml); | |||||
| return fromXml(plainText); | |||||
| } | |||||
| public static WxMpXmlMessage fromEncryptedXml(InputStream is, | |||||
| WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce, | |||||
| String msgSignature) { | |||||
| try { | |||||
| return fromEncryptedXml(IOUtils.toString(is, "UTF-8"), wxMpConfigStorage, | |||||
| timestamp, nonce, msgSignature); | |||||
| } catch (IOException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| public String getStatus() { | public String getStatus() { | ||||
| return this.status; | return this.status; | ||||
| } | } | ||||
| @@ -683,7 +757,7 @@ public class WxMpXmlMessage implements Serializable { | |||||
| } | } | ||||
| public void setSendLocationInfo( | public void setSendLocationInfo( | ||||
| WxMpXmlMessage.SendLocationInfo sendLocationInfo) { | |||||
| WxMpXmlMessage.SendLocationInfo sendLocationInfo) { | |||||
| this.sendLocationInfo = sendLocationInfo; | this.sendLocationInfo = sendLocationInfo; | ||||
| } | } | ||||
| @@ -719,21 +793,62 @@ public class WxMpXmlMessage implements Serializable { | |||||
| this.fromKfAccount = fromKfAccount; | this.fromKfAccount = fromKfAccount; | ||||
| } | } | ||||
| @XStreamAlias("ScanCodeInfo") | |||||
| public static class ScanCodeInfo { | |||||
| @Override | |||||
| public String toString() { | |||||
| return ToStringUtils.toSimpleString(this); | |||||
| } | |||||
| @XStreamAlias("HardWare") | |||||
| public static class HardWare { | |||||
| /** | |||||
| * 消息展示,目前支持myrank(排行榜) | |||||
| */ | |||||
| @XStreamAlias("MessageView") | |||||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||||
| private String messageView; | |||||
| /** | |||||
| * 消息点击动作,目前支持ranklist(点击跳转排行榜) | |||||
| */ | |||||
| @XStreamAlias("MessageAction") | |||||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||||
| private String messageAction; | |||||
| @Override | @Override | ||||
| public String toString() { | public String toString() { | ||||
| return ToStringUtils.toSimpleString(this); | return ToStringUtils.toSimpleString(this); | ||||
| } | } | ||||
| public String getMessageView() { | |||||
| return messageView; | |||||
| } | |||||
| public void setMessageView(String messageView) { | |||||
| this.messageView = messageView; | |||||
| } | |||||
| public String getMessageAction() { | |||||
| return messageAction; | |||||
| } | |||||
| public void setMessageAction(String messageAction) { | |||||
| this.messageAction = messageAction; | |||||
| } | |||||
| } | |||||
| @XStreamAlias("ScanCodeInfo") | |||||
| public static class ScanCodeInfo { | |||||
| @XStreamAlias("ScanType") | @XStreamAlias("ScanType") | ||||
| @XStreamConverter(value = XStreamCDataConverter.class) | @XStreamConverter(value = XStreamCDataConverter.class) | ||||
| private String scanType; | private String scanType; | ||||
| @XStreamAlias("ScanResult") | @XStreamAlias("ScanResult") | ||||
| @XStreamConverter(value = XStreamCDataConverter.class) | @XStreamConverter(value = XStreamCDataConverter.class) | ||||
| private String scanResult; | private String scanResult; | ||||
| @Override | |||||
| public String toString() { | |||||
| return ToStringUtils.toSimpleString(this); | |||||
| } | |||||
| /** | /** | ||||
| * 扫描类型,一般是qrcode | * 扫描类型,一般是qrcode | ||||
| */ | */ | ||||
| @@ -761,17 +876,16 @@ public class WxMpXmlMessage implements Serializable { | |||||
| @XStreamAlias("SendPicsInfo") | @XStreamAlias("SendPicsInfo") | ||||
| public static class SendPicsInfo { | public static class SendPicsInfo { | ||||
| @XStreamAlias("PicList") | |||||
| protected final List<Item> picList = new ArrayList<>(); | |||||
| @XStreamAlias("Count") | |||||
| private Long count; | |||||
| @Override | @Override | ||||
| public String toString() { | public String toString() { | ||||
| return ToStringUtils.toSimpleString(this); | return ToStringUtils.toSimpleString(this); | ||||
| } | } | ||||
| @XStreamAlias("Count") | |||||
| private Long count; | |||||
| @XStreamAlias("PicList") | |||||
| protected final List<Item> picList = new ArrayList<>(); | |||||
| public Long getCount() { | public Long getCount() { | ||||
| return this.count; | return this.count; | ||||
| } | } | ||||
| @@ -786,15 +900,15 @@ public class WxMpXmlMessage implements Serializable { | |||||
| @XStreamAlias("item") | @XStreamAlias("item") | ||||
| public static class Item { | public static class Item { | ||||
| @XStreamAlias("PicMd5Sum") | |||||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||||
| private String picMd5Sum; | |||||
| @Override | @Override | ||||
| public String toString() { | public String toString() { | ||||
| return ToStringUtils.toSimpleString(this); | return ToStringUtils.toSimpleString(this); | ||||
| } | } | ||||
| @XStreamAlias("PicMd5Sum") | |||||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||||
| private String picMd5Sum; | |||||
| public String getPicMd5Sum() { | public String getPicMd5Sum() { | ||||
| return this.picMd5Sum; | return this.picMd5Sum; | ||||
| } | } | ||||
| @@ -873,9 +987,4 @@ public class WxMpXmlMessage implements Serializable { | |||||
| this.poiname = poiname; | this.poiname = poiname; | ||||
| } | } | ||||
| } | } | ||||
| @Override | |||||
| public String toString() { | |||||
| return ToStringUtils.toSimpleString(this); | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,57 @@ | |||||
| package me.chanjar.weixin.mp.bean.pay.request; | |||||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
| /** | |||||
| * <pre> | |||||
| * 注释中各行对应含义: | |||||
| * 字段名 | |||||
| * 字段 | |||||
| * 必填 | |||||
| * 示例值 | |||||
| * 类型 | |||||
| * 说明 | |||||
| * Created by Binary Wang on 2016-11-28. | |||||
| * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("xml") | |||||
| public class WxPayRedpackQueryRequest extends WxPayBaseRequest { | |||||
| /** | |||||
| * 商户订单号 | |||||
| * mch_billno | |||||
| * 是 | |||||
| * 10000098201411111234567890 | |||||
| * String(28) | |||||
| * 商户发放红包的商户订单号 | |||||
| */ | |||||
| @XStreamAlias("mch_billno") | |||||
| private String mchBillNo; | |||||
| /** | |||||
| * 订单类型 | |||||
| * bill_type | |||||
| * 是 | |||||
| * MCHT | |||||
| * String(32) | |||||
| * MCHT:通过商户订单号获取红包信息。 | |||||
| */ | |||||
| @XStreamAlias("bill_type") | |||||
| private String billType; | |||||
| public String getBillType() { | |||||
| return billType; | |||||
| } | |||||
| public void setBillType(String billType) { | |||||
| this.billType = billType; | |||||
| } | |||||
| public String getMchBillNo() { | |||||
| return mchBillNo; | |||||
| } | |||||
| public void setMchBillNo(String mchBillNo) { | |||||
| this.mchBillNo = mchBillNo; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,135 @@ | |||||
| package me.chanjar.weixin.mp.bean.pay.request; | |||||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
| /** | |||||
| * <pre> | |||||
| * Created by Binary Wang on 2016-11-24. | |||||
| * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("xml") | |||||
| public class WxPayRefundQueryRequest extends WxPayBaseRequest { | |||||
| /** | |||||
| * <pre> | |||||
| * 设备号 | |||||
| * device_info | |||||
| * 否 | |||||
| * String(32) | |||||
| * 013467007045764 | |||||
| * 商户自定义的终端设备号,如门店编号、设备的ID等 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("device_info") | |||||
| private String deviceInfo; | |||||
| /** | |||||
| * <pre> | |||||
| * 签名类型 | |||||
| * sign_type | |||||
| * 否 | |||||
| * String(32) | |||||
| * HMAC-SHA256 | |||||
| * 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("sign_type") | |||||
| private String signType; | |||||
| //************以下四选一************ | |||||
| /** | |||||
| * <pre> | |||||
| * 微信订单号 | |||||
| * transaction_id | |||||
| * String(32) | |||||
| * 1217752501201407033233368018 | |||||
| * 微信订单号 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("transaction_id") | |||||
| private String transactionId; | |||||
| /** | |||||
| * <pre> | |||||
| * 商户订单号 | |||||
| * out_trade_no | |||||
| * String(32) | |||||
| * 1217752501201407033233368018 | |||||
| * 商户系统内部的订单号 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("out_trade_no") | |||||
| private String outTradeNo; | |||||
| /** | |||||
| * <pre> | |||||
| * 商户退款单号 | |||||
| * out_refund_no | |||||
| * String(32) | |||||
| * 1217752501201407033233368018 | |||||
| * 商户侧传给微信的退款单号 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("out_refund_no") | |||||
| private String outRefundNo; | |||||
| /** | |||||
| * <pre> | |||||
| * 微信退款单号 | |||||
| * refund_id | |||||
| * String(28) | |||||
| * 1217752501201407033233368018 | |||||
| * 微信生成的退款单号,在申请退款接口有返回 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("refund_id") | |||||
| private String refundId; | |||||
| public String getDeviceInfo() { | |||||
| return deviceInfo; | |||||
| } | |||||
| public void setDeviceInfo(String deviceInfo) { | |||||
| this.deviceInfo = deviceInfo; | |||||
| } | |||||
| public String getSignType() { | |||||
| return signType; | |||||
| } | |||||
| public void setSignType(String signType) { | |||||
| this.signType = signType; | |||||
| } | |||||
| public String getTransactionId() { | |||||
| return transactionId; | |||||
| } | |||||
| public void setTransactionId(String transactionId) { | |||||
| this.transactionId = transactionId; | |||||
| } | |||||
| public String getOutTradeNo() { | |||||
| return outTradeNo; | |||||
| } | |||||
| public void setOutTradeNo(String outTradeNo) { | |||||
| this.outTradeNo = outTradeNo; | |||||
| } | |||||
| public String getOutRefundNo() { | |||||
| return outRefundNo; | |||||
| } | |||||
| public void setOutRefundNo(String outRefundNo) { | |||||
| this.outRefundNo = outRefundNo; | |||||
| } | |||||
| public String getRefundId() { | |||||
| return refundId; | |||||
| } | |||||
| public void setRefundId(String refundId) { | |||||
| this.refundId = refundId; | |||||
| } | |||||
| } | |||||
| @@ -14,7 +14,7 @@ public class WxPaySendRedpackRequest { | |||||
| * 商户订单号(每个订单号必须唯一) 组成:mch_id+yyyymmdd+10位一天内不能重复的数字。 接口根据商户订单号支持重入,如出现超时可再调用。 | * 商户订单号(每个订单号必须唯一) 组成:mch_id+yyyymmdd+10位一天内不能重复的数字。 接口根据商户订单号支持重入,如出现超时可再调用。 | ||||
| */ | */ | ||||
| @XStreamAlias("mch_billno") | @XStreamAlias("mch_billno") | ||||
| private String mchBillno; | |||||
| private String mchBillNo; | |||||
| /** | /** | ||||
| * send_name | * send_name | ||||
| @@ -157,12 +157,12 @@ public class WxPaySendRedpackRequest { | |||||
| @XStreamAlias("consume_mch_id") | @XStreamAlias("consume_mch_id") | ||||
| private String consumeMchId; | private String consumeMchId; | ||||
| public String getMchBillno() { | |||||
| return this.mchBillno; | |||||
| public String getMchBillNo() { | |||||
| return mchBillNo; | |||||
| } | } | ||||
| public void setMchBillno(String mchBillno) { | |||||
| this.mchBillno = mchBillno; | |||||
| public void setMchBillNo(String mchBillNo) { | |||||
| this.mchBillNo = mchBillNo; | |||||
| } | } | ||||
| public String getSendName() { | public String getSendName() { | ||||
| @@ -1,10 +1,10 @@ | |||||
| package me.chanjar.weixin.mp.bean.pay.result; | package me.chanjar.weixin.mp.bean.pay.result; | ||||
| import java.util.List; | |||||
| import com.google.common.collect.Lists; | import com.google.common.collect.Lists; | ||||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
| import java.util.List; | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| * 查询订单 返回结果对象 | * 查询订单 返回结果对象 | ||||
| @@ -0,0 +1,405 @@ | |||||
| package me.chanjar.weixin.mp.bean.pay.result; | |||||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
| /** | |||||
| * <pre> | |||||
| * 注释中各行对应含义: | |||||
| * 字段名 | |||||
| * 字段 | |||||
| * 必填 | |||||
| * 示例值 | |||||
| * 类型 | |||||
| * 说明 | |||||
| * Created by Binary Wang on 2016-11-28. | |||||
| * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
| * </pre> | |||||
| */ | |||||
| public class WxPayRedpackQueryResult extends WxPayBaseResult { | |||||
| /** | |||||
| * <pre> | |||||
| * 商户订单号 | |||||
| * mch_billno | |||||
| * 是 | |||||
| * 10000098201411111234567890 | |||||
| * String(28) | |||||
| * 商户使用查询API填写的商户单号的原路返回 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("mch_billno") | |||||
| private String mchBillNo; | |||||
| /** | |||||
| * <pre> | |||||
| * 红包单号 | |||||
| * detail_id | |||||
| * 是 | |||||
| * 1000000000201503283103439304 | |||||
| * String(32) | |||||
| * 使用API发放现金红包时返回的红包单号 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("detail_id") | |||||
| private String detailId; | |||||
| /** | |||||
| * <pre> | |||||
| * 红包状态 | |||||
| * status | |||||
| * 是 | |||||
| * RECEIVED | |||||
| * string(16) | |||||
| * SENDING:发放中, | |||||
| * SENT:已发放待领取, | |||||
| * FAILED:发放失败, | |||||
| * RECEIVED:已领取, | |||||
| * RFUND_ING:退款中, | |||||
| * REFUND:已退款 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("status") | |||||
| private String status; | |||||
| /** | |||||
| * <pre> | |||||
| * 发放类型 | |||||
| * send_type | |||||
| * 是 | |||||
| * API | |||||
| * String(32) | |||||
| * API:通过API接口发放, | |||||
| * UPLOAD:通过上传文件方式发放, | |||||
| * ACTIVITY:通过活动方式发放 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("send_type") | |||||
| private String sendType; | |||||
| /** | |||||
| * <pre> | |||||
| * 红包类型 | |||||
| * hb_type | |||||
| * 是 | |||||
| * GROUP | |||||
| * String(32) | |||||
| * GROUP:裂变红包, | |||||
| * NORMAL:普通红包 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("hb_type") | |||||
| private String hbType; | |||||
| /** | |||||
| * <pre> | |||||
| * 红包个数 | |||||
| * total_num | |||||
| * 是 | |||||
| * 1 | |||||
| * int | |||||
| * 红包个数 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("total_num") | |||||
| private Integer totalNum; | |||||
| /** | |||||
| * <pre> | |||||
| * 红包金额 | |||||
| * total_amount | |||||
| * 是 | |||||
| * 5000 | |||||
| * int | |||||
| * 红包总金额(单位分) | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("total_amount") | |||||
| private Integer totalAmount; | |||||
| /** | |||||
| * <pre> | |||||
| * 失败原因 | |||||
| * reason | |||||
| * 否 | |||||
| * 余额不足 | |||||
| * String(32) | |||||
| * 发送失败原因 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("reason") | |||||
| private String reason; | |||||
| /** | |||||
| * <pre> | |||||
| * 红包发送时间 | |||||
| * send_time | |||||
| * 是 | |||||
| * 2015-04-21 20:00:00 | |||||
| * String(32) | |||||
| * 红包的发送时间 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("send_time") | |||||
| private String sendTime; | |||||
| /** | |||||
| * <pre> | |||||
| * 红包退款时间 | |||||
| * refund_time | |||||
| * 否 | |||||
| * 2015-04-21 23:03:00 | |||||
| * String(32) | |||||
| * 红包的退款时间(如果其未领取的退款) | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("refund_time") | |||||
| private String refundTime; | |||||
| /** | |||||
| * <pre> | |||||
| * 红包退款金额 | |||||
| * refund_amount | |||||
| * 否 | |||||
| * 8000 | |||||
| * Int | |||||
| * 红包退款金额 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("refund_amount") | |||||
| private Integer refundAmount; | |||||
| /** | |||||
| * <pre> | |||||
| * 祝福语 | |||||
| * wishing | |||||
| * 否 | |||||
| * 新年快乐 | |||||
| * String(128) | |||||
| * 祝福语 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("wishing") | |||||
| private String wishing; | |||||
| /** | |||||
| * <pre> | |||||
| * 活动描述 | |||||
| * remark | |||||
| * 否 | |||||
| * 新年红包 | |||||
| * String(256) | |||||
| * 活动描述,低版本微信可见 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("remark") | |||||
| private String remark; | |||||
| /** | |||||
| * <pre> | |||||
| * 活动名称 | |||||
| * act_name | |||||
| * 否 | |||||
| * 新年红包 | |||||
| * String(32) | |||||
| * 发红包的活动名称 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("act_name") | |||||
| private String actName; | |||||
| /** | |||||
| * <pre> | |||||
| * 裂变红包领取列表 | |||||
| * hblist | |||||
| * 否 | |||||
| * | |||||
| * | |||||
| * 裂变红包的领取列表 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("hblist") | |||||
| private String hblist; | |||||
| /** | |||||
| * <pre> | |||||
| * 领取红包的Openid | |||||
| * openid | |||||
| * 是 | |||||
| * ohO4GtzOAAYMp2yapORH3dQB3W18 | |||||
| * String(32) | |||||
| * 领取红包的openid | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("openid") | |||||
| private String openid; | |||||
| /** | |||||
| * <pre> | |||||
| * 金额 | |||||
| * amount | |||||
| * 是 | |||||
| * 100 | |||||
| * int | |||||
| * 领取金额 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("amount") | |||||
| private Integer amount; | |||||
| /** | |||||
| * <pre> | |||||
| * 接收时间 | |||||
| * rcv_time | |||||
| * 是 | |||||
| * 2015-04-21 20:00:00 | |||||
| * String(32) | |||||
| * 领取红包的时间 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("rcv_time") | |||||
| private String receiveTime; | |||||
| public String getMchBillNo() { | |||||
| return mchBillNo; | |||||
| } | |||||
| public void setMchBillNo(String mchBillNo) { | |||||
| this.mchBillNo = mchBillNo; | |||||
| } | |||||
| public String getDetailId() { | |||||
| return detailId; | |||||
| } | |||||
| public void setDetailId(String detailId) { | |||||
| this.detailId = detailId; | |||||
| } | |||||
| public String getStatus() { | |||||
| return status; | |||||
| } | |||||
| public void setStatus(String status) { | |||||
| this.status = status; | |||||
| } | |||||
| public String getSendType() { | |||||
| return sendType; | |||||
| } | |||||
| public void setSendType(String sendType) { | |||||
| this.sendType = sendType; | |||||
| } | |||||
| public String getHbType() { | |||||
| return hbType; | |||||
| } | |||||
| public void setHbType(String hbType) { | |||||
| this.hbType = hbType; | |||||
| } | |||||
| public Integer getTotalNum() { | |||||
| return totalNum; | |||||
| } | |||||
| public void setTotalNum(Integer totalNum) { | |||||
| this.totalNum = totalNum; | |||||
| } | |||||
| public Integer getTotalAmount() { | |||||
| return totalAmount; | |||||
| } | |||||
| public void setTotalAmount(Integer totalAmount) { | |||||
| this.totalAmount = totalAmount; | |||||
| } | |||||
| public String getReason() { | |||||
| return reason; | |||||
| } | |||||
| public void setReason(String reason) { | |||||
| this.reason = reason; | |||||
| } | |||||
| public String getSendTime() { | |||||
| return sendTime; | |||||
| } | |||||
| public void setSendTime(String sendTime) { | |||||
| this.sendTime = sendTime; | |||||
| } | |||||
| public String getRefundTime() { | |||||
| return refundTime; | |||||
| } | |||||
| public void setRefundTime(String refundTime) { | |||||
| this.refundTime = refundTime; | |||||
| } | |||||
| public Integer getRefundAmount() { | |||||
| return refundAmount; | |||||
| } | |||||
| public void setRefundAmount(Integer refundAmount) { | |||||
| this.refundAmount = refundAmount; | |||||
| } | |||||
| public String getWishing() { | |||||
| return wishing; | |||||
| } | |||||
| public void setWishing(String wishing) { | |||||
| this.wishing = wishing; | |||||
| } | |||||
| public String getRemark() { | |||||
| return remark; | |||||
| } | |||||
| public void setRemark(String remark) { | |||||
| this.remark = remark; | |||||
| } | |||||
| public String getActName() { | |||||
| return actName; | |||||
| } | |||||
| public void setActName(String actName) { | |||||
| this.actName = actName; | |||||
| } | |||||
| public String getHblist() { | |||||
| return hblist; | |||||
| } | |||||
| public void setHblist(String hblist) { | |||||
| this.hblist = hblist; | |||||
| } | |||||
| public String getOpenid() { | |||||
| return openid; | |||||
| } | |||||
| public void setOpenid(String openid) { | |||||
| this.openid = openid; | |||||
| } | |||||
| public Integer getAmount() { | |||||
| return amount; | |||||
| } | |||||
| public void setAmount(Integer amount) { | |||||
| this.amount = amount; | |||||
| } | |||||
| public String getReceiveTime() { | |||||
| return receiveTime; | |||||
| } | |||||
| public void setReceiveTime(String receiveTime) { | |||||
| this.receiveTime = receiveTime; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,488 @@ | |||||
| package me.chanjar.weixin.mp.bean.pay.result; | |||||
| import com.google.common.collect.Lists; | |||||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
| import java.util.List; | |||||
| /** | |||||
| * <pre> | |||||
| * Created by Binary Wang on 2016-11-24. | |||||
| * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("xml") | |||||
| public class WxPayRefundQueryResult extends WxPayBaseResult { | |||||
| /** | |||||
| * <pre> | |||||
| * 设备号 | |||||
| * device_info | |||||
| * 否 | |||||
| * String(32) | |||||
| * 013467007045764 | |||||
| * 终端设备号 | |||||
| */ | |||||
| @XStreamAlias("device_info") | |||||
| private String deviceInfo; | |||||
| /** | |||||
| * <pre> | |||||
| * 微信订单号 | |||||
| * transaction_id | |||||
| * 是 | |||||
| * String(32) | |||||
| * 1217752501201407033233368018 | |||||
| * 微信订单号 | |||||
| */ | |||||
| @XStreamAlias("transaction_id") | |||||
| private String transactionId; | |||||
| /** | |||||
| * <pre> | |||||
| * 商户订单号 | |||||
| * out_trade_no | |||||
| * 是 | |||||
| * String(32) | |||||
| * 1217752501201407033233368018 | |||||
| * 商户系统内部的订单号 | |||||
| */ | |||||
| @XStreamAlias("out_trade_no") | |||||
| private String outTradeNo; | |||||
| /** | |||||
| * <pre> | |||||
| * 订单金额 | |||||
| * total_fee | |||||
| * 是 | |||||
| * Int | |||||
| * 100 | |||||
| * 订单总金额,单位为分,只能为整数,详见支付金额 | |||||
| */ | |||||
| @XStreamAlias("total_fee") | |||||
| private Integer totalFee; | |||||
| /** | |||||
| * <pre> | |||||
| * 应结订单金额 | |||||
| * settlement_total_fee | |||||
| * 否 | |||||
| * Int | |||||
| * 100 | |||||
| * 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。 | |||||
| */ | |||||
| @XStreamAlias("settlement_total_fee") | |||||
| private Integer settlementTotalFee; | |||||
| /** | |||||
| * <pre> | |||||
| * 货币种类 | |||||
| * fee_type | |||||
| * 否 | |||||
| * String(8) | |||||
| * CNY | |||||
| * 订单金额货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 | |||||
| */ | |||||
| @XStreamAlias("fee_type") | |||||
| private String feeType; | |||||
| /** | |||||
| * <pre> | |||||
| * 现金支付金额 | |||||
| * cash_fee | |||||
| * 是 | |||||
| * Int | |||||
| * 100 | |||||
| * 现金支付金额,单位为分,只能为整数,详见支付金额 | |||||
| */ | |||||
| @XStreamAlias("cash_fee") | |||||
| private Integer cashFee; | |||||
| /** | |||||
| * <pre> | |||||
| * 退款笔数 | |||||
| * refund_count | |||||
| * 是 | |||||
| * Int | |||||
| * 1 | |||||
| * 退款记录数 | |||||
| */ | |||||
| @XStreamAlias("refund_count") | |||||
| private Integer refundCount; | |||||
| private List<RefundRecord> refundRecords; | |||||
| public String getDeviceInfo() { | |||||
| return deviceInfo; | |||||
| } | |||||
| public void setDeviceInfo(String deviceInfo) { | |||||
| this.deviceInfo = deviceInfo; | |||||
| } | |||||
| public String getTransactionId() { | |||||
| return transactionId; | |||||
| } | |||||
| public void setTransactionId(String transactionId) { | |||||
| this.transactionId = transactionId; | |||||
| } | |||||
| public String getOutTradeNo() { | |||||
| return outTradeNo; | |||||
| } | |||||
| public void setOutTradeNo(String outTradeNo) { | |||||
| this.outTradeNo = outTradeNo; | |||||
| } | |||||
| public Integer getTotalFee() { | |||||
| return totalFee; | |||||
| } | |||||
| public void setTotalFee(Integer totalFee) { | |||||
| this.totalFee = totalFee; | |||||
| } | |||||
| public Integer getSettlementTotalFee() { | |||||
| return settlementTotalFee; | |||||
| } | |||||
| public void setSettlementTotalFee(Integer settlementTotalFee) { | |||||
| this.settlementTotalFee = settlementTotalFee; | |||||
| } | |||||
| public String getFeeType() { | |||||
| return feeType; | |||||
| } | |||||
| public void setFeeType(String feeType) { | |||||
| this.feeType = feeType; | |||||
| } | |||||
| public Integer getCashFee() { | |||||
| return cashFee; | |||||
| } | |||||
| public void setCashFee(Integer cashFee) { | |||||
| this.cashFee = cashFee; | |||||
| } | |||||
| public Integer getRefundCount() { | |||||
| return refundCount; | |||||
| } | |||||
| public void setRefundCount(Integer refundCount) { | |||||
| this.refundCount = refundCount; | |||||
| } | |||||
| public List<RefundRecord> getRefundRecords() { | |||||
| return refundRecords; | |||||
| } | |||||
| public void setRefundRecords(List<RefundRecord> refundRecords) { | |||||
| this.refundRecords = refundRecords; | |||||
| } | |||||
| public void composeRefundRecords(String xmlString) { | |||||
| if (this.refundCount != null && this.refundCount > 0) { | |||||
| this.refundRecords = Lists.newArrayList(); | |||||
| //TODO 暂时待实现 | |||||
| } | |||||
| } | |||||
| public static class RefundRecord { | |||||
| /** | |||||
| * <pre> | |||||
| * 商户退款单号 | |||||
| * out_refund_no_$n | |||||
| * 是 | |||||
| * String(32) | |||||
| * 1217752501201407033233368018 | |||||
| * 商户退款单号 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("out_refund_no") | |||||
| private String outRefundNo; | |||||
| /** | |||||
| * <pre> | |||||
| * 微信退款单号 | |||||
| * refund_id_$n | |||||
| * 是 | |||||
| * String(28) | |||||
| * 1217752501201407033233368018 | |||||
| * 微信退款单号 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("refund_id") | |||||
| private String refundId; | |||||
| /** | |||||
| * <pre> | |||||
| * 退款渠道 | |||||
| * refund_channel_$n | |||||
| * 否 | |||||
| * String(16) | |||||
| * ORIGINAL | |||||
| * ORIGINAL—原路退款 BALANCE—退回到余额 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("refund_channel") | |||||
| private String refundChannel; | |||||
| /** | |||||
| * <pre> | |||||
| * 申请退款金额 | |||||
| * refund_fee_$n | |||||
| * 是 | |||||
| * Int | |||||
| * 100 | |||||
| * 退款总金额,单位为分,可以做部分退款 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("refund_fee") | |||||
| private String refundFee; | |||||
| /** | |||||
| * <pre> | |||||
| * 退款金额 | |||||
| * settlement_refund_fee_$n | |||||
| * 否 | |||||
| * Int | |||||
| * 100 | |||||
| * 退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("settlement_refund_fee") | |||||
| private String settlementRefundFee; | |||||
| /** | |||||
| * <pre> | |||||
| * 退款资金来源 | |||||
| * refund_account | |||||
| * 否 | |||||
| * String(30) | |||||
| * REFUND_SOURCE_RECHARGE_FUNDS | |||||
| * REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款/基本账户, REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("refund_account") | |||||
| private String refundAccount; | |||||
| /** | |||||
| * <pre> | |||||
| * 代金券类型 | |||||
| * coupon_type_$n | |||||
| * 否 | |||||
| * Int | |||||
| * CASH | |||||
| * CASH--充值代金券 , NO_CASH---非充值代金券。订单使用代金券时有返回(取值:CASH、NO_CASH)。$n为下标,从0开始编号,举例:coupon_type_$0 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("coupon_type") | |||||
| private String couponType; | |||||
| /** | |||||
| * <pre> | |||||
| * 代金券退款金额 | |||||
| * coupon_refund_fee_$n | |||||
| * 否 | |||||
| * Int | |||||
| * 100 | |||||
| * 代金券退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("coupon_refund_fee") | |||||
| private String couponRefundFee; | |||||
| /** | |||||
| * <pre> | |||||
| * 退款代金券使用数量 | |||||
| * coupon_refund_count_$n | |||||
| * 否 | |||||
| * Int | |||||
| * 1 | |||||
| * 退款代金券使用数量 ,$n为下标,从0开始编号 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("coupon_refund_count") | |||||
| private String couponRefundCount; | |||||
| private List<RefundCoupon> refundCoupons; | |||||
| /** | |||||
| * <pre> | |||||
| * 退款状态 | |||||
| * refund_status_$n | |||||
| * 是 | |||||
| * String(16) | |||||
| * SUCCESS | |||||
| * 退款状态: | |||||
| * SUCCESS—退款成功, | |||||
| * FAIL—退款失败, | |||||
| * PROCESSING—退款处理中, | |||||
| * CHANGE—转入代发, | |||||
| * 退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,资金回流到商户的现金帐号,需要商户人工干预,通过线下或者财付通转账的方式进行退款。 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("refund_status") | |||||
| private String refundStatus; | |||||
| /** | |||||
| * <pre> | |||||
| * 退款入账账户 | |||||
| * refund_recv_accout_$n | |||||
| * 是 | |||||
| * String(64) | |||||
| * 招商银行信用卡0403 | |||||
| * 取当前退款单的退款入账方,1)退回银行卡:{银行名称}{卡类型}{卡尾号},2)退回支付用户零钱:支付用户零钱 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("refund_recv_accout") | |||||
| private String refundRecvAccout; | |||||
| public String getOutRefundNo() { | |||||
| return outRefundNo; | |||||
| } | |||||
| public void setOutRefundNo(String outRefundNo) { | |||||
| this.outRefundNo = outRefundNo; | |||||
| } | |||||
| public String getRefundId() { | |||||
| return refundId; | |||||
| } | |||||
| public void setRefundId(String refundId) { | |||||
| this.refundId = refundId; | |||||
| } | |||||
| public String getRefundChannel() { | |||||
| return refundChannel; | |||||
| } | |||||
| public void setRefundChannel(String refundChannel) { | |||||
| this.refundChannel = refundChannel; | |||||
| } | |||||
| public String getRefundFee() { | |||||
| return refundFee; | |||||
| } | |||||
| public void setRefundFee(String refundFee) { | |||||
| this.refundFee = refundFee; | |||||
| } | |||||
| public String getSettlementRefundFee() { | |||||
| return settlementRefundFee; | |||||
| } | |||||
| public void setSettlementRefundFee(String settlementRefundFee) { | |||||
| this.settlementRefundFee = settlementRefundFee; | |||||
| } | |||||
| public String getRefundAccount() { | |||||
| return refundAccount; | |||||
| } | |||||
| public void setRefundAccount(String refundAccount) { | |||||
| this.refundAccount = refundAccount; | |||||
| } | |||||
| public String getCouponType() { | |||||
| return couponType; | |||||
| } | |||||
| public void setCouponType(String couponType) { | |||||
| this.couponType = couponType; | |||||
| } | |||||
| public String getCouponRefundFee() { | |||||
| return couponRefundFee; | |||||
| } | |||||
| public void setCouponRefundFee(String couponRefundFee) { | |||||
| this.couponRefundFee = couponRefundFee; | |||||
| } | |||||
| public String getCouponRefundCount() { | |||||
| return couponRefundCount; | |||||
| } | |||||
| public void setCouponRefundCount(String couponRefundCount) { | |||||
| this.couponRefundCount = couponRefundCount; | |||||
| } | |||||
| public List<RefundCoupon> getRefundCoupons() { | |||||
| return refundCoupons; | |||||
| } | |||||
| public void setRefundCoupons(List<RefundCoupon> refundCoupons) { | |||||
| this.refundCoupons = refundCoupons; | |||||
| } | |||||
| public String getRefundStatus() { | |||||
| return refundStatus; | |||||
| } | |||||
| public void setRefundStatus(String refundStatus) { | |||||
| this.refundStatus = refundStatus; | |||||
| } | |||||
| public String getRefundRecvAccout() { | |||||
| return refundRecvAccout; | |||||
| } | |||||
| public void setRefundRecvAccout(String refundRecvAccout) { | |||||
| this.refundRecvAccout = refundRecvAccout; | |||||
| } | |||||
| public static class RefundCoupon { | |||||
| /** | |||||
| * <pre> | |||||
| * 退款代金券批次ID | |||||
| * coupon_refund_batch_id_$n_$m | |||||
| * 否 | |||||
| * String(20) | |||||
| * 100 | |||||
| * 退款代金券批次ID ,$n为下标,$m为下标,从0开始编号 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("coupon_refund_batch_id") | |||||
| private String couponRefundBatchId; | |||||
| /** | |||||
| * <pre> | |||||
| * 退款代金券ID | |||||
| * coupon_refund_id_$n_$m | |||||
| * 否 | |||||
| * String(20) | |||||
| * 10000 | |||||
| * 退款代金券ID, $n为下标,$m为下标,从0开始编号 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("coupon_refund_id") | |||||
| private String couponRefundId; | |||||
| /** | |||||
| * <pre> | |||||
| * 单个退款代金券支付金额 | |||||
| * coupon_refund_fee_$n_$m | |||||
| * 否 | |||||
| * Int | |||||
| * 100 | |||||
| * 单个退款代金券支付金额, $n为下标,$m为下标,从0开始编号 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("coupon_refund_fee") | |||||
| private String couponRefundFee; | |||||
| public RefundCoupon(String couponRefundBatchId, String couponRefundId, String couponRefundFee) { | |||||
| this.couponRefundBatchId = couponRefundBatchId; | |||||
| this.couponRefundId = couponRefundId; | |||||
| this.couponRefundFee = couponRefundFee; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,18 +1,20 @@ | |||||
| package me.chanjar.weixin.mp.bean.template; | package me.chanjar.weixin.mp.bean.template; | ||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||||
| import java.io.Serializable; | import java.io.Serializable; | ||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.List; | import java.util.List; | ||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||||
| /** | |||||
| * 参考 http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277&token=&lang=zh_CN 发送模板消息接口部分 | |||||
| */ | |||||
| public class WxMpTemplateMessage implements Serializable { | public class WxMpTemplateMessage implements Serializable { | ||||
| private static final long serialVersionUID = 5063374783759519418L; | private static final long serialVersionUID = 5063374783759519418L; | ||||
| private String toUser; | private String toUser; | ||||
| private String templateId; | private String templateId; | ||||
| private String url; | private String url; | ||||
| private String topColor; | |||||
| private List<WxMpTemplateData> data = new ArrayList<>(); | private List<WxMpTemplateData> data = new ArrayList<>(); | ||||
| public String getToUser() { | public String getToUser() { | ||||
| @@ -39,14 +41,6 @@ public class WxMpTemplateMessage implements Serializable { | |||||
| this.url = url; | this.url = url; | ||||
| } | } | ||||
| public String getTopColor() { | |||||
| return this.topColor; | |||||
| } | |||||
| public void setTopColor(String topColor) { | |||||
| this.topColor = topColor; | |||||
| } | |||||
| public List<WxMpTemplateData> getData() { | public List<WxMpTemplateData> getData() { | ||||
| return this.data; | return this.data; | ||||
| } | } | ||||
| @@ -71,7 +65,6 @@ public class WxMpTemplateMessage implements Serializable { | |||||
| private String toUser; | private String toUser; | ||||
| private String templateId; | private String templateId; | ||||
| private String url; | private String url; | ||||
| private String topColor; | |||||
| private List<WxMpTemplateData> data = new ArrayList<>(); | private List<WxMpTemplateData> data = new ArrayList<>(); | ||||
| public WxMpTemplateMessageBuilder toUser(String toUser) { | public WxMpTemplateMessageBuilder toUser(String toUser) { | ||||
| @@ -89,11 +82,6 @@ public class WxMpTemplateMessage implements Serializable { | |||||
| return this; | return this; | ||||
| } | } | ||||
| public WxMpTemplateMessageBuilder topColor(String topColor) { | |||||
| this.topColor = topColor; | |||||
| return this; | |||||
| } | |||||
| public WxMpTemplateMessageBuilder data(List<WxMpTemplateData> data) { | public WxMpTemplateMessageBuilder data(List<WxMpTemplateData> data) { | ||||
| this.data = data; | this.data = data; | ||||
| return this; | return this; | ||||
| @@ -103,7 +91,6 @@ public class WxMpTemplateMessage implements Serializable { | |||||
| this.toUser(origin.toUser); | this.toUser(origin.toUser); | ||||
| this.templateId(origin.templateId); | this.templateId(origin.templateId); | ||||
| this.url(origin.url); | this.url(origin.url); | ||||
| this.topColor(origin.topColor); | |||||
| this.data(origin.data); | this.data(origin.data); | ||||
| return this; | return this; | ||||
| } | } | ||||
| @@ -113,7 +100,6 @@ public class WxMpTemplateMessage implements Serializable { | |||||
| m.toUser = this.toUser; | m.toUser = this.toUser; | ||||
| m.templateId = this.templateId; | m.templateId = this.templateId; | ||||
| m.url = this.url; | m.url = this.url; | ||||
| m.topColor = this.topColor; | |||||
| m.data = this.data; | m.data = this.data; | ||||
| return m; | return m; | ||||
| } | } | ||||
| @@ -28,9 +28,6 @@ public class WxMpTemplateMessageGsonAdapter implements JsonSerializer<WxMpTempla | |||||
| if (message.getUrl() != null) { | if (message.getUrl() != null) { | ||||
| messageJson.addProperty("url", message.getUrl()); | messageJson.addProperty("url", message.getUrl()); | ||||
| } | } | ||||
| if (message.getTopColor() != null) { | |||||
| messageJson.addProperty("topcolor", message.getTopColor()); | |||||
| } | |||||
| JsonObject data = new JsonObject(); | JsonObject data = new JsonObject(); | ||||
| messageJson.add("data", data); | messageJson.add("data", data); | ||||
| @@ -1,25 +1,25 @@ | |||||
| package me.chanjar.weixin.mp.util.xml; | package me.chanjar.weixin.mp.util.xml; | ||||
| import java.io.InputStream; | |||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| import com.thoughtworks.xstream.XStream; | import com.thoughtworks.xstream.XStream; | ||||
| import me.chanjar.weixin.common.util.xml.XStreamInitializer; | import me.chanjar.weixin.common.util.xml.XStreamInitializer; | ||||
| import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | |||||
| import me.chanjar.weixin.mp.bean.message.WxMpXmlOutImageMessage; | |||||
| import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | |||||
| import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMusicMessage; | |||||
| import me.chanjar.weixin.mp.bean.message.WxMpXmlOutNewsMessage; | |||||
| import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTextMessage; | |||||
| import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTransferKefuMessage; | |||||
| import me.chanjar.weixin.mp.bean.message.WxMpXmlOutVideoMessage; | |||||
| import me.chanjar.weixin.mp.bean.message.WxMpXmlOutVoiceMessage; | |||||
| import me.chanjar.weixin.mp.bean.message.*; | |||||
| public class XStreamTransformer { | |||||
| import java.io.InputStream; | |||||
| import java.util.*; | |||||
| protected static final Map<Class<?>, XStream> CLASS_2_XSTREAM_INSTANCE = configXStreamInstance(); | |||||
| public class XStreamTransformer { | |||||
| private static final Map<Class<?>, XStream> CLASS_2_XSTREAM_INSTANCE = new HashMap<>(); | |||||
| static { | |||||
| registerClass(WxMpXmlMessage.class); | |||||
| registerClass(WxMpXmlOutMusicMessage.class); | |||||
| registerClass(WxMpXmlOutNewsMessage.class); | |||||
| registerClass(WxMpXmlOutTextMessage.class); | |||||
| registerClass(WxMpXmlOutImageMessage.class); | |||||
| registerClass(WxMpXmlOutVideoMessage.class); | |||||
| registerClass(WxMpXmlOutVoiceMessage.class); | |||||
| registerClass(WxMpXmlOutTransferKefuMessage.class); | |||||
| } | |||||
| /** | /** | ||||
| * xml -> pojo | * xml -> pojo | ||||
| @@ -36,16 +36,6 @@ public class XStreamTransformer { | |||||
| return object; | return object; | ||||
| } | } | ||||
| /** | |||||
| * 注册扩展消息的解析器 | |||||
| * @param clz 类型 | |||||
| * @param xStream xml解析器 | |||||
| */ | |||||
| public static void register(Class<?> clz, XStream xStream) { | |||||
| CLASS_2_XSTREAM_INSTANCE.put(clz,xStream); | |||||
| } | |||||
| /** | /** | ||||
| * pojo -> xml | * pojo -> xml | ||||
| */ | */ | ||||
| @@ -53,83 +43,44 @@ public class XStreamTransformer { | |||||
| return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); | return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); | ||||
| } | } | ||||
| private static Map<Class<?>, XStream> configXStreamInstance() { | |||||
| Map<Class<?>, XStream> map = new HashMap<>(); | |||||
| map.put(WxMpXmlMessage.class, config_WxMpXmlMessage()); | |||||
| map.put(WxMpXmlOutMusicMessage.class, config_WxMpXmlOutMusicMessage()); | |||||
| map.put(WxMpXmlOutNewsMessage.class, config_WxMpXmlOutNewsMessage()); | |||||
| map.put(WxMpXmlOutTextMessage.class, config_WxMpXmlOutTextMessage()); | |||||
| map.put(WxMpXmlOutImageMessage.class, config_WxMpXmlOutImageMessage()); | |||||
| map.put(WxMpXmlOutVideoMessage.class, config_WxMpXmlOutVideoMessage()); | |||||
| map.put(WxMpXmlOutVoiceMessage.class, config_WxMpXmlOutVoiceMessage()); | |||||
| map.put(WxMpXmlOutTransferKefuMessage.class, config_WxMpXmlOutTransferCustomerServiceMessage()); | |||||
| return map; | |||||
| } | |||||
| private static XStream config_WxMpXmlMessage() { | |||||
| XStream xstream = XStreamInitializer.getInstance(); | |||||
| xstream.processAnnotations(WxMpXmlMessage.class); | |||||
| xstream.processAnnotations(WxMpXmlMessage.ScanCodeInfo.class); | |||||
| xstream.processAnnotations(WxMpXmlMessage.SendPicsInfo.class); | |||||
| xstream.processAnnotations(WxMpXmlMessage.SendPicsInfo.Item.class); | |||||
| xstream.processAnnotations(WxMpXmlMessage.SendLocationInfo.class); | |||||
| xstream.aliasField("MsgID", WxMpXmlMessage.class, "msgId"); | |||||
| return xstream; | |||||
| } | |||||
| private static XStream config_WxMpXmlOutImageMessage() { | |||||
| XStream xstream = XStreamInitializer.getInstance(); | |||||
| xstream.processAnnotations(WxMpXmlOutMessage.class); | |||||
| xstream.processAnnotations(WxMpXmlOutImageMessage.class); | |||||
| return xstream; | |||||
| } | |||||
| private static XStream config_WxMpXmlOutNewsMessage() { | |||||
| XStream xstream = XStreamInitializer.getInstance(); | |||||
| xstream.processAnnotations(WxMpXmlOutMessage.class); | |||||
| xstream.processAnnotations(WxMpXmlOutNewsMessage.class); | |||||
| xstream.processAnnotations(WxMpXmlOutNewsMessage.Item.class); | |||||
| return xstream; | |||||
| /** | |||||
| * 注册扩展消息的解析器 | |||||
| * | |||||
| * @param clz 类型 | |||||
| * @param xStream xml解析器 | |||||
| */ | |||||
| public static void register(Class<?> clz, XStream xStream) { | |||||
| CLASS_2_XSTREAM_INSTANCE.put(clz, xStream); | |||||
| } | } | ||||
| private static XStream config_WxMpXmlOutMusicMessage() { | |||||
| /** | |||||
| * 会自动注册该类及其子类 | |||||
| * @param clz 要注册的类 | |||||
| */ | |||||
| public static void registerClass(Class<?> clz) { | |||||
| XStream xstream = XStreamInitializer.getInstance(); | XStream xstream = XStreamInitializer.getInstance(); | ||||
| xstream.processAnnotations(WxMpXmlOutMessage.class); | |||||
| xstream.processAnnotations(WxMpXmlOutMusicMessage.class); | |||||
| xstream.processAnnotations(WxMpXmlOutMusicMessage.Music.class); | |||||
| return xstream; | |||||
| xstream.processAnnotations(clz); | |||||
| xstream.processAnnotations(getInnerClasses(clz)); | |||||
| register(clz, xstream); | |||||
| } | } | ||||
| private static XStream config_WxMpXmlOutTextMessage() { | |||||
| XStream xstream = XStreamInitializer.getInstance(); | |||||
| xstream.processAnnotations(WxMpXmlOutMessage.class); | |||||
| xstream.processAnnotations(WxMpXmlOutTextMessage.class); | |||||
| return xstream; | |||||
| } | |||||
| private static Class<?>[] getInnerClasses(Class<?> clz) { | |||||
| Class<?>[] innerClasses = clz.getClasses(); | |||||
| if (innerClasses == null) { | |||||
| return null; | |||||
| } | |||||
| private static XStream config_WxMpXmlOutVideoMessage() { | |||||
| XStream xstream = XStreamInitializer.getInstance(); | |||||
| xstream.processAnnotations(WxMpXmlOutMessage.class); | |||||
| xstream.processAnnotations(WxMpXmlOutVideoMessage.class); | |||||
| xstream.processAnnotations(WxMpXmlOutVideoMessage.Video.class); | |||||
| return xstream; | |||||
| } | |||||
| List<Class<?>> result = new ArrayList<>(); | |||||
| result.addAll(Arrays.asList(innerClasses)); | |||||
| for (Class<?> inner : innerClasses) { | |||||
| Class<?>[] innerClz = getInnerClasses(inner); | |||||
| if (innerClz == null) { | |||||
| continue; | |||||
| } | |||||
| private static XStream config_WxMpXmlOutVoiceMessage() { | |||||
| XStream xstream = XStreamInitializer.getInstance(); | |||||
| xstream.processAnnotations(WxMpXmlOutMessage.class); | |||||
| xstream.processAnnotations(WxMpXmlOutVoiceMessage.class); | |||||
| return xstream; | |||||
| } | |||||
| result.addAll(Arrays.asList(innerClz)); | |||||
| } | |||||
| private static XStream config_WxMpXmlOutTransferCustomerServiceMessage() { | |||||
| XStream xstream = XStreamInitializer.getInstance(); | |||||
| xstream.processAnnotations(WxMpXmlOutMessage.class); | |||||
| xstream.processAnnotations(WxMpXmlOutTransferKefuMessage.class); | |||||
| xstream.processAnnotations(WxMpXmlOutTransferKefuMessage.TransInfo.class); | |||||
| return xstream; | |||||
| return result.toArray(new Class<?>[0]); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,15 +1,15 @@ | |||||
| package me.chanjar.weixin.mp.api; | package me.chanjar.weixin.mp.api; | ||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import com.google.inject.Binder; | import com.google.inject.Binder; | ||||
| import com.google.inject.Module; | import com.google.inject.Module; | ||||
| import com.thoughtworks.xstream.XStream; | import com.thoughtworks.xstream.XStream; | ||||
| import me.chanjar.weixin.common.util.xml.XStreamInitializer; | import me.chanjar.weixin.common.util.xml.XStreamInitializer; | ||||
| import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; | import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; | ||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import java.util.concurrent.locks.ReentrantLock; | |||||
| public class ApiTestModule implements Module { | public class ApiTestModule implements Module { | ||||
| @Override | @Override | ||||
| @@ -18,6 +18,7 @@ public class ApiTestModule implements Module { | |||||
| .getSystemResourceAsStream("test-config.xml")) { | .getSystemResourceAsStream("test-config.xml")) { | ||||
| WxXmlMpInMemoryConfigStorage config = this | WxXmlMpInMemoryConfigStorage config = this | ||||
| .fromXml(WxXmlMpInMemoryConfigStorage.class, is1); | .fromXml(WxXmlMpInMemoryConfigStorage.class, is1); | ||||
| config.setAccessTokenLock(new ReentrantLock()); | |||||
| WxMpService wxService = new WxMpServiceImpl(); | WxMpService wxService = new WxMpServiceImpl(); | ||||
| wxService.setWxMpConfigStorage(config); | wxService.setWxMpConfigStorage(config); | ||||
| @@ -1,8 +1,9 @@ | |||||
| package me.chanjar.weixin.mp.api; | package me.chanjar.weixin.mp.api; | ||||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
| import org.apache.commons.lang3.builder.ToStringBuilder; | import org.apache.commons.lang3.builder.ToStringBuilder; | ||||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
| import java.util.concurrent.locks.Lock; | |||||
| @XStreamAlias("xml") | @XStreamAlias("xml") | ||||
| public class WxXmlMpInMemoryConfigStorage | public class WxXmlMpInMemoryConfigStorage | ||||
| @@ -50,4 +51,7 @@ public class WxXmlMpInMemoryConfigStorage | |||||
| this.templateId = templateId; | this.templateId = templateId; | ||||
| } | } | ||||
| } | |||||
| public void setAccessTokenLock(Lock lock){ | |||||
| super.accessTokenLock = lock; | |||||
| } | |||||
| } | |||||
| @@ -1,18 +1,17 @@ | |||||
| package me.chanjar.weixin.mp.api.impl; | package me.chanjar.weixin.mp.api.impl; | ||||
| import me.chanjar.weixin.mp.api.WxMpService; | |||||
| import org.testng.Assert; | |||||
| import org.testng.annotations.DataProvider; | |||||
| import org.testng.annotations.Guice; | |||||
| import org.testng.annotations.Test; | |||||
| import com.google.inject.Inject; | import com.google.inject.Inject; | ||||
| import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
| import me.chanjar.weixin.common.bean.menu.WxMenu; | import me.chanjar.weixin.common.bean.menu.WxMenu; | ||||
| import me.chanjar.weixin.common.bean.menu.WxMenuButton; | import me.chanjar.weixin.common.bean.menu.WxMenuButton; | ||||
| import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
| import me.chanjar.weixin.mp.api.ApiTestModule; | import me.chanjar.weixin.mp.api.ApiTestModule; | ||||
| import me.chanjar.weixin.mp.api.WxMpService; | |||||
| import me.chanjar.weixin.mp.bean.menu.WxMpGetSelfMenuInfoResult; | |||||
| import org.testng.Assert; | |||||
| import org.testng.annotations.DataProvider; | |||||
| import org.testng.annotations.Guice; | |||||
| import org.testng.annotations.Test; | |||||
| /** | /** | ||||
| * 测试菜单 | * 测试菜单 | ||||
| @@ -28,11 +27,22 @@ public class WxMpMenuServiceImplTest { | |||||
| protected WxMpService wxService; | protected WxMpService wxService; | ||||
| @Test(dataProvider = "menu") | @Test(dataProvider = "menu") | ||||
| public void testCreateMenu(WxMenu wxMenu) throws WxErrorException { | |||||
| public void testMenuCreate(WxMenu wxMenu) throws WxErrorException { | |||||
| System.out.println(wxMenu.toJson()); | System.out.println(wxMenu.toJson()); | ||||
| this.wxService.getMenuService().menuCreate(wxMenu); | this.wxService.getMenuService().menuCreate(wxMenu); | ||||
| } | } | ||||
| @Test | |||||
| public void testMenuTryMatch() throws Exception { | |||||
| //TODO | |||||
| } | |||||
| @Test | |||||
| public void testGetSelfMenuInfo() throws Exception { | |||||
| WxMpGetSelfMenuInfoResult selfMenuInfo = this.wxService.getMenuService().getSelfMenuInfo(); | |||||
| System.out.println(selfMenuInfo); | |||||
| } | |||||
| @Test | @Test | ||||
| public void testCreateMenu2() throws WxErrorException { | public void testCreateMenu2() throws WxErrorException { | ||||
| String a = "{\n" | String a = "{\n" | ||||
| @@ -77,15 +87,15 @@ public class WxMpMenuServiceImplTest { | |||||
| this.wxService.getMenuService().menuCreate(menu); | this.wxService.getMenuService().menuCreate(menu); | ||||
| } | } | ||||
| @Test(dependsOnMethods = { "testCreateMenu"}) | |||||
| public void testGetMenu() throws WxErrorException { | |||||
| @Test(dependsOnMethods = { "testMenuCreate"}) | |||||
| public void testMenuGet() throws WxErrorException { | |||||
| WxMenu wxMenu = this.wxService.getMenuService().menuGet(); | WxMenu wxMenu = this.wxService.getMenuService().menuGet(); | ||||
| Assert.assertNotNull(wxMenu); | Assert.assertNotNull(wxMenu); | ||||
| System.out.println(wxMenu.toJson()); | System.out.println(wxMenu.toJson()); | ||||
| } | } | ||||
| @Test(dependsOnMethods = { "testGetMenu"}) | |||||
| public void testDeleteMenu() throws WxErrorException { | |||||
| @Test(dependsOnMethods = { "testMenuGet"}) | |||||
| public void testMenuDelete() throws WxErrorException { | |||||
| this.wxService.getMenuService().menuDelete(); | this.wxService.getMenuService().menuDelete(); | ||||
| } | } | ||||
| @@ -9,9 +9,7 @@ import me.chanjar.weixin.mp.bean.pay.request.WxEntPayRequest; | |||||
| import me.chanjar.weixin.mp.bean.pay.request.WxPayRefundRequest; | import me.chanjar.weixin.mp.bean.pay.request.WxPayRefundRequest; | ||||
| import me.chanjar.weixin.mp.bean.pay.request.WxPaySendRedpackRequest; | import me.chanjar.weixin.mp.bean.pay.request.WxPaySendRedpackRequest; | ||||
| import me.chanjar.weixin.mp.bean.pay.request.WxPayUnifiedOrderRequest; | import me.chanjar.weixin.mp.bean.pay.request.WxPayUnifiedOrderRequest; | ||||
| import me.chanjar.weixin.mp.bean.pay.result.WxPayRefundResult; | |||||
| import me.chanjar.weixin.mp.bean.pay.result.WxPaySendRedpackResult; | |||||
| import me.chanjar.weixin.mp.bean.pay.result.WxPayUnifiedOrderResult; | |||||
| import me.chanjar.weixin.mp.bean.pay.result.*; | |||||
| import org.testng.annotations.Guice; | import org.testng.annotations.Guice; | ||||
| import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
| @@ -20,6 +18,7 @@ import java.io.File; | |||||
| /** | /** | ||||
| * 测试支付相关接口 | * 测试支付相关接口 | ||||
| * Created by Binary Wang on 2016/7/28. | * Created by Binary Wang on 2016/7/28. | ||||
| * | |||||
| * @author binarywang (https://github.com/binarywang) | * @author binarywang (https://github.com/binarywang) | ||||
| */ | */ | ||||
| @Test | @Test | ||||
| @@ -34,6 +33,9 @@ public class WxMpPayServiceImplTest { | |||||
| } | } | ||||
| /** | |||||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#refund(WxPayRefundRequest, File)} . | |||||
| */ | |||||
| @Test | @Test | ||||
| public void testRefund() throws Exception { | public void testRefund() throws Exception { | ||||
| WxPayRefundRequest request = new WxPayRefundRequest(); | WxPayRefundRequest request = new WxPayRefundRequest(); | ||||
| @@ -46,33 +48,64 @@ public class WxMpPayServiceImplTest { | |||||
| System.err.println(result); | System.err.println(result); | ||||
| } | } | ||||
| /** | |||||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#refundQuery(String, String, String, String)} . | |||||
| */ | |||||
| @Test | |||||
| public void testRefundQuery() throws Exception { | |||||
| WxPayRefundQueryResult result = this.wxService.getPayService().refundQuery("1", "", "", ""); | |||||
| result = this.wxService.getPayService().refundQuery("", "2", "", ""); | |||||
| System.err.println(result); | |||||
| result = this.wxService.getPayService().refundQuery("", "", "3", ""); | |||||
| System.err.println(result); | |||||
| result = this.wxService.getPayService().refundQuery("", "", "", "4"); | |||||
| System.err.println(result); | |||||
| //测试四个参数都填的情况,应该报异常的 | |||||
| result = this.wxService.getPayService().refundQuery("1", "2", "3", "4"); | |||||
| System.err.println(result); | |||||
| } | |||||
| @Test | @Test | ||||
| public void testCheckJSSDKCallbackDataSignature() throws Exception { | public void testCheckJSSDKCallbackDataSignature() throws Exception { | ||||
| } | } | ||||
| /** | |||||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#sendRedpack(WxPaySendRedpackRequest, File)} . | |||||
| */ | |||||
| @Test | @Test | ||||
| public void testSendRedpack() throws Exception { | public void testSendRedpack() throws Exception { | ||||
| WxPaySendRedpackRequest request = new WxPaySendRedpackRequest(); | WxPaySendRedpackRequest request = new WxPaySendRedpackRequest(); | ||||
| request.setActName("abc"); | request.setActName("abc"); | ||||
| request.setClientIp("aaa"); | request.setClientIp("aaa"); | ||||
| request.setMchBillno("aaaa"); | |||||
| request.setMchBillNo("aaaa"); | |||||
| request | request | ||||
| .setReOpenid(((WxXmlMpInMemoryConfigStorage) this.wxService.getWxMpConfigStorage()).getOpenid()); | |||||
| .setReOpenid(((WxXmlMpInMemoryConfigStorage) this.wxService.getWxMpConfigStorage()).getOpenid()); | |||||
| File keyFile = new File("E:\\dlt.p12"); | File keyFile = new File("E:\\dlt.p12"); | ||||
| WxPaySendRedpackResult redpackResult = this.wxService.getPayService().sendRedpack(request, keyFile); | WxPaySendRedpackResult redpackResult = this.wxService.getPayService().sendRedpack(request, keyFile); | ||||
| System.err.println(redpackResult); | System.err.println(redpackResult); | ||||
| } | } | ||||
| /** | |||||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#queryRedpack(String, File)}. | |||||
| */ | |||||
| @Test | |||||
| public void testQueryRedpack() throws Exception { | |||||
| File keyFile = new File("E:\\dlt.p12"); | |||||
| WxPayRedpackQueryResult redpackResult = this.wxService.getPayService().queryRedpack("aaaa", keyFile); | |||||
| System.err.println(redpackResult); | |||||
| } | |||||
| /** | /** | ||||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#unifiedOrder(WxPayUnifiedOrderRequest)}. | * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#unifiedOrder(WxPayUnifiedOrderRequest)}. | ||||
| */ | */ | ||||
| @Test | @Test | ||||
| public void testUnifiedOrder() throws WxErrorException { | public void testUnifiedOrder() throws WxErrorException { | ||||
| WxPayUnifiedOrderResult result = this.wxService.getPayService() | WxPayUnifiedOrderResult result = this.wxService.getPayService() | ||||
| .unifiedOrder(WxPayUnifiedOrderRequest.builder().body("1111111") | |||||
| .totalFee(1).spbillCreateIp("111111").notifyURL("111111") | |||||
| .tradeType("JSAPI").openid("122").outTradeNo("111111").build()); | |||||
| .unifiedOrder(WxPayUnifiedOrderRequest.builder().body("1111111") | |||||
| .totalFee(1).spbillCreateIp("111111").notifyURL("111111") | |||||
| .tradeType("JSAPI").openid("122").outTradeNo("111111").build()); | |||||
| System.err.println(result); | System.err.println(result); | ||||
| } | } | ||||
| @@ -9,6 +9,7 @@ import me.chanjar.weixin.mp.bean.template.WxMpTemplate; | |||||
| import me.chanjar.weixin.mp.bean.template.WxMpTemplateData; | import me.chanjar.weixin.mp.bean.template.WxMpTemplateData; | ||||
| import me.chanjar.weixin.mp.bean.template.WxMpTemplateIndustry; | import me.chanjar.weixin.mp.bean.template.WxMpTemplateIndustry; | ||||
| import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage; | import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage; | ||||
| import org.apache.commons.lang3.RandomStringUtils; | |||||
| import org.testng.Assert; | import org.testng.Assert; | ||||
| import org.testng.annotations.Guice; | import org.testng.annotations.Guice; | ||||
| import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
| @@ -28,7 +29,7 @@ public class WxMpTemplateMsgServiceImplTest { | |||||
| @Inject | @Inject | ||||
| protected WxMpService wxService; | protected WxMpService wxService; | ||||
| @Test(invocationCount = 10, threadPoolSize = 10) | |||||
| @Test(invocationCount = 5, threadPoolSize = 3) | |||||
| public void testSendTemplateMsg() throws WxErrorException { | public void testSendTemplateMsg() throws WxErrorException { | ||||
| SimpleDateFormat dateFormat = new SimpleDateFormat( | SimpleDateFormat dateFormat = new SimpleDateFormat( | ||||
| "yyyy-MM-dd HH:mm:ss.SSS"); | "yyyy-MM-dd HH:mm:ss.SSS"); | ||||
| @@ -38,7 +39,10 @@ public class WxMpTemplateMsgServiceImplTest { | |||||
| .toUser(configStorage.getOpenid()) | .toUser(configStorage.getOpenid()) | ||||
| .templateId(configStorage.getTemplateId()).build(); | .templateId(configStorage.getTemplateId()).build(); | ||||
| templateMessage.addWxMpTemplateData( | templateMessage.addWxMpTemplateData( | ||||
| new WxMpTemplateData("first", dateFormat.format(new Date()))); | |||||
| new WxMpTemplateData("first", dateFormat.format(new Date()),"#FF00FF")); | |||||
| templateMessage.addWxMpTemplateData( | |||||
| new WxMpTemplateData("remark", RandomStringUtils.randomAlphanumeric(100), "#FF00FF")); | |||||
| templateMessage.setUrl(" "); | |||||
| String msgId = this.wxService.getTemplateMsgService().sendTemplateMsg(templateMessage); | String msgId = this.wxService.getTemplateMsgService().sendTemplateMsg(templateMessage); | ||||
| Assert.assertNotNull(msgId); | Assert.assertNotNull(msgId); | ||||
| System.out.println(msgId); | System.out.println(msgId); | ||||
| @@ -6,7 +6,7 @@ | |||||
| <parent> | <parent> | ||||
| <groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
| <artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
| <version>2.3.0</version> | |||||
| <version>2.4.0</version> | |||||
| </parent> | </parent> | ||||
| <artifactId>weixin-java-osgi</artifactId> | <artifactId>weixin-java-osgi</artifactId> | ||||