From 5778b42e1e73970859f9497bf85b34a07997bdcd Mon Sep 17 00:00:00 2001 From: ben Date: Fri, 25 Dec 2015 14:40:18 +0800 Subject: [PATCH 01/22] =?UTF-8?q?XStreamTransformer=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E6=96=B9=E6=B3=95,=E6=9D=A5=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E8=87=AA=E5=AE=9A=E4=B9=89=E7=9A=84=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chanjar/weixin/cp/util/xml/XStreamTransformer.java | 9 +++++++++ .../chanjar/weixin/mp/util/xml/XStreamTransformer.java | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java index c6c5a5f1..12dd8992 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java @@ -31,6 +31,15 @@ public class XStreamTransformer { return object; } + /** + * 注册扩展消息的解析器 + * @param clz 类型 + * @param xStream xml解析器 + */ + public static void register(Class clz,XStream xStream){ + CLASS_2_XSTREAM_INSTANCE.put(clz,xStream); + } + /** * pojo -> xml * diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java index 71844eb9..254f2147 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java @@ -31,6 +31,16 @@ public class XStreamTransformer { return object; } + /** + * 注册扩展消息的解析器 + * @param clz 类型 + * @param xStream xml解析器 + */ + public static void register(Class clz,XStream xStream){ + CLASS_2_XSTREAM_INSTANCE.put(clz,xStream); + } + + /** * pojo -> xml * From 0619c3f01edc2a1d85ecd8ea5a19807f07558a4c Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 29 Dec 2015 10:10:45 +0800 Subject: [PATCH 02/22] =?UTF-8?q?=E4=BF=AE=E6=94=B9me.chanjar.weixin.mp.be?= =?UTF-8?q?an.WxMpXmlOutTransferCustomerServiceMessage=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=B0=86=E6=B6=88=E6=81=AF=E8=BD=AC=E5=8F=91=E5=88=B0?= =?UTF-8?q?=E5=A4=9A=E5=AE=A2=E6=9C=8D(https://mp.weixin.qq.com/wiki/5/ae2?= =?UTF-8?q?30189c9bd07a6b221f48619aeef35.html)=E4=B8=AD=E7=9A=84=E4=B8=A4?= =?UTF-8?q?=E7=A7=8D=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...pXmlOutTransferCustomerServiceMessage.java | 11 ++- .../TransferCustomerServiceBuilder.java | 7 +- ...OutTransferCustomerServiceMessageTest.java | 70 +++++++++++++++++++ 3 files changed, 81 insertions(+), 7 deletions(-) create mode 100644 weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMpXmlOutTransferCustomerServiceMessageTest.java diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutTransferCustomerServiceMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutTransferCustomerServiceMessage.java index 5783d2b4..67c3d28e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutTransferCustomerServiceMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutTransferCustomerServiceMessage.java @@ -4,23 +4,22 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; -import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter; @XStreamAlias("xml") public class WxMpXmlOutTransferCustomerServiceMessage extends WxMpXmlOutMessage { @XStreamAlias("TransInfo") - protected final TransInfo transInfo = new TransInfo(); + protected TransInfo transInfo; public WxMpXmlOutTransferCustomerServiceMessage() { this.msgType = WxConsts.CUSTOM_MSG_TRANSFER_CUSTOMER_SERVICE; } - public String getKfAccount() { - return transInfo.getKfAccount(); + public TransInfo getTransInfo() { + return transInfo; } - public void setKfAccount(String kfAccount) { - transInfo.setKfAccount(kfAccount); + public void setTransInfo(TransInfo transInfo) { + this.transInfo = transInfo; } @XStreamAlias("TransInfo") diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/TransferCustomerServiceBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/TransferCustomerServiceBuilder.java index 01acf8d7..d004eb56 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/TransferCustomerServiceBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/TransferCustomerServiceBuilder.java @@ -1,5 +1,6 @@ package me.chanjar.weixin.mp.bean.outxmlbuilder; +import me.chanjar.weixin.common.util.StringUtils; import me.chanjar.weixin.mp.bean.WxMpXmlOutTransferCustomerServiceMessage; /** @@ -22,7 +23,11 @@ public final class TransferCustomerServiceBuilder extends BaseBuilder" + + "" + + "1399197672" + + "" + + ""; + System.out.println(m.toXml()); + Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); + + expected = " " + + "" + + "" + + "1399197672" + + "" + + "" + + "" + + "" + + ""; + WxMpXmlOutTransferCustomerServiceMessage.TransInfo transInfo = new WxMpXmlOutTransferCustomerServiceMessage.TransInfo(); + transInfo.setKfAccount("test1@test"); + m.setTransInfo(transInfo); + System.out.println(m.toXml()); + Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); + } + + @Test + public void testBuild() { + WxMpXmlOutTransferCustomerServiceMessage m = WxMpXmlOutMessage.TRANSFER_CUSTOMER_SERVICE().fromUser("fromuser").toUser("touser").build(); + m.setCreateTime(1399197672L); + String expected = "" + + "" + + "" + + "1399197672" + + "" + + ""; + System.out.println(m.toXml()); + Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); + + + expected = " " + + "" + + "" + + "1399197672" + + "" + + "" + + "" + + "" + + ""; + m = WxMpXmlOutMessage.TRANSFER_CUSTOMER_SERVICE().kfAccount("test1@test").fromUser("fromuser").toUser("touser").build(); + m.setCreateTime(1399197672L); + System.out.println(m.toXml()); + Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); + } +} \ No newline at end of file From 06e4dda310a90987d0d9334d90cea9a7c970c405 Mon Sep 17 00:00:00 2001 From: Daniel Qian Date: Thu, 7 Jan 2016 18:02:20 +0800 Subject: [PATCH 03/22] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E8=AE=A1=E7=AE=97=E7=B2=BE=E5=BA=A6=E9=97=AE=E9=A2=98=20#248?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java index 4223caef..141edf17 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java @@ -884,7 +884,7 @@ public class WxMpServiceImpl implements WxMpService { packageParams.put("mch_id", wxMpConfigStorage.getPartnerId()); packageParams.put("body", body); packageParams.put("out_trade_no", outTradeNo); - packageParams.put("total_fee", (int) (amt * 100) + ""); + packageParams.put("total_fee", String.format("%.0f", amt * 100)); packageParams.put("spbill_create_ip", ip); packageParams.put("notify_url", callbackUrl); packageParams.put("trade_type", tradeType); From d759e6935ed3ca608288b9a690548b6918c2fa42 Mon Sep 17 00:00:00 2001 From: Daniel Qian Date: Thu, 7 Jan 2016 18:07:31 +0800 Subject: [PATCH 04/22] =?UTF-8?q?fix:=20#256,=20#172=20CloseableHttpRespon?= =?UTF-8?q?se=20=E4=BD=BF=E7=94=A8=E5=A4=96=E6=B2=A1=E6=9C=89=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weixin/mp/api/WxMpServiceImpl.java | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java index 141edf17..949be563 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java @@ -158,14 +158,15 @@ public class WxMpServiceImpl implements WxMpService { RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); httpGet.setConfig(config); } - CloseableHttpResponse response = getHttpclient().execute(httpGet); - String resultContent = new BasicResponseHandler().handleResponse(response); - WxError error = WxError.fromJson(resultContent); - if (error.getErrorCode() != 0) { - throw new WxErrorException(error); + 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); + wxMpConfigStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); } - WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); - wxMpConfigStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); } catch (ClientProtocolException e) { throw new RuntimeException(e); } catch (IOException e) { @@ -851,8 +852,7 @@ public class WxMpServiceImpl implements WxMpService { StringEntity entity = new StringEntity(request.toString(), Consts.UTF_8); httpPost.setEntity(entity); - try { - CloseableHttpResponse response = getHttpclient().execute(httpPost); + try(CloseableHttpResponse response = getHttpclient().execute(httpPost)) { String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); XStream xstream = XStreamInitializer.getInstance(); xstream.alias("xml", WxMpPrepayIdResult.class); @@ -944,8 +944,7 @@ public class WxMpServiceImpl implements WxMpService { StringEntity entity = new StringEntity(request.toString(), Consts.UTF_8); httpPost.setEntity(entity); - try { - CloseableHttpResponse response = httpClient.execute(httpPost); + try(CloseableHttpResponse response = httpClient.execute(httpPost)) { String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); XStream xstream = XStreamInitializer.getInstance(); xstream.alias("xml", WxMpPayResult.class); @@ -1000,8 +999,7 @@ public class WxMpServiceImpl implements WxMpService { StringEntity entity = new StringEntity(request.toString(), Consts.UTF_8); httpPost.setEntity(entity); - try { - CloseableHttpResponse response = getHttpclient().execute(httpPost); + try(CloseableHttpResponse response = getHttpclient().execute(httpPost)) { String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); XStream xstream = XStreamInitializer.getInstance(); xstream.processAnnotations(WxRedpackResult.class); From 25eb592f2663b831e0f7c3ff9b21f1b1c6ecc74c Mon Sep 17 00:00:00 2001 From: Daniel Qian Date: Sun, 10 Jan 2016 10:34:25 +0800 Subject: [PATCH 05/22] fix: #172 based on PR #259 --- pom.xml | 6 ++ .../util/http/JoddGetRequestExecutor.java | 55 ++++++++++++++ .../util/http/JoddPostRequestExecutor.java | 49 +++++++++++++ .../weixin/cp/api/WxCpServiceImpl.java | 21 ++---- .../weixin/mp/api/WxMpServiceImpl.java | 72 +++++++++---------- 5 files changed, 150 insertions(+), 53 deletions(-) create mode 100644 weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddGetRequestExecutor.java create mode 100644 weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddPostRequestExecutor.java diff --git a/pom.xml b/pom.xml index 61a04739..7f6b66a6 100644 --- a/pom.xml +++ b/pom.xml @@ -44,6 +44,7 @@ 4.5 1.7.10 1.1.2 + 3.6.7 @@ -68,6 +69,11 @@ httpmime ${httpclient.version} + + org.jodd + jodd-http + ${jodd-http.version} + com.google.code.gson gson diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddGetRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddGetRequestExecutor.java new file mode 100644 index 00000000..aa6fa903 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddGetRequestExecutor.java @@ -0,0 +1,55 @@ +package me.chanjar.weixin.common.util.http; + +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.http.net.SocketHttpConnectionProvider; +import me.chanjar.weixin.common.bean.result.WxError; +import me.chanjar.weixin.common.exception.WxErrorException; +import org.apache.http.HttpHost; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.IOException; + +/** + * 简单的GET请求执行器,请求的参数是String, 返回的结果也是String + * + * @author Daniel Qian + */ +public class JoddGetRequestExecutor implements RequestExecutor { + + @Override + public String execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, + String queryParam) throws WxErrorException, IOException { + if (queryParam != null) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") ? queryParam : '&' + queryParam; + } + + SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider(); + + if (httpProxy != null) { + ProxyInfo proxyInfoObj = new ProxyInfo( + ProxyInfo.ProxyType.HTTP, + httpProxy.getAddress().getHostAddress(), + httpProxy.getPort(), "", ""); + provider.useProxy(proxyInfoObj); + } + + HttpRequest request = HttpRequest.get(uri); + request.method("GET"); + request.charset("UTF-8"); + + HttpResponse response = request.open(provider).send(); + String result = response.bodyText(); + + WxError error = WxError.fromJson(result); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return result; + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddPostRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddPostRequestExecutor.java new file mode 100644 index 00000000..690a144d --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddPostRequestExecutor.java @@ -0,0 +1,49 @@ +package me.chanjar.weixin.common.util.http; + +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.http.net.SocketHttpConnectionProvider; +import me.chanjar.weixin.common.bean.result.WxError; +import me.chanjar.weixin.common.exception.WxErrorException; +import org.apache.http.HttpHost; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.IOException; + +/** + * 简单的POST请求执行器,请求的参数是String, 返回的结果也是String + * + * @author Edison Guo + */ +public class JoddPostRequestExecutor implements RequestExecutor { + + @Override + public String execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, + String postEntity) throws WxErrorException, IOException { + SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider(); + + if (httpProxy != null) { + ProxyInfo proxyInfoObj = new ProxyInfo( + ProxyInfo.ProxyType.HTTP, + httpProxy.getAddress().getHostAddress(), + httpProxy.getPort(), "", ""); + provider.useProxy(proxyInfoObj); + } + + HttpRequest request = HttpRequest.get(uri); + request.method("POST"); + request.charset("UTF-8"); + request.bodyText(postEntity); + + HttpResponse response = request.open(provider).send(); + String result = response.bodyText(); + + WxError error = WxError.fromJson(result); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return result; + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java index 3b9e8fe7..a34108b3 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java @@ -25,8 +25,8 @@ import me.chanjar.weixin.common.util.fs.FileUtils; import me.chanjar.weixin.common.util.http.MediaDownloadRequestExecutor; import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; -import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; +import me.chanjar.weixin.common.util.http.JoddGetRequestExecutor; +import me.chanjar.weixin.common.util.http.JoddPostRequestExecutor; import me.chanjar.weixin.common.util.http.URIUtil; import me.chanjar.weixin.common.util.json.GsonHelper; import me.chanjar.weixin.cp.bean.WxCpDepart; @@ -156,7 +156,7 @@ public class WxCpServiceImpl implements WxCpService { synchronized (globalJsapiTicketRefreshLock) { if (wxCpConfigStorage.isJsapiTicketExpired()) { String url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket"; - String responseContent = execute(new SimpleGetRequestExecutor(), url, null); + String responseContent = execute(new JoddGetRequestExecutor(), url, null); JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); @@ -256,7 +256,7 @@ public class WxCpServiceImpl implements WxCpService { public Integer departCreate(WxCpDepart depart) throws WxErrorException { String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create"; String responseContent = execute( - new SimplePostRequestExecutor(), + new JoddPostRequestExecutor(), url, depart.toJson()); JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); @@ -512,11 +512,11 @@ public class WxCpServiceImpl implements WxCpService { } public String get(String url, String queryParam) throws WxErrorException { - return execute(new SimpleGetRequestExecutor(), url, queryParam); + return execute(new JoddGetRequestExecutor(), url, queryParam); } public String post(String url, String postData) throws WxErrorException { - return execute(new SimplePostRequestExecutor(), url, postData); + return execute(new JoddPostRequestExecutor(), url, postData); } /** @@ -685,12 +685,5 @@ public class WxCpServiceImpl implements WxCpService { this.tmpDirFile = tmpDirFile; } - public static void main(String[] args) { - Float a = 3.1f; - System.out.println(3.1d); - System.out.println(new BigDecimal(3.1d)); - System.out.println(new BigDecimal(a)); - System.out.println(a.toString()); - System.out.println(a.doubleValue()); - } + } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java index 949be563..7a5b7ace 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java @@ -28,13 +28,7 @@ import me.chanjar.weixin.common.util.StringUtils; import me.chanjar.weixin.common.util.crypto.SHA1; import me.chanjar.weixin.common.util.crypto.WxCryptUtil; import me.chanjar.weixin.common.util.fs.FileUtils; -import me.chanjar.weixin.common.util.http.MediaDownloadRequestExecutor; -import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; -import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; -import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; -import me.chanjar.weixin.common.util.http.URIUtil; -import me.chanjar.weixin.common.util.http.Utf8ResponseHandler; +import me.chanjar.weixin.common.util.http.*; import me.chanjar.weixin.common.util.json.GsonHelper; import me.chanjar.weixin.common.util.json.WxGsonBuilder; import me.chanjar.weixin.common.util.xml.XStreamInitializer; @@ -190,7 +184,7 @@ public class WxMpServiceImpl implements WxMpService { synchronized (globalJsapiTicketRefreshLock) { if (wxMpConfigStorage.isJsapiTicketExpired()) { String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi"; - String responseContent = execute(new SimpleGetRequestExecutor(), url, null); + String responseContent = execute(new JoddGetRequestExecutor(), url, null); JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); @@ -227,33 +221,33 @@ public class WxMpServiceImpl implements WxMpService { public void customMessageSend(WxMpCustomMessage message) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send"; - execute(new SimplePostRequestExecutor(), url, message.toJson()); + execute(new JoddPostRequestExecutor(), url, message.toJson()); } public void menuCreate(WxMenu menu) throws WxErrorException { if (menu.getMatchRule() != null) { String url = "https://api.weixin.qq.com/cgi-bin/menu/addconditional"; - execute(new SimplePostRequestExecutor(), url, menu.toJson()); + execute(new JoddPostRequestExecutor(), url, menu.toJson()); } else { String url = "https://api.weixin.qq.com/cgi-bin/menu/create"; - execute(new SimplePostRequestExecutor(), url, menu.toJson()); + execute(new JoddPostRequestExecutor(), url, menu.toJson()); } } public void menuDelete() throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/menu/delete"; - execute(new SimpleGetRequestExecutor(), url, null); + execute(new JoddGetRequestExecutor(), url, null); } public void menuDelete(String menuid) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/menu/delconditional"; - execute(new SimpleGetRequestExecutor(), url, "menuid=" + menuid); + execute(new JoddGetRequestExecutor(), url, "menuid=" + menuid); } public WxMenu menuGet() throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/menu/get"; try { - String resultContent = execute(new SimpleGetRequestExecutor(), url, null); + String resultContent = execute(new JoddGetRequestExecutor(), url, null); return WxMenu.fromJson(resultContent); } catch (WxErrorException e) { // 46003 不存在的菜单数据 @@ -267,7 +261,7 @@ public class WxMpServiceImpl implements WxMpService { public WxMenu menuTryMatch(String userid) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/menu/trymatch"; try { - String resultContent = execute(new SimpleGetRequestExecutor(), url, "user_id=" + userid); + String resultContent = execute(new JoddGetRequestExecutor(), url, "user_id=" + userid); return WxMenu.fromJson(resultContent); } catch (WxErrorException e) { // 46003 不存在的菜单数据 46002 不存在的菜单版本 @@ -380,25 +374,25 @@ public class WxMpServiceImpl implements WxMpService { public WxMpMassUploadResult massNewsUpload(WxMpMassNews news) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/media/uploadnews"; - String responseContent = execute(new SimplePostRequestExecutor(), url, news.toJson()); + String responseContent = execute(new JoddPostRequestExecutor(), url, news.toJson()); return WxMpMassUploadResult.fromJson(responseContent); } public WxMpMassUploadResult massVideoUpload(WxMpMassVideo video) throws WxErrorException { String url = "http://file.api.weixin.qq.com/cgi-bin/media/uploadvideo"; - String responseContent = execute(new SimplePostRequestExecutor(), url, video.toJson()); + String responseContent = execute(new JoddPostRequestExecutor(), url, video.toJson()); return WxMpMassUploadResult.fromJson(responseContent); } public WxMpMassSendResult massGroupMessageSend(WxMpMassGroupMessage message) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall"; - String responseContent = execute(new SimplePostRequestExecutor(), url, message.toJson()); + String responseContent = execute(new JoddPostRequestExecutor(), url, message.toJson()); return WxMpMassSendResult.fromJson(responseContent); } public WxMpMassSendResult massOpenIdsMessageSend(WxMpMassOpenIdsMessage message) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/message/mass/send"; - String responseContent = execute(new SimplePostRequestExecutor(), url, message.toJson()); + String responseContent = execute(new JoddPostRequestExecutor(), url, message.toJson()); return WxMpMassSendResult.fromJson(responseContent); } @@ -410,7 +404,7 @@ public class WxMpServiceImpl implements WxMpService { groupJson.addProperty("name", name); String responseContent = execute( - new SimplePostRequestExecutor(), + new JoddPostRequestExecutor(), url, json.toString()); return WxMpGroup.fromJson(responseContent); @@ -418,7 +412,7 @@ public class WxMpServiceImpl implements WxMpService { public List groupGet() throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/groups/get"; - String responseContent = execute(new SimpleGetRequestExecutor(), url, null); + String responseContent = execute(new JoddGetRequestExecutor(), url, null); /* * 操蛋的微信API,创建时返回的是 { group : { id : ..., name : ...} } * 查询时返回的是 { groups : [ { id : ..., name : ..., count : ... }, ... ] } @@ -433,14 +427,14 @@ public class WxMpServiceImpl implements WxMpService { String url = "https://api.weixin.qq.com/cgi-bin/groups/getid"; JsonObject o = new JsonObject(); o.addProperty("openid", openid); - String responseContent = execute(new SimplePostRequestExecutor(), url, o.toString()); + String responseContent = execute(new JoddPostRequestExecutor(), url, o.toString()); JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); return GsonHelper.getAsLong(tmpJsonElement.getAsJsonObject().get("groupid")); } public void groupUpdate(WxMpGroup group) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/groups/update"; - execute(new SimplePostRequestExecutor(), url, group.toJson()); + execute(new JoddPostRequestExecutor(), url, group.toJson()); } public void userUpdateGroup(String openid, long to_groupid) throws WxErrorException { @@ -448,7 +442,7 @@ public class WxMpServiceImpl implements WxMpService { JsonObject json = new JsonObject(); json.addProperty("openid", openid); json.addProperty("to_groupid", to_groupid); - execute(new SimplePostRequestExecutor(), url, json.toString()); + execute(new JoddPostRequestExecutor(), url, json.toString()); } public void userUpdateRemark(String openid, String remark) throws WxErrorException { @@ -456,19 +450,19 @@ public class WxMpServiceImpl implements WxMpService { JsonObject json = new JsonObject(); json.addProperty("openid", openid); json.addProperty("remark", remark); - execute(new SimplePostRequestExecutor(), url, json.toString()); + execute(new JoddPostRequestExecutor(), url, json.toString()); } public WxMpUser userInfo(String openid, String lang) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/user/info"; lang = lang == null ? "zh_CN" : lang; - String responseContent = execute(new SimpleGetRequestExecutor(), url, "openid=" + openid + "&lang=" + lang); + String responseContent = execute(new JoddGetRequestExecutor(), url, "openid=" + openid + "&lang=" + lang); return WxMpUser.fromJson(responseContent); } public WxMpUserList userList(String next_openid) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/user/get"; - String responseContent = execute(new SimpleGetRequestExecutor(), url, next_openid == null ? null : "next_openid=" + next_openid); + String responseContent = execute(new JoddGetRequestExecutor(), url, next_openid == null ? null : "next_openid=" + next_openid); return WxMpUserList.fromJson(responseContent); } @@ -484,7 +478,7 @@ public class WxMpServiceImpl implements WxMpService { scene.addProperty("scene_id", scene_id); actionInfo.add("scene", scene); json.add("action_info", actionInfo); - String responseContent = execute(new SimplePostRequestExecutor(), url, json.toString()); + String responseContent = execute(new JoddPostRequestExecutor(), url, json.toString()); return WxMpQrCodeTicket.fromJson(responseContent); } @@ -497,7 +491,7 @@ public class WxMpServiceImpl implements WxMpService { scene.addProperty("scene_id", scene_id); actionInfo.add("scene", scene); json.add("action_info", actionInfo); - String responseContent = execute(new SimplePostRequestExecutor(), url, json.toString()); + String responseContent = execute(new JoddPostRequestExecutor(), url, json.toString()); return WxMpQrCodeTicket.fromJson(responseContent); } @@ -510,7 +504,7 @@ public class WxMpServiceImpl implements WxMpService { scene.addProperty("scene_str", scene_str); actionInfo.add("scene", scene); json.add("action_info", actionInfo); - String responseContent = execute(new SimplePostRequestExecutor(), url, json.toString()); + String responseContent = execute(new JoddPostRequestExecutor(), url, json.toString()); return WxMpQrCodeTicket.fromJson(responseContent); } @@ -524,14 +518,14 @@ public class WxMpServiceImpl implements WxMpService { JsonObject o = new JsonObject(); o.addProperty("action", "long2short"); o.addProperty("long_url", long_url); - String responseContent = execute(new SimplePostRequestExecutor(), url, o.toString()); + String responseContent = execute(new JoddPostRequestExecutor(), url, o.toString()); JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); return tmpJsonElement.getAsJsonObject().get("short_url").getAsString(); } public String templateSend(WxMpTemplateMessage templateMessage) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/message/template/send"; - String responseContent = execute(new SimplePostRequestExecutor(), url, templateMessage.toJson()); + String responseContent = execute(new JoddPostRequestExecutor(), url, templateMessage.toJson()); JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); final JsonObject jsonObject = tmpJsonElement.getAsJsonObject(); if (jsonObject.get("errcode").getAsInt() == 0) @@ -541,7 +535,7 @@ public class WxMpServiceImpl implements WxMpService { public WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException { String url = "https://api.weixin.qq.com/semantic/semproxy/search"; - String responseContent = execute(new SimplePostRequestExecutor(), url, semanticQuery.toJson()); + String responseContent = execute(new JoddPostRequestExecutor(), url, semanticQuery.toJson()); return WxMpSemanticQueryResult.fromJson(responseContent); } @@ -573,7 +567,7 @@ public class WxMpServiceImpl implements WxMpService { url += "&grant_type=authorization_code"; try { - RequestExecutor executor = new SimpleGetRequestExecutor(); + RequestExecutor executor = new JoddGetRequestExecutor(); String responseText = executor.execute(getHttpclient(), httpProxy, url, null); return WxMpOAuth2AccessToken.fromJson(responseText); } catch (ClientProtocolException e) { @@ -591,7 +585,7 @@ public class WxMpServiceImpl implements WxMpService { url += "&refresh_token=" + refreshToken; try { - RequestExecutor executor = new SimpleGetRequestExecutor(); + RequestExecutor executor = new JoddGetRequestExecutor(); String responseText = executor.execute(getHttpclient(), httpProxy, url, null); return WxMpOAuth2AccessToken.fromJson(responseText); } catch (ClientProtocolException e) { @@ -613,7 +607,7 @@ public class WxMpServiceImpl implements WxMpService { } try { - RequestExecutor executor = new SimpleGetRequestExecutor(); + RequestExecutor executor = new JoddGetRequestExecutor(); String responseText = executor.execute(getHttpclient(), httpProxy, url, null); return WxMpUser.fromJson(responseText); } catch (ClientProtocolException e) { @@ -630,7 +624,7 @@ public class WxMpServiceImpl implements WxMpService { url += "&openid=" + oAuth2AccessToken.getOpenId(); try { - RequestExecutor executor = new SimpleGetRequestExecutor(); + RequestExecutor executor = new JoddGetRequestExecutor(); executor.execute(getHttpclient(), httpProxy, url, null); } catch (ClientProtocolException e) { throw new RuntimeException(e); @@ -683,11 +677,11 @@ public class WxMpServiceImpl implements WxMpService { } public String get(String url, String queryParam) throws WxErrorException { - return execute(new SimpleGetRequestExecutor(), url, queryParam); + return execute(new JoddGetRequestExecutor(), url, queryParam); } public String post(String url, String postData) throws WxErrorException { - return execute(new SimplePostRequestExecutor(), url, postData); + return execute(new JoddPostRequestExecutor(), url, postData); } /** From c8ed7738ab29d82f06bf6b6e1720324364cdc0dd Mon Sep 17 00:00:00 2001 From: Daniel Qian Date: Mon, 11 Jan 2016 09:01:48 +0800 Subject: [PATCH 06/22] issue #172 jodd HttpResponse add charset("UTF-8") --- .../chanjar/weixin/common/util/http/JoddGetRequestExecutor.java | 1 + .../chanjar/weixin/common/util/http/JoddPostRequestExecutor.java | 1 + 2 files changed, 2 insertions(+) diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddGetRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddGetRequestExecutor.java index aa6fa903..82e28f7f 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddGetRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddGetRequestExecutor.java @@ -43,6 +43,7 @@ public class JoddGetRequestExecutor implements RequestExecutor { request.charset("UTF-8"); HttpResponse response = request.open(provider).send(); + response.charset("UTF-8"); String result = response.bodyText(); WxError error = WxError.fromJson(result); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddPostRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddPostRequestExecutor.java index 690a144d..d054835b 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddPostRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddPostRequestExecutor.java @@ -37,6 +37,7 @@ public class JoddPostRequestExecutor implements RequestExecutor request.bodyText(postEntity); HttpResponse response = request.open(provider).send(); + response.charset("UTF-8"); String result = response.bodyText(); WxError error = WxError.fromJson(result); From 89e092b04ded5cb2c26d48704e81d5b19aa09fa7 Mon Sep 17 00:00:00 2001 From: YuJian Date: Sat, 31 Oct 2015 17:52:50 +0800 Subject: [PATCH 07/22] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=85=B3=E4=BA=8E?= =?UTF-8?q?=E5=8D=A1=E5=88=B8=E4=BA=8B=E4=BB=B6=E6=8E=A8=E9=80=81=E7=9A=84?= =?UTF-8?q?=E5=B8=B8=E9=87=8F=E3=80=81XML=E6=B6=88=E6=81=AF=E5=AD=97?= =?UTF-8?q?=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chanjar/weixin/common/api/WxConsts.java | 11 +++- .../weixin/mp/bean/WxMpXmlMessage.java | 66 +++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java index 0455e6c2..85efcd3f 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java @@ -47,7 +47,7 @@ public class WxConsts { /////////////////////// public static final String MASS_ST_SUCCESS = "send success"; public static final String MASS_ST_FAIL = "send fail"; - public static final String MASS_ST_涉嫌广告 = "err(10001)"; + public static final String MASS_ST_涉嫌广告 = "err(10001)"; public static final String MASS_ST_涉嫌政治 = "err(20001)"; public static final String MASS_ST_涉嫌社会 = "err(20004)"; public static final String MASS_ST_涉嫌色情 = "err(20002)"; @@ -93,6 +93,15 @@ public class WxConsts { public static final String EVT_LOCATION_SELECT = "location_select"; public static final String EVT_TEMPLATESENDJOBFINISH = "TEMPLATESENDJOBFINISH"; public static final String EVT_ENTER_AGENT = "enter_agent"; + public static final String EVT_CARD_PASS_CHECK = "card_pass_check"; + public static final String EVT_CARD_NOT_PASS_CHECK = "card_not_pass_check"; + public static final String EVT_USER_GET_CARD = "user_get_card"; + public static final String EVT_USER_DEL_CARD = "user_del_card"; + public static final String EVT_USER_CONSUME_CARD = "user_consume_card"; + public static final String EVT_USER_PAY_FROM_PAY_CELL = "user_pay_from_pay_cell"; + public static final String EVT_USER_VIEW_CARD = "user_view_card"; + public static final String EVT_USER_ENTER_SESSION_FROM_CARD = "user_enter_session_from_card"; + public static final String EVT_CARD_SKU_REMIND = "card_sku_remind"; // 库存报警 /////////////////////// // 上传多媒体文件的类型 diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlMessage.java index 8796b110..9c109d24 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlMessage.java @@ -150,6 +150,27 @@ public class WxMpXmlMessage implements Serializable { @XStreamAlias("ErrorCount") private Integer errorCount; + /////////////////////////////////////// + // 卡券相关事件推送 + /////////////////////////////////////// + @XStreamAlias("CardId") + @XStreamConverter(value=XStreamCDataConverter.class) + private String cardId; + + @XStreamAlias("IsGiveByFriend") + private Integer isGiveByFriend; // 是否为转赠,1代表是,0代表否 + + @XStreamAlias("UserCardCode") + @XStreamConverter(value=XStreamCDataConverter.class) + private String userCardCode; + + @XStreamAlias("OldUserCardCode") + @XStreamConverter(value=XStreamCDataConverter.class) + private String oldUserCardCode; + + @XStreamAlias("OuterId") + private Integer outerId; + @XStreamAlias("ScanCodeInfo") private ScanCodeInfo scanCodeInfo = new ScanCodeInfo(); @@ -456,6 +477,46 @@ public class WxMpXmlMessage implements Serializable { this.errorCount = errorCount; } + public String getCardId() { + return cardId; + } + + public void setCardId(String cardId) { + this.cardId = cardId; + } + + public Integer getIsGiveByFriend() { + return isGiveByFriend; + } + + public void setIsGiveByFriend(Integer isGiveByFriend) { + this.isGiveByFriend = isGiveByFriend; + } + + public String getUserCardCode() { + return userCardCode; + } + + public void setUserCardCode(String userCardCode) { + this.userCardCode = userCardCode; + } + + public String getOldUserCardCode() { + return oldUserCardCode; + } + + public void setOldUserCardCode(String oldUserCardCode) { + this.oldUserCardCode = oldUserCardCode; + } + + public Integer getOuterId() { + return outerId; + } + + public void setOuterId(Integer outerId) { + this.outerId = outerId; + } + public WxMpXmlMessage.ScanCodeInfo getScanCodeInfo() { return scanCodeInfo; } @@ -652,6 +713,11 @@ public class WxMpXmlMessage implements Serializable { ", filterCount=" + filterCount + ", sentCount=" + sentCount + ", errorCount=" + errorCount + + ", cardId='" + cardId + '\'' + + ", isGiveByFriend=" + isGiveByFriend + + ", userCardCode='" + userCardCode + '\'' + + ", oldUserCardCode='" + oldUserCardCode + '\'' + + ", outerId=" + outerId + ", scanCodeInfo=" + scanCodeInfo + ", sendPicsInfo=" + sendPicsInfo + ", sendLocationInfo=" + sendLocationInfo + From 65c532e45b73e907fa6e6b2d7852a5ddfebe8cd7 Mon Sep 17 00:00:00 2001 From: YuJian Date: Sat, 7 Nov 2015 20:26:01 +0800 Subject: [PATCH 08/22] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8D=A1=E5=88=B8?= =?UTF-8?q?=E7=9A=84FriendUserName=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../me/chanjar/weixin/mp/bean/WxMpXmlMessage.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlMessage.java index 9c109d24..9f42ffc7 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlMessage.java @@ -157,6 +157,10 @@ public class WxMpXmlMessage implements Serializable { @XStreamConverter(value=XStreamCDataConverter.class) private String cardId; + @XStreamAlias("FriendUserName") + @XStreamConverter(value=XStreamCDataConverter.class) + private String friendUserName; + @XStreamAlias("IsGiveByFriend") private Integer isGiveByFriend; // 是否为转赠,1代表是,0代表否 @@ -485,6 +489,14 @@ public class WxMpXmlMessage implements Serializable { this.cardId = cardId; } + public String getFriendUserName() { + return friendUserName; + } + + public void setFriendUserName(String friendUserName) { + this.friendUserName = friendUserName; + } + public Integer getIsGiveByFriend() { return isGiveByFriend; } From 980ad0b7bf312707a75f1361feb8c4acdddf6265 Mon Sep 17 00:00:00 2001 From: YuJian Date: Mon, 11 Jan 2016 20:04:27 +0800 Subject: [PATCH 09/22] =?UTF-8?q?WxMpInMemoryConfigStorage=E6=96=B0?= =?UTF-8?q?=E5=A2=9EcardApiTicket=E7=9A=84=E5=AD=98=E5=82=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weixin/mp/api/WxMpConfigStorage.java | 15 +++++++++++ .../mp/api/WxMpInMemoryConfigStorage.java | 26 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java index 09d90d64..14cc03bf 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java @@ -50,6 +50,21 @@ public interface WxMpConfigStorage { */ public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); + public String getCardApiTicket(); + + public boolean isCardApiTicketExpired(); + + /** + * 强制将卡券api ticket过期掉 + */ + public void expireCardApiTicket(); + + /** + * 应该是线程安全的 + * @param cardApiTicket + */ + public void updateCardApiTicket(String cardApiTicket, int expiresInSeconds); + public String getAppId(); public String getSecret(); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java index e0c7aaa3..9eacbe01 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java @@ -32,6 +32,9 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { protected volatile String jsapiTicket; protected volatile long jsapiTicketExpiresTime; + protected volatile String cardApiTicket; + protected volatile long cardApiTicketExpiresTime; + /** * 临时文件目录 */ @@ -90,6 +93,27 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { this.jsapiTicketExpiresTime = 0; } + /** + * 卡券api_ticket + */ + public String getCardApiTicket() { + return cardApiTicket; + } + + public boolean isCardApiTicketExpired() { + return System.currentTimeMillis() > this.cardApiTicketExpiresTime; + } + + public synchronized void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) { + this.cardApiTicket = cardApiTicket; + // 预留200秒的时间 + this.cardApiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; + } + + public void expireCardApiTicket() { + this.cardApiTicketExpiresTime = 0; + } + public String getAppId() { return this.appId; } @@ -192,6 +216,8 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { ", http_proxy_password='" + http_proxy_password + '\'' + ", jsapiTicket='" + jsapiTicket + '\'' + ", jsapiTicketExpiresTime='" + jsapiTicketExpiresTime + '\'' + + ", cardApiTicket='" + cardApiTicket + '\'' + + ", cardApiTicketExpiresTime='" + cardApiTicketExpiresTime + '\'' + ", tmpDirFile='" + tmpDirFile + '\'' + '}'; } From 779e36b1fae6e9d6465abdb7cc8a22d303f65068 Mon Sep 17 00:00:00 2001 From: YuJian Date: Mon, 11 Jan 2016 20:53:06 +0800 Subject: [PATCH 10/22] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8D=A1=E5=88=B8?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A36=E4=B8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. getCardApiTicket获得卡券的api_ticket 2. createCardApiSignature创建调用卡券api时的签名 3. decryptCardCode卡券code解码 4. queryCardCode卡券code查询 5. consumeCardCode卡券核销 6. markCardCode卡券mark接口 --- .../common/bean/WxCardApiSignature.java | 102 ++++++++++ .../me/chanjar/weixin/mp/api/WxMpService.java | 80 +++++++- .../weixin/mp/api/WxMpServiceImpl.java | 177 ++++++++++++++++++ .../me/chanjar/weixin/mp/bean/WxMpCard.java | 72 +++++++ .../weixin/mp/bean/result/WxMpCardResult.java | 87 +++++++++ .../mp/util/json/WxMpCardGsonAdapter.java | 38 ++++ .../util/json/WxMpCardResultGsonAdapter.java | 45 +++++ .../weixin/mp/util/json/WxMpGsonBuilder.java | 2 + 8 files changed, 602 insertions(+), 1 deletion(-) create mode 100644 weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxCardApiSignature.java create mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpCard.java create mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCardResult.java create mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardGsonAdapter.java create mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardResultGsonAdapter.java diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxCardApiSignature.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxCardApiSignature.java new file mode 100644 index 00000000..5d4d46a9 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxCardApiSignature.java @@ -0,0 +1,102 @@ +package me.chanjar.weixin.common.bean; + +import java.io.Serializable; + +/** + * 卡券Api签名 + * + * @author YuJian + * @version 15/11/8 + */ +public class WxCardApiSignature implements Serializable { + + private String appId; + + private String cardId; + + private String cardType; + + private String locationId; + + private String code; + + private String openId; + + private Long timestamp; + + private String nonceStr; + + private String signature; + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getCardId() { + return cardId; + } + + public void setCardId(String cardId) { + this.cardId = cardId; + } + + public String getCardType() { + return cardType; + } + + public void setCardType(String cardType) { + this.cardType = cardType; + } + + public String getLocationId() { + return locationId; + } + + public void setLocationId(String locationId) { + this.locationId = locationId; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getOpenId() { + return openId; + } + + public void setOpenId(String openId) { + this.openId = openId; + } + + public Long getTimestamp() { + return timestamp; + } + + public void setTimestamp(Long timestamp) { + this.timestamp = timestamp; + } + + public String getNonceStr() { + return nonceStr; + } + + public void setNonceStr(String nonceStr) { + this.nonceStr = nonceStr; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java index e6ce30cf..d0e2c190 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java @@ -1,5 +1,6 @@ package me.chanjar.weixin.mp.api; +import me.chanjar.weixin.common.bean.WxCardApiSignature; import me.chanjar.weixin.common.bean.WxMenu; import me.chanjar.weixin.common.bean.WxJsapiSignature; import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; @@ -770,7 +771,7 @@ public interface WxMpService { * @return */ public boolean checkJSSDKCallbackDataSignature(Map kvm, String signature); - + /** * 发送微信红包给个人用户 * @param parameters @@ -778,4 +779,81 @@ public interface WxMpService { * @throws WxErrorException */ public WxRedpackResult sendRedpack(Map parameters) throws WxErrorException; + + /** + * 获得卡券api_ticket,不强制刷新卡券api_ticket + * @see #getCardApiTicket(boolean) + * @return 卡券api_ticket + * @throws WxErrorException + */ + public String getCardApiTicket() throws WxErrorException; + + /** + *
+   * 获得卡券api_ticket
+   * 获得时会检查卡券apiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干
+   *
+   * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD.954-.E5.8D.A1.E5.88.B8.E6.89.A9.E5.B1.95.E5.AD.97.E6.AE.B5.E5.8F.8A.E7.AD.BE.E5.90.8D.E7.94.9F.E6.88.90.E7.AE.97.E6.B3.95
+   * 
+ * @param forceRefresh 强制刷新 + * @return 卡券api_ticket + * @throws WxErrorException + */ + public String getCardApiTicket(boolean forceRefresh) throws WxErrorException; + + /** + *
+   * 创建调用卡券api时所需要的签名
+   *
+   * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD
+   * .954-.E5.8D.A1.E5.88.B8.E6.89.A9.E5.B1.95.E5.AD.97.E6.AE.B5.E5.8F.8A.E7.AD.BE.E5.90.8D.E7.94
+   * .9F.E6.88.90.E7.AE.97.E6.B3.95
+   * 
+ * + * @param optionalSignParam 参与签名的参数数组。 + * 可以为下列字段:app_id, card_id, card_type, code, openid, location_id + * @return 卡券Api签名对象 + */ + public WxCardApiSignature createCardApiSignature(String... optionalSignParam) throws + WxErrorException; + + /** + * 卡券Code解码 + * @param encryptCode 加密Code,通过JSSDK的chooseCard接口获得 + * @return 解密后的Code + * @throws WxErrorException + */ + public String decryptCardCode(String encryptCode) throws WxErrorException; + + /** + * 卡券Code查询 + * @param cardId 卡券ID代表一类卡券 + * @param code 单张卡券的唯一标准 + * @param checkConsume 是否校验code核销状态,填入true和false时的code异常状态返回数据不同 + * @return WxMpCardResult对象 + * @throws WxErrorException + */ + public WxMpCardResult queryCardCode(String cardId, String code, boolean checkConsume) + throws WxErrorException; + + /** + * 卡券Code核销。核销失败会抛出异常 + * @param code 单张卡券的唯一标准 + * @return + * @throws WxErrorException + */ + public void consumeCardCode(String code) throws WxErrorException; + + /** + * 卡券Mark接口。 + * 开发者在帮助消费者核销卡券之前,必须帮助先将此code(卡券串码)与一个openid绑定(即mark住), + * 才能进一步调用核销接口,否则报错。 + * @param code 卡券的code码 + * @param cardId 卡券的ID + * @param openId 用券用户的openid + * @param isMark 是否要mark(占用)这个code,填写true或者false,表示占用或解除占用 + * @throws WxErrorException + */ + public void markCardCode(String code, String cardId, String openId, boolean isMark) throws + WxErrorException; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java index 7a5b7ace..aefd94b6 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.security.NoSuchAlgorithmException; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -14,8 +15,10 @@ import java.util.SortedMap; import java.util.TreeMap; import java.util.UUID; +import com.google.gson.JsonPrimitive; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.bean.WxCardApiSignature; import me.chanjar.weixin.common.bean.WxJsapiSignature; import me.chanjar.weixin.common.bean.WxMenu; import me.chanjar.weixin.common.bean.result.WxError; @@ -43,6 +46,7 @@ import me.chanjar.weixin.mp.bean.WxMpMaterialArticleUpdate; import me.chanjar.weixin.mp.bean.WxMpMaterialNews; import me.chanjar.weixin.mp.bean.WxMpSemanticQuery; import me.chanjar.weixin.mp.bean.WxMpTemplateMessage; +import me.chanjar.weixin.mp.bean.result.WxMpCardResult; import me.chanjar.weixin.mp.bean.result.WxMpMassSendResult; import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult; import me.chanjar.weixin.mp.bean.result.WxMpMaterialCountResult; @@ -112,6 +116,11 @@ public class WxMpServiceImpl implements WxMpService { */ protected final Object globalJsapiTicketRefreshLock = new Object(); + /** + * 全局的是否正在刷新卡券api_ticket的锁 + */ + protected final Object globalCardApiTicketRefreshLock = new Object(); + protected WxMpConfigStorage wxMpConfigStorage; protected CloseableHttpClient httpClient; @@ -1006,4 +1015,172 @@ public class WxMpServiceImpl implements WxMpService { throw new WxErrorException(error); } } + + /** + * 获得卡券api_ticket,不强制刷新卡券api_ticket + * + * @return 卡券api_ticket + * @throws WxErrorException + * @see #getCardApiTicket(boolean) + */ + @Override + public String getCardApiTicket() throws WxErrorException { + return getCardApiTicket(false); + } + + /** + *
+   * 获得卡券api_ticket
+   * 获得时会检查卡券apiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干
+   *
+   * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD
+   * .954-.E5.8D.A1.E5.88.B8.E6.89.A9.E5.B1.95.E5.AD.97.E6.AE.B5.E5.8F.8A.E7.AD.BE.E5.90.8D.E7.94
+   * .9F.E6.88.90.E7.AE.97.E6.B3.95
+   * 
+ * + * @param forceRefresh 强制刷新 + * @return 卡券api_ticket + * @throws WxErrorException + */ + @Override + public String getCardApiTicket(boolean forceRefresh) throws WxErrorException { + if (forceRefresh) { + wxMpConfigStorage.expireCardApiTicket(); + } + if (wxMpConfigStorage.isCardApiTicketExpired()) { + synchronized (globalCardApiTicketRefreshLock) { + if (wxMpConfigStorage.isCardApiTicketExpired()) { + String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card"; + String responseContent = execute(new SimpleGetRequestExecutor(), url, null); + JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); + JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); + String cardApiTicket = tmpJsonObject.get("ticket").getAsString(); + int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); + wxMpConfigStorage.updateCardApiTicket(cardApiTicket, expiresInSeconds); + } + } + } + return wxMpConfigStorage.getCardApiTicket(); + } + + /** + *
+   * 创建调用卡券api时所需要的签名
+   *
+   * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD
+   * .954-.E5.8D.A1.E5.88.B8.E6.89.A9.E5.B1.95.E5.AD.97.E6.AE.B5.E5.8F.8A.E7.AD.BE.E5.90.8D.E7.94
+   * .9F.E6.88.90.E7.AE.97.E6.B3.95
+   * 
+ * + * @param optionalSignParam 参与签名的参数数组。 + * 可以为下列字段:app_id, card_id, card_type, code, openid, location_id + * @return 卡券Api签名对象 + */ + @Override + public WxCardApiSignature createCardApiSignature(String... optionalSignParam) throws + WxErrorException { + long timestamp = System.currentTimeMillis() / 1000; + String nonceStr = RandomUtils.getRandomStr(); + String cardApiTicket = getCardApiTicket(false); + + String[] signParam = Arrays.copyOf(optionalSignParam, optionalSignParam.length + 3); + signParam[optionalSignParam.length] = String.valueOf(timestamp); + signParam[optionalSignParam.length + 1] = nonceStr; + signParam[optionalSignParam.length + 2] = cardApiTicket; + try { + String signature = SHA1.gen(signParam); + WxCardApiSignature cardApiSignature = new WxCardApiSignature(); + cardApiSignature.setTimestamp(timestamp); + cardApiSignature.setNonceStr(nonceStr); + cardApiSignature.setSignature(signature); + return cardApiSignature; + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + /** + * 卡券Code解码 + * + * @param encryptCode 加密Code,通过JSSDK的chooseCard接口获得 + * @return 解密后的Code + * @throws WxErrorException + */ + @Override + public String decryptCardCode(String encryptCode) throws WxErrorException { + String url = "https://api.weixin.qq.com/card/code/decrypt"; + JsonObject param = new JsonObject(); + param.addProperty("encrypt_code", encryptCode); + String responseContent = post(url, param.toString()); + JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); + JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); + JsonPrimitive jsonPrimitive = tmpJsonObject.getAsJsonPrimitive("code"); + return jsonPrimitive.getAsString(); + } + + /** + * 卡券Code查询 + * + * @param cardId 卡券ID代表一类卡券 + * @param code 单张卡券的唯一标准 + * @param checkConsume 是否校验code核销状态,填入true和false时的code异常状态返回数据不同 + * @return WxMpCardResult对象 + * @throws WxErrorException + */ + @Override + public WxMpCardResult queryCardCode(String cardId, String code, boolean checkConsume) throws WxErrorException { + String url = "https://api.weixin.qq.com/card/code/get"; + JsonObject param = new JsonObject(); + param.addProperty("card_id", cardId); + param.addProperty("code", code); + param.addProperty("check_consume", checkConsume); + String responseContent = post(url, param.toString()); + JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); + return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement, + new TypeToken() { + }.getType()); + } + + /** + * 卡券Code核销。核销失败会抛出异常 + * + * @param code 单张卡券的唯一标准 + * @throws WxErrorException + */ + @Override + public void consumeCardCode(String code) throws WxErrorException { + String url = "https://api.weixin.qq.com/card/code/consume"; + JsonObject param = new JsonObject(); + param.addProperty("code", code); + post(url, param.toString()); + } + + /** + * 卡券Mark接口。 + * 开发者在帮助消费者核销卡券之前,必须帮助先将此code(卡券串码)与一个openid绑定(即mark住), + * 才能进一步调用核销接口,否则报错。 + * + * @param code 卡券的code码 + * @param cardId 卡券的ID + * @param openId 用券用户的openid + * @param isMark 是否要mark(占用)这个code,填写true或者false,表示占用或解除占用 + * @throws WxErrorException + */ + @Override + public void markCardCode(String code, String cardId, String openId, boolean isMark) throws + WxErrorException { + String url = "https://api.weixin.qq.com/card/code/mark"; + JsonObject param = new JsonObject(); + param.addProperty("code", code); + param.addProperty("card_id", cardId); + param.addProperty("openid", openId); + param.addProperty("is_mark", isMark); + String responseContent = post(url, param.toString()); + JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); + WxMpCardResult cardResult = WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement, + new TypeToken() { }.getType()); + if (!cardResult.getErrorCode().equals("0")) { + log.warn("朋友的券mark失败:{}", cardResult.getErrorMsg()); + } + } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpCard.java new file mode 100644 index 00000000..d530fbe8 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpCard.java @@ -0,0 +1,72 @@ +package me.chanjar.weixin.mp.bean; + + +/** + * 微信卡券 + * + * @author YuJian + * @version 15/11/11 + */ +public class WxMpCard { + + private String cardId; + + private Long beginTime; + + private Long endTime; + + private String userCardStatus; + + private Boolean canConsume; + + public String getCardId() { + return cardId; + } + + public void setCardId(String cardId) { + this.cardId = cardId; + } + + public Long getBeginTime() { + return beginTime; + } + + public void setBeginTime(Long beginTime) { + this.beginTime = beginTime; + } + + public Long getEndTime() { + return endTime; + } + + public void setEndTime(Long endTime) { + this.endTime = endTime; + } + + public String getUserCardStatus() { + return userCardStatus; + } + + public void setUserCardStatus(String userCardStatus) { + this.userCardStatus = userCardStatus; + } + + public Boolean getCanConsume() { + return canConsume; + } + + public void setCanConsume(Boolean canConsume) { + this.canConsume = canConsume; + } + + @Override + public String toString() { + return "WxMpCard{" + + "cardId='" + cardId + '\'' + + ", beginTime=" + beginTime + + ", endTime=" + endTime + + ", userCardStatus='" + userCardStatus + '\'' + + ", canConsume=" + canConsume + + '}'; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCardResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCardResult.java new file mode 100644 index 00000000..e6cd9a5f --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCardResult.java @@ -0,0 +1,87 @@ +package me.chanjar.weixin.mp.bean.result; + +import me.chanjar.weixin.mp.bean.WxMpCard; + +import java.io.Serializable; + +/** + * 卡券查询Code,核销Code接口返回结果 + * + * @author YuJian + * @version 15/11/11 + */ +public class WxMpCardResult implements Serializable { + + private String errorCode; + + private String errorMsg; + + private String openId; + + private WxMpCard card; + + private String userCardStatus; + + private Boolean canConsume; + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + public String getOpenId() { + return openId; + } + + public void setOpenId(String openId) { + this.openId = openId; + } + + public WxMpCard getCard() { + return card; + } + + public void setCard(WxMpCard card) { + this.card = card; + } + + @Override + public String toString() { + return "WxMpCardResult{" + + "errorCode='" + errorCode + '\'' + + ", errorMsg='" + errorMsg + '\'' + + ", openId='" + openId + '\'' + + ", card=" + card + + ", userCardStatus='" + userCardStatus + '\'' + + ", canConsume=" + canConsume + + '}'; + } + + public String getUserCardStatus() { + return userCardStatus; + } + + public void setUserCardStatus(String userCardStatus) { + this.userCardStatus = userCardStatus; + } + + public Boolean getCanConsume() { + return canConsume; + } + + public void setCanConsume(Boolean canConsume) { + this.canConsume = canConsume; + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardGsonAdapter.java new file mode 100644 index 00000000..dab414a3 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardGsonAdapter.java @@ -0,0 +1,38 @@ +package me.chanjar.weixin.mp.util.json; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.mp.bean.WxMpCard; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Type; +import java.util.List; + +/** + * Created by YuJian on 15/11/11. + * + * @author YuJian + * @version 15/11/11 + */ +public class WxMpCardGsonAdapter implements JsonDeserializer { + + @Override + public WxMpCard deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext + jsonDeserializationContext) throws JsonParseException { + WxMpCard card = new WxMpCard(); + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + card.setCardId(GsonHelper.getString(jsonObject, "card_id")); + card.setBeginTime(GsonHelper.getLong(jsonObject, "begin_time")); + card.setEndTime(GsonHelper.getLong(jsonObject, "end_time")); + + return card; + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardResultGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardResultGsonAdapter.java new file mode 100644 index 00000000..be1a12da --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardResultGsonAdapter.java @@ -0,0 +1,45 @@ +package me.chanjar.weixin.mp.util.json; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.mp.bean.WxMpCard; +import me.chanjar.weixin.mp.bean.result.WxMpCardResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Type; +import java.text.ParseException; +import java.util.List; + +/** + * Created by YuJian on 15/11/11. + * + * @author YuJian + * @version 15/11/11 + */ +public class WxMpCardResultGsonAdapter implements JsonDeserializer { + @Override + public WxMpCardResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + WxMpCardResult cardResult = new WxMpCardResult(); + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + cardResult.setOpenId(GsonHelper.getString(jsonObject, "openid")); + cardResult.setErrorCode(GsonHelper.getString(jsonObject, "errcode")); + cardResult.setErrorMsg(GsonHelper.getString(jsonObject, "errmsg")); + cardResult.setCanConsume(GsonHelper.getBoolean(jsonObject, "can_consume")); + cardResult.setUserCardStatus(GsonHelper.getString(jsonObject, "user_card_status")); + + WxMpCard card = WxMpGsonBuilder.INSTANCE.create().fromJson(jsonObject.get("card"), + new TypeToken() { + }.getType()); + + cardResult.setCard(card); + + return cardResult; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java index 20f8d9d4..316f22f5 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java @@ -38,6 +38,8 @@ public class WxMpGsonBuilder { INSTANCE.registerTypeAdapter(WxMpMaterialNewsBatchGetResult.WxMaterialNewsBatchGetNewsItem.class, new WxMpMaterialNewsBatchGetGsonItemAdapter()); INSTANCE.registerTypeAdapter(WxMpMaterialFileBatchGetResult.class, new WxMpMaterialFileBatchGetGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpMaterialFileBatchGetResult.WxMaterialFileBatchGetNewsItem.class, new WxMpMaterialFileBatchGetGsonItemAdapter()); + INSTANCE.registerTypeAdapter(WxMpCardResult.class, new WxMpCardResultGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMpCard.class, new WxMpCardGsonAdapter()); } public static Gson create() { From 54ced62f302c2c3af3c9d7b295428fc7da409487 Mon Sep 17 00:00:00 2001 From: YuJian Date: Mon, 11 Jan 2016 21:17:46 +0800 Subject: [PATCH 11/22] =?UTF-8?q?=E6=8D=A2=E7=94=A8JoddGetRequestExecutor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 解决NoHttpResoponseException问题 --- .../src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java index aefd94b6..25f86e56 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java @@ -1051,7 +1051,7 @@ public class WxMpServiceImpl implements WxMpService { synchronized (globalCardApiTicketRefreshLock) { if (wxMpConfigStorage.isCardApiTicketExpired()) { String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card"; - String responseContent = execute(new SimpleGetRequestExecutor(), url, null); + String responseContent = execute(new JoddGetRequestExecutor(), url, null); JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); String cardApiTicket = tmpJsonObject.get("ticket").getAsString(); From 852017f7b4c15260f16096e6974d2fe9fc5ac3f8 Mon Sep 17 00:00:00 2001 From: Liu Kai Date: Fri, 29 Jan 2016 17:30:14 +0800 Subject: [PATCH 12/22] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E9=80=80=E6=AC=BE=E6=8E=A5=E5=8F=A3=E4=BB=A5=E5=8F=8A=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E5=87=A0=E4=B8=AA=E5=8D=A1=E5=88=B8=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E7=9A=84=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 增加微信退款接口 2. 增加取得卡券详情接口 3. 修改取卡券签名接口的说明 4. 将卡券核销接口的返回值由Void改为String Change-Id: I2bac2d090871a4988f7279a9794eca5722451cdd Signed-off-by: Liu Kai --- .../me/chanjar/weixin/mp/api/WxMpService.java | 36 ++- .../weixin/mp/api/WxMpServiceImpl.java | 83 +++++- .../mp/bean/result/WxMpPayRefundResult.java | 278 ++++++++++++++++++ 3 files changed, 391 insertions(+), 6 deletions(-) create mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpPayRefundResult.java diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java index d0e2c190..b28ce775 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java @@ -761,6 +761,20 @@ public interface WxMpService { */ WxMpPayCallback getJSSDKCallbackData(String xmlData); + /** + * 微信支付-申请退款 + * 详见 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4 + * @param parameters 需要传入的退款参数的Map。以下几项为参数的必须项:
+ *
  • transaction_id + *
  • out_trade_no (仅在上述transaction_id为空时是必须项) + *
  • out_refund_no + *
  • total_fee + *
  • refund_fee + * @return 退款操作结果 + * @throws WxErrorException + */ + public WxMpPayRefundResult refundPay(Map parameters) throws WxErrorException; + /** *
          * 计算Map键值对是否和签名相符,
    @@ -812,6 +826,7 @@ public interface WxMpService {
        *
        * @param optionalSignParam 参与签名的参数数组。
        *                  可以为下列字段:app_id, card_id, card_type, code, openid, location_id
    +   *                  
    注意:当做wx.chooseCard调用时,必须传入app_id参与签名,否则会造成签名失败导致拉取卡券列表为空 * @return 卡券Api签名对象 */ public WxCardApiSignature createCardApiSignature(String... optionalSignParam) throws @@ -839,10 +854,12 @@ public interface WxMpService { /** * 卡券Code核销。核销失败会抛出异常 * @param code 单张卡券的唯一标准 - * @return - * @throws WxErrorException - */ - public void consumeCardCode(String code) throws WxErrorException; + * @param cardId 当自定义Code卡券时需要传入card_id + * @return 调用返回的JSON字符串。 + *
    可用 com.google.gson.JsonParser#parse 等方法直接取JSON串中的errcode等信息。 + * @throws WxErrorException + */ + public String consumeCardCode(String code, String cardId) throws WxErrorException; /** * 卡券Mark接口。 @@ -856,4 +873,15 @@ public interface WxMpService { */ public void markCardCode(String code, String cardId, String openId, boolean isMark) throws WxErrorException; + + /** + * 查看卡券详情接口 + * 详见 https://mp.weixin.qq.com/wiki/14/8dd77aeaee85f922db5f8aa6386d385e.html#.E6.9F.A5.E7.9C.8B.E5.8D.A1.E5.88.B8.E8.AF.A6.E6.83.85 + * @param cardId 卡券的ID + * @return 返回的卡券详情JSON字符串 + *
    [注] 由于返回的JSON格式过于复杂,难以定义其对应格式的Bean并且难以维护,因此只返回String格式的JSON串。 + *
    可由 com.google.gson.JsonParser#parse 等方法直接取JSON串中的某个字段。 + * @throws WxErrorException + */ + public String getCardDetail(String cardId) throws WxErrorException; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java index 25f86e56..e9f6ee0a 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java @@ -56,6 +56,7 @@ import me.chanjar.weixin.mp.bean.result.WxMpMaterialUploadResult; import me.chanjar.weixin.mp.bean.result.WxMpMaterialVideoInfoResult; import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; import me.chanjar.weixin.mp.bean.result.WxMpPayCallback; +import me.chanjar.weixin.mp.bean.result.WxMpPayRefundResult; import me.chanjar.weixin.mp.bean.result.WxMpPayResult; import me.chanjar.weixin.mp.bean.result.WxMpPrepayIdResult; import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; @@ -97,6 +98,7 @@ import org.slf4j.helpers.MessageFormatter; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import com.google.gson.internal.Streams; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; @@ -971,6 +973,54 @@ public class WxMpServiceImpl implements WxMpService { return new WxMpPayCallback(); } + @Override + public WxMpPayRefundResult refundPay(Map parameters) throws WxErrorException { + SortedMap refundParams = new TreeMap(parameters); + refundParams.put("appid", wxMpConfigStorage.getAppId()); + refundParams.put("mch_id", wxMpConfigStorage.getPartnerId()); + refundParams.put("nonceStr", System.currentTimeMillis() + ""); + refundParams.put("op_user_id", wxMpConfigStorage.getPartnerId()); + String sign = WxCryptUtil.createSign(refundParams, wxMpConfigStorage.getPartnerKey()); + refundParams.put("sign", sign); + + StringBuilder request = new StringBuilder(""); + for (Entry para : refundParams.entrySet()) { + request.append(String.format("<%s>%s", para.getKey(), para.getValue(), para.getKey())); + } + request.append(""); + + HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund"); + if (httpProxy != null) { + RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); + httpPost.setConfig(config); + } + + StringEntity entity = new StringEntity(request.toString(), Consts.UTF_8); + httpPost.setEntity(entity); + try( + CloseableHttpResponse response = getHttpclient().execute(httpPost)) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + XStream xstream = XStreamInitializer.getInstance(); + xstream.processAnnotations(WxRedpackResult.class); + WxMpPayRefundResult wxMpPayRefundResult = (WxMpPayRefundResult) xstream.fromXML(responseContent); + + if ("FAIL".equals(wxMpPayRefundResult.getResultCode())) { + WxError error = new WxError(); + error.setErrorCode(-1); + error.setErrorMsg(wxMpPayRefundResult.getErrCodeDes()); + throw new WxErrorException(error); + } + + return wxMpPayRefundResult; + } catch (IOException e) { + log.error(MessageFormatter.format("The exception was happened when sending refund '{}'.", request.toString()).getMessage(), e); + WxError error = new WxError(); + error.setErrorCode(-1); + error.setErrorMsg("incorrect response."); + throw new WxErrorException(error); + } + } + @Override public boolean checkJSSDKCallbackDataSignature(Map kvm, String signature) { return signature.equals(WxCryptUtil.createSign(kvm, wxMpConfigStorage.getPartnerKey())); @@ -1074,6 +1124,7 @@ public class WxMpServiceImpl implements WxMpService { * * @param optionalSignParam 参与签名的参数数组。 * 可以为下列字段:app_id, card_id, card_type, code, openid, location_id + *
    注意:当做wx.chooseCard调用时,必须传入app_id参与签名,否则会造成签名失败导致拉取卡券列表为空 * @return 卡券Api签名对象 */ @Override @@ -1148,11 +1199,17 @@ public class WxMpServiceImpl implements WxMpService { * @throws WxErrorException */ @Override - public void consumeCardCode(String code) throws WxErrorException { + public String consumeCardCode(String code, String cardId) throws WxErrorException { String url = "https://api.weixin.qq.com/card/code/consume"; JsonObject param = new JsonObject(); param.addProperty("code", code); - post(url, param.toString()); + + if (cardId != null && !"".equals(cardId)) { + param.addProperty("card_id", cardId); + } + + String responseContent = post(url, param.toString()); + return responseContent; } /** @@ -1183,4 +1240,26 @@ public class WxMpServiceImpl implements WxMpService { log.warn("朋友的券mark失败:{}", cardResult.getErrorMsg()); } } + + @Override + public String getCardDetail(String cardId) throws WxErrorException { + String url = "https://api.weixin.qq.com/card/get"; + JsonObject param = new JsonObject(); + param.addProperty("card_id", cardId); + String responseContent = post(url, param.toString()); + + // 判断返回值 + JsonObject json = (new JsonParser()).parse(responseContent).getAsJsonObject(); + String errcode = json.get("errcode").getAsString(); + if (!"0".equals(errcode)) { + String errmsg = json.get("errmsg").getAsString(); + WxError error = new WxError(); + error.setErrorCode(Integer.valueOf(errcode)); + error.setErrorMsg(errmsg); + throw new WxErrorException(error); + } + + return responseContent; + } + } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpPayRefundResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpPayRefundResult.java new file mode 100644 index 00000000..1e8327e3 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpPayRefundResult.java @@ -0,0 +1,278 @@ +package me.chanjar.weixin.mp.bean.result; + +import java.io.Serializable; + +import com.thoughtworks.xstream.annotations.XStreamAlias; + +/** + * 微信支付-申请退款返回结果 + * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4 + * @author liukaitj + * + */ +@XStreamAlias("xml") +public class WxMpPayRefundResult implements Serializable { + + private static final long serialVersionUID = 1L; + + @XStreamAlias("return_code") + private String returnCode; + + @XStreamAlias("return_msg") + private String returnMsg; + + @XStreamAlias("result_code") + private String resultCode; + + @XStreamAlias("err_code") + private String errCode; + + @XStreamAlias("err_code_des") + private String errCodeDes; + + @XStreamAlias("appid") + private String appid; + + @XStreamAlias("mch_id") + private String mchId; + + @XStreamAlias("device_info") + private String deviceInfo; + + @XStreamAlias("nonce_str") + private String nonceStr; + + @XStreamAlias("sign") + private String sign; + + @XStreamAlias("transaction_id") + private String transactionId; + + @XStreamAlias("out_trade_no") + private String outTradeNo; + + @XStreamAlias("out_refund_no") + private String outRefundNo; + + @XStreamAlias("refund_id") + private String refundId; + + @XStreamAlias("refund_channel") + private String refundChannel; + + @XStreamAlias("refund_fee") + private String refundFee; + + @XStreamAlias("total_fee") + private String totalFee; + + @XStreamAlias("fee_type") + private String feeType; + + @XStreamAlias("cash_fee") + private String cashFee; + + @XStreamAlias("cash_refund_fee") + private String cashRefundfee; + + @XStreamAlias("coupon_refund_fee") + private String couponRefundFee; + + @XStreamAlias("coupon_refund_count") + private String couponRefundCount; + + @XStreamAlias("coupon_refund_id") + private String couponRefundId; + + public String getReturnCode() { + return returnCode; + } + + public void setReturnCode(String returnCode) { + this.returnCode = returnCode; + } + + public String getReturnMsg() { + return returnMsg; + } + + public void setReturnMsg(String returnMsg) { + this.returnMsg = returnMsg; + } + + public String getResultCode() { + return resultCode; + } + + public void setResultCode(String resultCode) { + this.resultCode = resultCode; + } + + public String getErrCode() { + return errCode; + } + + public void setErrCode(String errCode) { + this.errCode = errCode; + } + + public String getErrCodeDes() { + return errCodeDes; + } + + public void setErrCodeDes(String errCodeDes) { + this.errCodeDes = errCodeDes; + } + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } + + public String getMchId() { + return mchId; + } + + public void setMchId(String mchId) { + this.mchId = mchId; + } + + public String getDeviceInfo() { + return deviceInfo; + } + + public void setDeviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + } + + public String getNonceStr() { + return nonceStr; + } + + public void setNonceStr(String nonceStr) { + this.nonceStr = nonceStr; + } + + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } + + 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; + } + + 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 getTotalFee() { + return totalFee; + } + + public void setTotalFee(String totalFee) { + this.totalFee = totalFee; + } + + public String getFeeType() { + return feeType; + } + + public void setFeeType(String feeType) { + this.feeType = feeType; + } + + public String getCashFee() { + return cashFee; + } + + public void setCashFee(String cashFee) { + this.cashFee = cashFee; + } + + public String getCashRefundfee() { + return cashRefundfee; + } + + public void setCashRefundfee(String cashRefundfee) { + this.cashRefundfee = cashRefundfee; + } + + 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 String getCouponRefundId() { + return couponRefundId; + } + + public void setCouponRefundId(String couponRefundId) { + this.couponRefundId = couponRefundId; + } + + @Override + public String toString() { + return "[" + + "return_code:" + returnCode + ";" + + "return_msg" + returnMsg + ";"; + } + +} From 2a6993713aa4be9b8c9813e12735129a4c0e914e Mon Sep 17 00:00:00 2001 From: Liu Kai Date: Sat, 30 Jan 2016 12:21:16 +0800 Subject: [PATCH 13/22] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98JSSDK?= =?UTF-8?q?=E5=8F=8A=E9=80=80=E6=AC=BE=E6=8E=A5=E5=8F=A3=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为解决接口调用端无法取到调用微信服务器的返回结果的问题,微信支付JSSDK及退款接口增加了异常处理。 Change-Id: I2fe5255edb416336b64bef84a4bac5ba51231d67 Signed-off-by: Liu Kai --- .../me/chanjar/weixin/mp/api/WxMpService.java | 6 ++-- .../weixin/mp/api/WxMpServiceImpl.java | 33 +++++++++++++++---- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java index b28ce775..af574c1b 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java @@ -726,8 +726,9 @@ public interface WxMpService { * @param parameters * the required or optional parameters * @return + * @throws WxErrorException */ - Map getJSSDKPayInfo(Map parameters); + Map getJSSDKPayInfo(Map parameters) throws WxErrorException; /** * 该接口调用“统一下单”接口,并拼装JSSDK发起支付请求需要的参数 @@ -740,10 +741,11 @@ public interface WxMpService { * @param ip 发起支付的客户端IP * @param notifyUrl 通知地址 * @return + * @throws WxErrorException * @deprecated Use me.chanjar.weixin.mp.api.WxMpService.getJSSDKPayInfo(Map) instead */ @Deprecated - Map getJSSDKPayInfo(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String notifyUrl); + Map getJSSDKPayInfo(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String notifyUrl) throws WxErrorException; /** * 该接口提供所有微信支付订单的查询,当支付通知处理异常戒丢失的情冴,商户可以通过该接口查询订单支付状态。 diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java index e9f6ee0a..8aaae17b 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java @@ -883,7 +883,8 @@ public class WxMpServiceImpl implements WxMpService { } @Override - public Map getJSSDKPayInfo(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String callbackUrl) { + public Map getJSSDKPayInfo(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String callbackUrl) + throws WxErrorException { Map packageParams = new HashMap(); packageParams.put("appid", wxMpConfigStorage.getAppId()); packageParams.put("mch_id", wxMpConfigStorage.getPartnerId()); @@ -899,8 +900,21 @@ public class WxMpServiceImpl implements WxMpService { } @Override - public Map getJSSDKPayInfo(Map parameters) { + public Map getJSSDKPayInfo(Map parameters) throws WxErrorException { WxMpPrepayIdResult wxMpPrepayIdResult = getPrepayId(parameters); + + if (!"SUCCESS".equalsIgnoreCase(wxMpPrepayIdResult.getReturn_code()) + ||!"SUCCESS".equalsIgnoreCase(wxMpPrepayIdResult.getResult_code())) { + WxError error = new WxError(); + error.setErrorCode(-1); + error.setErrorMsg("return_code:" + wxMpPrepayIdResult.getReturn_code() + + "return_msg:" + wxMpPrepayIdResult.getReturn_msg() + + "result_code:" + wxMpPrepayIdResult.getResult_code() + + "err_code" + wxMpPrepayIdResult.getErr_code() + + "err_code_des" + wxMpPrepayIdResult.getErr_code_des()); + throw new WxErrorException(error); + } + String prepayId = wxMpPrepayIdResult.getPrepay_id(); if (prepayId == null || prepayId.equals("")) { throw new RuntimeException(String.format("Failed to get prepay id due to error code '%s'(%s).", wxMpPrepayIdResult.getErr_code(), wxMpPrepayIdResult.getErr_code_des())); @@ -1004,11 +1018,16 @@ public class WxMpServiceImpl implements WxMpService { xstream.processAnnotations(WxRedpackResult.class); WxMpPayRefundResult wxMpPayRefundResult = (WxMpPayRefundResult) xstream.fromXML(responseContent); - if ("FAIL".equals(wxMpPayRefundResult.getResultCode())) { - WxError error = new WxError(); - error.setErrorCode(-1); - error.setErrorMsg(wxMpPayRefundResult.getErrCodeDes()); - throw new WxErrorException(error); + if (!"SUCCESS".equalsIgnoreCase(wxMpPayRefundResult.getResultCode()) + ||!"SUCCESS".equalsIgnoreCase(wxMpPayRefundResult.getReturnCode())) { + WxError error = new WxError(); + error.setErrorCode(-1); + error.setErrorMsg("return_code:" + wxMpPayRefundResult.getReturnCode() + + "return_msg:" + wxMpPayRefundResult.getReturnMsg() + + "result_code:" + wxMpPayRefundResult.getResultCode() + + "err_code" + wxMpPayRefundResult.getErrCode() + + "err_code_des" + wxMpPayRefundResult.getErrCodeDes()); + throw new WxErrorException(error); } return wxMpPayRefundResult; From 5f75a03ede82a3862aca741c445b14262170633f Mon Sep 17 00:00:00 2001 From: Liu Kai Date: Sat, 30 Jan 2016 15:25:02 +0800 Subject: [PATCH 14/22] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E9=80=80=E6=AC=BE=E6=8E=A5=E5=8F=A3=E7=9A=84=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修复微信退款接口的错误。 2. 格式化微信退款接口及微信支付JSSDK调用接口失败时抛出异常的msg。 Change-Id: Ib3b14965ea29ee518047535a75b81a4972b91271 Signed-off-by: Liu Kai --- .../weixin/mp/api/WxMpServiceImpl.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java index 8aaae17b..99bb8463 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java @@ -908,10 +908,10 @@ public class WxMpServiceImpl implements WxMpService { WxError error = new WxError(); error.setErrorCode(-1); error.setErrorMsg("return_code:" + wxMpPrepayIdResult.getReturn_code() + - "return_msg:" + wxMpPrepayIdResult.getReturn_msg() + - "result_code:" + wxMpPrepayIdResult.getResult_code() + - "err_code" + wxMpPrepayIdResult.getErr_code() + - "err_code_des" + wxMpPrepayIdResult.getErr_code_des()); + ";return_msg:" + wxMpPrepayIdResult.getReturn_msg() + + ";result_code:" + wxMpPrepayIdResult.getResult_code() + + ";err_code" + wxMpPrepayIdResult.getErr_code() + + ";err_code_des" + wxMpPrepayIdResult.getErr_code_des()); throw new WxErrorException(error); } @@ -992,7 +992,7 @@ public class WxMpServiceImpl implements WxMpService { SortedMap refundParams = new TreeMap(parameters); refundParams.put("appid", wxMpConfigStorage.getAppId()); refundParams.put("mch_id", wxMpConfigStorage.getPartnerId()); - refundParams.put("nonceStr", System.currentTimeMillis() + ""); + refundParams.put("nonce_str", System.currentTimeMillis() + ""); refundParams.put("op_user_id", wxMpConfigStorage.getPartnerId()); String sign = WxCryptUtil.createSign(refundParams, wxMpConfigStorage.getPartnerKey()); refundParams.put("sign", sign); @@ -1015,7 +1015,7 @@ public class WxMpServiceImpl implements WxMpService { CloseableHttpResponse response = getHttpclient().execute(httpPost)) { String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); XStream xstream = XStreamInitializer.getInstance(); - xstream.processAnnotations(WxRedpackResult.class); + xstream.processAnnotations(WxMpPayRefundResult.class); WxMpPayRefundResult wxMpPayRefundResult = (WxMpPayRefundResult) xstream.fromXML(responseContent); if (!"SUCCESS".equalsIgnoreCase(wxMpPayRefundResult.getResultCode()) @@ -1023,10 +1023,10 @@ public class WxMpServiceImpl implements WxMpService { WxError error = new WxError(); error.setErrorCode(-1); error.setErrorMsg("return_code:" + wxMpPayRefundResult.getReturnCode() + - "return_msg:" + wxMpPayRefundResult.getReturnMsg() + - "result_code:" + wxMpPayRefundResult.getResultCode() + - "err_code" + wxMpPayRefundResult.getErrCode() + - "err_code_des" + wxMpPayRefundResult.getErrCodeDes()); + ";return_msg:" + wxMpPayRefundResult.getReturnMsg() + + ";result_code:" + wxMpPayRefundResult.getResultCode() + + ";err_code" + wxMpPayRefundResult.getErrCode() + + ";err_code_des" + wxMpPayRefundResult.getErrCodeDes()); throw new WxErrorException(error); } From b805a9967ceb3e77a75a5dea11a1b44b81405611 Mon Sep 17 00:00:00 2001 From: miller Date: Mon, 15 Feb 2016 16:54:54 +0800 Subject: [PATCH 15/22] =?UTF-8?q?1.WxMenuRule=E5=A2=9E=E5=8A=A0language?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=202.=E6=9B=B4=E6=94=B9=E4=BA=86WxMenu?= =?UTF-8?q?=E7=9A=84toJson=E6=96=B9=E6=B3=95=E4=BD=BFmenurule=E8=BD=AC?= =?UTF-8?q?=E6=8D=A2=E6=88=90=E6=AD=A3=E7=A1=AE=E7=9A=84=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2=203.WxMenuTest=E4=B8=AD=E5=A2=9E=E5=8A=A0=E4=BA=86?= =?UTF-8?q?=E7=9B=B8=E5=BA=94=E7=9A=84=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../me/chanjar/weixin/common/bean/WxMenu.java | 21 +++++--- .../common/util/json/WxMenuGsonAdapter.java | 21 +++++--- .../weixin/common/bean/WxMenuTest.java | 49 +++++++++++++++++++ 3 files changed, 79 insertions(+), 12 deletions(-) diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxMenu.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxMenu.java index e20be680..ccc7bc49 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxMenu.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxMenu.java @@ -1,15 +1,14 @@ package me.chanjar.weixin.common.bean; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import org.apache.commons.codec.Charsets; + import java.io.InputStream; import java.io.InputStreamReader; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import org.apache.commons.codec.Charsets; - -import me.chanjar.weixin.common.util.json.WxGsonBuilder; - /** * 企业号菜单 * @author Daniel Qian @@ -136,6 +135,7 @@ public class WxMenu implements Serializable { private String province; private String city; private String clientPlatformType; + private String language; public String getGroupId() { return groupId; @@ -184,8 +184,16 @@ public class WxMenu implements Serializable { public void setClientPlatformType(String clientPlatformType) { this.clientPlatformType = clientPlatformType; } - - @Override + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + @Override public String toString() { return "matchrule:{" + "group_id='" + groupId + '\'' + @@ -194,6 +202,7 @@ public class WxMenu implements Serializable { ", province" + province + '\'' + ", city" + city + '\'' + ", client_platform_type" + clientPlatformType + '\'' + + ", language" + language + '\'' + "}"; } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java index cb3e8c69..d4d9ea5a 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java @@ -8,9 +8,6 @@ */ package me.chanjar.weixin.common.util.json; -import java.lang.reflect.Type; - -import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; @@ -19,9 +16,10 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; - import me.chanjar.weixin.common.bean.WxMenu; +import java.lang.reflect.Type; + /** * * @author Daniel Qian @@ -40,8 +38,7 @@ public class WxMenuGsonAdapter implements JsonSerializer, JsonDeserializ json.add("button", buttonArray); if (menu.getMatchRule() != null) { - Gson gson = new Gson(); - json.add("matchrule", gson.toJsonTree(menu.getMatchRule())); + json.add("matchrule", convertToJson(menu.getMatchRule())); } return json; @@ -63,6 +60,18 @@ public class WxMenuGsonAdapter implements JsonSerializer, JsonDeserializ return buttonJson; } + protected JsonObject convertToJson(WxMenu.WxMenuRule menuRule){ + JsonObject matchRule = new JsonObject(); + matchRule.addProperty("group_id",menuRule.getGroupId()); + matchRule.addProperty("sex",menuRule.getSex()); + matchRule.addProperty("country",menuRule.getCountry()); + matchRule.addProperty("province",menuRule.getProvince()); + matchRule.addProperty("city",menuRule.getCity()); + matchRule.addProperty("client_platform_type",menuRule.getClientPlatformType()); + matchRule.addProperty("language",menuRule.getLanguage()); + return matchRule; + } + public WxMenu deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { /* * 操蛋的微信 diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxMenuTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxMenuTest.java index 015889d7..79d28e47 100644 --- a/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxMenuTest.java +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxMenuTest.java @@ -55,6 +55,29 @@ public class WxMenuTest { Assert.assertEquals(menu.toJson(), json); } + + @Test(dataProvider = "wxAddConditionalMenu") + public void testAddConditionalToJson(String json) { + WxMenu menu = new WxMenu(); + WxMenuButton button1 = new WxMenuButton(); + button1.setType("click"); + button1.setName("今日歌曲"); + button1.setKey("V1001_TODAY_MUSIC"); + + menu.getButtons().add(button1); + + WxMenu.WxMenuRule wxMenuRule = new WxMenu.WxMenuRule(); + wxMenuRule.setGroupId("2"); + wxMenuRule.setSex("1"); + wxMenuRule.setCountry("中国"); + wxMenuRule.setProvince("广东"); + wxMenuRule.setCity("广州"); + wxMenuRule.setClientPlatformType("2"); + wxMenuRule.setLanguage("zh_CN"); + menu.setMatchRule(wxMenuRule); + + Assert.assertEquals(menu.toJson(), json); + } @DataProvider public Object[][] wxReturnMenu() { @@ -106,5 +129,31 @@ public class WxMenuTest { new Object[] { json } }; } + + @DataProvider(name = "wxAddConditionalMenu") + public Object[][] addConditionalMenuJson(){ + String json = + "{" + +"\"button\":[" + +"{" + +"\"type\":\"click\"," + +"\"name\":\"今日歌曲\"," + +"\"key\":\"V1001_TODAY_MUSIC\"" + +"}" + +"]," + +"\"matchrule\":{" + +"\"group_id\":\"2\"," + +"\"sex\":\"1\"," + +"\"country\":\"中国\"," + +"\"province\":\"广东\"," + +"\"city\":\"广州\"," + +"\"client_platform_type\":\"2\"," + +"\"language\":\"zh_CN\"" + +"}" + +"}"; + return new Object[][]{ + new Object[]{json} + }; + } } From c61fcb26c6e3eea10ba93d4d210c569025f71e41 Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 23 Feb 2016 18:12:49 +0800 Subject: [PATCH 16/22] =?UTF-8?q?=E6=9B=BF=E6=8D=A2=E5=9B=9Ehttpclient,?= =?UTF-8?q?=E5=B0=86httpclient=E9=85=8D=E7=BD=AE=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=E5=87=BA=E6=9D=A5=E6=8F=90=E4=BE=9B=E6=9B=B4=E5=A4=9A=E5=8F=AF?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/http/ApacheHttpClientBuilder.java | 51 +++++ .../DefaultApacheHttpHttpClientBuilder.java | 199 ++++++++++++++++++ .../http/MediaDownloadRequestExecutor.java | 2 + .../util/http/MediaUploadRequestExecutor.java | 13 +- .../util/http/SimpleGetRequestExecutor.java | 11 +- .../util/http/SimplePostRequestExecutor.java | 13 +- .../weixin/cp/api/WxCpConfigStorage.java | 10 +- .../cp/api/WxCpInMemoryConfigStorage.java | 15 +- .../weixin/cp/api/WxCpServiceImpl.java | 91 +++----- .../weixin/mp/api/WxMpConfigStorage.java | 12 +- .../mp/api/WxMpInMemoryConfigStorage.java | 18 +- .../weixin/mp/api/WxMpServiceImpl.java | 191 ++++++----------- .../http/MaterialDeleteRequestExecutor.java | 17 +- .../http/MaterialNewsInfoRequestExecutor.java | 18 +- .../http/MaterialUploadRequestExecutor.java | 17 +- .../MaterialVideoInfoRequestExecutor.java | 17 +- ...lVoiceAndImageDownloadRequestExecutor.java | 29 +-- .../mp/util/http/QrCodeRequestExecutor.java | 5 +- 18 files changed, 472 insertions(+), 257 deletions(-) create mode 100644 weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/ApacheHttpClientBuilder.java create mode 100644 weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/DefaultApacheHttpHttpClientBuilder.java diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/ApacheHttpClientBuilder.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/ApacheHttpClientBuilder.java new file mode 100644 index 00000000..1bd6a98c --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/ApacheHttpClientBuilder.java @@ -0,0 +1,51 @@ +package me.chanjar.weixin.common.util.http; + +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; + +/** + * httpclient build interface + */ +public interface ApacheHttpClientBuilder { + + /** + * 构建httpclient实例 + * @return new instance of CloseableHttpClient + */ + CloseableHttpClient build(); + + /** + * 代理服务器地址 + * @param httpProxyHost + * @return + */ + ApacheHttpClientBuilder httpProxyHost(String httpProxyHost); + + /** + * 代理服务器端口 + * @param httpProxyPort + * @return + */ + ApacheHttpClientBuilder httpProxyPort(int httpProxyPort); + + /** + * 代理服务器用户名 + * @param httpProxyUsername + * @return + */ + ApacheHttpClientBuilder httpProxyUsername(String httpProxyUsername); + + /** + * 代理服务器密码 + * @param httpProxyPassword + * @return + */ + ApacheHttpClientBuilder httpProxyPassword(String httpProxyPassword); + + /** + * ssl连接socket工厂 + * @param sslConnectionSocketFactory + * @return + */ + ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory); +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/DefaultApacheHttpHttpClientBuilder.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/DefaultApacheHttpHttpClientBuilder.java new file mode 100644 index 00000000..aa20fa0b --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/DefaultApacheHttpHttpClientBuilder.java @@ -0,0 +1,199 @@ +package me.chanjar.weixin.common.util.http; + +import me.chanjar.weixin.common.util.StringUtils; +import org.apache.http.annotation.NotThreadSafe; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.HttpRequestRetryHandler; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.config.SocketConfig; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.protocol.HttpContext; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +/** + * httpclient 连接管理器 + */ +@NotThreadSafe +public class DefaultApacheHttpHttpClientBuilder implements ApacheHttpClientBuilder { + private int connectionRequestTimeout = 3000; + private int connectionTimeout = 5000; + private int soTimeout = 5000; + private int idleConnTimeout = 60000; + private int checkWaitTime = 5000; + private int maxConnPerHost = 10; + private int maxTotalConn = 50; + private String userAgent; + private HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() { + @Override + public boolean retryRequest(IOException exception, int executionCount, HttpContext context) { + return false; + } + }; + private SSLConnectionSocketFactory sslConnectionSocketFactory = SSLConnectionSocketFactory.getSocketFactory(); + private PlainConnectionSocketFactory plainConnectionSocketFactory = PlainConnectionSocketFactory.getSocketFactory(); + + private String httpProxyHost; + private int httpProxyPort; + private String httpProxyUsername; + private String httpProxyPassword; + + /** + * 连接管理器 + */ + private PoolingHttpClientConnectionManager connectionManager; + /** + * 闲置连接监控线程 + */ + private IdleConnectionMonitorThread idleConnectionMonitorThread; + + /** + * httpClientBuilder + */ + private HttpClientBuilder httpClientBuilder; + + private boolean prepared = false; + + private DefaultApacheHttpHttpClientBuilder() { + } + + public static DefaultApacheHttpHttpClientBuilder get() { + return new DefaultApacheHttpHttpClientBuilder(); + } + + public ApacheHttpClientBuilder httpProxyHost(String httpProxyHost) { + this.httpProxyHost = httpProxyHost; + return this; + } + + public ApacheHttpClientBuilder httpProxyPort(int httpProxyPort) { + this.httpProxyPort = httpProxyPort; + return this; + } + + public ApacheHttpClientBuilder httpProxyUsername(String httpProxyUsername) { + this.httpProxyUsername = httpProxyUsername; + return this; + } + + public ApacheHttpClientBuilder httpProxyPassword(String httpProxyPassword) { + this.httpProxyPassword = httpProxyPassword; + return this; + } + + public ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory){ + this.sslConnectionSocketFactory = sslConnectionSocketFactory; + return this; + } + + public IdleConnectionMonitorThread getIdleConnectionMonitorThread() { + return idleConnectionMonitorThread; + } + + private void prepare(){ + Registry registry = RegistryBuilder.create() + .register("http", plainConnectionSocketFactory) + .register("https", sslConnectionSocketFactory) + .build(); + connectionManager = new PoolingHttpClientConnectionManager(registry); + connectionManager.setMaxTotal(maxTotalConn); + connectionManager.setDefaultMaxPerRoute(maxConnPerHost); + connectionManager.setDefaultSocketConfig( + SocketConfig.copy(SocketConfig.DEFAULT) + .setSoTimeout(soTimeout) + .build() + ); + + idleConnectionMonitorThread = new IdleConnectionMonitorThread(connectionManager, idleConnTimeout, checkWaitTime); + idleConnectionMonitorThread.setDaemon(true); + idleConnectionMonitorThread.start(); + + httpClientBuilder = HttpClients.custom() + .setConnectionManager(connectionManager) + .setDefaultRequestConfig( + RequestConfig.custom() + .setSocketTimeout(soTimeout) + .setConnectTimeout(connectionTimeout) + .setConnectionRequestTimeout(connectionRequestTimeout) + .build() + ) + .setRetryHandler(httpRequestRetryHandler); + + if (StringUtils.isNotBlank(httpProxyHost) && StringUtils.isNotBlank(httpProxyUsername)) { + // 使用代理服务器 需要用户认证的代理服务器 + CredentialsProvider credsProvider = new BasicCredentialsProvider(); + credsProvider.setCredentials( + new AuthScope(httpProxyHost, httpProxyPort), + new UsernamePasswordCredentials(httpProxyUsername, httpProxyPassword)); + httpClientBuilder.setDefaultCredentialsProvider(credsProvider); + } + + if (StringUtils.isNotBlank(userAgent)) { + httpClientBuilder.setUserAgent(userAgent); + } + + } + + public CloseableHttpClient build() { + if(!prepared){ + prepare(); + prepared = true; + } + + return httpClientBuilder.build(); + } + + public static class IdleConnectionMonitorThread extends Thread { + private final HttpClientConnectionManager connMgr; + private final int idleConnTimeout; + private final int checkWaitTime; + private volatile boolean shutdown; + + public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr, int idleConnTimeout, int checkWaitTime) { + super("IdleConnectionMonitorThread"); + this.connMgr = connMgr; + this.idleConnTimeout = idleConnTimeout; + this.checkWaitTime = checkWaitTime; + } + + @Override + public void run() { + try { + while (!shutdown) { + synchronized (this) { + wait(checkWaitTime); + connMgr.closeExpiredConnections(); + connMgr.closeIdleConnections(idleConnTimeout, TimeUnit.MILLISECONDS); + } + } + } catch (InterruptedException ignore) { + } + } + + public void trigger() { + synchronized (this) { + notifyAll(); + } + } + + public void shutdown() { + shutdown = true; + synchronized (this) { + notifyAll(); + } + } + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java index f9e3a25f..c0bd21ce 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java @@ -74,6 +74,8 @@ public class MediaDownloadRequestExecutor implements RequestExecutor throw new WxErrorException(error); } return responseContent; + }finally { + httpGet.releaseConnection(); } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java index 1ab1ad7e..1613c96c 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java @@ -1,24 +1,17 @@ package me.chanjar.weixin.common.util.http; -import java.io.IOException; - import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import org.apache.http.Consts; import org.apache.http.HttpHost; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.CredentialsProvider; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.util.EntityUtils; + +import java.io.IOException; /** * 简单的POST请求执行器,请求的参数是String, 返回的结果也是String @@ -47,6 +40,8 @@ public class SimplePostRequestExecutor implements RequestExecutor groupGet() throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/groups/get"; - String responseContent = execute(new JoddGetRequestExecutor(), url, null); + String responseContent = execute(new SimpleGetRequestExecutor(), url, null); /* * 操蛋的微信API,创建时返回的是 { group : { id : ..., name : ...} } * 查询时返回的是 { groups : [ { id : ..., name : ..., count : ... }, ... ] } @@ -438,14 +386,14 @@ public class WxMpServiceImpl implements WxMpService { String url = "https://api.weixin.qq.com/cgi-bin/groups/getid"; JsonObject o = new JsonObject(); o.addProperty("openid", openid); - String responseContent = execute(new JoddPostRequestExecutor(), url, o.toString()); + String responseContent = execute(new SimplePostRequestExecutor(), url, o.toString()); JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); return GsonHelper.getAsLong(tmpJsonElement.getAsJsonObject().get("groupid")); } public void groupUpdate(WxMpGroup group) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/groups/update"; - execute(new JoddPostRequestExecutor(), url, group.toJson()); + execute(new SimplePostRequestExecutor(), url, group.toJson()); } public void userUpdateGroup(String openid, long to_groupid) throws WxErrorException { @@ -453,7 +401,7 @@ public class WxMpServiceImpl implements WxMpService { JsonObject json = new JsonObject(); json.addProperty("openid", openid); json.addProperty("to_groupid", to_groupid); - execute(new JoddPostRequestExecutor(), url, json.toString()); + execute(new SimplePostRequestExecutor(), url, json.toString()); } public void userUpdateRemark(String openid, String remark) throws WxErrorException { @@ -461,19 +409,19 @@ public class WxMpServiceImpl implements WxMpService { JsonObject json = new JsonObject(); json.addProperty("openid", openid); json.addProperty("remark", remark); - execute(new JoddPostRequestExecutor(), url, json.toString()); + execute(new SimplePostRequestExecutor(), url, json.toString()); } public WxMpUser userInfo(String openid, String lang) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/user/info"; lang = lang == null ? "zh_CN" : lang; - String responseContent = execute(new JoddGetRequestExecutor(), url, "openid=" + openid + "&lang=" + lang); + String responseContent = execute(new SimpleGetRequestExecutor(), url, "openid=" + openid + "&lang=" + lang); return WxMpUser.fromJson(responseContent); } public WxMpUserList userList(String next_openid) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/user/get"; - String responseContent = execute(new JoddGetRequestExecutor(), url, next_openid == null ? null : "next_openid=" + next_openid); + String responseContent = execute(new SimpleGetRequestExecutor(), url, next_openid == null ? null : "next_openid=" + next_openid); return WxMpUserList.fromJson(responseContent); } @@ -489,7 +437,7 @@ public class WxMpServiceImpl implements WxMpService { scene.addProperty("scene_id", scene_id); actionInfo.add("scene", scene); json.add("action_info", actionInfo); - String responseContent = execute(new JoddPostRequestExecutor(), url, json.toString()); + String responseContent = execute(new SimplePostRequestExecutor(), url, json.toString()); return WxMpQrCodeTicket.fromJson(responseContent); } @@ -502,7 +450,7 @@ public class WxMpServiceImpl implements WxMpService { scene.addProperty("scene_id", scene_id); actionInfo.add("scene", scene); json.add("action_info", actionInfo); - String responseContent = execute(new JoddPostRequestExecutor(), url, json.toString()); + String responseContent = execute(new SimplePostRequestExecutor(), url, json.toString()); return WxMpQrCodeTicket.fromJson(responseContent); } @@ -515,7 +463,7 @@ public class WxMpServiceImpl implements WxMpService { scene.addProperty("scene_str", scene_str); actionInfo.add("scene", scene); json.add("action_info", actionInfo); - String responseContent = execute(new JoddPostRequestExecutor(), url, json.toString()); + String responseContent = execute(new SimplePostRequestExecutor(), url, json.toString()); return WxMpQrCodeTicket.fromJson(responseContent); } @@ -529,14 +477,14 @@ public class WxMpServiceImpl implements WxMpService { JsonObject o = new JsonObject(); o.addProperty("action", "long2short"); o.addProperty("long_url", long_url); - String responseContent = execute(new JoddPostRequestExecutor(), url, o.toString()); + String responseContent = execute(new SimplePostRequestExecutor(), url, o.toString()); JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); return tmpJsonElement.getAsJsonObject().get("short_url").getAsString(); } public String templateSend(WxMpTemplateMessage templateMessage) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/message/template/send"; - String responseContent = execute(new JoddPostRequestExecutor(), url, templateMessage.toJson()); + String responseContent = execute(new SimplePostRequestExecutor(), url, templateMessage.toJson()); JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); final JsonObject jsonObject = tmpJsonElement.getAsJsonObject(); if (jsonObject.get("errcode").getAsInt() == 0) @@ -546,7 +494,7 @@ public class WxMpServiceImpl implements WxMpService { public WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException { String url = "https://api.weixin.qq.com/semantic/semproxy/search"; - String responseContent = execute(new JoddPostRequestExecutor(), url, semanticQuery.toJson()); + String responseContent = execute(new SimplePostRequestExecutor(), url, semanticQuery.toJson()); return WxMpSemanticQueryResult.fromJson(responseContent); } @@ -578,7 +526,7 @@ public class WxMpServiceImpl implements WxMpService { url += "&grant_type=authorization_code"; try { - RequestExecutor executor = new JoddGetRequestExecutor(); + RequestExecutor executor = new SimpleGetRequestExecutor(); String responseText = executor.execute(getHttpclient(), httpProxy, url, null); return WxMpOAuth2AccessToken.fromJson(responseText); } catch (ClientProtocolException e) { @@ -596,7 +544,7 @@ public class WxMpServiceImpl implements WxMpService { url += "&refresh_token=" + refreshToken; try { - RequestExecutor executor = new JoddGetRequestExecutor(); + RequestExecutor executor = new SimpleGetRequestExecutor(); String responseText = executor.execute(getHttpclient(), httpProxy, url, null); return WxMpOAuth2AccessToken.fromJson(responseText); } catch (ClientProtocolException e) { @@ -618,7 +566,7 @@ public class WxMpServiceImpl implements WxMpService { } try { - RequestExecutor executor = new JoddGetRequestExecutor(); + RequestExecutor executor = new SimpleGetRequestExecutor(); String responseText = executor.execute(getHttpclient(), httpProxy, url, null); return WxMpUser.fromJson(responseText); } catch (ClientProtocolException e) { @@ -635,7 +583,7 @@ public class WxMpServiceImpl implements WxMpService { url += "&openid=" + oAuth2AccessToken.getOpenId(); try { - RequestExecutor executor = new JoddGetRequestExecutor(); + RequestExecutor executor = new SimpleGetRequestExecutor(); executor.execute(getHttpclient(), httpProxy, url, null); } catch (ClientProtocolException e) { throw new RuntimeException(e); @@ -688,11 +636,11 @@ public class WxMpServiceImpl implements WxMpService { } public String get(String url, String queryParam) throws WxErrorException { - return execute(new JoddGetRequestExecutor(), url, queryParam); + return execute(new SimpleGetRequestExecutor(), url, queryParam); } public String post(String url, String postData) throws WxErrorException { - return execute(new JoddPostRequestExecutor(), url, postData); + return execute(new SimplePostRequestExecutor(), url, postData); } /** @@ -772,36 +720,25 @@ public class WxMpServiceImpl implements WxMpService { public void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider) { this.wxMpConfigStorage = wxConfigProvider; - String http_proxy_host = wxMpConfigStorage.getHttp_proxy_host(); - int http_proxy_port = wxMpConfigStorage.getHttp_proxy_port(); - String http_proxy_username = wxMpConfigStorage.getHttp_proxy_username(); - String http_proxy_password = wxMpConfigStorage.getHttp_proxy_password(); - - final HttpClientBuilder builder = HttpClients.custom(); - if (StringUtils.isNotBlank(http_proxy_host)) { - // 使用代理服务器 - if (StringUtils.isNotBlank(http_proxy_username)) { - // 需要用户认证的代理服务器 - CredentialsProvider credsProvider = new BasicCredentialsProvider(); - credsProvider.setCredentials( - new AuthScope(http_proxy_host, http_proxy_port), - new UsernamePasswordCredentials(http_proxy_username, http_proxy_password)); - builder - .setDefaultCredentialsProvider(credsProvider); - } else { - // 无需用户认证的代理服务器 - } - httpProxy = new HttpHost(http_proxy_host, http_proxy_port); + ApacheHttpClientBuilder apacheHttpClientBuilder = wxMpConfigStorage.getApacheHttpClientBuilder(); + if (null == apacheHttpClientBuilder) { + apacheHttpClientBuilder = DefaultApacheHttpHttpClientBuilder.get(); } + apacheHttpClientBuilder.httpProxyHost(wxMpConfigStorage.getHttp_proxy_host()) + .httpProxyPort(wxMpConfigStorage.getHttp_proxy_port()) + .httpProxyUsername(wxMpConfigStorage.getHttp_proxy_username()) + .httpProxyPassword(wxMpConfigStorage.getHttp_proxy_password()); + if (wxConfigProvider.getSSLContext() != null){ SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( wxConfigProvider.getSSLContext(), new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); - builder.setSSLSocketFactory(sslsf); + apacheHttpClientBuilder.sslConnectionSocketFactory(sslsf); } - httpClient = builder.build(); + + httpClient = apacheHttpClientBuilder.build(); } @Override @@ -865,6 +802,8 @@ public class WxMpServiceImpl implements WxMpService { return wxMpPrepayIdResult; } catch (IOException e) { throw new RuntimeException("Failed to get prepay id due to IO exception.", e); + }finally { + httpPost.releaseConnection(); } } @@ -1037,6 +976,8 @@ public class WxMpServiceImpl implements WxMpService { error.setErrorCode(-1); error.setErrorMsg("incorrect response."); throw new WxErrorException(error); + }finally { + httpPost.releaseConnection(); } } @@ -1082,6 +1023,8 @@ public class WxMpServiceImpl implements WxMpService { WxError error = new WxError(); error.setErrorCode(-1); throw new WxErrorException(error); + }finally { + httpPost.releaseConnection(); } } @@ -1120,7 +1063,7 @@ public class WxMpServiceImpl implements WxMpService { synchronized (globalCardApiTicketRefreshLock) { if (wxMpConfigStorage.isCardApiTicketExpired()) { String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card"; - String responseContent = execute(new JoddGetRequestExecutor(), url, null); + String responseContent = execute(new SimpleGetRequestExecutor(), url, null); JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); String cardApiTicket = tmpJsonObject.get("ticket").getAsString(); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialDeleteRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialDeleteRequestExecutor.java index 8e7c41de..751261bf 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialDeleteRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialDeleteRequestExecutor.java @@ -34,13 +34,16 @@ public class MaterialDeleteRequestExecutor implements RequestExecutor params = new HashMap<>(); params.put("media_id", materialId); httpPost.setEntity(new StringEntity(WxGsonBuilder.create().toJson(params))); - CloseableHttpResponse response = httpclient.execute(httpPost); - String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); - WxError error = WxError.fromJson(responseContent); - if (error.getErrorCode() != 0) { - throw new WxErrorException(error); - } else { - return true; + try(CloseableHttpResponse response = httpclient.execute(httpPost)){ + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + WxError error = WxError.fromJson(responseContent); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } else { + return true; + } + }finally { + httpPost.releaseConnection(); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialNewsInfoRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialNewsInfoRequestExecutor.java index e23ecc50..fb77b54d 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialNewsInfoRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialNewsInfoRequestExecutor.java @@ -35,14 +35,18 @@ public class MaterialNewsInfoRequestExecutor implements RequestExecutor params = new HashMap<>(); params.put("media_id", materialId); httpPost.setEntity(new StringEntity(WxGsonBuilder.create().toJson(params))); - CloseableHttpResponse response = httpclient.execute(httpPost); - String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); - WxError error = WxError.fromJson(responseContent); - if (error.getErrorCode() != 0) { - throw new WxErrorException(error); - } else { - return WxMpGsonBuilder.create().fromJson(responseContent, WxMpMaterialNews.class); + try(CloseableHttpResponse response = httpclient.execute(httpPost)){ + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + WxError error = WxError.fromJson(responseContent); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } else { + return WxMpGsonBuilder.create().fromJson(responseContent, WxMpMaterialNews.class); + } + }finally { + httpPost.releaseConnection(); } + } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialUploadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialUploadRequestExecutor.java index 41ee9883..20169f0e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialUploadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialUploadRequestExecutor.java @@ -48,13 +48,16 @@ public class MaterialUploadRequestExecutor implements RequestExecutor params = new HashMap<>(); params.put("media_id", materialId); httpPost.setEntity(new StringEntity(WxGsonBuilder.create().toJson(params))); - CloseableHttpResponse response = httpclient.execute(httpPost); - String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); - WxError error = WxError.fromJson(responseContent); - if (error.getErrorCode() != 0) { - throw new WxErrorException(error); - } else { - return WxMpMaterialVideoInfoResult.fromJson(responseContent); + try(CloseableHttpResponse response = httpclient.execute(httpPost)){ + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + WxError error = WxError.fromJson(responseContent); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } else { + return WxMpMaterialVideoInfoResult.fromJson(responseContent); + } + }finally { + httpPost.releaseConnection(); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialVoiceAndImageDownloadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialVoiceAndImageDownloadRequestExecutor.java index 72917a93..a26f3520 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialVoiceAndImageDownloadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialVoiceAndImageDownloadRequestExecutor.java @@ -44,22 +44,25 @@ public class MaterialVoiceAndImageDownloadRequestExecutor implements RequestExec Map params = new HashMap<>(); params.put("media_id", materialId); httpPost.setEntity(new StringEntity(WxGsonBuilder.create().toJson(params))); - CloseableHttpResponse response = httpclient.execute(httpPost); - // 下载媒体文件出错 - InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response); - byte[] responseContent = IOUtils.toByteArray(inputStream); - String responseContentString = new String(responseContent, "UTF-8"); - if (responseContentString.length() < 100) { - try { - WxError wxError = WxGsonBuilder.create().fromJson(responseContentString, WxError.class); - if (wxError.getErrorCode() != 0) { - throw new WxErrorException(wxError); + try(CloseableHttpResponse response = httpclient.execute(httpPost)){ + // 下载媒体文件出错 + InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response); + byte[] responseContent = IOUtils.toByteArray(inputStream); + String responseContentString = new String(responseContent, "UTF-8"); + if (responseContentString.length() < 100) { + try { + WxError wxError = WxGsonBuilder.create().fromJson(responseContentString, WxError.class); + if (wxError.getErrorCode() != 0) { + throw new WxErrorException(wxError); + } + } catch (com.google.gson.JsonSyntaxException ex) { + return new ByteArrayInputStream(responseContent); } - } catch (com.google.gson.JsonSyntaxException ex) { - return new ByteArrayInputStream(responseContent); } + return new ByteArrayInputStream(responseContent); + }finally { + httpPost.releaseConnection(); } - return new ByteArrayInputStream(responseContent); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/QrCodeRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/QrCodeRequestExecutor.java index 12606096..bde25cfb 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/QrCodeRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/QrCodeRequestExecutor.java @@ -58,8 +58,9 @@ public class QrCodeRequestExecutor implements RequestExecutor Date: Mon, 29 Feb 2016 12:44:25 +0800 Subject: [PATCH 17/22] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E6=B0=B8=E4=B9=85=E7=B4=A0=E6=9D=90=E6=8A=A5=E2=80=9CConnectio?= =?UTF-8?q?n=20reset=E2=80=9D=E9=94=99=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../http/MaterialUploadRequestExecutor.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialUploadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialUploadRequestExecutor.java index 41ee9883..dcd4ef8e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialUploadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialUploadRequestExecutor.java @@ -15,7 +15,6 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntityBuilder; -import org.apache.http.entity.mime.content.InputStreamBody; import org.apache.http.impl.client.CloseableHttpClient; import java.io.*; @@ -23,6 +22,7 @@ import java.util.Map; public class MaterialUploadRequestExecutor implements RequestExecutor { + @Override public WxMpMaterialUploadResult execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, WxMpMaterial material) throws WxErrorException, ClientProtocolException, IOException { HttpPost httpPost = new HttpPost(uri); if (httpProxy != null) { @@ -35,10 +35,10 @@ public class MaterialUploadRequestExecutor implements RequestExecutor form = material.getForm(); if (material.getForm() != null) { @@ -48,13 +48,14 @@ public class MaterialUploadRequestExecutor implements RequestExecutor Date: Fri, 11 Mar 2016 17:57:51 +0800 Subject: [PATCH 18/22] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E4=BB=A3=E7=90=86=E7=9A=84Bug=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改设置代理的Bug。 Signed-off-by: Wang Zhengjie --- .../chanjar/weixin/common/util/http/JoddGetRequestExecutor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddGetRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddGetRequestExecutor.java index 82e28f7f..10011304 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddGetRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddGetRequestExecutor.java @@ -33,7 +33,7 @@ public class JoddGetRequestExecutor implements RequestExecutor { if (httpProxy != null) { ProxyInfo proxyInfoObj = new ProxyInfo( ProxyInfo.ProxyType.HTTP, - httpProxy.getAddress().getHostAddress(), + httpProxy.getHostName(), httpProxy.getPort(), "", ""); provider.useProxy(proxyInfoObj); } From 4979a16ae7e62bfcfeda7f39e59a853f624e6ab4 Mon Sep 17 00:00:00 2001 From: fxdfxq Date: Mon, 21 Mar 2016 12:53:37 +0800 Subject: [PATCH 19/22] Update WxMpServiceImpl.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加getJSSDKPayInfo返回字段code_url,用于微信扫码支付生成二维码图片使用 --- .../src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java index d141d436..7c109be5 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java @@ -866,6 +866,7 @@ public class WxMpServiceImpl implements WxMpService { payInfo.put("nonceStr", System.currentTimeMillis() + ""); payInfo.put("package", "prepay_id=" + prepayId); payInfo.put("signType", "MD5"); + payInfo.put("code_url",wxMpPrepayIdResult.getCode_url()); String finalSign = WxCryptUtil.createSign(payInfo, wxMpConfigStorage.getPartnerKey()); payInfo.put("paySign", finalSign); From 35697dafcfdb19c9ec1fec555d7cf69518814067 Mon Sep 17 00:00:00 2001 From: miller Date: Mon, 28 Mar 2016 14:54:57 +0800 Subject: [PATCH 20/22] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E5=9B=BE=E6=96=87=E7=B4=A0=E6=9D=90=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E7=AB=A0=E7=9A=84=E5=B0=81=E9=9D=A2=E5=9B=BEurl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chanjar/weixin/mp/bean/WxMpMaterialNews.java | 14 +++++++++++++- .../json/WxMpMaterialNewsArticleGsonAdapter.java | 5 +++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMaterialNews.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMaterialNews.java index f1de7d9e..75ee479f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMaterialNews.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMaterialNews.java @@ -46,6 +46,10 @@ public class WxMpMaterialNews implements Serializable { * (必填) 图文消息缩略图的media_id,可以在基础支持-上传多媒体文件接口中获得 */ private String thumbMediaId; + /** + * 图文消息的封面url + */ + private String thumbUrl; /** * 图文消息的作者 */ @@ -141,9 +145,17 @@ public class WxMpMaterialNews implements Serializable { this.url = url; } + public String getThumbUrl() { + return thumbUrl; + } + + public void setThumbUrl(String thumbUrl) { + this.thumbUrl = thumbUrl; + } + @Override public String toString() { - return "WxMpMassNewsArticle [" + "thumbMediaId=" + thumbMediaId + ", author=" + author + ", title=" + title + + return "WxMpMassNewsArticle [" + "thumbMediaId=" + thumbMediaId + "thumbUrl=" + thumbUrl + ", author=" + author + ", title=" + title + ", contentSourceUrl=" + contentSourceUrl + ", content=" + content + ", digest=" + digest + ", showCoverPic=" + showCoverPic +", url=" + url + "]"; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsArticleGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsArticleGsonAdapter.java index 141baeab..96cc6aab 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsArticleGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsArticleGsonAdapter.java @@ -20,6 +20,7 @@ public class WxMpMaterialNewsArticleGsonAdapter implements JsonSerializer Date: Sun, 10 Apr 2016 16:26:28 +0800 Subject: [PATCH 21/22] =?UTF-8?q?=E5=8D=A1=E5=88=B8=E6=A0=B8=E9=94=80?= =?UTF-8?q?=E7=9A=84API=E5=A2=9E=E5=8A=A0=E5=8F=82=E6=95=B0=E9=87=8D?= =?UTF-8?q?=E8=BD=BD=E7=9A=84=E6=96=B9=E6=B3=95=EF=BC=8C=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E5=85=BC=E5=AE=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../me/chanjar/weixin/mp/api/WxMpService.java | 17 ++++++++++++++--- .../chanjar/weixin/mp/api/WxMpServiceImpl.java | 18 +++++++++++++++++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java index af574c1b..5281e7a4 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java @@ -852,13 +852,24 @@ public interface WxMpService { */ public WxMpCardResult queryCardCode(String cardId, String code, boolean checkConsume) throws WxErrorException; - - /** + + /** * 卡券Code核销。核销失败会抛出异常 + * * @param code 单张卡券的唯一标准 + * @return 调用返回的JSON字符串。 + *
    可用 com.google.gson.JsonParser#parse 等方法直接取JSON串中的errcode等信息。 + * @throws WxErrorException + */ + public String consumeCardCode(String code) throws WxErrorException; + + /** + * 卡券Code核销。核销失败会抛出异常 + * + * @param code 单张卡券的唯一标准 * @param cardId 当自定义Code卡券时需要传入card_id * @return 调用返回的JSON字符串。 - *
    可用 com.google.gson.JsonParser#parse 等方法直接取JSON串中的errcode等信息。 + *
    可用 com.google.gson.JsonParser#parse 等方法直接取JSON串中的errcode等信息。 * @throws WxErrorException */ public String consumeCardCode(String code, String cardId) throws WxErrorException; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java index 7c109be5..7c4f290f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java @@ -1159,6 +1159,22 @@ public class WxMpServiceImpl implements WxMpService { * 卡券Code核销。核销失败会抛出异常 * * @param code 单张卡券的唯一标准 + * @return 调用返回的JSON字符串。 + *
    可用 com.google.gson.JsonParser#parse 等方法直接取JSON串中的errcode等信息。 + * @throws WxErrorException + */ + @Override + public String consumeCardCode(String code) throws WxErrorException { + return consumeCardCode(code, null); + } + + /** + * 卡券Code核销。核销失败会抛出异常 + * + * @param code 单张卡券的唯一标准 + * @param cardId 当自定义Code卡券时需要传入card_id + * @return 调用返回的JSON字符串。 + *
    可用 com.google.gson.JsonParser#parse 等方法直接取JSON串中的errcode等信息。 * @throws WxErrorException */ @Override @@ -1168,7 +1184,7 @@ public class WxMpServiceImpl implements WxMpService { param.addProperty("code", code); if (cardId != null && !"".equals(cardId)) { - param.addProperty("card_id", cardId); + param.addProperty("card_id", cardId); } String responseContent = post(url, param.toString()); From cfc15480dee22d19d46b0fcfad42ac9d42dcad29 Mon Sep 17 00:00:00 2001 From: Lance7in Date: Wed, 13 Apr 2016 17:37:22 +0800 Subject: [PATCH 22/22] =?UTF-8?q?=E5=8F=91=E9=80=81=E7=BA=A2=E5=8C=85?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E7=9A=84=E5=85=A5=E5=8F=82=E6=98=AFMap,?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=E4=B8=AD=E6=B7=BB=E5=8A=A0=E4=BA=86=E6=89=80?= =?UTF-8?q?=E9=9C=80=E5=BF=85=E5=A1=AB=E5=8F=82=E6=95=B0=E7=9A=84=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E5=8F=8A=E6=8F=8F=E8=BF=B0.=20=E5=8F=91=E9=80=81?= =?UTF-8?q?=E7=BA=A2=E5=8C=85=E6=8E=A5=E5=8F=A3=E9=9C=80=E8=A6=81=E5=9C=A8?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6test-config.xml=E4=B8=AD?= =?UTF-8?q?=E9=A2=9D=E5=A4=96=E8=AE=BE=E7=BD=AE=E4=B8=A4=E4=B8=AAkey,?= =?UTF-8?q?=E5=B7=B2=E5=9C=A8=E6=B3=A8=E9=87=8A=E4=B8=AD=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../me/chanjar/weixin/mp/api/WxMpService.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java index af574c1b..c7915f1b 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java @@ -790,6 +790,23 @@ public interface WxMpService { /** * 发送微信红包给个人用户 + * + * 需要传入的必填参数如下: + * mch_billno//商户订单号 + * send_name//商户名称 + * re_openid//用户openid + * total_amount//红包总额 + * total_num//红包发放总人数 + * wishing//红包祝福语 + * client_ip//服务器Ip地址 + * act_name//活动名称 + * remark //备注 + * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_5 + * + * 使用现金红包功能需要在xml配置文件中额外设置: + * 微信商户平台ID + * 商户平台设置的API密钥 + * * @param parameters * @return * @throws WxErrorException