From ff436b6f036f00e500bd27c160d6d1582c36d092 Mon Sep 17 00:00:00 2001 From: "jy.hu" Date: Sat, 8 Nov 2014 21:12:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9EV2=E7=89=88=E6=9C=AC"?= =?UTF-8?q?=E9=80=80=E6=AC=BE=E7=94=B3=E8=AF=B7","=E9=80=80=E6=AC=BE?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2","=E5=AF=B9=E8=B4=A6=E5=8D=95=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD"=E4=B8=89=E4=B8=AA=E6=8E=A5=E5=8F=A3=E4=BB=A5?= =?UTF-8?q?=E5=8F=8A=E8=AF=AD=E4=B9=89=E7=90=86=E8=A7=A3=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E9=B8=A1=E8=82=8B=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +- weixin4j-base/pom.xml | 5 + .../weixin4j/exception/WeixinException.java | 1 + .../foxinmy/weixin4j/http/HttpRequest.java | 108 ++++++-- .../com/foxinmy/weixin4j/http/Parameter.java | 12 +- .../com/foxinmy/weixin4j/http/Response.java | 46 +++- .../foxinmy/weixin4j/http/SSLHttpRequest.java | 13 +- .../com/foxinmy/weixin4j/http/XmlResult.java | 11 +- .../java/com/foxinmy/weixin4j/http/error.xml | 77 ++++++ .../com/foxinmy/weixin4j/model/Consts.java | 11 + .../foxinmy/weixin4j/model/WeixinAccount.java | 15 ++ .../weixin4j/msg/event/ScanEventMessage.java | 3 +- .../com/foxinmy/weixin4j/util/DateUtil.java | 5 +- .../com/foxinmy/weixin4j/util/MapUtil.java | 24 +- weixin4j-mp/README.md | 8 +- weixin4j-mp/weixin4j-mp-api/README.md | 8 +- weixin4j-mp/weixin4j-mp-api/pom.xml | 13 - .../com/foxinmy/weixin4j/mp/WeixinProxy.java | 48 ++-- .../com/foxinmy/weixin4j/mp/api/BaseApi.java | 5 +- .../foxinmy/weixin4j/mp/api/HelperApi.java | 28 ++ .../com/foxinmy/weixin4j/mp/api/PayApi.java | 252 ++++++++++++++---- .../foxinmy/weixin4j/mp/api/weixin.properties | 71 +++-- .../com/foxinmy/weixin4j/mp/model/Button.java | 6 +- .../foxinmy/weixin4j/mp/model/SemQuery.java | 88 ++++++ .../foxinmy/weixin4j/mp/model/SemResult.java | 88 ++++++ .../weixin4j/mp/payment/PayAction.java | 3 +- .../foxinmy/weixin4j/mp/payment/PayUtil.java | 6 +- .../weixin4j/mp/payment/{v3 => }/Refund.java | 25 +- .../mp/payment/{v3 => }/RefundConverter.java | 6 +- .../mp/payment/{v3 => }/RefundDetail.java | 60 ++++- .../mp/payment/{v3 => }/RefundResult.java | 38 ++- .../foxinmy/weixin4j/mp/payment/cacert.pem | 21 ++ .../mp/payment/v3/NativePayResponseV3.java | 7 +- .../weixin4j/mp/payment/v3/PayRequestV3.java | 6 +- .../weixin4j/mp/spider/WeixinExecutor.java | 48 ++-- .../foxinmy/weixin4j/mp/type/BillType.java | 11 +- .../weixin4j/mp/type/RefundChannel.java | 5 +- .../weixin4j/mp/type/RefundStatus.java | 5 +- .../foxinmy/weixin4j/mp/type/SemCategory.java | 61 +++++ .../src/main/resources/weixin.properties | 2 +- .../weixin4j/mp/test/SemanticTest.java | 27 ++ 41 files changed, 1037 insertions(+), 248 deletions(-) create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/Consts.java create mode 100644 weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/SemQuery.java create mode 100644 weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/SemResult.java rename weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/{v3 => }/Refund.java (64%) rename weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/{v3 => }/RefundConverter.java (96%) rename weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/{v3 => }/RefundDetail.java (51%) rename weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/{v3 => }/RefundResult.java (67%) create mode 100644 weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/cacert.pem create mode 100644 weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/SemCategory.java create mode 100644 weixin4j-mp/weixin4j-mp-api/src/test/java/com/foxinmy/weixin4j/mp/test/SemanticTest.java diff --git a/README.md b/README.md index 876eefc4..11f9ca2b 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ weixin4j * 2014-10-31 - + **weixin4j-mp**: `weixin.properties`切分为API调用地址/公众号信息两部分 + + **weixin4j-mp**: `weixin.properties`切分为API调用地址和公众号信息两部分 + **weixin4j-base**: `TokenApi`重命名为`TokenHolder` @@ -54,6 +54,12 @@ weixin4j + **weixin-mp**: 新增`退款接口` +* 2014-11-08 + + + **weixin-mp**: 新增V2版本`退款申请`、`退款查询`、`对账单下载`三个接口 + + + **weixin-mp**: 新增一个简单的`语义理解`接口 + 接下来 ------ * 公众号智能接口 diff --git a/weixin4j-base/pom.xml b/weixin4j-base/pom.xml index 0d438250..9fec1614 100644 --- a/weixin4j-base/pom.xml +++ b/weixin4j-base/pom.xml @@ -103,5 +103,10 @@ commons-codec ${commons.codec.version} + + org.jsoup + jsoup + ${jsoup.version} + diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/exception/WeixinException.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/exception/WeixinException.java index e27673eb..e5172c5f 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/exception/WeixinException.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/exception/WeixinException.java @@ -22,6 +22,7 @@ public class WeixinException extends Exception { } public WeixinException(String errorMsg) { + this.errorCode = ""; this.errorMsg = errorMsg; } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpRequest.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpRequest.java index d5eca567..26b7e062 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpRequest.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpRequest.java @@ -3,10 +3,11 @@ package com.foxinmy.weixin4j.http; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import org.apache.commons.lang3.StringUtils; import org.apache.http.Consts; import org.apache.http.Header; import org.apache.http.HttpEntity; @@ -32,9 +33,13 @@ import org.apache.http.impl.conn.PoolingClientConnectionManager; import org.apache.http.params.CoreConnectionPNames; import org.apache.http.params.CoreProtocolPNames; import org.apache.http.util.EntityUtils; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; import com.alibaba.fastjson.JSONException; import com.foxinmy.weixin4j.exception.WeixinException; +import com.foxinmy.weixin4j.util.MapUtil; +import com.thoughtworks.xstream.mapper.CannotResolveClassException; /** * 调用微信相关接口的HttpRequest,对于其他请求可能并不试用 @@ -46,7 +51,7 @@ import com.foxinmy.weixin4j.exception.WeixinException; * @see */ public class HttpRequest { - private final String SUCCESS = "success"; + protected AbstractHttpClient client; public HttpRequest() { @@ -84,6 +89,14 @@ public class HttpRequest { return get(url, (Parameter[]) null); } + public Response get(String url, Map para) + throws WeixinException { + return get( + String.format("%s?%s", url, + MapUtil.toJoinString(para, false, false, null)), + (Parameter[]) null); + } + public Response get(String url, Parameter... parameters) throws WeixinException { StringBuilder sb = new StringBuilder(url); @@ -106,11 +119,13 @@ public class HttpRequest { public Response post(String url, Parameter... parameters) throws WeixinException { HttpPost method = new HttpPost(url); - List params = new ArrayList(); - for (Parameter parameter : parameters) { - params.add(parameter.toPostPara()); + if (parameters != null && parameters.length > 0) { + List params = new ArrayList(); + for (Parameter parameter : parameters) { + params.add(parameter.toPostPara()); + } + method.setEntity(new UrlEncodedFormEntity(params, Consts.UTF_8)); } - method.setEntity(new UrlEncodedFormEntity(params, Consts.UTF_8)); return doRequest(method); } @@ -167,42 +182,39 @@ public class HttpRequest { String.format("the page was redirected to %s", httpResponse.getFirstHeader("location"))); } + byte[] data = EntityUtils.toByteArray(httpEntity); response = new Response(); response.setBody(data); response.setStatusCode(status); response.setStatusText(statusLine.getReasonPhrase()); response.setStream(new ByteArrayInputStream(data)); + response.setText(new String(data, Consts.UTF_8)); EntityUtils.consume(httpEntity); Header contentType = httpResponse .getFirstHeader(HttpHeaders.CONTENT_TYPE); + // error with html if (contentType.getValue().contains( - ContentType.APPLICATION_JSON.getMimeType()) - || contentType.getValue().contains( - ContentType.TEXT_PLAIN.getMimeType())) { - response.setText(new String(data, StandardCharsets.UTF_8)); + ContentType.TEXT_HTML.getMimeType())) { + response.setText(new String(data, "gbk")); + Document doc = Jsoup.parse(response.getAsString()); + throw new WeixinException(doc.body().text()); + } else if (contentType.getValue().contains( + ContentType.APPLICATION_JSON.getMimeType())) { + checkJson(response); + } else if (contentType.getValue().contains( + ContentType.TEXT_XML.getMimeType())) { + checkXml(response); + } else if (contentType.getValue().contains( + ContentType.TEXT_PLAIN.getMimeType())) { try { - JsonResult jsonResult = response.getAsJsonResult(); - response.setJsonResult(true); - if (jsonResult.getCode() != 0) { - throw new WeixinException(Integer.toString(jsonResult - .getCode()), jsonResult.getDesc()); - } + checkJson(response); return response; } catch (JSONException e) { ; } - XmlResult xmlResult = response.getAsXmlResult(); - response.setXmlResult(true); - if (!xmlResult.getReturnCode().equalsIgnoreCase(SUCCESS)) { - throw new WeixinException(xmlResult.getReturnCode(), - xmlResult.getReturnMsg()); - } - if (!xmlResult.getResultCode().equalsIgnoreCase(SUCCESS)) { - throw new WeixinException(xmlResult.getErrCode(), - xmlResult.getErrCodeDes()); - } + checkXml(response); } } catch (IOException e) { throw new WeixinException("-1", e.getMessage()); @@ -211,4 +223,48 @@ public class HttpRequest { } return response; } + + private void checkJson(Response response) throws WeixinException { + response.setJsonResult(true); + JsonResult jsonResult = response.getAsJsonResult(); + if (jsonResult.getCode() != 0) { + if (StringUtils.isBlank(jsonResult.getDesc())) { + jsonResult = response.getTextError(jsonResult.getCode()); + } + throw new WeixinException(Integer.toString(jsonResult.getCode()), + jsonResult.getDesc()); + } + } + + private void checkXml(Response response) throws WeixinException { + response.setXmlResult(true); + XmlResult xmlResult = null; + try { + xmlResult = response.getAsXmlResult(); + } catch (CannotResolveClassException ex) { + // + String newXml = response.getAsString() + .replaceFirst("", "") + .replaceFirst("", "") + .replaceFirst("", "") + .replaceFirst("", "") + .replaceFirst("", "") + .replaceFirst("", ""); + response.setText(newXml); + xmlResult = response.getAsXmlResult(); + } + if (xmlResult.getReturnCode().equals("0")) { + return; + } + if (!xmlResult.getReturnCode().equalsIgnoreCase( + com.foxinmy.weixin4j.model.Consts.SUCCESS)) { + throw new WeixinException(xmlResult.getReturnCode(), + xmlResult.getReturnMsg()); + } + if (!xmlResult.getResultCode().equalsIgnoreCase( + com.foxinmy.weixin4j.model.Consts.SUCCESS)) { + throw new WeixinException(xmlResult.getErrCode(), + xmlResult.getErrCodeDes()); + } + } } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Parameter.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Parameter.java index c3e8beb9..4f20188d 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Parameter.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Parameter.java @@ -2,15 +2,13 @@ package com.foxinmy.weixin4j.http; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; +import org.apache.http.Consts; import org.apache.http.NameValuePair; import org.apache.http.message.BasicNameValuePair; public class Parameter { - - private final static String CHARSET = StandardCharsets.UTF_8.name(); - + private String name; private String value; @@ -41,7 +39,8 @@ public class Parameter { public String toGetPara() { try { - return String.format("&%s=%s", name, URLEncoder.encode(value, CHARSET)); + return String.format("&%s=%s", name, + URLEncoder.encode(value, Consts.UTF_8.name())); } catch (UnsupportedEncodingException e) { return String.format("&%s=%s", name, value); } @@ -49,7 +48,8 @@ public class Parameter { public NameValuePair toPostPara() { try { - return new BasicNameValuePair(name, URLEncoder.encode(value, CHARSET)); + return new BasicNameValuePair(name, URLEncoder.encode(value, + Consts.UTF_8.name())); } catch (UnsupportedEncodingException e) { return new BasicNameValuePair(name, value); } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Response.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Response.java index 22052b8f..440b7123 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Response.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Response.java @@ -2,6 +2,7 @@ package com.foxinmy.weixin4j.http; import java.io.InputStream; +import org.apache.commons.lang3.StringUtils; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Node; @@ -73,18 +74,41 @@ public class Response { * @return * @throws DocumentException */ - public JsonResult getTextError() throws DocumentException { - JsonResult result = getAsJsonResult(); - if (result.getCode() != 0) { - SAXReader reader = new SAXReader(); - Document doc = reader.read(Response.class - .getResourceAsStream("error.xml")); - Node node = doc.getRootElement().selectSingleNode( - String.format("error/code[text()='%d']", result.getCode())); - if (node != null) { - result.setText(node.getParent().selectSingleNode("text") - .getStringValue()); + public JsonResult getTextError(int code) { + JsonResult result = new JsonResult(); + result.setCode(code); + SAXReader reader = new SAXReader(); + Document doc = null; + try { + doc = reader.read(Response.class.getResourceAsStream("error.xml")); + } catch (DocumentException e) { + e.printStackTrace(); + } + Node node = doc.getRootElement().selectSingleNode( + String.format("error/code[text()=%d]", code)); + if (node != null) { + node = node.getParent(); + String desc = null; + Node _node = node.selectSingleNode("desc"); + if (_node != null) { + desc = _node.getStringValue(); } + String text = null; + _node = node.selectSingleNode("text"); + if (_node != null) { + text = _node.getStringValue(); + } + if (StringUtils.isBlank(desc) && StringUtils.isNotBlank(text)) { + desc = text; + } + if (StringUtils.isBlank(text) && StringUtils.isNotBlank(desc)) { + text = desc; + } + result.setDesc(desc); + result.setText(text); + } else { + result.setDesc("unknown error"); + result.setText("未知错误"); } return result; } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SSLHttpRequest.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SSLHttpRequest.java index dd5acbd7..947a1ba9 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SSLHttpRequest.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SSLHttpRequest.java @@ -6,7 +6,10 @@ import java.io.IOException; import java.io.InputStream; import java.security.KeyStore; +import javax.net.ssl.SSLContext; + import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeSocketFactory; import org.apache.http.conn.ssl.SSLSocketFactory; /** @@ -27,7 +30,8 @@ public class SSLHttpRequest extends HttpRequest { public SSLHttpRequest(String password, InputStream inputStream) { super(); try { - KeyStore trustStore = KeyStore.getInstance("PKCS12"); + KeyStore trustStore = KeyStore + .getInstance(com.foxinmy.weixin4j.model.Consts.PKCS12); trustStore.load(inputStream, password.toCharArray()); SSLSocketFactory socketFactory = new SSLSocketFactory(trustStore, password); @@ -45,4 +49,11 @@ public class SSLHttpRequest extends HttpRequest { } } } + + public SSLHttpRequest(SSLContext sslContext) { + super(); + SchemeSocketFactory socketFactory = new SSLSocketFactory(sslContext); + client.getConnectionManager().getSchemeRegistry() + .register(new Scheme("https", 443, socketFactory)); + } } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/XmlResult.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/XmlResult.java index 9ac244da..8e12dbb8 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/XmlResult.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/XmlResult.java @@ -2,6 +2,7 @@ package com.foxinmy.weixin4j.http; import java.io.Serializable; +import com.foxinmy.weixin4j.model.Consts; import com.thoughtworks.xstream.annotations.XStreamAlias; /** @@ -16,8 +17,6 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; public class XmlResult implements Serializable { private static final long serialVersionUID = -6185313616955051150L; - public static final String SUCCESS = "SUCCESS"; - public static final String FAIL = "FAIL"; @XStreamAlias("return_code") private String returnCode;// 此字段是通信标识,非交易 标识,交易是否成功需要查 看 result_code 来判断 非空 @@ -71,15 +70,15 @@ public class XmlResult implements Serializable { } public XmlResult() { - this(SUCCESS.toLowerCase(), ""); + this(Consts.SUCCESS.toLowerCase(), ""); } public XmlResult(String returnCode, String returnMsg) { this.returnCode = returnCode; this.returnMsg = returnMsg; - if (returnCode.equalsIgnoreCase(SUCCESS)) { - this.resultCode = SUCCESS.toLowerCase(); - this.errCode = SUCCESS.toLowerCase(); + if (returnCode.equalsIgnoreCase(Consts.SUCCESS)) { + this.resultCode = Consts.SUCCESS.toLowerCase(); + this.errCode = Consts.SUCCESS.toLowerCase(); this.errCodeDes = ""; } } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/error.xml b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/error.xml index 03758181..8f6cf196 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/error.xml +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/error.xml @@ -362,6 +362,83 @@ api unauthorized 接口未授权 + + + 7000000 + 请求正常,无语义结果 + + + 7000001 + 缺失请求参数 + + + 7000002 + signature 参数无效 + + + 7000003 + 地理位置相关配置 1 无效 + + + 7000004 + 地理位置相关配置 2 无效 + + + 7000005 + 请求地理位置信息失败 + + + 7000006 + 地理位置结果解析失败 + + + 7000007 + 内部初始化失败 + + + 7000008 + 非法 appid(获取密钥失败) + + + 7000009 + 请求语义服务失败 + + + 7000010 + 非法 post 请求 + + + 7000011 + post 请求 json 字段无效 + + + 7000030 + 查询 query 太短 + + + 7000031 + 查询 query 太长 + + + 7000032 + 城市、经纬度信息缺失 + + + 7000033 + query 请求语义处理失败 + + + 7000034 + 获取天气信息失败 + + + 7000035 + 获取股票信息失败 + + + 7000036 + utf8 编码转换失败 + SYSTEMERROR diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/Consts.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/Consts.java new file mode 100644 index 00000000..bb8e7ee8 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/Consts.java @@ -0,0 +1,11 @@ +package com.foxinmy.weixin4j.model; + +public final class Consts { + public static final String SUCCESS = "SUCCESS"; + public static final String FAIL = "FAIL"; + public static final String SunX509 = "SunX509"; + public static final String JKS = "JKS"; + public static final String PKCS12 = "PKCS12"; + public static final String TLS = "TLS"; + public static final String X509 = "X.509"; +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/WeixinAccount.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/WeixinAccount.java index 8c7ae02a..20eb1198 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/WeixinAccount.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/WeixinAccount.java @@ -2,6 +2,8 @@ package com.foxinmy.weixin4j.model; import java.io.Serializable; +import org.apache.commons.lang3.StringUtils; + /** * 微信账户信息 * @@ -31,6 +33,8 @@ public class WeixinAccount implements Serializable { private String mchId; // 微信支付分配的设备号 private String deviceInfo; + // 微信支付版本号(如果无则按照mchId来做判断) + private int version; // 是否已经认证 private boolean isAlive; @@ -135,6 +139,17 @@ public class WeixinAccount implements Serializable { this.isSubscribe = isSubscribe; } + public int getVersion() { + if (version == 0) { + return StringUtils.isNotBlank(mchId) ? 3 : 2; + } + return version; + } + + public void setVersion(int version) { + this.version = version; + } + public WeixinAccount() { } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/msg/event/ScanEventMessage.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/msg/event/ScanEventMessage.java index ccf2a6b8..ec697abe 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/msg/event/ScanEventMessage.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/msg/event/ScanEventMessage.java @@ -19,7 +19,6 @@ public class ScanEventMessage extends EventMessage { } private static final long serialVersionUID = 8078674062833071562L; - private static final String PARA_PREFIX = "qrscene_"; @XStreamAlias("EventKey") private String eventKey; // 事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id @@ -35,7 +34,7 @@ public class ScanEventMessage extends EventMessage { } public String getParameter() { - return eventKey.replace(PARA_PREFIX, ""); + return eventKey.replace("qrscene_", ""); } @Override diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/DateUtil.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/DateUtil.java index 9b2a086c..9ffd2b1f 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/DateUtil.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/DateUtil.java @@ -16,12 +16,15 @@ import java.util.Date; */ public class DateUtil { private static final String yyyyMMdd = "yyyyMMdd"; + private static final String yyyy_MM_dd = "yyyy-MM-dd"; private static final String yyyyMMddHHmmss = "yyyyMMddHHmmss"; public static String fortmat2yyyyMMdd(Date date) { return new SimpleDateFormat(yyyyMMdd).format(date); } - + public static String fortmat2yyyy_MM_dd(Date date) { + return new SimpleDateFormat(yyyy_MM_dd).format(date); + } public static String fortmat2yyyyMMddHHmmss(Date date) { return new SimpleDateFormat(yyyyMMddHHmmss).format(date); } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/MapUtil.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/MapUtil.java index dc7f3df1..958681d4 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/MapUtil.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/MapUtil.java @@ -2,19 +2,19 @@ package com.foxinmy.weixin4j.util; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.util.Comparator; import java.util.Map; import java.util.Set; import java.util.TreeMap; +import org.apache.http.Consts; + import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.TypeReference; /** * 签名工具类 + * * @className MapUtil * @author jy * @date 2014年10月31日 @@ -22,10 +22,8 @@ import com.alibaba.fastjson.TypeReference; * @see */ public class MapUtil { - private final static Charset charset = StandardCharsets.UTF_8; - public static String toJoinString(Object object, boolean encoder, - boolean lowerCase, JSONObject extra) { + boolean lowerCase, Map extra) { String text = JSON.toJSONString(object); Map map = new TreeMap( new Comparator() { @@ -34,14 +32,18 @@ public class MapUtil { return o1.compareTo(o2); } }); + map.putAll(JSON.parseObject(text, new TypeReference>() { })); if (extra != null && !extra.isEmpty()) { - for (String key : extra.keySet()) { - map.put(key, extra.getString(key)); - } + map.putAll(extra); } + return toJoinString(map, encoder, lowerCase); + } + + public static String toJoinString(Map map, boolean encoder, + boolean lowerCase) { StringBuilder sb = new StringBuilder(); Set> set = map.entrySet(); try { @@ -50,14 +52,14 @@ public class MapUtil { sb.append(entry.getKey().toLowerCase()) .append("=") .append(URLEncoder.encode(entry.getValue(), - charset.name())).append("&"); + Consts.UTF_8.name())).append("&"); } } else if (encoder) { for (Map.Entry entry : set) { sb.append(entry.getKey()) .append("=") .append(URLEncoder.encode(entry.getValue(), - charset.name())).append("&"); + Consts.UTF_8.name())).append("&"); } } else if (lowerCase) { for (Map.Entry entry : set) { diff --git a/weixin4j-mp/README.md b/weixin4j-mp/README.md index d7a261ad..92ebd2ff 100644 --- a/weixin4j-mp/README.md +++ b/weixin4j-mp/README.md @@ -52,4 +52,10 @@ weixin4j-mp * 2014-11-06 - + **weixin-mp**: 新增`退款接口` \ No newline at end of file + + **weixin-mp-api**: 新增`退款接口` + +* 2014-11-08 + + + **weixin-mp-api**: 新增V2版本`退款申请`、`退款查询`、`对账单下载`三个接口 + + + **weixin-mp-api**: 新增一个简单的`语义理解`接口 \ No newline at end of file diff --git a/weixin4j-mp/weixin4j-mp-api/README.md b/weixin4j-mp/weixin4j-mp-api/README.md index 7f8fcf9f..ad9e5592 100644 --- a/weixin4j-mp/weixin4j-mp-api/README.md +++ b/weixin4j-mp/weixin4j-mp-api/README.md @@ -92,4 +92,10 @@ weixin.properties说明 * 2014-11-06 - + 新增`退款申请`接口 \ No newline at end of file + + 新增`退款申请`接口 + +* 2014-11-08 + + + 新增V2版本`退款申请`、`退款查询`、`对账单下载`三个接口 + + + 新增一个简单的`语义理解`接口 \ No newline at end of file diff --git a/weixin4j-mp/weixin4j-mp-api/pom.xml b/weixin4j-mp/weixin4j-mp-api/pom.xml index 31d9ad28..2325bf6c 100644 --- a/weixin4j-mp/weixin4j-mp-api/pom.xml +++ b/weixin4j-mp/weixin4j-mp-api/pom.xml @@ -14,21 +14,8 @@ 微信公众号API weixin4j-mp-api - - - src/main/resources - - *.* - - - - - org.jsoup - jsoup - ${jsoup.version} - jaxen jaxen diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/WeixinProxy.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/WeixinProxy.java index 0d819c8c..06af2d9d 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/WeixinProxy.java +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/WeixinProxy.java @@ -32,9 +32,9 @@ import com.foxinmy.weixin4j.mp.model.User; import com.foxinmy.weixin4j.mp.msg.model.Article; import com.foxinmy.weixin4j.mp.msg.model.BaseMsg; import com.foxinmy.weixin4j.mp.msg.notify.BaseNotify; +import com.foxinmy.weixin4j.mp.payment.Refund; +import com.foxinmy.weixin4j.mp.payment.RefundResult; import com.foxinmy.weixin4j.mp.payment.v2.Order; -import com.foxinmy.weixin4j.mp.payment.v3.Refund; -import com.foxinmy.weixin4j.mp.payment.v3.RefundResult; import com.foxinmy.weixin4j.mp.response.TemplateMessage; import com.foxinmy.weixin4j.mp.type.BillType; import com.foxinmy.weixin4j.mp.type.IdQuery; @@ -740,15 +740,16 @@ public class WeixinProxy { * 用户ID * @param transid * 交易单号 - * @param orderNo + * @param outTradeNo * 订单号 * @param status * 成功|失败 * @param statusMsg * status为失败时携带的信息 - * @return 调用结果 - * @throws WeixinException + * @return 发货处理结果 + * @since V2 & V3 * @see com.foxinmy.weixin4j.mp.api.PayApi + * @throws WeixinException */ public JsonResult deliverNotify(String openId, String transid, String outTradeNo, boolean status, String statusMsg) @@ -780,6 +781,7 @@ public class WeixinProxy { * 维权单号 * @return 调用结果 * @see com.foxinmy.weixin4j.mp.api.PayApi + * @since V2 & V3 * @throws WeixinException */ public JsonResult updateFeedback(String openId, String feedbackId) @@ -814,6 +816,7 @@ public class WeixinProxy { * 下载对账单的类型 ALL,返回当日所有订单信息, 默认值 SUCCESS,返回当日成功支付的订单 * REFUND,返回当日退款订单 * @return excel表格 + * @since V2 & V3 * @see com.foxinmy.weixin4j.mp.api.PayApi * @throws WeixinException * @throws IOException @@ -824,7 +827,7 @@ public class WeixinProxy { } /** - * 申请退款(请求需要双向证书)
+ * 申请退款(V3请求需要双向证书)
*

* 交易时间超过 1 年的订单无法提交退款;
* 支持部分退款,部分退需要设置相同的订单号和不同的 out_refund_no。一笔退款失 败后重新提交,要采用原来的 @@ -832,7 +835,7 @@ public class WeixinProxy { *

* * @param ca - * 证书文件 + * 证书文件V2版本时无需传入 * @param idQuery * ) 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级: * transaction_id> out_trade_no @@ -844,28 +847,36 @@ public class WeixinProxy { * 退款总金额,单位为元,可以做部分退款 * @param opUserId * 操作员帐号, 默认为商户号 + * @param opUserPasswd + * V3版本留空,V2版本需传入值 + * + * @return 退款申请结果 * @see com.foxinmy.weixin4j.mp.api.PayApi + * @see com.foxinmy.weixin4j.mp.payment.RefundResult + * @since V2 & V3 * @throws WeixinException * @throws IOException - * @return 退款结果 */ public RefundResult refund(InputStream ca, IdQuery idQuery, String outRefundNo, double totalFee, double refundFee, - String opUserId) throws WeixinException, IOException { - return payApi.refund(idQuery, outRefundNo, totalFee, refundFee, - opUserId); + String opUserId, String opUserPasswd) throws WeixinException, + IOException { + return payApi.refund(ca, idQuery, outRefundNo, totalFee, refundFee, + opUserId, opUserPasswd); } /** - * 使用properties中配置的ca文件 + * 不同的退款接口选择
+ * V3支付则采用properties中配置的ca文件
+ * V2支付则需要传入opUserPasswd参数
* - * @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#refund(InputStream, IdQuery, String, double, double, String)} + * @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#refund(InputStream, IdQuery, String, double, double, String,String)} */ public RefundResult refund(IdQuery idQuery, String outRefundNo, - double totalFee, double refundFee, String opUserId) - throws WeixinException, IOException { + double totalFee, double refundFee, String opUserId, + String opUserPasswd) throws WeixinException, IOException { return payApi.refund(idQuery, outRefundNo, totalFee, refundFee, - opUserId); + opUserId, opUserPasswd); } /** @@ -876,8 +887,10 @@ public class WeixinProxy { * 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id * 四个参数必填一个,优先级为: * refund_id>out_refund_no>transaction_id>out_trade_no - * @see com.foxinmy.weixin4j.mp.api.PayApi * @return 退款记录 + * @see com.foxinmy.weixin4j.mp.api.PayApi + * @see com.foxinmy.weixin4j.mp.payment.Refund + * @since V2 & V3 * @throws WeixinException */ public Refund refundQuery(IdQuery idQuery) throws WeixinException { @@ -893,6 +906,7 @@ public class WeixinProxy { * 商户系统内部的订单号 * @return 执行结果 * @see com.foxinmy.weixin4j.mp.api.PayApi + * @since V3 * @throws WeixinException */ public XmlResult closeOrder(String outTradeNo) throws WeixinException { diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/BaseApi.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/BaseApi.java index b7d422d2..6248698a 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/BaseApi.java +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/BaseApi.java @@ -1,7 +1,5 @@ package com.foxinmy.weixin4j.mp.api; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.ResourceBundle; import java.util.regex.Matcher; @@ -24,7 +22,6 @@ import com.thoughtworks.xstream.mapper.DefaultMapper; public class BaseApi { protected final HttpRequest request = new HttpRequest(); protected final static XStream mapXstream = XStream.get(); - protected final Charset utf8 = StandardCharsets.UTF_8; private final static ResourceBundle weixinBundle; static { weixinBundle = ResourceBundle @@ -40,7 +37,7 @@ public class BaseApi { @SuppressWarnings("unchecked") protected Map xml2map(String xml) { - return mapXstream.fromXML(xml,Map.class); + return mapXstream.fromXML(xml, Map.class); } protected String getRequestUri(String key) { diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/HelperApi.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/HelperApi.java index c30d22f2..1c74d7c0 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/HelperApi.java +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/HelperApi.java @@ -1,9 +1,13 @@ package com.foxinmy.weixin4j.mp.api; import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.TypeReference; import com.foxinmy.weixin4j.exception.WeixinException; import com.foxinmy.weixin4j.http.Response; import com.foxinmy.weixin4j.model.Token; +import com.foxinmy.weixin4j.model.WeixinAccount; +import com.foxinmy.weixin4j.mp.model.SemQuery; +import com.foxinmy.weixin4j.mp.model.SemResult; import com.foxinmy.weixin4j.token.TokenHolder; /** @@ -44,4 +48,28 @@ public class HelperApi extends BaseApi { return response.getAsJson().getString("short_url"); } + + /** + * 语义理解 + * + * @param semQuery + * 语义理解协议 + * @return 语义理解结果 + * @see com.foxinmy.weixin4j.mp.model.SemQuery + * @see com.foxinmy.weixin4j.mp.model.SemResult + * @see 语义理解 + * @throws WeixinException + */ + public SemResult semantic(SemQuery semQuery) throws WeixinException { + WeixinAccount weixinAccount = tokenHolder.getAccount(); + String semantic_uri = getRequestUri("semantic_uri"); + Token token = tokenHolder.getToken(); + semQuery.appid(weixinAccount.getAppId()); + Response response = request.post( + String.format(semantic_uri, token.getAccessToken()), + semQuery.toJson()); + return response.getAsObject(new TypeReference() { + }); + } } diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/PayApi.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/PayApi.java index 6e7486da..99e03638 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/PayApi.java +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/PayApi.java @@ -6,19 +6,29 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.StringReader; +import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.security.cert.CertificateFactory; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; + import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.http.Consts; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import com.alibaba.fastjson.JSON; @@ -33,10 +43,10 @@ import com.foxinmy.weixin4j.http.XmlResult; import com.foxinmy.weixin4j.model.Token; import com.foxinmy.weixin4j.model.WeixinAccount; import com.foxinmy.weixin4j.mp.payment.PayUtil; +import com.foxinmy.weixin4j.mp.payment.Refund; +import com.foxinmy.weixin4j.mp.payment.RefundConverter; +import com.foxinmy.weixin4j.mp.payment.RefundResult; import com.foxinmy.weixin4j.mp.payment.v2.Order; -import com.foxinmy.weixin4j.mp.payment.v3.Refund; -import com.foxinmy.weixin4j.mp.payment.v3.RefundConverter; -import com.foxinmy.weixin4j.mp.payment.v3.RefundResult; import com.foxinmy.weixin4j.mp.type.BillType; import com.foxinmy.weixin4j.mp.type.IdQuery; import com.foxinmy.weixin4j.mp.type.IdType; @@ -77,6 +87,7 @@ public class PayApi extends BaseApi { * @param statusMsg * status为失败时携带的信息 * @return 发货处理结果 + * @since V2 & V3 * @throws WeixinException */ public JsonResult deliverNotify(String openId, String transid, @@ -96,8 +107,7 @@ public class PayApi extends BaseApi { param.put("deliver_timestamp", System.currentTimeMillis() / 1000 + ""); param.put("deliver_status", status ? "1" : "0"); param.put("deliver_msg", statusMsg); - param.put("app_signature", DigestUtils.sha1Hex(MapUtil.toJoinString( - param, false, true, null))); + param.put("app_signature", PayUtil.paysignSha(param, null)); param.put("sign_method", "sha1"); Response response = request.post( @@ -135,10 +145,9 @@ public class PayApi extends BaseApi { obj.put("appkey", weixinAccount.getPaySignKey()); obj.put("package", sb.toString()); obj.put("timestamp", timestamp); - String signature = DigestUtils.sha1Hex(MapUtil.toJoinString(obj, false, - true, null)); + String signature = PayUtil.paysignSha(obj, null); - obj = new JSONObject(); + obj.clear(); obj.put("appid", weixinAccount.getAppId()); obj.put("package", sb.toString()); obj.put("timestamp", timestamp); @@ -170,6 +179,7 @@ public class PayApi extends BaseApi { * @param feedbackId * 维权单号 * @return 维权处理结果 + * @since V2 & V3 * @throws WeixinException */ public JsonResult updateFeedback(String openId, String feedbackId) @@ -226,46 +236,146 @@ public class PayApi extends BaseApi { * 退款总金额,单位为元,可以做部分退款 * @param opUserId * 操作员帐号, 默认为商户号 + * @param opUserPasswd + * V3版本留空,V2版本需传入值 * * @return 退款申请结果 - * @see com.foxinmy.weixin4j.mp.payment.v3.RefundResult + * @see com.foxinmy.weixin4j.mp.payment.RefundResult + * @since V2 & V3 * @throws WeixinException * @throws IOException */ public RefundResult refund(InputStream ca, IdQuery idQuery, String outRefundNo, double totalFee, double refundFee, - String opUserId) throws WeixinException, IOException { + String opUserId, String opUserPasswd) throws WeixinException, + IOException { WeixinAccount weixinAccount = tokenHolder.getAccount(); - Map map = baseMap2V3(idQuery); - map.put("out_refund_no", outRefundNo); - map.put("total_fee", DateUtil.formaFee2Fen(totalFee)); - map.put("refund_fee", DateUtil.formaFee2Fen(refundFee)); - if (StringUtils.isBlank(opUserId)) { - opUserId = weixinAccount.getMchId(); + + int version = weixinAccount.getVersion(); + String refund_uri = getRequestUri(String.format("refund_v%d_uri", + version)); + Response response = null; + if (version == 2) { + Map map = new HashMap(); + map.put("input_charset", Consts.UTF_8.name()); + // 版本号 + // 填写为 1.0 时,操作员密码为明文 + // 填写为 1.1 时,操作员密码为 MD5(密码)值 + map.put("service_version", "1.1"); + map.put("partner", weixinAccount.getPartnerId()); + map.put("out_refund_no", outRefundNo); + map.put("total_fee", DateUtil.formaFee2Fen(totalFee)); + map.put("refund_fee", DateUtil.formaFee2Fen(refundFee)); + map.put(idQuery.getType().getName(), idQuery.getId()); + if (StringUtils.isBlank(opUserId)) { + opUserId = weixinAccount.getPartnerId(); + } + map.put("op_user_id", opUserId); + map.put("op_user_passwd", DigestUtils.md5Hex(opUserPasswd)); + // 以下几个字段可能用不到 记录下来 + // [接收人帐号] + // recv_user_id + // 转账退款接收退款的财付通帐号。 + // 一般无需填写,只有退银行失败,资金转入商 户号现金账号时(即状态为转入代发,查询返 回的 refund_status 是 7 或 + // 11),填写原退款 单号并填写此字段,资金才会退到指定财付通 账号。其他情况此字段忽略 + // --------------------------------------------------------------------------------- + // [接收人姓名] + // reccv_user_name + // 转账退款接收退款的姓名(需与接收退款的财 付通帐号绑定的姓名一致) + // --------------------------------------------------------------------------------- + // [通过商户订单号退款] + // use_spbill_no_flag + // 若通过接口 (https://www.tenpay.com/cgi-bin/v1.0/pay _gate.cgi) + // 支付的商户订单号来退款,则取值 为 1;而通过本文档支付接口的,则无需传值。 + // --------------------------------------------------------------------------------- + // [退款类型] + // refund_type + // 为空或者填 1:商户号余额退款;2:现金帐号 退款;3:优先商户号退款,若商户号余额不足, 再做现金帐号退款。使用 2 或 3 + // 时,需联系财 付通开通此功能。 + String sign = PayUtil + .paysignMd5(map, weixinAccount.getPartnerKey()); + map.put("sign", sign.toLowerCase()); + + SSLContext ctx = null; + try { + File file = new File(ConfigUtil.getValue("ca_file")); + + String jksPwd = ""; + File jksFile = new File(String.format("%s/tenpay_cacert.jks", + file.getParent())); + KeyStore ks = null; + // create jks ca + if (!jksFile.exists()) { + CertificateFactory cf = CertificateFactory + .getInstance(com.foxinmy.weixin4j.model.Consts.X509); + java.security.cert.Certificate cert = cf + .generateCertificate(PayUtil.class + .getResourceAsStream("cacert.pem")); + ks = KeyStore + .getInstance(com.foxinmy.weixin4j.model.Consts.JKS); + ks.load(null, null); + ks.setCertificateEntry("tenpay", cert); + ks.store(new FileOutputStream(jksFile), + jksPwd.toCharArray()); + } + // load jks ca + TrustManagerFactory tmf = TrustManagerFactory + .getInstance(com.foxinmy.weixin4j.model.Consts.SunX509); + ks = KeyStore + .getInstance(com.foxinmy.weixin4j.model.Consts.JKS); + ks.load(new FileInputStream(jksFile), jksPwd.toCharArray()); + tmf.init(ks); + // load pfx ca + KeyManagerFactory kmf = KeyManagerFactory + .getInstance(com.foxinmy.weixin4j.model.Consts.SunX509); + ks = KeyStore + .getInstance(com.foxinmy.weixin4j.model.Consts.PKCS12); + ks.load(ca, weixinAccount.getPartnerId().toCharArray()); + kmf.init(ks, weixinAccount.getPartnerId().toCharArray()); + + ctx = SSLContext + .getInstance(com.foxinmy.weixin4j.model.Consts.TLS); + ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), + new SecureRandom()); + } catch (Exception e) { + throw new WeixinException(e.getMessage()); + } + SSLHttpRequest request = new SSLHttpRequest(ctx); + response = request.get(refund_uri, map); + } else if (version == 3) { + Map map = baseMap2V3(idQuery); + map.put("out_refund_no", outRefundNo); + map.put("total_fee", DateUtil.formaFee2Fen(totalFee)); + map.put("refund_fee", DateUtil.formaFee2Fen(refundFee)); + if (StringUtils.isBlank(opUserId)) { + opUserId = weixinAccount.getMchId(); + } + map.put("op_user_id", opUserId); + String sign = PayUtil + .paysignMd5(map, weixinAccount.getPaySignKey()); + map.put("sign", sign); + String param = map2xml(map); + SSLHttpRequest request = new SSLHttpRequest( + weixinAccount.getMchId(), ca); + response = request.post(refund_uri, param); } - map.put("op_user_id", opUserId); - String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey()); - map.put("sign", sign); - String param = map2xml(map); - String refund_uri = getRequestUri("refund_uri"); - SSLHttpRequest request = new SSLHttpRequest(weixinAccount.getMchId(), - ca); - Response response = request.post(refund_uri, param); return response.getAsObject(new TypeReference() { }); } /** - * 使用properties中配置的ca文件 + * 不同的退款接口选择
+ * 默认采用properties中配置的ca文件
+ * V2支付则需要传入opUserPasswd参数
* - * @see {@link com.foxinmy.weixin4j.mp.api.PayApi#refund(InputStream, IdQuery, String, double, double, String)} + * @see {@link com.foxinmy.weixin4j.mp.api.PayApi#refund(InputStream, IdQuery, String, double, double, String, String)} */ public RefundResult refund(IdQuery idQuery, String outRefundNo, - double totalFee, double refundFee, String opUserId) - throws WeixinException, IOException { - File caFile = new File(ConfigUtil.getValue("ca_file")); - return refund(new FileInputStream(caFile), idQuery, outRefundNo, - totalFee, refundFee, opUserId); + double totalFee, double refundFee, String opUserId, + String opUserPasswd) throws WeixinException, IOException { + File ca = new File(ConfigUtil.getValue("ca_file")); + return refund(new FileInputStream(ca), idQuery, outRefundNo, totalFee, + refundFee, opUserId, opUserPasswd); } /** @@ -283,8 +393,8 @@ public class PayApi extends BaseApi { String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey()); map.put("sign", sign); try { - map.put("long_url", URLEncoder.encode(url, utf8.name())); - } catch (UnsupportedEncodingException e) { + map.put("long_url", URLEncoder.encode(url, Consts.UTF_8.name())); + } catch (UnsupportedEncodingException ignore) { ; } String param = map2xml(map); @@ -302,6 +412,7 @@ public class PayApi extends BaseApi { * @param outTradeNo * 商户系统内部的订单号 * @return 处理结果 + * @since V3 * @throws WeixinException */ public XmlResult closeOrder(String outTradeNo) throws WeixinException { @@ -329,6 +440,7 @@ public class PayApi extends BaseApi { * 下载对账单的类型 ALL,返回当日所有订单信息, 默认值 SUCCESS,返回当日成功支付的订单 * REFUND,返回当日退款订单 * @return excel表格 + * @since V2 & V3 * @throws WeixinException * @throws IOException */ @@ -337,7 +449,7 @@ public class PayApi extends BaseApi { WeixinAccount weixinAccount = tokenHolder.getAccount(); if (billDate == null) { Calendar now = Calendar.getInstance(); - now.add(Calendar.DAY_OF_MONTH, -10); + now.add(Calendar.DAY_OF_MONTH, -1); billDate = now.getTime(); } if (billType == null) { @@ -351,17 +463,37 @@ public class PayApi extends BaseApi { if (file.exists()) { return file; } - Map map = baseMap2V3(null); - map.put("bill_date", _billDate); - map.put("bill_type", billType.name()); - String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey()); - map.put("sign", sign); - String param = map2xml(map); - String downloadbill_uri = getRequestUri("downloadbill_uri"); - Response response = request.post(downloadbill_uri, param); + int version = weixinAccount.getVersion(); + String downloadbill_uri = getRequestUri(String.format( + "downloadbill_v%d_uri", version)); + Response response = null; + Charset charset = Consts.UTF_8; + if (version == 2) { + Map map = new LinkedHashMap(); + map.put("spid", weixinAccount.getPartnerId()); + map.put("trans_time", DateUtil.fortmat2yyyy_MM_dd(billDate)); + map.put("stamp", Long.toString(System.currentTimeMillis() / 100)); + map.put("cft_signtype", "0"); + map.put("mchtype", Integer.toString(billType.getVal())); + map.put("key", weixinAccount.getPartnerKey()); + String sign = DigestUtils.md5Hex(MapUtil.toJoinString(map, false, + false)); + map.put("sign", sign.toLowerCase()); + response = request.get(downloadbill_uri, map); + charset = Charset.forName("GBK"); + } else if (version == 3) { + Map map = baseMap2V3(null); + map.put("bill_date", _billDate); + map.put("bill_type", billType.name()); + String sign = PayUtil + .paysignMd5(map, weixinAccount.getPaySignKey()); + map.put("sign", sign); + String param = map2xml(map); + response = request.post(downloadbill_uri, param); + } - BufferedReader reader = new BufferedReader(new StringReader( - response.getAsString())); + BufferedReader reader = new BufferedReader(new InputStreamReader( + response.getStream(), charset)); String line = null; List bills = new LinkedList(); while ((line = reader.readLine()) != null) { @@ -389,17 +521,33 @@ public class PayApi extends BaseApi { * 四个参数必填一个,优先级为: * refund_id>out_refund_no>transaction_id>out_trade_no * @return 退款记录 - * @see com.foxinmy.weixin4j.mp.payment.v3.Refund + * @see com.foxinmy.weixin4j.mp.payment.Refund + * @since V2 & V3 * @throws WeixinException */ public Refund refundQuery(IdQuery idQuery) throws WeixinException { WeixinAccount weixinAccount = tokenHolder.getAccount(); - Map map = baseMap2V3(idQuery); - String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey()); - map.put("sign", sign); - String param = map2xml(map); - String refundquery_uri = getRequestUri("refundquery_uri"); - Response response = request.post(refundquery_uri, param); + int version = weixinAccount.getVersion(); + String refundquery_uri = getRequestUri(String.format( + "refundquery_v%d_uri", version)); + Response response = null; + if (version == 2) { + Map map = new HashMap(); + map.put("input_charset", Consts.UTF_8.name()); + map.put("partner", weixinAccount.getPartnerId()); + map.put(idQuery.getType().getName(), idQuery.getId()); + String sign = PayUtil + .paysignMd5(map, weixinAccount.getPartnerKey()); + map.put("sign", sign.toLowerCase()); + response = request.get(refundquery_uri, map); + } else if (version == 3) { + Map map = baseMap2V3(idQuery); + String sign = PayUtil + .paysignMd5(map, weixinAccount.getPaySignKey()); + map.put("sign", sign); + String param = map2xml(map); + response = request.post(refundquery_uri, param); + } return new RefundConverter().fromXML(response.getAsString()); } diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/weixin.properties b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/weixin.properties index b343bc02..f2deb48b 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/weixin.properties +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/weixin.properties @@ -5,10 +5,15 @@ # http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E5%8F%A3%E9%A2%91%E7%8E%87%E9%99%90%E5%88%B6%E8%AF%B4%E6%98%8E # ---------------------------------------------------------------------------- -api_base_url=https://api.weixin.qq.com/cgi-bin +api_base_url=https://api.weixin.qq.com +api_cgi_url={api_base_url}/cgi-bin mp_base_url=https://mp.weixin.qq.com/cgi-bin file_base_url=http://file.api.weixin.qq.com/cgi-bin mch_base_url=https://api.mch.weixin.qq.com +tenpay_base_url=http://mch.tenpay.com +tenpay_ssl_base_url=https://mch.tenpay.com +tenpay_gw_base_url=https://gw.tenpay.com + # \u7f51\u9875\u6388\u6743\u83b7\u53d6\u7528\u6237\u4fe1\u606f user_auth_uri=https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect @@ -16,68 +21,78 @@ sns_user_token_uri=https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&se sns_user_info_uri=https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN # \u76f4\u63a5\u83b7\u53d6\u7528\u6237\u4fe1\u606f -api_user_info_uri={api_base_url}/user/info?access_token=%s&openid=%s&lang=zh_CN +api_user_info_uri={api_cgi_url}/user/info?access_token=%s&openid=%s&lang=zh_CN # \u83b7\u53d6token -api_token_uri={api_base_url}/token?grant_type=client_credential&appid=%s&secret=%s +api_token_uri={api_cgi_url}/token?grant_type=client_credential&appid=%s&secret=%s # \u83b7\u53d6\u4e8c\u7ef4\u7801 -qr_ticket_uri={api_base_url}/qrcode/create?access_token=%s +qr_ticket_uri={api_cgi_url}/qrcode/create?access_token=%s qr_image_uri={mp_base_url}/showqrcode?ticket=%s # \u4e0a\u4f20\u5a92\u4f53\u6587\u4ef6 file_upload_uri={file_base_url}/media/upload?access_token=%s&type=%s # \u4e0b\u8f7d\u5a92\u4f53\u6587\u4ef6 file_download_uri={file_base_url}/media/get?access_token=%s&media_id=%s # \u53d1\u9001\u5ba2\u670d\u6d88\u606f -custom_notify_uri={api_base_url}/message/custom/send?access_token=%s +custom_notify_uri={api_cgi_url}/message/custom/send?access_token=%s # \u521b\u5efa\u5206\u7ec4 -group_create_uri={api_base_url}/groups/create?access_token=%s +group_create_uri={api_cgi_url}/groups/create?access_token=%s # \u67e5\u8be2\u5206\u7ec4 -group_get_uri={api_base_url}/groups/get?access_token=%s +group_get_uri={api_cgi_url}/groups/get?access_token=%s # \u67e5\u8be2\u7528\u6237\u6240\u5728\u5206\u7ec4 -group_getid_uri={api_base_url}/groups/getid?access_token=%s +group_getid_uri={api_cgi_url}/groups/getid?access_token=%s # \u4fee\u6539\u5206\u7ec4\u540d -group_modify_uri={api_base_url}/groups/update?access_token=%s +group_modify_uri={api_cgi_url}/groups/update?access_token=%s # \u79fb\u52a8\u7528\u6237\u5206\u7ec4 -group_move_uri={api_base_url}/groups/members/update?access_token=%s +group_move_uri={api_cgi_url}/groups/members/update?access_token=%s # \u83b7\u53d6\u5173\u6ce8\u7740 -following_uri={api_base_url}/user/get?access_token=%s&next_openid=%s +following_uri={api_cgi_url}/user/get?access_token=%s&next_openid=%s # \u81ea\u5b9a\u4e49\u83dc\u5355 -menu_create_uri={api_base_url}/menu/create?access_token=%s +menu_create_uri={api_cgi_url}/menu/create?access_token=%s # \u67e5\u8be2\u83dc\u5355 -menu_get_uri={api_base_url}/menu/get?access_token=%s +menu_get_uri={api_cgi_url}/menu/get?access_token=%s # \u5220\u9664\u83dc\u5355 -menu_delete_uri={api_base_url}/menu/delete?access_token=%s +menu_delete_uri={api_cgi_url}/menu/delete?access_token=%s # \u4e0a\u4f20\u56fe\u6587 -article_upload_uri={api_base_url}/media/uploadnews?access_token=%s +article_upload_uri={api_cgi_url}/media/uploadnews?access_token=%s # \u4e0a\u4f20\u89c6\u9891 video_upload_uri={file_base_url}/media/uploadvideo?access_token=%s # \u5206\u7ec4\u7fa4\u53d1 -mass_group_uri={api_base_url}/message/mass/sendall?access_token=%s +mass_group_uri={api_cgi_url}/message/mass/sendall?access_token=%s # openId\u7fa4\u53d1 -mass_openid_uri={api_base_url}/message/mass/send?access_token=%s +mass_openid_uri={api_cgi_url}/message/mass/send?access_token=%s # \u5220\u9664\u7fa4\u53d1 -mass_delete_uri={api_base_url}/message/mass/delete?access_token=%s +mass_delete_uri={api_cgi_url}/message/mass/delete?access_token=%s # \u5ba2\u670d\u804a\u5929\u8bb0\u5f55 -custom_record_uri={api_base_url}/customservice/getrecord?access_token=%s +custom_record_uri={api_cgi_url}/customservice/getrecord?access_token=%s # \u957f\u94fe\u63a5\u8f6c\u77ed\u94fe\u63a5 -shorturl_uri={api_base_url}/shorturl?access_token=%s +shorturl_uri={api_cgi_url}/shorturl?access_token=%s p_shorturl_uri={mch_base_url}/tools/shorturl # \u8bbe\u7f6e\u5907\u6ce8\u540d -updateremark_uri={api_base_url}/user/info/updateremark?access_token=%s +updateremark_uri={api_cgi_url}/user/info/updateremark?access_token=%s # \u6a21\u677f\u6d88\u606f -template_send_uri={api_base_url}/message/template/send?access_token=%s +template_send_uri={api_cgi_url}/message/template/send?access_token=%s +# \u8bed\u4e49\u7406\u89e3 +semantic_uri={api_base_url}/semantic/semproxy/search?access_token=%s + # \u8ba2\u5355\u67e5\u8be2 -orderquery_uri=https://api.weixin.qq.com/pay/orderquery?access_token=%s +orderquery_uri={api_base_url}/pay/orderquery?access_token=%s # \u53d1\u8d27\u901a\u77e5 -delivernotify_uri=https://api.weixin.qq.com/pay/delivernotify?access_token=%s +delivernotify_uri={api_base_url}/pay/delivernotify?access_token=%s # \u7ef4\u6743\u5904\u7406 -payfeedback_update_uri=https://api.weixin.qq.com/payfeedback/update?access_token=%s&openid=%s&feedbackid=%s +payfeedback_update_uri={api_base_url}/payfeedback/update?access_token=%s&openid=%s&feedbackid=%s +# \u5bf9\u8d26\u5355\u4e0b\u8f7d +downloadbill_v2_uri={tenpay_base_url}/cgi-bin/mchdown_real_new.cgi +# \u9000\u6b3e\u67e5\u8be2 +refundquery_v2_uri={tenpay_gw_base_url}/gateway/normalrefundquery.xml +# \u9000\u6b3e\u7533\u8bf7 +refund_v2_uri={tenpay_ssl_base_url}/refundapi/gateway/refund.xml + # \u8ba2\u5355\u67e5\u8be2 orderquery_v3_uri={mch_base_url}/pay/orderquery # \u5173\u95ed\u8ba2\u5355 closeorder_uri={mch_base_url}/pay/closeorder # \u5bf9\u8d26\u5355\u4e0b\u8f7d -downloadbill_uri={mch_base_url}/pay/downloadbill +downloadbill_v3_uri={mch_base_url}/pay/downloadbill # \u9000\u6b3e\u67e5\u8be2 -refundquery_uri={mch_base_url}/pay/refundquery +refundquery_v3_uri={mch_base_url}/pay/refundquery # \u9000\u6b3e\u7533\u8bf7 -refund_uri={mch_base_url}/secapi/pay/refund +refund_v3_uri={mch_base_url}/secapi/pay/refund diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/Button.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/Button.java index c02b4fe8..32fbbd98 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/Button.java +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/Button.java @@ -38,10 +38,10 @@ public class Button implements Serializable { public Button(String name, String value, ButtonType buttonType) { this.name = name; this.type = buttonType; - if (buttonType == ButtonType.click) { - this.key = value; - } else if (buttonType == ButtonType.view) { + if (buttonType == ButtonType.view) { this.url = value; + } else { + this.key = value; } } diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/SemQuery.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/SemQuery.java new file mode 100644 index 00000000..f02ced35 --- /dev/null +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/SemQuery.java @@ -0,0 +1,88 @@ +package com.foxinmy.weixin4j.mp.model; + +import java.io.Serializable; + +import com.alibaba.fastjson.JSONObject; +import com.foxinmy.weixin4j.mp.type.SemCategory; + +/** + * 语义理解参数 + * + * @className SemQuery + * @author jy + * @date 2014年11月7日 + * @since JDK 1.7 + * @see + */ +public class SemQuery implements Serializable { + + private static final long serialVersionUID = 679548284525912436L; + private JSONObject jsonObj; + + // 输入文本串 + public SemQuery(String query) { + jsonObj = new JSONObject(); + jsonObj.put("query", query); + } + + // 城市名称,与经纬度二选一传入 + public SemQuery city(String city) { + jsonObj.put("city", city); + return this; + } + + // 需要使用的服务类别,多个用,隔开,不能为空 + public SemQuery category(SemCategory... categorys) { + String category = ""; + if (categorys.length == 1) { + category = categorys[0].name(); + } else { + for (int i = 0; i < categorys.length - 1; i++) { + category += categorys[i].name() + ","; + } + category += categorys[categorys.length - 1].name(); + } + jsonObj.put("category", category); + return this; + } + + // App id,开发者的唯一标识,用于区分开放者, 如果为空,则没法使用上下文理解功能。 + public SemQuery appid(String appid) { + jsonObj.put("appid", appid); + return this; + } + + // 用户唯一 id(并非开发者 id),用于区分该开发者下不同用户,如果为空,则没法使用上下文理解功能。appid 和 uid + // 同时存在的情况下,才可以使用上下文理解功能。 + public SemQuery uid(String uid) { + jsonObj.put("uid", uid); + return this; + } + + // 区域名称,在城市存在的情况下可省;与经纬度 二选一传入 + public SemQuery region(String region) { + jsonObj.put("region", region); + return this; + } + + // 纬度经度;与城市二选一传入 + public SemQuery location(float latitude, float longitude) { + jsonObj.put("latitude", latitude); + jsonObj.put("longitude", longitude); + return this; + } + + // 输入文本串 + public static SemQuery build(String query) { + return new SemQuery(query); + } + + public String toJson() { + return jsonObj.toJSONString(); + } + + @Override + public String toString() { + return "SemQuery " + jsonObj; + } +} diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/SemResult.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/SemResult.java new file mode 100644 index 00000000..738284d8 --- /dev/null +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/SemResult.java @@ -0,0 +1,88 @@ +package com.foxinmy.weixin4j.mp.model; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.foxinmy.weixin4j.http.JsonResult; + +/** + * 语义理解结果 + * + * @className SemResult + * @author jy + * @date 2014年11月7日 + * @since JDK 1.7 + * @see 语义理解 + */ +public class SemResult extends JsonResult { + + private static final long serialVersionUID = 9051214458161068387L; + // 用户的输入字符串 + private String query; + // 服务的全局类型id,详见协议文档中垂直服务协议定义 + private String type; + // 语义理解后的结构化标识,各服务不同 + private JSONObject semantic; + // 部分类别的结果 + private JSONArray result; + // 部分类别的结果html5展示,目前不支持 + private String answer; + // 特殊回复说明 + private String text; + + public String getQuery() { + return query; + } + + public void setQuery(String query) { + this.query = query; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public JSONObject getSemantic() { + return semantic; + } + + public void setSemantic(JSONObject semantic) { + this.semantic = semantic; + } + + public JSONArray getResult() { + return result; + } + + public void setResult(JSONArray result) { + this.result = result; + } + + public String getAnswer() { + return answer; + } + + public void setAnswer(String answer) { + this.answer = answer; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + @Override + public String toString() { + return "SemResult [query=" + query + ", type=" + type + ", semantic=" + + semantic + ", result=" + result + ", answer=" + answer + + ", text=" + text + ", getCode()=" + getCode() + + ", getDesc()=" + getDesc() + "]"; + } +} diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/PayAction.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/PayAction.java index 5cd210cc..757a3574 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/PayAction.java +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/PayAction.java @@ -11,6 +11,7 @@ import org.slf4j.LoggerFactory; import com.alibaba.fastjson.JSONObject; import com.foxinmy.weixin4j.exception.PayException; import com.foxinmy.weixin4j.http.XmlResult; +import com.foxinmy.weixin4j.model.Consts; import com.foxinmy.weixin4j.model.WeixinAccount; import com.foxinmy.weixin4j.mp.payment.v2.NativePayNotifyV2; import com.foxinmy.weixin4j.mp.payment.v2.NativePayResponseV2; @@ -163,7 +164,7 @@ public class PayAction { weixinAccount.getPaySignKey()); log.info("微信签名----->sign={},vaild_sign={}", sign, valid_sign); if (!sign.equals(valid_sign)) { - return XStream.to(new XmlResult(XmlResult.FAIL, "签名错误")); + return XStream.to(new XmlResult(Consts.FAIL, "签名错误")); } return XStream.to(new XmlResult()); } diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/PayUtil.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/PayUtil.java index e22369b9..165268b5 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/PayUtil.java +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/PayUtil.java @@ -122,9 +122,9 @@ public class PayUtil { * @return */ public static String paysignSha(Object obj, String paySignKey) { - JSONObject extra = null; + Map extra = null; if (StringUtils.isNotBlank(paySignKey)) { - extra = new JSONObject(); + extra = new HashMap(); extra.put("appKey", paySignKey); } return DigestUtils.sha1Hex(MapUtil @@ -255,7 +255,7 @@ public class PayUtil { Map param = new HashMap(); param.put("appId", appId); param.put("url", url); - param.put("timeStamp", System.currentTimeMillis() / 1000 + ""); + param.put("timeStamp", Long.toString(System.currentTimeMillis() / 1000)); param.put("nonceStr", RandomUtil.generateString(16)); param.put("accessToken", accessToken); String sign = paysignSha(param, null); diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/Refund.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/Refund.java similarity index 64% rename from weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/Refund.java rename to weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/Refund.java index e03915e5..cd9e8853 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/Refund.java +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/Refund.java @@ -1,8 +1,7 @@ -package com.foxinmy.weixin4j.mp.payment.v3; +package com.foxinmy.weixin4j.mp.payment; import java.util.List; -import com.foxinmy.weixin4j.mp.payment.ApiResult; import com.thoughtworks.xstream.annotations.XStreamAlias; /** @@ -12,7 +11,7 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; * @author jy * @date 2014年11月1日 * @since JDK 1.7 - * @see com.foxinmy.weixin4j.mp.payment.v3.RefundDetail + * @see com.foxinmy.weixin4j.mp.payment.RefundDetail */ @XStreamAlias("xml") public class Refund extends ApiResult { @@ -27,6 +26,7 @@ public class Refund extends ApiResult { private String subMchId; // @XStreamAlias("refund_count") private int count;// 退款笔数 + private String partner; // 商户号V2 private List details; public String getTransactionId() { @@ -45,6 +45,10 @@ public class Refund extends ApiResult { return count; } + public String getPartner() { + return partner; + } + public List getDetails() { return details; } @@ -57,12 +61,13 @@ public class Refund extends ApiResult { public String toString() { return "Refund [transactionId=" + transactionId + ", orderNo=" + orderNo + ", subMchId=" + subMchId + ", count=" + count - + ", details=" + details + ", getAppId()=" + getAppId() - + ", getMchId()=" + getMchId() + ", getNonceStr()=" - + getNonceStr() + ", getSign()=" + getSign() - + ", getDeviceInfo()=" + getDeviceInfo() + ", getReturnCode()=" - + getReturnCode() + ", getReturnMsg()=" + getReturnMsg() - + ", getResultCode()=" + getResultCode() + ", getErrCode()=" - + getErrCode() + ", getErrCodeDes()=" + getErrCodeDes() + "]"; + + ", partner=" + partner + ", details=" + details + + ", getAppId()=" + getAppId() + ", getMchId()=" + getMchId() + + ", getNonceStr()=" + getNonceStr() + ", getSign()=" + + getSign() + ", getDeviceInfo()=" + getDeviceInfo() + + ", getReturnCode()=" + getReturnCode() + ", getReturnMsg()=" + + getReturnMsg() + ", getResultCode()=" + getResultCode() + + ", getErrCode()=" + getErrCode() + ", getErrCodeDes()=" + + getErrCodeDes() + "]"; } } diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RefundConverter.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundConverter.java similarity index 96% rename from weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RefundConverter.java rename to weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundConverter.java index b31f9040..0630f117 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RefundConverter.java +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundConverter.java @@ -1,4 +1,4 @@ -package com.foxinmy.weixin4j.mp.payment.v3; +package com.foxinmy.weixin4j.mp.payment; import java.lang.reflect.Field; import java.util.HashMap; @@ -26,8 +26,8 @@ import com.thoughtworks.xstream.mapper.Mapper; * @author jy * @date 2014年11月2日 * @since JDK 1.7 - * @see com.foxinmy.weixin4j.mp.payment.v3.Refund - * @see com.foxinmy.weixin4j.mp.payment.v3.RefundDetail + * @see com.foxinmy.weixin4j.mp.payment.Refund + * @see com.foxinmy.weixin4j.mp.payment.RefundDetail */ public class RefundConverter { private final static XStream xStream = XStream.get(); diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RefundDetail.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundDetail.java similarity index 51% rename from weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RefundDetail.java rename to weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundDetail.java index 007f05b2..0c09a2e2 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RefundDetail.java +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundDetail.java @@ -1,4 +1,4 @@ -package com.foxinmy.weixin4j.mp.payment.v3; +package com.foxinmy.weixin4j.mp.payment; import java.io.Serializable; @@ -32,7 +32,15 @@ public class RefundDetail implements Serializable { @XStreamAlias("coupon_refund_fee") private int couponRefundFee; // 现金券退款金额<=退款金额,退款金额-现金券退款金额为现金 @XStreamAlias("refund_status") - private RefundStatus refundStatus; // 退款状态 + private String refundStatus; // 退款状态 + @XStreamAlias("recv_user_id") + private String recvUserId;// 转账退款接收退款的财付通帐号 + @XStreamAlias("reccv_user_name") + private String reccvUserName;// 转账退款接收退款的姓名(需与接收退款的财 付通帐号绑定的姓名一致) + @XStreamAlias("sign_key_index") + private String signKeyIndex;// 多密钥支持的密钥序号,默认 1 + @XStreamAlias("sign_type") + private String signType;// 签名类型,取值:MD5、RSA,默认:MD5 public String getOutRefundNo() { return outRefundNo; @@ -43,8 +51,16 @@ public class RefundDetail implements Serializable { } public String getRefundChannel() { - return StringUtils.isBlank(refundChannel) ? RefundChannel.BALANCE - .name() : refundChannel; + if (StringUtils.isBlank(refundChannel)) { + return RefundChannel.BALANCE.name(); + } + // V2 + if (refundChannel.equals("0")) { + return RefundChannel.TENPAY.name(); + } else if (refundChannel.equals("1")) { + return RefundChannel.BALANCE.name(); + } + return refundChannel; } /** @@ -66,7 +82,36 @@ public class RefundDetail implements Serializable { } public RefundStatus getRefundStatus() { - return refundStatus; + // V2 + if ("4,10,".contains(refundStatus + ",")) { + return RefundStatus.SUCCES; + } else if ("3,5,6,".contains(refundStatus + ",")) { + return RefundStatus.FAIL; + } else if ("8,9,10,".contains(refundStatus + ",")) { + return RefundStatus.PROCESSING; + } else if ("1,2,".contains(refundStatus + ",")) { + return RefundStatus.NOTSURE; + } else if ("7,".contains(refundStatus + ",")) { + return RefundStatus.CHANGE; + } else { + return RefundStatus.valueOf(refundStatus); + } + } + + public String getRecvUserId() { + return recvUserId; + } + + public String getReccvUserName() { + return reccvUserName; + } + + public String getSignKeyIndex() { + return signKeyIndex; + } + + public String getSignType() { + return signType; } @Override @@ -74,6 +119,9 @@ public class RefundDetail implements Serializable { return "RefundDetail [outRefundNo=" + outRefundNo + ", refundId=" + refundId + ", refundChannel=" + refundChannel + ", refundFee=" + refundFee + ", couponRefundFee=" - + couponRefundFee + ", refundStatus=" + refundStatus + "]"; + + couponRefundFee + ", refundStatus=" + refundStatus + + ", recvUserId=" + recvUserId + ", reccvUserName=" + + reccvUserName + ", signKeyIndex=" + signKeyIndex + + ", signType=" + signType + "]"; } } diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RefundResult.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundResult.java similarity index 67% rename from weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RefundResult.java rename to weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundResult.java index de5ed3f9..49a17458 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RefundResult.java +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundResult.java @@ -1,8 +1,7 @@ -package com.foxinmy.weixin4j.mp.payment.v3; +package com.foxinmy.weixin4j.mp.payment; import org.apache.commons.lang3.StringUtils; -import com.foxinmy.weixin4j.mp.payment.ApiResult; import com.foxinmy.weixin4j.mp.type.RefundChannel; import com.thoughtworks.xstream.annotations.XStreamAlias; @@ -41,6 +40,14 @@ public class RefundResult extends ApiResult { // 现金券退款金额<=退款金 额,退款金额-现金券退款金 额为现金 @XStreamAlias("coupon_refund_fee") private int couponRefundFee; + @XStreamAlias("recv_user_id") + private String recvUserId;// 转账退款接收退款的财付通帐号 + @XStreamAlias("reccv_user_name") + private String reccvUserName;// 转账退款接收退款的姓名(需与接收退款的财 付通帐号绑定的姓名一致) + @XStreamAlias("sign_key_index") + private String signKeyIndex;// 多密钥支持的密钥序号,默认 1 + @XStreamAlias("sign_type") + private String signType;// 签名类型,取值:MD5、RSA,默认:MD5 public String getTransactionId() { return transactionId; @@ -59,8 +66,16 @@ public class RefundResult extends ApiResult { } public String getRefundChannel() { - return StringUtils.isBlank(refundChannel) ? RefundChannel.BALANCE - .name() : refundChannel; + if (StringUtils.isBlank(refundChannel)) { + return RefundChannel.BALANCE.name(); + } + // V2 + if (refundChannel.equals("0")) { + return RefundChannel.TENPAY.name(); + } else if (refundChannel.equals("1")) { + return RefundChannel.BALANCE.name(); + } + return refundChannel; } /** @@ -81,14 +96,25 @@ public class RefundResult extends ApiResult { return couponRefundFee / 100d; } + public String getSignKeyIndex() { + return signKeyIndex; + } + + public String getSignType() { + return signType; + } + @Override public String toString() { return "RefundResult [transactionId=" + transactionId + ", outTradeNo=" + outTradeNo + ", outRefundNo=" + outRefundNo + ", refundId=" + refundId + ", refundChannel=" + refundChannel + ", refundFee=" + refundFee + ", couponRefundFee=" - + couponRefundFee + ", getAppId()=" + getAppId() - + ", getMchId()=" + getMchId() + ", getNonceStr()=" + + couponRefundFee + ", recvUserId=" + recvUserId + + ", reccvUserName=" + reccvUserName + ", signKeyIndex=" + + signKeyIndex + ", signType=" + signType + ", getAppId()=" + + getAppId() + ", getMchId()=" + getMchId() + + ", getSubMchId()=" + getSubMchId() + ", getNonceStr()=" + getNonceStr() + ", getSign()=" + getSign() + ", getDeviceInfo()=" + getDeviceInfo() + ", getReturnCode()=" + getReturnCode() + ", getReturnMsg()=" + getReturnMsg() diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/cacert.pem b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/cacert.pem new file mode 100644 index 00000000..e379d950 --- /dev/null +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/cacert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDezCCAuSgAwIBAgIJAI7aVO7iYwcQMA0GCSqGSIb3DQEBBAUAMIGGMQswCQYD +VQQGEwJDTjESMBAGA1UECBMJR1VBTkdET05HMREwDwYDVQQHEwhTSEVOWkhFTjEQ +MA4GA1UEChMHVEVOQ0VOVDEMMAoGA1UECxMDT1NTMQwwCgYDVQQDEwNDRlQxIjAg +BgkqhkiG9w0BCQEWE3RpcHlsdW9AdGVuY2VudC5jb20wHhcNMDYwMzE0MTQyMjIz +WhcNMTYwMzExMTQyMjIzWjCBhjELMAkGA1UEBhMCQ04xEjAQBgNVBAgTCUdVQU5H +RE9ORzERMA8GA1UEBxMIU0hFTlpIRU4xEDAOBgNVBAoTB1RFTkNFTlQxDDAKBgNV +BAsTA09TUzEMMAoGA1UEAxMDQ0ZUMSIwIAYJKoZIhvcNAQkBFhN0aXB5bHVvQHRl +bmNlbnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZF0wb1UIgAbwq +RgnQtGRZon1LG1NLd1CiFQO41aESrJ/3hWvnzlpxepIwi9H3xUuMeoP3kOSJoT5O +EKOVkcgkzVcPebfBgnnLToMTduuHHJ+iWWPmzFsJQB5xetJXD1lqsxlDLZk2+eiD +FuGfPKDaW76V24YPLCQ6uzaRMiFDaQIDAQABo4HuMIHrMB0GA1UdDgQWBBTi3asX +klhRZA2dvy0onxvowmx2YzCBuwYDVR0jBIGzMIGwgBTi3asXklhRZA2dvy0onxvo +wmx2Y6GBjKSBiTCBhjELMAkGA1UEBhMCQ04xEjAQBgNVBAgTCUdVQU5HRE9ORzER +MA8GA1UEBxMIU0hFTlpIRU4xEDAOBgNVBAoTB1RFTkNFTlQxDDAKBgNVBAsTA09T +UzEMMAoGA1UEAxMDQ0ZUMSIwIAYJKoZIhvcNAQkBFhN0aXB5bHVvQHRlbmNlbnQu +Y29tggkAjtpU7uJjBxAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQCM +VOZNmGO9XAtEPFuA3Q42LkfcwHK9Q0CAMbMPA4pNADxBWaQvv5okE3YG1RNGLp/y +wyxXDs62qpEHcDPQh5porQKRP3PB54pL3AFLouEcuH4j7e8f8tqokOMmtTfeSUTJ +wYWlbwUEu53AhNBlQl1zATkGmeURsmXg5GnuNAWafg== +-----END CERTIFICATE----- diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/NativePayResponseV3.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/NativePayResponseV3.java index efb367b6..a323e7c4 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/NativePayResponseV3.java +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/NativePayResponseV3.java @@ -3,6 +3,7 @@ package com.foxinmy.weixin4j.mp.payment.v3; import org.apache.commons.lang3.StringUtils; import com.foxinmy.weixin4j.exception.PayException; +import com.foxinmy.weixin4j.model.Consts; import com.foxinmy.weixin4j.mp.payment.ApiResult; import com.foxinmy.weixin4j.mp.payment.PayUtil; import com.foxinmy.weixin4j.util.RandomUtil; @@ -27,9 +28,11 @@ public class NativePayResponseV3 extends ApiResult { public NativePayResponseV3(PayPackageV3 payPackage, String returnMsg, String resultMsg) throws PayException { super.setReturnMsg(returnMsg); - super.setReturnCode(StringUtils.isNotBlank(returnMsg) ? FAIL : SUCCESS); + super.setReturnCode(StringUtils.isNotBlank(returnMsg) ? Consts.FAIL + : Consts.SUCCESS); this.setErrCodeDes(resultMsg); - this.setResultCode(StringUtils.isNotBlank(resultMsg) ? FAIL : SUCCESS); + this.setResultCode(StringUtils.isNotBlank(resultMsg) ? Consts.FAIL + : Consts.SUCCESS); this.setMchId(payPackage.getMch_id()); this.setAppId(payPackage.getAppid()); this.setNonceStr(RandomUtil.generateString(16)); diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/PayRequestV3.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/PayRequestV3.java index 0abbde66..e185a266 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/PayRequestV3.java +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/PayRequestV3.java @@ -2,7 +2,7 @@ package com.foxinmy.weixin4j.mp.payment.v3; import com.alibaba.fastjson.annotation.JSONField; import com.foxinmy.weixin4j.exception.PayException; -import com.foxinmy.weixin4j.http.XmlResult; +import com.foxinmy.weixin4j.model.Consts; import com.foxinmy.weixin4j.mp.payment.PayRequest; import com.thoughtworks.xstream.annotations.XStreamOmitField; @@ -32,11 +32,11 @@ public class PayRequestV3 extends PayRequest { private PrePay prePay; public PayRequestV3(PrePay prePay) throws PayException { - if (!prePay.getReturnCode().equalsIgnoreCase(XmlResult.SUCCESS)) { + if (!prePay.getReturnCode().equalsIgnoreCase(Consts.SUCCESS)) { throw new PayException(prePay.getReturnMsg(), prePay.getReturnCode()); } - if (!prePay.getResultCode().equalsIgnoreCase(XmlResult.SUCCESS)) { + if (!prePay.getResultCode().equalsIgnoreCase(Consts.SUCCESS)) { throw new PayException(prePay.getResultCode(), prePay.getErrCodeDes()); } diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/spider/WeixinExecutor.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/spider/WeixinExecutor.java index 001fe741..d4fdd685 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/spider/WeixinExecutor.java +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/spider/WeixinExecutor.java @@ -2,8 +2,6 @@ package com.foxinmy.weixin4j.mp.spider; import java.io.Serializable; import java.net.URI; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -12,6 +10,7 @@ import java.util.Map; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.http.Consts; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; @@ -59,8 +58,6 @@ public class WeixinExecutor implements Serializable { private final Logger logger = LoggerFactory.getLogger(getClass()); - private final static Charset charset = StandardCharsets.UTF_8; - private final static Map accountMap = new HashMap() { private static final long serialVersionUID = 1L; @@ -186,13 +183,13 @@ public class WeixinExecutor implements Serializable { if (!StringUtils.isBlank(imgcode)) { method.addHeader("Cookie", "sig=" + sig); } - method.setEntity(new UrlEncodedFormEntity(parameters, charset)); + method.setEntity(new UrlEncodedFormEntity(parameters, Consts.UTF_8)); method.addHeader("Referer", weixin.getString("base")); HttpResponse response = client.execute(host, method); HttpEntity entity = response.getEntity(); - Document root = Jsoup.parse(entity.getContent(), charset.name(), - weixin.getString("base")); + Document root = Jsoup.parse(entity.getContent(), + Consts.UTF_8.name(), weixin.getString("base")); StatusLine line = response.getStatusLine(); logger.info("step1_login--->status={},body=\n{}", line, root.toString()); @@ -270,7 +267,8 @@ public class WeixinExecutor implements Serializable { .getValue()).append(";base64,"); base64.append(new String( Base64.encodeBase64(IOUtil.toByteArray(response - .getEntity().getContent())), charset)); + .getEntity().getContent())), + Consts.UTF_8)); weixin.put("verifydata", base64.toString()); List cookieList = client.getCookieStore() .getCookies(); @@ -311,8 +309,8 @@ public class WeixinExecutor implements Serializable { HttpResponse response = client.execute(host, method); HttpEntity entity = response.getEntity(); - Document root = Jsoup.parse(entity.getContent(), charset.name(), - weixin.getString("base")); + Document root = Jsoup.parse(entity.getContent(), + Consts.UTF_8.name(), weixin.getString("base")); StatusLine line = response.getStatusLine(); logger.info("step2_setting--->status={},body=\n{}", line, root.toString()); @@ -331,7 +329,7 @@ public class WeixinExecutor implements Serializable { response = client.execute(host, method); entity = response.getEntity(); - root = Jsoup.parse(entity.getContent(), charset.name(), + root = Jsoup.parse(entity.getContent(), Consts.UTF_8.name(), weixin.getString("base")); line = response.getStatusLine(); weixin.put("step", "2-1"); @@ -378,8 +376,8 @@ public class WeixinExecutor implements Serializable { response = client.execute(host, method); entity = response.getEntity(); - root = Jsoup.parse(entity.getContent(), charset.name(), - weixin.getString("base")); + root = Jsoup.parse(entity.getContent(), + Consts.UTF_8.name(), weixin.getString("base")); line = response.getStatusLine(); if (line.getStatusCode() == HttpStatus.SC_OK) { @@ -402,11 +400,12 @@ public class WeixinExecutor implements Serializable { System.currentTimeMillis() + "")); post.setEntity(new UrlEncodedFormEntity(parameters, - charset)); + Consts.UTF_8)); response = client.execute(host, post); entity = response.getEntity(); root = Jsoup.parse(entity.getContent(), - charset.name(), weixin.getString("base")); + Consts.UTF_8.name(), + weixin.getString("base")); line = response.getStatusLine(); logger.info( "step2_bedeveloper--->status={},body=\n{}", @@ -422,7 +421,7 @@ public class WeixinExecutor implements Serializable { response = client.execute(host, method); entity = response.getEntity(); root = Jsoup.parse(entity.getContent(), - charset.name(), + Consts.UTF_8.name(), weixin.getString("base")); } else { weixin.put("code", "-100"); @@ -495,13 +494,13 @@ public class WeixinExecutor implements Serializable { .add(new BasicNameValuePair("callback_encrypt_mode", "0")); parameters.add(new BasicNameValuePair("operation_seq", RandomUtil .generateStringByNumberChar(9))); - method.setEntity(new UrlEncodedFormEntity(parameters, charset)); + method.setEntity(new UrlEncodedFormEntity(parameters, Consts.UTF_8)); method.addHeader("Referer", weixin.getString("developerModifyUrl")); HttpResponse response = client.execute(host, method); HttpEntity entity = response.getEntity(); - Document root = Jsoup.parse(entity.getContent(), charset.name(), - weixin.getString("base")); + Document root = Jsoup.parse(entity.getContent(), + Consts.UTF_8.name(), weixin.getString("base")); StatusLine line = response.getStatusLine(); logger.info("step3_setting--->status={},body=\n{}", line, root.toString()); @@ -565,13 +564,13 @@ public class WeixinExecutor implements Serializable { .currentTimeMillis() + "")); method.setEntity(new UrlEncodedFormEntity(parameters, - charset)); + Consts.UTF_8)); method.setURI(URI.create(weixin.getString("start"))); response = client.execute(host, method); entity = response.getEntity(); - root = Jsoup.parse(entity.getContent(), charset.name(), - weixin.getString("base")); + root = Jsoup.parse(entity.getContent(), + Consts.UTF_8.name(), weixin.getString("base")); line = response.getStatusLine(); logger.info("step3_setting--->status={},body=\n{}", line, root.toString()); @@ -624,13 +623,14 @@ public class WeixinExecutor implements Serializable { parameters.add(new BasicNameValuePair("lang", "zh_CN")); parameters.add(new BasicNameValuePair("random", System .currentTimeMillis() + "")); - method.setEntity(new UrlEncodedFormEntity(parameters, charset)); + method.setEntity(new UrlEncodedFormEntity(parameters, + Consts.UTF_8)); method.addHeader("Referer", weixin.getString("developerUrl")); HttpResponse response = client.execute(host, method); HttpEntity entity = response.getEntity(); Document root = Jsoup.parse(entity.getContent(), - charset.name(), weixin.getString("base")); + Consts.UTF_8.name(), weixin.getString("base")); StatusLine line = response.getStatusLine(); logger.info("step4_back--->status={},body=\n{}", line, root.toString()); diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/BillType.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/BillType.java index 7bd7b8c6..7cfc69b6 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/BillType.java +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/BillType.java @@ -2,6 +2,7 @@ package com.foxinmy.weixin4j.mp.type; /** * 对账单类型 + * * @className BillType * @author jy * @date 2014年10月31日 @@ -9,5 +10,13 @@ package com.foxinmy.weixin4j.mp.type; * @see */ public enum BillType { - ALL, SUCCESS, REFUND + ALL(0), SUCCESS(1), REFUND(2); + private int val; + + BillType(int val) { + this.val = val; + } + public int getVal() { + return val; + } } diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/RefundChannel.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/RefundChannel.java index 672bdcf0..26721d75 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/RefundChannel.java +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/RefundChannel.java @@ -11,5 +11,8 @@ package com.foxinmy.weixin4j.mp.type; */ public enum RefundChannel { ORIGINAL, // 原路退款 - BALANCE;// 退回到余额 + BALANCE,// 退回到余额 + + TENPAY, // 财付通 + BANK; // 银行 } diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/RefundStatus.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/RefundStatus.java index 12f3b46a..9c996b99 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/RefundStatus.java +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/RefundStatus.java @@ -14,7 +14,6 @@ public enum RefundStatus { FAIL, // 退款失败 PROCESSING, // 退款处理中 NOTSURE, // 未确定,需要商户 原退款单号重新发起 - // 转入代发,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,资金回流到商户的现金帐号,需要商户人工干 - // 预,通过线下或者财付通转 账的方式进行退款。 - CHANGE; + CHANGE; // 转入代发,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,资金回流到商户的现金帐号,需要商户人工干预,通过线下或者财付通转 + // 账的方式进行退款。 } diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/SemCategory.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/SemCategory.java new file mode 100644 index 00000000..7f03212c --- /dev/null +++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/SemCategory.java @@ -0,0 +1,61 @@ +package com.foxinmy.weixin4j.mp.type; + +/** + * 垂直服务协议 + * + * @className SemCategory + * @author jy + * @date 2014年11月7日 + * @since JDK 1.7 + * @see + */ +public enum SemCategory { + // 生活类 + restaurant("生活类", "餐馆", "查询餐馆的服务,例如:中关村附近的面馆"), map("生活类", "地图", + "查询地图服务,例如:从银科大厦到天坛公园怎么走"), nearby("生活类", "周边", + "查询周边的服务,例如:我想去打保龄球"), coupon("生活类", "优惠券/团购", + "查询优惠券/团购的服务,例如:“附 近有什么优惠券”;如果查已有类别 的优惠券,比如:“附近有什么酒店 优惠券”,那么就会优先是酒店类。"), + // 旅行类 + hotel("旅行类", "酒店", "查询酒店服务,例如:查一下中关村附近有没有七天酒店"), travel("旅行类", "旅游", + "查询旅游服务,例如:故宫门票多少钱"), flight("旅行类", "航班", "查询航班的服务,例如:明天从北京到上海的机票"), train( + "旅行类", "火车", "查询火车服务,例如:查一下从北京到西安的火车"), + // 娱乐类 + movie("娱乐类", "上映电影", "查询上映电影的服务,例如:最近有什么好看的电影"), music("娱乐类", "音乐", + "查询音乐的服务,例如:来点刘德华的歌"), video("娱乐类", "视频", "查询视频服务,例如:我想看甄嬛传"), novel( + "娱乐类", "小说", "查询小说的服务,例如:来点言情小说看看"), + // 工具类 + weather("工具类", "天气", "查询天气的服务,例如:明天北京天气"), stock("工具类", "股票", + "查询股票的服务,例如:腾讯股价多少了"), remind("工具类", "提醒", "提醒服务,例如:提醒我明天上午十点开会"), telephone( + "工具类", "常用电影", "查询常用电话号码服务,例如:查询一下招行信用卡的电话"), + // 知识类 + cookbook("知识类", "菜谱", "查询菜谱服务,例如:宫保鸡丁怎么做"), baike("知识类", "百科", + "查询百科服务,例如:查一下刘德华的百科资料"), news("知识类", "资讯", "查询新闻服务,例如:今天有什么新闻"), + // 其他类 + tv("其他类", "电视节目预告", "查询电视节目服务,例如:湖南台今晚有什么节目"), instruction("其他类", "通用指令", + "通用指令服务,例如:把声音调高一点"), tv_instruction("其他类", "电视指令", + "电视指令服务,例如:切换到中央五台"), car_instruction("其他类", "车载指令", + "车载指令服务,例如:把空调设为 25 度"), app("其他类", "应用", "查询应用服务,例如:打开愤怒的小鸟"), website( + "其他类", "网址", "查询网址服务,例如:帮我打开腾讯网"), search("其他类", "网页搜索", + "网页搜索服务,例如:百度一下意大利对乌拉圭"); + SemCategory(String main, String desc, String remark) { + this.main = main; + this.desc = desc; + this.remark = remark; + } + + private String main; + private String desc; + private String remark; + + public String getMain() { + return main; + } + + public String getDesc() { + return desc; + } + + public String getRemark() { + return remark; + } +} diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/resources/weixin.properties b/weixin4j-mp/weixin4j-mp-api/src/main/resources/weixin.properties index 68a632f1..b423ddfe 100644 --- a/weixin4j-mp/weixin4j-mp-api/src/main/resources/weixin.properties +++ b/weixin4j-mp/weixin4j-mp-api/src/main/resources/weixin.properties @@ -15,4 +15,4 @@ media_path=/tmp/weixin/media # \u5bf9\u8d26\u5355\u4fdd\u5b58\u8def\u5f84 bill_path=/tmp/weixin/bill # ca\u8bc1\u4e66\u5b58\u653e\u7684\u5b8c\u6574\u8def\u5f84 -ca_file=/tmp/weixin/xxxxx.p12 \ No newline at end of file +ca_file=/Users/jy/Downloads/1221928801.pfx \ No newline at end of file diff --git a/weixin4j-mp/weixin4j-mp-api/src/test/java/com/foxinmy/weixin4j/mp/test/SemanticTest.java b/weixin4j-mp/weixin4j-mp-api/src/test/java/com/foxinmy/weixin4j/mp/test/SemanticTest.java new file mode 100644 index 00000000..913ed43f --- /dev/null +++ b/weixin4j-mp/weixin4j-mp-api/src/test/java/com/foxinmy/weixin4j/mp/test/SemanticTest.java @@ -0,0 +1,27 @@ +package com.foxinmy.weixin4j.mp.test; + +import org.junit.Before; +import org.junit.Test; + +import com.foxinmy.weixin4j.exception.WeixinException; +import com.foxinmy.weixin4j.mp.api.HelperApi; +import com.foxinmy.weixin4j.mp.model.SemQuery; +import com.foxinmy.weixin4j.mp.type.SemCategory; + +public class SemanticTest extends TokenTest { + private HelperApi helperApi; + + @Before + public void init() { + helperApi = new HelperApi(tokenHolder); + } + + @Test + public void testApi() throws WeixinException { + SemQuery query = SemQuery.build("附近有啥 100 元以内的保龄球馆"); + query.city("北京").category(SemCategory.nearby); + query.uid("opKwyt6IhrqPmTTZshyqH5W9gIVo"); + System.out.println(query); + System.out.println(helperApi.semantic(query)); + } +}