diff --git a/README.md b/README.md index a519eaeb..9691368a 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ weixin-java-tools me.chanjar weixin-java-mp - 1.3.0 + 1.3.1 ``` @@ -27,7 +27,7 @@ weixin-java-tools me.chanjar weixin-java-cp - 1.3.0 + 1.3.1 ``` diff --git a/pom.xml b/pom.xml index 40e2f2f2..01b42407 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 me.chanjar weixin-java-parent - 1.3.0 + 1.3.1 pom WeiXin Java Tools - Parent 微信公众号、企业号上级POM diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml index a8d1da30..a8366c7a 100644 --- a/weixin-java-common/pom.xml +++ b/weixin-java-common/pom.xml @@ -6,7 +6,7 @@ me.chanjar weixin-java-parent - 1.3.0 + 1.3.1 weixin-java-common diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java index c44ceea7..ce1cca01 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java @@ -11,6 +11,7 @@ import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; 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 me.chanjar.weixin.common.bean.result.WxError; @@ -35,6 +36,7 @@ public class MediaUploadRequestExecutor implements RequestExecutor me.chanjar weixin-java-parent - 1.3.0 + 1.3.1 weixin-java-cp 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 7b95c32c..3b9e8fe7 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 @@ -555,7 +555,7 @@ public class WxCpServiceImpl implements WxCpService { throw new RuntimeException("微信服务端异常,超出重试次数"); } - protected T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException { + protected synchronized T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException { if (uri.indexOf("access_token=") != -1) { throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri); } diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml index 48999b06..e4695436 100644 --- a/weixin-java-mp/pom.xml +++ b/weixin-java-mp/pom.xml @@ -6,7 +6,7 @@ me.chanjar weixin-java-parent - 1.3.0 + 1.3.1 weixin-java-mp WeiXin Java Tools - MP 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 5c15e89b..b583798e 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 @@ -671,33 +671,57 @@ public interface WxMpService { */ void setMaxRetryTimes(int maxRetryTimes); - /** - * 统一下单(详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1) - * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识" - * @param openId 支付人openId - * @param outTradeNo 商户端对应订单号 - * @param amt 金额(单位元) - * @param body 商品描述 - * @param tradeType 交易类型 JSAPI,NATIVE,APP,WAP - * @param ip 发起支付的客户端IP - * @param notifyUrl 通知地址 - * @return - */ - WxMpPrepayIdResult getPrepayId(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String notifyUrl); + /** + * 统一下单(详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1) + * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识" + * @param openId 支付人openId + * @param outTradeNo 商户端对应订单号 + * @param amt 金额(单位元) + * @param body 商品描述 + * @param tradeType 交易类型 JSAPI,NATIVE,APP,WAP + * @param ip 发起支付的客户端IP + * @param notifyUrl 通知地址 + * @return + * @deprecated Use me.chanjar.weixin.mp.api.WxMpService.getPrepayId(Map) instead + */ + @Deprecated + WxMpPrepayIdResult getPrepayId(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String notifyUrl); - /** - * 该接口调用“统一下单”接口,并拼装JSSDK发起支付请求需要的参数 - * 详见http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E5.8F.91.E8.B5.B7.E4.B8.80.E4.B8.AA.E5.BE.AE.E4.BF.A1.E6.94.AF.E4.BB.98.E8.AF.B7.E6.B1.82 - * @param openId 支付人openId - * @param outTradeNo 商户端对应订单号 - * @param amt 金额(单位元) - * @param body 商品描述 - * @param tradeType 交易类型 JSAPI,NATIVE,APP,WAP - * @param ip 发起支付的客户端IP - * @param notifyUrl 通知地址 - * @return - */ - Map getJSSDKPayInfo(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String notifyUrl); + /** + * 统一下单(详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1) + * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识" + * + * @param parameters + * All required/optional parameters for weixin payment + * @return + * @throws IllegalArgumentException + */ + WxMpPrepayIdResult getPrepayId(Map parameters); + + /** + * 该接口调用“统一下单”接口,并拼装JSSDK发起支付请求需要的参数 + * 详见http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E5.8F.91.E8.B5.B7.E4.B8.80.E4.B8.AA.E5.BE.AE.E4.BF.A1.E6.94.AF.E4.BB.98.E8.AF.B7.E6.B1.82 + * @param parameters + * the required or optional parameters + * @return + */ + Map getJSSDKPayInfo(Map parameters); + + /** + * 该接口调用“统一下单”接口,并拼装JSSDK发起支付请求需要的参数 + * 详见http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E5.8F.91.E8.B5.B7.E4.B8.80.E4.B8.AA.E5.BE.AE.E4.BF.A1.E6.94.AF.E4.BB.98.E8.AF.B7.E6.B1.82 + * @param openId 支付人openId + * @param outTradeNo 商户端对应订单号 + * @param amt 金额(单位元) + * @param body 商品描述 + * @param tradeType 交易类型 JSAPI,NATIVE,APP,WAP + * @param ip 发起支付的客户端IP + * @param notifyUrl 通知地址 + * @return + * @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); /** * 该接口提供所有微信支付订单的查询,当支付通知处理异常戒丢失的情冴,商户可以通过该接口查询订单支付状态。 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 6e616284..da4252fd 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 @@ -11,6 +11,7 @@ import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.SortedMap; import java.util.TreeMap; import java.util.UUID; @@ -634,8 +635,9 @@ public class WxMpServiceImpl implements WxMpService { param.addProperty("end_date", SIMPLE_DATE_FORMAT.format(endDate)); String responseContent = post(url, param.toString()); JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); - return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement.getAsJsonObject().get("list"), new TypeToken>() { - }.getType()); + return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement.getAsJsonObject().get("list"), + new TypeToken>() { + }.getType()); } @Override @@ -695,7 +697,7 @@ public class WxMpServiceImpl implements WxMpService { throw new RuntimeException("微信服务端异常,超出重试次数"); } - protected T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException { + protected synchronized T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException { if (uri.indexOf("access_token=") != -1) { throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri); } @@ -776,35 +778,37 @@ public class WxMpServiceImpl implements WxMpService { @Override public WxMpPrepayIdResult getPrepayId(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String callbackUrl) { - String nonce_str = System.currentTimeMillis() + ""; - - SortedMap packageParams = new TreeMap(); + Map packageParams = new HashMap(); packageParams.put("appid", wxMpConfigStorage.getAppId()); packageParams.put("mch_id", wxMpConfigStorage.getPartnerId()); - packageParams.put("nonce_str", nonce_str); packageParams.put("body", body); packageParams.put("out_trade_no", outTradeNo); - packageParams.put("total_fee", (int) (amt * 100) + ""); packageParams.put("spbill_create_ip", ip); packageParams.put("notify_url", callbackUrl); packageParams.put("trade_type", tradeType); packageParams.put("openid", openId); + return getPrepayId(packageParams); + } + + public WxMpPrepayIdResult getPrepayId(final Map parameters) { + String nonce_str = System.currentTimeMillis() + ""; + + final SortedMap packageParams = new TreeMap(parameters); + packageParams.put("appid", wxMpConfigStorage.getAppId()); + packageParams.put("mch_id", wxMpConfigStorage.getPartnerId()); + packageParams.put("nonce_str", nonce_str); + checkParameters(packageParams); + String sign = WxCryptUtil.createSign(packageParams, wxMpConfigStorage.getPartnerKey()); - String xml = "" + - "" + wxMpConfigStorage.getAppId() + "" + - "" + wxMpConfigStorage.getPartnerId() + "" + - "" + nonce_str + "" + - "" + sign + "" + - "" + - "" + outTradeNo + "" + - "" + packageParams.get("total_fee") + "" + - "" + ip + "" + - "" + callbackUrl + "" + - "" + tradeType + "" + - "" + openId + "" + - ""; + packageParams.put("sign", sign); + + StringBuilder request = new StringBuilder(""); + for (Entry para : packageParams.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/pay/unifiedorder"); if (httpProxy != null) { @@ -812,7 +816,7 @@ public class WxMpServiceImpl implements WxMpService { httpPost.setConfig(config); } - StringEntity entity = new StringEntity(xml, Consts.UTF_8); + StringEntity entity = new StringEntity(request.toString(), Consts.UTF_8); httpPost.setEntity(entity); try { CloseableHttpResponse response = getHttpclient().execute(httpPost); @@ -827,9 +831,39 @@ public class WxMpServiceImpl implements WxMpService { return new WxMpPrepayIdResult(); } + final String[] REQUIRED_ORDER_PARAMETERS = new String[] { "appid", "mch_id", "body", "out_trade_no", "total_fee", "spbill_create_ip", "notify_url", + "trade_type", }; + + private void checkParameters(Map parameters) { + for (String para : REQUIRED_ORDER_PARAMETERS) { + if (!parameters.containsKey(para)) + throw new IllegalArgumentException("Reqiured argument '" + para + "' is missing."); + } + if ("JSAPI".equals(parameters.get("trade_type")) && !parameters.containsKey("openid")) + throw new IllegalArgumentException("Reqiured argument 'openid' is missing when trade_type is 'JSAPI'."); + if ("NATIVE".equals(parameters.get("trade_type")) && !parameters.containsKey("product_id")) + throw new IllegalArgumentException("Reqiured argument 'product_id' is missing when trade_type is 'NATIVE'."); + } + @Override public Map getJSSDKPayInfo(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String callbackUrl) { - WxMpPrepayIdResult wxMpPrepayIdResult = getPrepayId(openId, outTradeNo, amt, body, tradeType, ip, callbackUrl); + Map packageParams = new HashMap(); + packageParams.put("appid", wxMpConfigStorage.getAppId()); + 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("spbill_create_ip", ip); + packageParams.put("notify_url", callbackUrl); + packageParams.put("trade_type", tradeType); + packageParams.put("openid", openId); + + return getJSSDKPayInfo(packageParams); + } + + @Override + public Map getJSSDKPayInfo(Map parameters) { + WxMpPrepayIdResult wxMpPrepayIdResult = getPrepayId(parameters); String prepayId = wxMpPrepayIdResult.getPrepay_id(); if (prepayId == null || prepayId.equals("")) { throw new RuntimeException("get prepayid error"); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpPrepayIdResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpPrepayIdResult.java index aa42299a..b5c09b21 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpPrepayIdResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpPrepayIdResult.java @@ -22,6 +22,9 @@ public class WxMpPrepayIdResult implements Serializable { private String result_code; private String prepay_id; private String trade_type; + private String err_code; + private String err_code_des; + private String code_url; public String getReturn_code() { return return_code; @@ -94,4 +97,28 @@ public class WxMpPrepayIdResult implements Serializable { public void setTrade_type(String trade_type) { this.trade_type = trade_type; } + + public String getErr_code() { + return err_code; + } + + public void setErr_code(String err_code) { + this.err_code = err_code; + } + + public String getErr_code_des() { + return err_code_des; + } + + public void setErr_code_des(String err_code_des) { + this.err_code_des = err_code_des; + } + + public String getCode_url() { + return code_url; + } + + public void setCode_url(String code_url) { + this.code_url = code_url; + } } 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 63d92523..41ee9883 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 @@ -13,6 +13,7 @@ import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; 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; @@ -36,7 +37,9 @@ public class MaterialUploadRequestExecutor implements RequestExecutor form = material.getForm(); if (material.getForm() != null) { multipartEntityBuilder.addTextBody("description", WxGsonBuilder.create().toJson(form)); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpUserGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpUserGsonAdapter.java index 9ffb41a0..0b80e90a 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpUserGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpUserGsonAdapter.java @@ -19,7 +19,10 @@ public class WxMpUserGsonAdapter implements JsonDeserializer { public WxMpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject o = json.getAsJsonObject(); WxMpUser wxMpUser = new WxMpUser(); - wxMpUser.setSubscribe(new Integer(0).equals(GsonHelper.getInteger(o, "subscribe")) ? false : true); + Integer subscribe = GsonHelper.getInteger(o, "subscribe"); + if (subscribe != null) { + wxMpUser.setSubscribe(new Integer(0).equals(subscribe) ? false : true); + } wxMpUser.setCity(GsonHelper.getString(o, "city")); wxMpUser.setCountry(GsonHelper.getString(o, "country")); wxMpUser.setHeadImgUrl(GsonHelper.getString(o, "headimgurl"));