diff --git a/README.md b/README.md
index 994a95d8..f17c8713 100644
--- a/README.md
+++ b/README.md
@@ -207,6 +207,18 @@ netty的代码没有放到maven中心仓库,也没什么意义,因为最终需
+ **weixin4j-qy**: 新增获取微信服务器IP接口
+ **weixin4j-qy**: 调整回调模式下的首次验证的签名方式
+
+* 2015-01-04
+
+ + **weixin4j-base**: 新增获取classpath目录下的资源路径的方法
+
+ + **weixin4j-mp**: 支付模块拆分为V2跟V3,新增WeixinPayProxy类
+
+ + **weixin4j-mp**: 退款相关类拆分V2跟V3
+
+ + **weixin4j-mp**: 新增接口上报接口
+
+ + **weixin4j-qy**: 新增批量删除员工接口
接下来
------
diff --git a/pom.xml b/pom.xml
index 01f3994e..23461acd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,5 +1,6 @@
-
+
4.0.0
com.foxinmy
weixin4j
@@ -209,6 +210,9 @@
**/*.xml
**/*.properties
**/*.pem
+ **/*.p12
+ **/*.pfx
+ **/*.pem
diff --git a/weixin4j-base/README.md b/weixin4j-base/README.md
index 2537c56a..6eae5023 100644
--- a/weixin4j-base/README.md
+++ b/weixin4j-base/README.md
@@ -37,4 +37,8 @@ weixin4j-base
* 2014-11-24
- + 将Action跟Mapping基础类并入到项目
\ No newline at end of file
+ + 将Action跟Mapping基础类并入到项目
+
+* 2015-01-04
+
+ + ConfigUtil类新增获取classpath目录下的资源路径的方法
\ No newline at end of file
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/BaseApi.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/BaseApi.java
index 90a969a4..15eeb642 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/BaseApi.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/BaseApi.java
@@ -13,6 +13,7 @@ import com.thoughtworks.xstream.mapper.DefaultMapper;
/**
* API基础
+ *
* @className BaseApi
* @author jy.hu
* @date 2014年9月26日
@@ -37,7 +38,9 @@ public abstract class BaseApi {
protected Map xml2map(String xml) {
return mapXstream.fromXML(xml, Map.class);
}
+
protected abstract ResourceBundle getWeixinBundle();
+
protected String getRequestUri(String key) {
String url = getWeixinBundle().getString(key);
Pattern p = Pattern.compile("(\\{[^\\}]*\\})");
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 744b5814..4195c36c 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
@@ -188,7 +188,6 @@ public class HttpRequest {
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);
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 84a28a94..cd3ae6d5 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
@@ -168,10 +168,9 @@ public class Response {
@Override
public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("[Response text=").append(text);
- sb.append(", statusCode=").append(statusCode);
- sb.append(", statusText=").append(statusText).append("]");
- return sb.toString();
+ return "Response [text=" + text + ", statusCode=" + statusCode
+ + ", statusText=" + statusText + ", stream=" + stream
+ + ", isJsonResult=" + isJsonResult + ", isXmlResult="
+ + isXmlResult + "]";
}
}
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 947a1ba9..a4e4c40c 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
@@ -1,8 +1,5 @@
package com.foxinmy.weixin4j.http;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
@@ -23,10 +20,6 @@ import org.apache.http.conn.ssl.SSLSocketFactory;
*/
public class SSLHttpRequest extends HttpRequest {
- public SSLHttpRequest(String password, File file) throws IOException {
- this(password, new FileInputStream(file));
- }
-
public SSLHttpRequest(String password, InputStream inputStream) {
super();
try {
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 878718ef..60d09ee5 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,7 +2,9 @@ package com.foxinmy.weixin4j.http;
import java.io.Serializable;
-import com.foxinmy.weixin4j.model.Consts;
+import org.apache.commons.lang3.StringUtils;
+
+import com.alibaba.fastjson.annotation.JSONField;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
@@ -19,16 +21,29 @@ public class XmlResult implements Serializable {
private static final long serialVersionUID = -6185313616955051150L;
@XStreamAlias("return_code")
+ @JSONField(name = "return_code")
private String returnCode;// 此字段是通信标识,非交易 标识,交易是否成功需要查 看 result_code 来判断 非空
@XStreamAlias("return_msg")
+ @JSONField(name = "return_msg")
private String returnMsg;// 返回信息,如非 空,为错误原因 可能为空
@XStreamAlias("result_code")
+ @JSONField(name = "result_code")
private String resultCode;// 业务结果SUCCESS/FAIL 非空
@XStreamAlias("err_code")
+ @JSONField(name = "err_code")
private String errCode;// 错误代码 可为空
@XStreamAlias("err_code_des")
+ @JSONField(name = "err_code_des")
private String errCodeDes;// 结果信息描述 可为空
+ public XmlResult() {
+ }
+
+ public XmlResult(String returnCode, String returnMsg) {
+ this.returnCode = returnCode;
+ this.returnMsg = returnMsg;
+ }
+
public String getReturnCode() {
return returnCode;
}
@@ -38,7 +53,7 @@ public class XmlResult implements Serializable {
}
public String getReturnMsg() {
- return returnMsg;
+ return StringUtils.isNotBlank(returnMsg) ? returnMsg : null;
}
public void setReturnMsg(String returnMsg) {
@@ -62,27 +77,13 @@ public class XmlResult implements Serializable {
}
public String getErrCodeDes() {
- return errCodeDes;
+ return StringUtils.isNotBlank(errCodeDes) ? errCodeDes : null;
}
public void setErrCodeDes(String errCodeDes) {
this.errCodeDes = errCodeDes;
}
- public XmlResult() {
- this(Consts.SUCCESS.toLowerCase(), "");
- }
-
- public XmlResult(String returnCode, String returnMsg) {
- this.returnCode = returnCode;
- this.returnMsg = returnMsg;
- if (returnCode.equalsIgnoreCase(Consts.SUCCESS)) {
- this.resultCode = Consts.SUCCESS.toLowerCase();
- this.errCode = Consts.SUCCESS.toLowerCase();
- this.errCodeDes = "";
- }
- }
-
@Override
public String toString() {
return "returnCode=" + returnCode + ", returnMsg=" + returnMsg
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
index 19563cc4..3e8ddc15 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/Consts.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/Consts.java
@@ -1,4 +1,7 @@
package com.foxinmy.weixin4j.model;
+
+import java.nio.charset.Charset;
+
/**
* 常量类
* @className Consts
@@ -8,6 +11,8 @@ package com.foxinmy.weixin4j.model;
* @see
*/
public final class Consts {
+ public static final Charset UTF_8 = Charset.forName("UTF-8");
+ public static final Charset GBK = Charset.forName("GBK");
public static final String SUCCESS = "SUCCESS";
public static final String FAIL = "FAIL";
public static final String SunX509 = "SunX509";
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/ConfigUtil.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/ConfigUtil.java
index 7a1781bf..eb932aee 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/ConfigUtil.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/ConfigUtil.java
@@ -19,16 +19,21 @@ import com.foxinmy.weixin4j.model.WeixinQyAccount;
* @see
*/
public class ConfigUtil {
+ private final static String CLASSPATH_PREFIX = "classpath:";
+ private final static String CLASSPATH_VALUE;
private final static ResourceBundle weixinBundle;
static {
weixinBundle = ResourceBundle.getBundle("weixin");
Set keySet = weixinBundle.keySet();
File file = null;
+ CLASSPATH_VALUE = Thread.currentThread().getContextClassLoader()
+ .getResource("").getPath();
for (String key : keySet) {
if (!key.endsWith("_path")) {
continue;
}
- file = new File(getValue(key));
+ file = new File(getValue(key).replaceFirst(CLASSPATH_PREFIX,
+ CLASSPATH_VALUE));
if (!file.exists() && !file.mkdirs()) {
System.err.append(String.format("%s create fail.%n",
file.getAbsolutePath()));
@@ -36,10 +41,27 @@ public class ConfigUtil {
}
}
+ /**
+ * 获取weixin.properties文件中的key值
+ *
+ * @param key
+ * @return
+ */
public static String getValue(String key) {
return weixinBundle.getString(key);
}
+ /**
+ * 判断属性是否存在[classpath:]如果存在则拼接项目路径后返回 一般用于文件的绝对路径获取
+ *
+ * @param key
+ * @return
+ */
+ public static String getClassPathValue(String key) {
+ return new File(getValue(key).replaceFirst(CLASSPATH_PREFIX,
+ CLASSPATH_VALUE)).getPath();
+ }
+
public static T getWeixinAccount(Class clazz) {
String text = getValue("account");
return JSON.parseObject(text, clazz);
diff --git a/weixin4j-mp/README.md b/weixin4j-mp/README.md
index 72373214..051522d0 100644
--- a/weixin4j-mp/README.md
+++ b/weixin4j-mp/README.md
@@ -65,6 +65,7 @@ weixin4j-mp
media_path=/tmp/weixin/media
bill_path=/tmp/weixin/bill
ca_file=/tmp/weixin/xxxxx.p12 | xxxx.pfx
+ #classpath路径下:ca_file=classpath:xxxxx.p12
3.在项目根目录下执行`mvn package`命令后得到jar包,将`weixin4j-mp-full`包或者`weixin4j-base`和`weixin4j-mp-api`两个包引入到自己的工程.
@@ -150,4 +151,12 @@ weixin4j-mp
+ **weixin4j-mp-api**: 新增群发消息预览、状态查询接口
- + **weixin4j-mp-api**: 新增多客服添加账号、更新账号、上传头像、删除账号接口
\ No newline at end of file
+ + **weixin4j-mp-api**: 新增多客服添加账号、更新账号、上传头像、删除账号接口
+
+* 2015-01-04
+
+ + **weixin4j-mp-api**: 支付模块拆分为V2跟V3,新增WeixinPayProxy类
+
+ + **weixin4j-mp-api**: 退款相关类拆分为V2跟V3
+
+ + **weixin4j-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 7c881498..9cad6a7f 100644
--- a/weixin4j-mp/weixin4j-mp-api/README.md
+++ b/weixin4j-mp/weixin4j-mp-api/README.md
@@ -60,6 +60,7 @@ weixin.properties说明
media_path=/tmp/weixin/media
bill_path=/tmp/weixin/bill
ca_file=/tmp/weixin/xxxxx.p12 | xxxx.pfx
+ #classpath路径下:ca_file=classpath:xxxxx.p12
2.实例化一个`WeixinProxy`对象,调用API,需要强调的是如果只传入appid,appsecret两个参数将无法调用支付相关接口
@@ -131,4 +132,12 @@ weixin.properties说明
+ 新增群发消息预览、状态查询接口
- + 新增多客服添加账号、更新账号、上传头像、删除账号接口
\ No newline at end of file
+ + 新增多客服添加账号、更新账号、上传头像、删除账号接口
+
+* 2015-01-04
+
+ + 支付模块拆分为V2跟V3,新增WeixinPayProxy类
+
+ + 退款相关类拆分为V2跟V3
+
+ + 新增接口上报接口
\ No newline at end of file
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/WeixinPayProxy.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/WeixinPayProxy.java
new file mode 100644
index 00000000..5bde1cc4
--- /dev/null
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/WeixinPayProxy.java
@@ -0,0 +1,449 @@
+package com.foxinmy.weixin4j.mp;
+
+import java.io.File;
+import java.util.Date;
+
+import com.foxinmy.weixin4j.exception.WeixinException;
+import com.foxinmy.weixin4j.http.JsonResult;
+import com.foxinmy.weixin4j.http.XmlResult;
+import com.foxinmy.weixin4j.model.WeixinMpAccount;
+import com.foxinmy.weixin4j.mp.api.Pay2Api;
+import com.foxinmy.weixin4j.mp.api.Pay3Api;
+import com.foxinmy.weixin4j.mp.api.PayApi;
+import com.foxinmy.weixin4j.mp.payment.v3.ApiResult;
+import com.foxinmy.weixin4j.mp.type.BillType;
+import com.foxinmy.weixin4j.mp.type.IdQuery;
+import com.foxinmy.weixin4j.mp.type.IdType;
+import com.foxinmy.weixin4j.mp.type.RefundType;
+import com.foxinmy.weixin4j.token.FileTokenHolder;
+import com.foxinmy.weixin4j.token.TokenHolder;
+import com.foxinmy.weixin4j.type.AccountType;
+import com.foxinmy.weixin4j.util.ConfigUtil;
+
+/**
+ * 微信支付接口实现
+ *
+ * @className WeixinPayProxy
+ * @author jy
+ * @date 2015年1月3日
+ * @since JDK 1.7
+ * @see com.foxinmy.weixin4j.mp.api.Pay2Api
+ * @see com.foxinmy.weixin4j.mp.api.Pay3Api
+ */
+public class WeixinPayProxy {
+ private final PayApi payApi;
+ private final Pay2Api pay2Api;
+ private final Pay3Api pay3Api;
+
+ /**
+ * 默认采用文件存放Token信息
+ */
+ public WeixinPayProxy() {
+ this(new FileTokenHolder(AccountType.MP));
+ }
+
+ /**
+ * WeixinAccount对象
+ *
+ * @param weixinAccount
+ * 微信账户
+ */
+ public WeixinPayProxy(WeixinMpAccount weixinAccount) {
+ this(new FileTokenHolder(weixinAccount));
+ }
+
+ /**
+ * TokenHolder对象
+ *
+ * @param tokenHolder
+ */
+ public WeixinPayProxy(TokenHolder tokenHolder) {
+ this.pay2Api = new Pay2Api(tokenHolder);
+ this.pay3Api = new Pay3Api(tokenHolder);
+ int version = ((WeixinMpAccount) tokenHolder.getAccount()).getVersion();
+ if (version == 2) {
+ this.payApi = this.pay2Api;
+ } else if (version == 3) {
+ this.payApi = this.pay3Api;
+ } else {
+ this.payApi = this.pay3Api;
+ }
+ }
+
+ /**
+ * 发货通知
+ *
+ * @param openId
+ * 用户ID
+ * @param transid
+ * 交易单号
+ * @param outTradeNo
+ * 订单号
+ * @param status
+ * 成功|失败
+ * @param statusMsg
+ * status为失败时携带的信息
+ * @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)
+ throws WeixinException {
+ return payApi.deliverNotify(openId, transid, outTradeNo, status,
+ statusMsg);
+ }
+
+ /**
+ * 维权处理
+ *
+ * @param openId
+ * 用户ID
+ * @param feedbackId
+ * 维权单号
+ * @return 调用结果
+ * @see com.foxinmy.weixin4j.mp.api.PayApi
+ * @since V2 & V3
+ * @throws WeixinException
+ */
+ public JsonResult updateFeedback(String openId, String feedbackId)
+ throws WeixinException {
+ return payApi.updateFeedback(openId, feedbackId);
+ }
+
+ /**
+ * V2订单查询
+ *
+ * @param idQuery
+ * 商户系统内部的订单号, transaction_id、out_trade_no 二 选一,如果同时存在优先级:
+ * transaction_id> out_trade_no
+ * @since V2
+ * @see com.foxinmy.weixin4j.mp.payment.v2.Order
+ * @see com.foxinmy.weixin4j.mp.api.PayApi
+ * @see com.foxinmy.weixin4j.mp.api.Pay2Api
+ * @return 订单详情
+ * @throws WeixinException
+ */
+ public com.foxinmy.weixin4j.mp.payment.v2.Order orderQueryV2(
+ String outTradeNo) throws WeixinException {
+ return pay2Api.orderQuery(new IdQuery(outTradeNo, IdType.TRADENO));
+ }
+
+ /**
+ * V3订单查询
+ *
+ * @param idQuery
+ * 商户系统内部的订单号, transaction_id、out_trade_no 二 选一,如果同时存在优先级:
+ * transaction_id> out_trade_no
+ * @since V3
+ * @see com.foxinmy.weixin4j.mp.payment.v3.Order
+ * @see com.foxinmy.weixin4j.mp.api.PayApi
+ * @see com.foxinmy.weixin4j.mp.api.Pay3Api
+ * @return 订单详情
+ * @throws WeixinException
+ */
+ public com.foxinmy.weixin4j.mp.payment.v3.Order orderQueryV3(IdQuery idQuery)
+ throws WeixinException {
+ return pay3Api.orderQuery(idQuery);
+ }
+
+ /**
+ * V2申请退款(请求需要双向证书)
+ *
+ * 交易时间超过 1 年的订单无法提交退款; 支持部分退款,部分退需要设置相同的订单号和不同的 out_refund_no。一笔退款失
+ * 败后重新提交,要采用原来的 out_refund_no。总退款金额不能超过用户实际支付金额。
+ *
+ *
+ * @param caFile
+ * 证书文件(后缀为*.pfx)
+ * @param idQuery
+ * ) 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
+ * transaction_id> out_trade_no
+ * @param outRefundNo
+ * 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔
+ * @param totalFee
+ * 订单总金额,单位为元
+ * @param refundFee
+ * 退款总金额,单位为元,可以做部分退款
+ * @param opUserId
+ * 操作员帐号, 默认为商户号
+ * @param opUserPasswd
+ * 操作员密码
+ *
+ * @return 退款申请结果
+ * @see com.foxinmy.weixin4j.mp.api.PayApi
+ * @see com.foxinmy.weixin4j.mp.api.Pay2Api
+ * @see com.foxinmy.weixin4j.mp.payment.v2.RefundResult
+ * @since V2
+ * @throws WeixinException
+ */
+ public com.foxinmy.weixin4j.mp.payment.v2.RefundResult refundV2(
+ File caFile, IdQuery idQuery, String outRefundNo, double totalFee,
+ double refundFee, String opUserId, String opUserPasswd)
+ throws WeixinException {
+ return pay2Api.refund(caFile, idQuery, outRefundNo, totalFee,
+ refundFee, opUserId, opUserPasswd);
+ }
+
+ /**
+ * V2退款申请采用properties中配置的ca文件
+ *
+ * @see {@link com.foxinmy.weixin4j.mp.WeixinPayProxy#refundV2(File, IdQuery, String, double, double, String,String)}
+ */
+ public com.foxinmy.weixin4j.mp.payment.v2.RefundResult refundV2(
+ IdQuery idQuery, String outRefundNo, double totalFee,
+ double refundFee, String opUserId, String opUserPasswd)
+ throws WeixinException {
+ File caFile = new File(ConfigUtil.getClassPathValue("ca_file"));
+ return refundV2(caFile, idQuery, outRefundNo, totalFee, refundFee,
+ opUserId, opUserPasswd);
+ }
+
+ /**
+ * V2退款申请
+ *
+ * @param caFile
+ * 证书文件(V2版本后缀为*.pfx)
+ * @param idQuery
+ * 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
+ * transaction_id> out_trade_no
+ * @param outRefundNo
+ * 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔
+ * @param totalFee
+ * 订单总金额,单位为元
+ * @param refundFee
+ * 退款总金额,单位为元,可以做部分退款
+ * @param opUserId
+ * 操作员帐号, 默认为商户号
+ * @param opUserPasswd
+ * 操作员密码,默认为商户后台登录密码
+ * @param recvUserId
+ * 转账退款接收退款的财付通帐号。 一般无需填写,只有退银行失败,资金转入商 户号现金账号时(即状态为转入代发,查询返 回的
+ * refund_status 是 7 或 11),填写原退款 单号并填写此字段,资金才会退到指定财付通
+ * 账号。其他情况此字段忽略
+ * @param reccvUserName
+ * 转账退款接收退款的姓名(需与接收退款的财 付通帐号绑定的姓名一致)
+ * @param refundType
+ * 为空或者填 1:商户号余额退款;2:现金帐号 退款;3:优先商户号退款,若商户号余额不足, 再做现金帐号退款。使用 2 或
+ * 3 时,需联系财 付通开通此功能
+ * @see com.foxinmy.weixin4j.mp.api.PayApi
+ * @see com.foxinmy.weixin4j.mp.api.Pay2Api
+ * @see com.foxinmy.weixin4j.mp.payment.v2.RefundResult
+ * @return 退款结果
+ */
+ public com.foxinmy.weixin4j.mp.payment.v2.RefundResult refundV2(
+ File caFile, IdQuery idQuery, String outRefundNo, double totalFee,
+ double refundFee, String opUserId, String opUserPasswd,
+ String recvUserId, String reccvUserName, RefundType refundType)
+ throws WeixinException {
+ return pay2Api.refund(caFile, idQuery, outRefundNo, totalFee,
+ refundFee, opUserId, opUserPasswd, recvUserId, reccvUserName,
+ refundType);
+ }
+
+ /**
+ * V2退款查询 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
+ *
+ * @param idQuery
+ * 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id
+ * 四个参数必填一个,优先级为:
+ * refund_id>out_refund_no>transaction_id>out_trade_no
+ * @return 退款记录
+ * @see com.foxinmy.weixin4j.mp.payment.v2.RefundRecord
+ * @see com.foxinmy.weixin4j.mp.api.PayApi
+ * @see com.foxinmy.weixin4j.mp.api.Pay2Api
+ * @since V2
+ * @throws WeixinException
+ */
+ public com.foxinmy.weixin4j.mp.payment.v2.RefundRecord refundQueryV2(
+ IdQuery idQuery) throws WeixinException {
+ return pay2Api.refundQuery(idQuery);
+ }
+
+ /**
+ * V3申请退款(请求需要双向证书)
+ *
+ * 交易时间超过 1 年的订单无法提交退款; 支持部分退款,部分退需要设置相同的订单号和不同的 out_refund_no。一笔退款失
+ * 败后重新提交,要采用原来的 out_refund_no。总退款金额不能超过用户实际支付金额。
+ *
+ *
+ * @param caFile
+ * 证书文件(后缀为*.p12)
+ * @param idQuery
+ * ) 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
+ * transaction_id> out_trade_no
+ * @param outRefundNo
+ * 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔
+ * @param totalFee
+ * 订单总金额,单位为元
+ * @param refundFee
+ * 退款总金额,单位为元,可以做部分退款
+ * @param opUserId
+ * 操作员帐号, 默认为商户号
+ *
+ * @return 退款申请结果
+ * @see com.foxinmy.weixin4j.mp.payment.v3.RefundResult
+ * @see com.foxinmy.weixin4j.mp.api.PayApi
+ * @see com.foxinmy.weixin4j.mp.api.Pay3Api
+ * @since V3
+ * @throws WeixinException
+ */
+ public com.foxinmy.weixin4j.mp.payment.v3.RefundResult refundV3(
+ File caFile, IdQuery idQuery, String outRefundNo, double totalFee,
+ double refundFee, String opUserId) throws WeixinException {
+ return pay3Api.refund(caFile, idQuery, outRefundNo, totalFee,
+ refundFee, opUserId);
+ }
+
+ /**
+ * V3退款申请采用properties中配置的ca文件
+ *
+ * @see {@link com.foxinmy.weixin4j.mp.WeixinPayProxy#refundV3(File, IdQuery, String, double, double, String)}
+ */
+ public com.foxinmy.weixin4j.mp.payment.v3.RefundResult refundV3(
+ IdQuery idQuery, String outRefundNo, double totalFee,
+ double refundFee, String opUserId) throws WeixinException {
+ File caFile = new File(ConfigUtil.getClassPathValue("ca_file"));
+ return pay3Api.refund(caFile, idQuery, outRefundNo, totalFee,
+ refundFee, opUserId);
+ }
+
+ /**
+ * V3退款查询 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
+ *
+ * @param idQuery
+ * 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id
+ * 四个参数必填一个,优先级为:
+ * refund_id>out_refund_no>transaction_id>out_trade_no
+ * @return 退款记录
+ * @see com.foxinmy.weixin4j.mp.api.PayApi
+ * @see com.foxinmy.weixin4j.mp.api.Pay3Api
+ * @see com.foxinmy.weixin4j.mp.payment.v3.RefundRecord
+ * @since V3
+ * @throws WeixinException
+ */
+ public com.foxinmy.weixin4j.mp.payment.v3.RefundRecord refundQueryV3(
+ IdQuery idQuery) throws WeixinException {
+ return pay3Api.refundQuery(idQuery);
+ }
+
+ /**
+ * 下载对账单
+ * 1.微信侧未成功下单的交易不会出现在对账单中。支付成功后撤销的交易会出现在对账 单中,跟原支付单订单号一致,bill_type 为
+ * REVOKED;
+ * 2.微信在次日 9 点启动生成前一天的对账单,建议商户 9 点半后再获取;
+ * 3.对账单中涉及金额的字段单位为“元”。
+ *
+ * @param billDate
+ * 下载对账单的日期
+ * @param billType
+ * 下载对账单的类型 ALL,返回当日所有订单信息, 默认值 SUCCESS,返回当日成功支付的订单
+ * REFUND,返回当日退款订单
+ * @return excel表格
+ * @since V2 & V3
+ * @see com.foxinmy.weixin4j.mp.api.PayApi
+ * @throws WeixinException
+ */
+ public File downloadbill(Date billDate, BillType billType)
+ throws WeixinException {
+ return payApi.downloadbill(billDate, billType);
+ }
+
+ /**
+ * 冲正订单(需要证书) 当支付返回失败,或收银系统超时需要取消交易,可以调用该接口 接口逻辑:支
+ * 付失败的关单,支付成功的撤销支付 7天以内的单可撤销,其他正常支付的单
+ * 如需实现相同功能请调用退款接口 调用扣款接口后请勿立即调用撤销,需要等待5秒以上。先调用查单接口,如果没有确切的返回,再调用撤销
+ *
+ * @param ca
+ * 证书文件(V2版本后缀为*.pfx,V3版本后缀为*.p12)
+ * @param idQuery
+ * 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
+ * transaction_id> out_trade_no
+ * @return 撤销结果
+ * @see com.foxinmy.weixin4j.mp.api.PayApi
+ * @see com.foxinmy.weixin4j.mp.api.Pay2Api
+ * @see com.foxinmy.weixin4j.mp.api.Pay3Api
+ * @since V3
+ * @throws WeixinException
+ */
+ public ApiResult reverse(File caFile, IdQuery idQuery)
+ throws WeixinException {
+ return payApi.reverse(caFile, idQuery);
+ }
+
+ /**
+ * 冲正撤销:默认采用properties中配置的ca文件
+ *
+ * @param idQuery
+ * transaction_id、out_trade_no 二选一
+ * @return 撤销结果
+ * @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#reverse(File, IdQuery)}
+ * @throws WeixinException
+ */
+ public ApiResult reverse(IdQuery idQuery) throws WeixinException {
+ File caFile = new File(ConfigUtil.getClassPathValue("ca_file"));
+ return payApi.reverse(caFile, idQuery);
+ }
+
+ /**
+ * 关闭订单 当订单支付失败,调用关单接口后用新订单号重新发起支付,如果关单失败,返回已完
+ * 成支付请按正常支付处理。如果出现银行掉单,调用关单成功后,微信后台会主动发起退款。
+ *
+ * @param outTradeNo
+ * 商户系统内部的订单号
+ * @return 执行结果
+ * @see com.foxinmy.weixin4j.mp.api.PayApi
+ * @see com.foxinmy.weixin4j.mp.api.Pay3Api
+ * @since V3
+ * @throws WeixinException
+ */
+ public ApiResult closeOrder(String outTradeNo) throws WeixinException {
+ return payApi.closeOrder(outTradeNo);
+ }
+
+ /**
+ * native支付URL转短链接
+ *
+ * @param url
+ * 具有native标识的支付URL
+ * @return 转换后的短链接
+ * @see com.foxinmy.weixin4j.mp.api.PayApi
+ * @see com.foxinmy.weixin4j.mp.api.Pay2Api
+ * @see com.foxinmy.weixin4j.mp.api.Pay3Api
+ * @since V2 & V3
+ * @throws WeixinException
+ */
+ public String getPayShorturl(String url) throws WeixinException {
+ return payApi.getShorturl(url);
+ }
+
+ /**
+ * 接口上报
+ *
+ * @param interfaceUrl
+ * 上报对应的接口的完整 URL, 类似: https://api.mch.weixin.q
+ * q.com/pay/unifiedorder
+ * @param executeTime
+ * 接口耗时情况,单位为毫秒
+ * @param outTradeNo
+ * 商户系统内部的订单号,商 户可以在上报时提供相关商户订单号方便微信支付更好 的提高服务质量。
+ * @param ip
+ * 发起接口调用时的机器 IP
+ * @param time
+ * 商户调用该接口时商户自己 系统的时间
+ * @param returnXml
+ * 调用接口返回的基本数据
+ * @return 处理结果
+ * @see com.foxinmy.weixin4j.mp.api.PayApi
+ * @see com.foxinmy.weixin4j.mp.api.Pay3Api
+ * @throws WeixinException
+ */
+ public XmlResult interfaceReport(String interfaceUrl, int executeTime,
+ String outTradeNo, String ip, Date time, XmlResult returnXml)
+ throws WeixinException {
+ return pay3Api.interfaceReport(interfaceUrl, executeTime, outTradeNo,
+ ip, time, returnXml);
+ }
+}
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 5a624159..eb3412b3 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
@@ -2,13 +2,11 @@ package com.foxinmy.weixin4j.mp;
import java.io.File;
import java.io.IOException;
-import java.io.InputStream;
import java.util.Date;
import java.util.List;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
-import com.foxinmy.weixin4j.http.XmlResult;
import com.foxinmy.weixin4j.model.Button;
import com.foxinmy.weixin4j.model.WeixinMpAccount;
import com.foxinmy.weixin4j.mp.api.CustomApi;
@@ -18,7 +16,6 @@ import com.foxinmy.weixin4j.mp.api.MassApi;
import com.foxinmy.weixin4j.mp.api.MediaApi;
import com.foxinmy.weixin4j.mp.api.MenuApi;
import com.foxinmy.weixin4j.mp.api.NotifyApi;
-import com.foxinmy.weixin4j.mp.api.PayApi;
import com.foxinmy.weixin4j.mp.api.QrApi;
import com.foxinmy.weixin4j.mp.api.TmplApi;
import com.foxinmy.weixin4j.mp.api.UserApi;
@@ -33,12 +30,6 @@ import com.foxinmy.weixin4j.mp.model.QRParameter;
import com.foxinmy.weixin4j.mp.model.SemQuery;
import com.foxinmy.weixin4j.mp.model.SemResult;
import com.foxinmy.weixin4j.mp.model.User;
-import com.foxinmy.weixin4j.mp.payment.ApiResult;
-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.type.BillType;
-import com.foxinmy.weixin4j.mp.type.IdQuery;
import com.foxinmy.weixin4j.mp.type.IndustryType;
import com.foxinmy.weixin4j.mp.type.Lang;
import com.foxinmy.weixin4j.msg.model.Base;
@@ -70,7 +61,6 @@ public class WeixinProxy {
private final QrApi qrApi;
private final TmplApi tmplApi;
private final HelperApi helperApi;
- private final PayApi payApi;
/**
* 默认采用文件存放Token信息
@@ -116,7 +106,6 @@ public class WeixinProxy {
this.qrApi = new QrApi(tokenHolder);
this.tmplApi = new TmplApi(tokenHolder);
this.helperApi = new HelperApi(tokenHolder);
- this.payApi = new PayApi(tokenHolder);
}
/**
@@ -502,8 +491,7 @@ public class WeixinProxy {
* href="http://mp.weixin.qq.com/wiki/15/5380a4e6f02f2ffdc7981a8ed7a40753.html#.E5.88.A0.E9.99.A4.E7.BE.A4.E5.8F.91.E3.80.90.E8.AE.A2.E9.98.85.E5.8F.B7.E4.B8.8E.E6.9C.8D.E5.8A.A1.E5.8F.B7.E8.AE.A4.E8.AF.81.E5.90.8E.E5.9D.87.E5.8F.AF.E7.94.A8.E3.80.91">删除群发
* @see com.foxinmy.weixin4j.mp.api.MassApi
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#massByGroupId(Base, int)}
- * @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#massByOpenIds(Base, String...)
-
+ * @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#massByOpenIds(Base, String...)
*/
public JsonResult deleteMassNews(String msgid) throws WeixinException {
return massApi.deleteMassNews(msgid);
@@ -756,7 +744,8 @@ public class WeixinProxy {
/**
* 自定义菜单
*
- * @param btnList 菜单列表
+ * @param btnList
+ * 菜单列表
* @throws WeixinException
* @see 创建自定义菜单
@@ -798,7 +787,8 @@ public class WeixinProxy {
/**
* 生成带参数的二维码
*
- * @param parameter 二维码参数
+ * @param parameter
+ * 二维码参数
* @return byte数据包
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.api.QrApi
@@ -900,7 +890,8 @@ public class WeixinProxy {
/**
* 长链接转短链接
*
- * @param url 待转换的链接
+ * @param url
+ * 待转换的链接
* @return 短链接
* @throws WeixinException
* @see out_trade_no
- * @see com.foxinmy.weixin4j.mp.api.PayApi
- * @throws WeixinException
- */
- public com.foxinmy.weixin4j.mp.payment.v3.Order orderQueryV3(IdQuery idQuery)
- throws WeixinException {
- return payApi.orderQueryV3(idQuery);
- }
-
- /**
- * 下载对账单
- * 1.微信侧未成功下单的交易不会出现在对账单中。支付成功后撤销的交易会出现在对账 单中,跟原支付单订单号一致,bill_type 为
- * REVOKED;
- * 2.微信在次日 9 点启动生成前一天的对账单,建议商户 9 点半后再获取;
- * 3.对账单中涉及金额的字段单位为“元”。
- *
- * @param billDate
- * 下载对账单的日期
- * @param billType
- * 下载对账单的类型 ALL,返回当日所有订单信息, 默认值 SUCCESS,返回当日成功支付的订单
- * REFUND,返回当日退款订单
- * @return excel表格
- * @since V2 & V3
- * @see com.foxinmy.weixin4j.mp.api.PayApi
- * @throws WeixinException
- * @throws IOException
- */
- public File downloadbill(Date billDate, BillType billType)
- throws WeixinException, IOException {
- return payApi.downloadbill(billDate, billType);
- }
-
- /**
- * 申请退款(V3请求需要双向证书)
- *
- * 交易时间超过 1 年的订单无法提交退款; 支持部分退款,部分退需要设置相同的订单号和不同的 out_refund_no。一笔退款失
- * 败后重新提交,要采用原来的 out_refund_no。总退款金额不能超过用户实际支付金额。
- *
- *
- * @param ca
- * 证书文件V2版本时无需传入
- * @param idQuery
- * ) 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
- * transaction_id> out_trade_no
- * @param outRefundNo
- * 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔
- * @param totalFee
- * 订单总金额,单位为元
- * @param refundFee
- * 退款总金额,单位为元,可以做部分退款
- * @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
- */
- public RefundResult refund(InputStream ca, IdQuery idQuery,
- String outRefundNo, double totalFee, double refundFee,
- String opUserId, String opUserPasswd) throws WeixinException,
- IOException {
- return payApi.refund(ca, idQuery, outRefundNo, totalFee, refundFee,
- opUserId, opUserPasswd);
- }
-
- /**
- * 不同的退款接口选择 V3支付则采用properties中配置的ca文件
- * V2支付则需要传入opUserPasswd参数
- *
- * @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,
- String opUserPasswd) throws WeixinException, IOException {
- return payApi.refund(idQuery, outRefundNo, totalFee, refundFee,
- opUserId, opUserPasswd);
- }
-
- /**
- * 退款查询 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
- *
- * @param idQuery
- * 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id
- * 四个参数必填一个,优先级为:
- * refund_id>out_refund_no>transaction_id>out_trade_no
- * @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 {
- return payApi.refundQuery(idQuery);
- }
-
- /**
- * 关闭订单 当订单支付失败,调用关单接口后用新订单号重新发起支付,如果关单失败,返回已完
- * 成支付请按正常支付处理。如果出现银行掉单,调用关单成功后,微信后台会主动发起退款。
- *
- * @param outTradeNo
- * 商户系统内部的订单号
- * @return 执行结果
- * @see com.foxinmy.weixin4j.mp.api.PayApi
- * @since V3
- * @throws WeixinException
- */
- public XmlResult closeOrder(String outTradeNo) throws WeixinException {
- return payApi.closeOrder(outTradeNo);
- }
-
- /**
- * native支付URL转短链接
- *
- * @param url
- * 具有native标识的支付URL
- * @return 转换后的短链接
- * @see com.foxinmy.weixin4j.mp.api.PayApi
- * @throws WeixinException
- */
- public String getPayShorturl(String url) throws WeixinException {
- return payApi.getShorturl(url);
- }
-
/**
* 语义理解
*
@@ -1129,39 +931,4 @@ public class WeixinProxy {
public List getcallbackip() throws WeixinException {
return helperApi.getcallbackip();
}
-
- /**
- * 冲正订单(需要证书) 当支付返回失败,或收银系统超时需要取消交易,可以调用该接口 接口逻辑:支
- * 付失败的关单,支付成功的撤销支付 7天以内的单可撤销,其他正常支付的单
- * 如需实现相同功能请调用退款接口 调用扣款接口后请勿立即调用撤销,需要等待5秒以上。先调用查单接口,如果没有确切的返回,再调用撤销
- *
- * @param ca
- * 证书文件
- * @param idQuery
- * 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
- * transaction_id> out_trade_no
- * @return 撤销结果
- * @see com.foxinmy.weixin4j.mp.api.PayApi
- * @throws WeixinException
- */
- public ApiResult reverse(InputStream ca, IdQuery idQuery)
- throws WeixinException {
- return payApi.reverse(ca, idQuery);
- }
-
- /**
- * 冲正撤销:默认采用properties中配置的ca文件
- *
- * @param idQuery
- * transaction_id、out_trade_no 二选一
- * @return 撤销结果
- * @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#reverse(InputStream, IdQuery)}
- * @throws WeixinException
- * @throws IOException
- */
- public ApiResult reverse(IdQuery idQuery) throws WeixinException,
- IOException {
- return payApi.reverse(idQuery);
- }
}
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/Pay2Api.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/Pay2Api.java
new file mode 100644
index 00000000..9c06d918
--- /dev/null
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/Pay2Api.java
@@ -0,0 +1,465 @@
+package com.foxinmy.weixin4j.mp.api;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+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;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.TypeReference;
+import com.alibaba.fastjson.parser.Feature;
+import com.foxinmy.weixin4j.exception.WeixinException;
+import com.foxinmy.weixin4j.http.Response;
+import com.foxinmy.weixin4j.http.SSLHttpRequest;
+import com.foxinmy.weixin4j.model.Token;
+import com.foxinmy.weixin4j.mp.payment.PayUtil;
+import com.foxinmy.weixin4j.mp.payment.RefundConverter;
+import com.foxinmy.weixin4j.mp.payment.v2.Order;
+import com.foxinmy.weixin4j.mp.payment.v2.RefundRecord;
+import com.foxinmy.weixin4j.mp.payment.v2.RefundResult;
+import com.foxinmy.weixin4j.mp.payment.v3.ApiResult;
+import com.foxinmy.weixin4j.mp.type.BillType;
+import com.foxinmy.weixin4j.mp.type.IdQuery;
+import com.foxinmy.weixin4j.mp.type.RefundType;
+import com.foxinmy.weixin4j.mp.type.SignType;
+import com.foxinmy.weixin4j.mp.util.ExcelUtil;
+import com.foxinmy.weixin4j.token.TokenHolder;
+import com.foxinmy.weixin4j.util.ConfigUtil;
+import com.foxinmy.weixin4j.util.DateUtil;
+import com.foxinmy.weixin4j.util.MapUtil;
+
+/**
+ * V2支付API
+ *
+ * @className PayApi
+ * @author jy
+ * @date 2014年10月28日
+ * @since JDK 1.7
+ * @see
+ */
+public class Pay2Api extends PayApi {
+
+ private final HelperApi helperApi;
+
+ public Pay2Api(TokenHolder tokenHolder) {
+ super(tokenHolder);
+ this.helperApi = new HelperApi(tokenHolder);
+ }
+
+ /**
+ * 订单查询
+ *
+ * @param idQuery
+ * 订单号
+ * @return 订单信息
+ * @see com.foxinmy.weixin4j.mp.payment.v2.Order
+ * @since V2
+ * @throws WeixinException
+ */
+ public Order orderQuery(IdQuery idQuery) throws WeixinException {
+ String orderquery_uri = getRequestUri("orderquery_uri");
+ Token token = tokenHolder.getToken();
+ StringBuilder sb = new StringBuilder();
+ sb.append(idQuery.getType().getName()).append("=")
+ .append(idQuery.getId());
+ sb.append("&partner=").append(weixinAccount.getPartnerId());
+ String part = sb.toString();
+ sb.append("&key=").append(weixinAccount.getPartnerKey());
+ String sign = DigestUtils.md5Hex(sb.toString()).toUpperCase();
+ sb.delete(0, sb.length());
+ sb.append(part).append("&sign=").append(sign);
+
+ String timestamp = DateUtil.timestamp2string();
+ JSONObject obj = new JSONObject();
+ obj.put("appid", weixinAccount.getId());
+ obj.put("appkey", weixinAccount.getPaySignKey());
+ obj.put("package", sb.toString());
+ obj.put("timestamp", timestamp);
+ String signature = PayUtil.paysignSha(obj);
+
+ obj.clear();
+ obj.put("appid", weixinAccount.getId());
+ obj.put("package", sb.toString());
+ obj.put("timestamp", timestamp);
+ obj.put("app_signature", signature);
+ obj.put("sign_method", SignType.SHA1.name().toLowerCase());
+
+ Response response = request.post(
+ String.format(orderquery_uri, token.getAccessToken()),
+ obj.toJSONString());
+
+ String order_info = response.getAsJson().getString("order_info");
+ Order order = JSON.parseObject(order_info, Order.class,
+ Feature.IgnoreNotMatch);
+ if (order.getRetCode() != 0) {
+ throw new WeixinException(Integer.toString(order.getRetCode()),
+ order.getRetMsg());
+ }
+ return order;
+ }
+
+ /**
+ * 申请退款(需要证书)
+ *
+ * 交易时间超过 1 年的订单无法提交退款; 支持部分退款,部分退需要设置相同的订单号和不同的 out_refund_no。一笔退款失
+ * 败后重新提交,要采用原来的 out_refund_no。总退款金额不能超过用户实际支付金额。
+ *
+ *
+ * @param caFile
+ * 证书文件(V2版本后缀为*.pfx)
+ * @param idQuery
+ * 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
+ * transaction_id> out_trade_no
+ * @param outRefundNo
+ * 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔
+ * @param totalFee
+ * 订单总金额,单位为元
+ * @param refundFee
+ * 退款总金额,单位为元,可以做部分退款
+ * @param opUserId
+ * 操作员帐号, 默认为商户号
+ * @param mopara
+ * 如 opUserPasswd
+ *
+ * @return 退款申请结果
+ * @see com.foxinmy.weixin4j.mp.payment.v2.RefundResult
+ * @since V2
+ * @throws WeixinException
+ */
+ @Override
+ protected RefundResult refund(File caFile, IdQuery idQuery,
+ String outRefundNo, double totalFee, double refundFee,
+ String opUserId, Map mopara) throws WeixinException {
+ String refund_uri = getRequestUri("refund_v2_uri");
+ Response response = null;
+ InputStream ca = null;
+ try {
+ ca = new FileInputStream(caFile);
+ 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);
+ if (mopara != null && !mopara.isEmpty()) {
+ map.putAll(mopara);
+ }
+ String sign = PayUtil
+ .paysignMd5(map, weixinAccount.getPartnerKey());
+ map.put("sign", sign.toLowerCase());
+
+ SSLContext ctx = null;
+ KeyStore ks = null;
+ String jksPwd = "";
+ File jksFile = new File(String.format("%s/tenpay_cacert.jks",
+ caFile.getParent()));
+ // 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());
+
+ SSLHttpRequest request = new SSLHttpRequest(ctx);
+ response = request.get(refund_uri, map);
+ } catch (WeixinException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new WeixinException(e.getMessage());
+ } finally {
+ if (ca != null) {
+ try {
+ ca.close();
+ } catch (IOException e) {
+ ;
+ }
+ }
+ }
+ return response.getAsObject(new TypeReference() {
+ });
+ }
+
+ /**
+ * 退款申请
+ *
+ * @param caFile
+ * 证书文件(V2版本后缀为*.pfx)
+ * @param idQuery
+ * 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
+ * transaction_id> out_trade_no
+ * @param outRefundNo
+ * 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔
+ * @param totalFee
+ * 订单总金额,单位为元
+ * @param refundFee
+ * 退款总金额,单位为元,可以做部分退款
+ * @param opUserId
+ * 操作员帐号, 默认为商户号
+ * @param opUserPasswd
+ * 操作员密码,默认为商户后台登录密码
+ * @see {@link com.foxinmy.weixin4j.mp.api.Pay2Api#refund(File, IdQuery, String, double, double, String, Map)}
+ */
+ public RefundResult refund(File caFile, IdQuery idQuery,
+ String outRefundNo, double totalFee, double refundFee,
+ String opUserId, String opUserPasswd) throws WeixinException {
+ Map mopara = new HashMap();
+ mopara.put("op_user_passwd", DigestUtils.md5Hex(opUserPasswd));
+ return refund(caFile, idQuery, outRefundNo, totalFee, refundFee,
+ opUserId, mopara);
+ }
+
+ /**
+ * 退款申请
+ *
+ * @param caFile
+ * 证书文件(V2版本后缀为*.pfx)
+ * @param idQuery
+ * 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
+ * transaction_id> out_trade_no
+ * @param outRefundNo
+ * 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔
+ * @param totalFee
+ * 订单总金额,单位为元
+ * @param refundFee
+ * 退款总金额,单位为元,可以做部分退款
+ * @param opUserId
+ * 操作员帐号, 默认为商户号
+ * @param opUserPasswd
+ * 操作员密码,默认为商户后台登录密码
+ * @param recvUserId
+ * 转账退款接收退款的财付通帐号。 一般无需填写,只有退银行失败,资金转入商 户号现金账号时(即状态为转入代发,查询返 回的
+ * refund_status 是 7 或 11),填写原退款 单号并填写此字段,资金才会退到指定财付通
+ * 账号。其他情况此字段忽略
+ * @param reccvUserName
+ * 转账退款接收退款的姓名(需与接收退款的财 付通帐号绑定的姓名一致)
+ * @param refundType
+ * 为空或者填 1:商户号余额退款;2:现金帐号 退款;3:优先商户号退款,若商户号余额不足, 再做现金帐号退款。使用 2 或
+ * 3 时,需联系财 付通开通此功能
+ * @see {@link com.foxinmy.weixin4j.mp.api.Pay2Api#refund(File, IdQuery, String, double, double, String, Map)}
+ * @return 退款结果
+ */
+ public RefundResult refund(File caFile, IdQuery idQuery,
+ String outRefundNo, double totalFee, double refundFee,
+ String opUserId, String opUserPasswd, String recvUserId,
+ String reccvUserName, RefundType refundType) throws WeixinException {
+ Map mopara = new HashMap();
+ mopara.put("op_user_passwd", DigestUtils.md5Hex(opUserPasswd));
+ if (StringUtils.isNotBlank(recvUserId)) {
+ mopara.put("recv_user_id", recvUserId);
+ }
+ if (StringUtils.isNotBlank(reccvUserName)) {
+ mopara.put("reccv_user_name", reccvUserName);
+ }
+ if (refundType != null) {
+ mopara.put("refund_type", Integer.toString(refundType.getVal()));
+ }
+ return refund(caFile, idQuery, outRefundNo, totalFee, refundFee,
+ opUserId, mopara);
+ }
+
+ /**
+ * 冲正订单(需要证书)V2暂不支持
+ *
+ * @param caFile
+ * 证书文件(V2版本后缀为*.pfx)
+ * @param idQuery
+ * 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
+ * transaction_id> out_trade_no
+ * @since V2
+ * @return 撤销结果
+ * @throws WeixinException
+ */
+ public ApiResult reverse(File caFile, IdQuery idQuery)
+ throws WeixinException {
+ throw new WeixinException("V2 unsupport reverse api");
+ }
+
+ /**
+ * 关闭订单 当订单支付失败,调用关单接口后用新订单号重新发起支付,如果关单失败,返回已完
+ * 成支付请按正常支付处理。如果出现银行掉单,调用关单成功后,微信后台会主动发起退款。
+ *
+ * @param outTradeNo
+ * 商户系统内部的订单号
+ * @return 处理结果
+ * @since V2
+ * @throws WeixinException
+ */
+ public ApiResult closeOrder(String outTradeNo) throws WeixinException {
+ throw new WeixinException("V2 unsupport closeOrder api");
+ }
+
+ /**
+ * 下载对账单
+ * 1.微信侧未成功下单的交易不会出现在对账单中。支付成功后撤销的交易会出现在对账 单中,跟原支付单订单号一致,bill_type 为
+ * REVOKED;
+ * 2.微信在次日 9 点启动生成前一天的对账单,建议商户 9 点半后再获取;
+ * 3.对账单中涉及金额的字段单位为“元”。
+ *
+ * @param billDate
+ * 下载对账单的日期 为空则取前一天
+ * @param billType
+ * 下载对账单的类型 ALL,返回当日所有订单信息, 默认值 SUCCESS,返回当日成功支付的订单
+ * REFUND,返回当日退款订单
+ * @return excel表格
+ * @since V2
+ * @throws WeixinException
+ */
+ public File downloadbill(Date billDate, BillType billType)
+ throws WeixinException {
+ if (billDate == null) {
+ Calendar now = Calendar.getInstance();
+ now.add(Calendar.DAY_OF_MONTH, -1);
+ billDate = now.getTime();
+ }
+ if (billType == null) {
+ billType = BillType.ALL;
+ }
+ String formatBillDate = DateUtil.fortmat2yyyyMMdd(billDate);
+ String bill_path = ConfigUtil.getValue("bill_path");
+ String fileName = String.format("%s_%s_%s.xls", formatBillDate,
+ billType.name().toLowerCase(), weixinAccount.getId());
+ File file = new File(String.format("%s/%s", bill_path, fileName));
+ if (file.exists()) {
+ return file;
+ }
+ String downloadbill_uri = getRequestUri("downloadbill_v2_uri");
+
+ Map map = new LinkedHashMap();
+ map.put("spid", weixinAccount.getPartnerId());
+ map.put("trans_time", DateUtil.fortmat2yyyy_MM_dd(billDate));
+ map.put("stamp", DateUtil.timestamp2string());
+ 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 response = request.get(downloadbill_uri, map);
+ BufferedReader reader = null;
+ OutputStream os = null;
+ try {
+ reader = new BufferedReader(
+ new InputStreamReader(response.getStream(),
+ com.foxinmy.weixin4j.model.Consts.GBK));
+ String line = null;
+ List bills = new LinkedList();
+ while ((line = reader.readLine()) != null) {
+ bills.add(line.replaceAll("`", "").split(","));
+ }
+
+ List headers = Arrays.asList(bills.remove(0));
+ List totalDatas = Arrays
+ .asList(bills.remove(bills.size() - 1));
+ List totalHeaders = Arrays
+ .asList(bills.remove(bills.size() - 1));
+ HSSFWorkbook wb = new HSSFWorkbook();
+ wb.createSheet(formatBillDate + "对账单");
+ ExcelUtil.list2excel(wb, headers, bills);
+ ExcelUtil.list2excel(wb, totalHeaders, totalDatas);
+ os = new FileOutputStream(file);
+ wb.write(os);
+ } catch (IOException e) {
+ throw new WeixinException(e.getMessage());
+ } finally {
+ try {
+ if (reader != null) {
+ reader.close();
+ }
+ if (os != null) {
+ os.close();
+ }
+ } catch (IOException ignore) {
+ ;
+ }
+ }
+ return file;
+ }
+
+ /**
+ * 退款查询 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
+ *
+ * @param idQuery
+ * 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id
+ * 四个参数必填一个,优先级为:
+ * refund_id>out_refund_no>transaction_id>out_trade_no
+ * @return 退款记录
+ * @see com.foxinmy.weixin4j.mp.payment.v2.RefundRecord
+ * @see com.foxinmy.weixin4j.mp.payment.v2.RefundDetail
+ * @since V2
+ * @throws WeixinException
+ */
+ public RefundRecord refundQuery(IdQuery idQuery) throws WeixinException {
+ String refundquery_uri = getRequestUri("refundquery_v2_uri");
+ 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 response = request.get(refundquery_uri, map);
+ return RefundConverter.fromXML(response.getAsString(),
+ RefundRecord.class);
+ }
+
+ @Override
+ public String getShorturl(String url) throws WeixinException {
+ return helperApi.getShorturl(url);
+ }
+}
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/Pay3Api.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/Pay3Api.java
new file mode 100644
index 00000000..fea847ab
--- /dev/null
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/Pay3Api.java
@@ -0,0 +1,430 @@
+package com.foxinmy.weixin4j.mp.api;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.Consts;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.TypeReference;
+import com.foxinmy.weixin4j.exception.WeixinException;
+import com.foxinmy.weixin4j.http.Response;
+import com.foxinmy.weixin4j.http.SSLHttpRequest;
+import com.foxinmy.weixin4j.http.XmlResult;
+import com.foxinmy.weixin4j.mp.payment.PayUtil;
+import com.foxinmy.weixin4j.mp.payment.RefundConverter;
+import com.foxinmy.weixin4j.mp.payment.v3.ApiResult;
+import com.foxinmy.weixin4j.mp.payment.v3.Order;
+import com.foxinmy.weixin4j.mp.payment.v3.RefundRecord;
+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;
+import com.foxinmy.weixin4j.mp.util.ExcelUtil;
+import com.foxinmy.weixin4j.token.TokenHolder;
+import com.foxinmy.weixin4j.util.ConfigUtil;
+import com.foxinmy.weixin4j.util.DateUtil;
+import com.foxinmy.weixin4j.util.RandomUtil;
+
+/**
+ * V3支付API
+ *
+ * @className PayApi
+ * @author jy
+ * @date 2014年10月28日
+ * @since JDK 1.7
+ * @see
+ */
+public class Pay3Api extends PayApi {
+
+ public Pay3Api(TokenHolder tokenHolder) {
+ super(tokenHolder);
+ }
+
+ /**
+ * 订单查询
+ *
+ * @param idQuery
+ * 商户系统内部的订单号, transaction_id、out_trade_no 二 选一,如果同时存在优先级:
+ * transaction_id> out_trade_no
+ * @return 订单信息
+ * @see com.foxinmy.weixin4j.mp.payment.v3.Order
+ * @since V3
+ * @throws WeixinException
+ */
+ public Order orderQuery(IdQuery idQuery) throws WeixinException {
+ Map map = baseMap(idQuery);
+ String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
+ map.put("sign", sign);
+ String param = map2xml(map);
+ String orderquery_uri = getRequestUri("orderquery_v3_uri");
+ Response response = request.post(orderquery_uri, param);
+ return response.getAsObject(new TypeReference() {
+ });
+ }
+
+ /**
+ * 申请退款(请求需要双向证书)
+ *
+ * 交易时间超过 1 年的订单无法提交退款; 支持部分退款,部分退需要设置相同的订单号和不同的 out_refund_no。一笔退款失
+ * 败后重新提交,要采用原来的 out_refund_no。总退款金额不能超过用户实际支付金额。
+ *
+ *
+ * @param caFile
+ * 证书文件(V3版本后缀为*.p12)
+ * @param idQuery
+ * 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
+ * transaction_id> out_trade_no
+ * @param outRefundNo
+ * 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔
+ * @param totalFee
+ * 订单总金额,单位为元
+ * @param refundFee
+ * 退款总金额,单位为元,可以做部分退款
+ * @param opUserId
+ * 操作员帐号, 默认为商户号
+ *
+ * @return 退款申请结果
+ * @see com.foxinmy.weixin4j.mp.payment.v3.RefundResult
+ * @since V3
+ * @throws WeixinException
+ */
+ protected RefundResult refund(File caFile, IdQuery idQuery,
+ String outRefundNo, double totalFee, double refundFee,
+ String opUserId, Map mopara) throws WeixinException {
+ String refund_uri = getRequestUri("refund_v3_uri");
+ Response response = null;
+ InputStream ca = null;
+ try {
+ ca = new FileInputStream(caFile);
+
+ Map map = baseMap(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);
+ if (mopara != null && !mopara.isEmpty()) {
+ map.putAll(mopara);
+ }
+ 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);
+ } catch (WeixinException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new WeixinException(e.getMessage());
+ } finally {
+ if (ca != null) {
+ try {
+ ca.close();
+ } catch (IOException e) {
+ ;
+ }
+ }
+ }
+ return response.getAsObject(new TypeReference() {
+ });
+ }
+
+ /**
+ * 退款申请
+ *
+ * @param caFile
+ * 证书文件(V2版本后缀为*.pfx)
+ * @param idQuery
+ * 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
+ * transaction_id> out_trade_no
+ * @param outRefundNo
+ * 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔
+ * @param totalFee
+ * 订单总金额,单位为元
+ * @param refundFee
+ * 退款总金额,单位为元,可以做部分退款
+ * @param opUserId
+ * 操作员帐号, 默认为商户号
+ * @see {@link com.foxinmy.weixin4j.mp.api.Pay3Api#refund(File, IdQuery, String, double, double, String, Map)}
+ */
+ public RefundResult refund(File caFile, IdQuery idQuery,
+ String outRefundNo, double totalFee, double refundFee,
+ String opUserId) throws WeixinException {
+ return refund(caFile, idQuery, outRefundNo, totalFee, refundFee,
+ opUserId, null);
+ }
+
+ /**
+ * 冲正订单(需要证书) 当支付返回失败,或收银系统超时需要取消交易,可以调用该接口 接口逻辑:支
+ * 付失败的关单,支付成功的撤销支付 7天以内的单可撤销,其他正常支付的单
+ * 如需实现相同功能请调用退款接口 调用扣款接口后请勿立即调用撤销,需要等待5秒以上。先调用查单接口,如果没有确切的返回,再调用撤销
+ *
+ * @param caFile
+ * 证书文件(V3版本后缀为*.p12)
+ * @param idQuery
+ * 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
+ * transaction_id> out_trade_no
+ * @return 撤销结果
+ * @since V3
+ * @throws WeixinException
+ */
+ public ApiResult reverse(File caFile, IdQuery idQuery)
+ throws WeixinException {
+ InputStream ca = null;
+ try {
+ ca = new FileInputStream(caFile);
+ SSLHttpRequest request = new SSLHttpRequest(
+ weixinAccount.getMchId(), ca);
+ String reverse_uri = getRequestUri("reverse_uri");
+ Map map = baseMap(idQuery);
+ String sign = PayUtil
+ .paysignMd5(map, weixinAccount.getPaySignKey());
+ map.put("sign", sign);
+ String param = map2xml(map);
+ Response response = request.post(reverse_uri, param);
+ return response.getAsObject(new TypeReference() {
+ });
+ } catch (IOException e) {
+ throw new WeixinException(e.getMessage());
+ } finally {
+ if (ca != null) {
+ try {
+ ca.close();
+ } catch (IOException e) {
+ ;
+ }
+ }
+ }
+ }
+
+ /**
+ * native支付URL转短链接
+ *
+ * @param url
+ * 具有native标识的支付URL
+ * @return 转换后的短链接
+ * @throws WeixinException
+ */
+ public String getShorturl(String url) throws WeixinException {
+ Map map = baseMap(null);
+ try {
+ map.put("long_url", URLEncoder.encode(url, Consts.UTF_8.name()));
+ } catch (UnsupportedEncodingException ignore) {
+ ;
+ }
+ String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
+ map.put("sign", sign);
+ String param = map2xml(map);
+ String shorturl_uri = getRequestUri("p_shorturl_uri");
+ Response response = request.post(shorturl_uri, param);
+ map = xml2map(response.getAsString());
+ return map.get("short_url");
+ }
+
+ /**
+ * 关闭订单 当订单支付失败,调用关单接口后用新订单号重新发起支付,如果关单失败,返回已完
+ * 成支付请按正常支付处理。如果出现银行掉单,调用关单成功后,微信后台会主动发起退款。
+ *
+ * @param outTradeNo
+ * 商户系统内部的订单号
+ * @return 处理结果
+ * @since V3
+ * @throws WeixinException
+ */
+ public ApiResult closeOrder(String outTradeNo) throws WeixinException {
+ Map map = baseMap(new IdQuery(outTradeNo,
+ IdType.TRADENO));
+ String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
+ map.put("sign", sign);
+ String param = map2xml(map);
+ String closeorder_uri = getRequestUri("closeorder_uri");
+ Response response = request.post(closeorder_uri, param);
+ return response.getAsObject(new TypeReference() {
+ });
+ }
+
+ /**
+ * 下载对账单
+ * 1.微信侧未成功下单的交易不会出现在对账单中。支付成功后撤销的交易会出现在对账 单中,跟原支付单订单号一致,bill_type 为
+ * REVOKED;
+ * 2.微信在次日 9 点启动生成前一天的对账单,建议商户 9 点半后再获取;
+ * 3.对账单中涉及金额的字段单位为“元”。
+ *
+ * @param billDate
+ * 下载对账单的日期
+ * @param billType
+ * 下载对账单的类型 ALL,返回当日所有订单信息, 默认值 SUCCESS,返回当日成功支付的订单
+ * REFUND,返回当日退款订单
+ * @return excel表格
+ * @since V3
+ * @throws WeixinException
+ */
+ public File downloadbill(Date billDate, BillType billType)
+ throws WeixinException {
+ if (billDate == null) {
+ Calendar now = Calendar.getInstance();
+ now.add(Calendar.DAY_OF_MONTH, -1);
+ billDate = now.getTime();
+ }
+ if (billType == null) {
+ billType = BillType.ALL;
+ }
+ String formatBillDate = DateUtil.fortmat2yyyyMMdd(billDate);
+ String bill_path = ConfigUtil.getValue("bill_path");
+ String fileName = String.format("%s_%s_%s.xls", formatBillDate,
+ billType.name().toLowerCase(), weixinAccount.getId());
+ File file = new File(String.format("%s/%s", bill_path, fileName));
+ if (file.exists()) {
+ return file;
+ }
+ String downloadbill_uri = getRequestUri("downloadbill_v3_uri");
+ Map map = baseMap(null);
+ map.put("bill_date", formatBillDate);
+ map.put("bill_type", billType.name());
+ String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
+ map.put("sign", sign);
+ String param = map2xml(map);
+ Response response = request.post(downloadbill_uri, param);
+
+ BufferedReader reader = null;
+ OutputStream os = null;
+ try {
+ reader = new BufferedReader(new InputStreamReader(
+ response.getStream(), Consts.UTF_8));
+ String line = null;
+ List bills = new LinkedList();
+ while ((line = reader.readLine()) != null) {
+ bills.add(line.replaceAll("`", "").split(","));
+ }
+
+ List headers = Arrays.asList(bills.remove(0));
+ List totalDatas = Arrays
+ .asList(bills.remove(bills.size() - 1));
+ List totalHeaders = Arrays
+ .asList(bills.remove(bills.size() - 1));
+ HSSFWorkbook wb = new HSSFWorkbook();
+ wb.createSheet(formatBillDate + "对账单");
+ ExcelUtil.list2excel(wb, headers, bills);
+ ExcelUtil.list2excel(wb, totalHeaders, totalDatas);
+ os = new FileOutputStream(file);
+ wb.write(os);
+ } catch (IOException e) {
+ throw new WeixinException(e.getMessage());
+ } finally {
+ try {
+ if (reader != null) {
+ reader.close();
+ }
+ if (os != null) {
+ os.close();
+ }
+ } catch (IOException ignore) {
+ ;
+ }
+ }
+ return file;
+ }
+
+ /**
+ * 退款查询 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
+ *
+ * @param idQuery
+ * 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id
+ * 四个参数必填一个,优先级为:
+ * refund_id>out_refund_no>transaction_id>out_trade_no
+ * @return 退款记录
+ * @see com.foxinmy.weixin4j.mp.payment.v3.RefundRecord
+ * @see com.foxinmy.weixin4j.mp.payment.v3.RefundDetail
+ * @since V3
+ * @throws WeixinException
+ */
+ public RefundRecord refundQuery(IdQuery idQuery) throws WeixinException {
+ String refundquery_uri = getRequestUri("refundquery_v3_uri");
+ Map map = baseMap(idQuery);
+ String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
+ map.put("sign", sign);
+ String param = map2xml(map);
+ Response response = request.post(refundquery_uri, param);
+ return RefundConverter.fromXML(response.getAsString(),
+ RefundRecord.class);
+ }
+
+ /**
+ * 接口上报
+ *
+ * @param interfaceUrl
+ * 上报对应的接口的完整 URL, 类似: https://api.mch.weixin.q
+ * q.com/pay/unifiedorder
+ * @param executeTime
+ * 接口耗时情况,单位为毫秒
+ * @param outTradeNo
+ * 商户系统内部的订单号,商 户可以在上报时提供相关商户订单号方便微信支付更好 的提高服务质量。
+ * @param ip
+ * 发起接口调用时的机器 IP
+ * @param time
+ * 商户调用该接口时商户自己 系统的时间
+ * @param returnXml
+ * 调用接口返回的基本数据
+ * @return 处理结果
+ * @throws WeixinException
+ */
+ @SuppressWarnings("unchecked")
+ public XmlResult interfaceReport(String interfaceUrl, int executeTime,
+ String outTradeNo, String ip, Date time, XmlResult returnXml)
+ throws WeixinException {
+ String pay_report_uri = getRequestUri("pay_report_uri");
+ Map map = baseMap(null);
+ map.put("interface_url", interfaceUrl);
+ map.put("execute_time_", Integer.toString(executeTime));
+ map.put("out_trade_no", outTradeNo);
+ map.put("user_ip", ip);
+ map.put("time", DateUtil.fortmat2yyyyMMddHHmmss(time));
+ map.putAll((Map) JSON.toJSON(returnXml));
+ String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
+ map.put("sign", sign);
+ String param = map2xml(map);
+ Response response = request.post(pay_report_uri, param);
+ return response.getAsXmlResult();
+ }
+
+ /**
+ * V3接口请求基本数据
+ *
+ * @return
+ */
+ private Map baseMap(IdQuery idQuery) {
+ Map map = new HashMap();
+ map.put("appid", weixinAccount.getId());
+ map.put("mch_id", weixinAccount.getMchId());
+ map.put("nonce_str", RandomUtil.generateString(16));
+ if (StringUtils.isNotBlank(weixinAccount.getDeviceInfo())) {
+ map.put("device_info", weixinAccount.getDeviceInfo());
+ }
+ if (idQuery != null) {
+ map.put(idQuery.getType().getName(), idQuery.getId());
+ }
+ return map;
+ }
+}
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 fb6b8d0d..de207017 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
@@ -1,64 +1,23 @@
package com.foxinmy.weixin4j.mp.api;
-import java.io.BufferedReader;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-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;
-import com.alibaba.fastjson.JSONObject;
-import com.alibaba.fastjson.TypeReference;
-import com.alibaba.fastjson.parser.Feature;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.http.Response;
-import com.foxinmy.weixin4j.http.SSLHttpRequest;
-import com.foxinmy.weixin4j.http.XmlResult;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.model.WeixinMpAccount;
-import com.foxinmy.weixin4j.mp.payment.ApiResult;
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.ApiResult;
import com.foxinmy.weixin4j.mp.type.BillType;
import com.foxinmy.weixin4j.mp.type.IdQuery;
-import com.foxinmy.weixin4j.mp.type.IdType;
import com.foxinmy.weixin4j.mp.type.SignType;
-import com.foxinmy.weixin4j.mp.util.ExcelUtil;
import com.foxinmy.weixin4j.token.TokenHolder;
-import com.foxinmy.weixin4j.util.ConfigUtil;
import com.foxinmy.weixin4j.util.DateUtil;
-import com.foxinmy.weixin4j.util.MapUtil;
-import com.foxinmy.weixin4j.util.RandomUtil;
/**
* 支付API
@@ -67,11 +26,12 @@ import com.foxinmy.weixin4j.util.RandomUtil;
* @author jy
* @date 2014年10月28日
* @since JDK 1.7
- * @see
+ * @see com.foxinmy.weixin4j.mp.api.Pay2Api
+ * @see com.foxinmy.weixin4j.mp.api.Pay3Api
*/
-public class PayApi extends MpApi {
- private final TokenHolder tokenHolder;
- private final WeixinMpAccount weixinAccount;
+public abstract class PayApi extends MpApi {
+ protected final TokenHolder tokenHolder;
+ protected final WeixinMpAccount weixinAccount;
public PayApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
@@ -92,7 +52,6 @@ public class PayApi extends MpApi {
* @param statusMsg
* status为失败时携带的信息
* @return 发货处理结果
- * @since V2 & V3
* @throws WeixinException
*/
public JsonResult deliverNotify(String openId, String transid,
@@ -116,63 +75,9 @@ public class PayApi extends MpApi {
Response response = request.post(
String.format(delivernotify_uri, token.getAccessToken()),
JSON.toJSONString(map));
-
return response.getAsJsonResult();
}
- /**
- * V2订单查询
- *
- * @param orderNo
- * 订单号
- * @return 订单信息
- * @see com.foxinmy.weixin4j.mp.payment.v2.Order
- * @throws WeixinException
- */
- public Order orderQueryV2(String orderNo) throws WeixinException {
- String orderquery_uri = getRequestUri("orderquery_uri");
- Token token = tokenHolder.getToken();
- StringBuilder sb = new StringBuilder();
- sb.append("out_trade_no=").append(orderNo);
- sb.append("&partner=").append(weixinAccount.getPartnerId());
- String part = sb.toString();
- sb.append("&key=").append(weixinAccount.getPartnerKey());
- String sign = DigestUtils.md5Hex(sb.toString()).toUpperCase();
- sb.delete(0, sb.length());
- sb.append(part).append("&sign=").append(sign);
-
- String timestamp = DateUtil.timestamp2string();
- JSONObject obj = new JSONObject();
- obj.put("appid", weixinAccount.getId());
- obj.put("appkey", weixinAccount.getPaySignKey());
- obj.put("package", sb.toString());
- obj.put("timestamp", timestamp);
- String signature = PayUtil.paysignSha(obj);
-
- obj.clear();
- obj.put("appid", weixinAccount.getId());
- obj.put("package", sb.toString());
- obj.put("timestamp", timestamp);
- obj.put("app_signature", signature);
- obj.put("sign_method", SignType.SHA1.name().toLowerCase());
-
- Response response = request.post(
- String.format(orderquery_uri, token.getAccessToken()),
- obj.toJSONString());
-
- String order_info = response.getAsJson().getString("order_info");
- Order order = JSON.parseObject(order_info, Order.class,
- Feature.IgnoreNotMatch);
- if (order.getRetCode() != 0) {
- throw new WeixinException(Integer.toString(order.getRetCode()),
- order.getRetMsg());
- }
- order.setMapData(JSON.parseObject(order_info,
- new TypeReference
*
- * @param ca
- * 证书文件
+ * @param caFile
+ * 证书文件(V2版本后缀为*.pfx,V3版本后缀为*.p12)
* @param idQuery
* 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
* transaction_id> out_trade_no
@@ -236,236 +130,45 @@ public class PayApi extends MpApi {
* 退款总金额,单位为元,可以做部分退款
* @param opUserId
* 操作员帐号, 默认为商户号
- * @param opUserPasswd
- * V3版本留空,V2版本需传入值
+ * @param mopara
+ * 更多参数 如V2版本的opUserPasswd
*
* @return 退款申请结果
- * @see com.foxinmy.weixin4j.mp.payment.RefundResult
- * @since V2 & V3
+ * @see com.foxinmy.weixin4j.mp.payment.v2.RefundResult
+ * @see com.foxinmy.weixin4j.mp.payment.v3.RefundResult
* @throws WeixinException
- * @throws IOException
*/
- public RefundResult refund(InputStream ca, IdQuery idQuery,
+ protected abstract Object refund(File caFile, IdQuery idQuery,
String outRefundNo, double totalFee, double refundFee,
- String opUserId, String opUserPasswd) throws WeixinException{
- 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 = baseMapV3(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);
- } else {
- throw new WeixinException(String.format("unknown version:%d",
- version));
- }
- return response.getAsObject(new TypeReference() {
- });
- }
+ String opUserId, Map mopara) throws WeixinException;
/**
- * 退款申请:默认采用properties中配置的ca文件 V2支付则需要传入opUserPasswd参数
+ * 退款查询 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
*
- * @see {@link com.foxinmy.weixin4j.mp.api.PayApi#refund(InputStream, IdQuery, String, double, double, String, String)}
+ * @param idQuery
+ * 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id
+ * 四个参数必填一个,优先级为:
+ * refund_id>out_refund_no>transaction_id>out_trade_no
+ * @return 退款记录
+ * @see com.foxinmy.weixin4j.mp.payment.v2.RefundRecord
+ * @see com.foxinmy.weixin4j.mp.payment.v3.RefundRecord
+ * @throws WeixinException
*/
- public RefundResult refund(IdQuery idQuery, String outRefundNo,
- 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);
- }
+ public abstract Object refundQuery(IdQuery idQuery) throws WeixinException;
/**
- * 冲正订单(需要证书) 当支付返回失败,或收银系统超时需要取消交易,可以调用该接口 接口逻辑:支
- * 付失败的关单,支付成功的撤销支付 7天以内的单可撤销,其他正常支付的单
- * 如需实现相同功能请调用退款接口 调用扣款接口后请勿立即调用撤销,需要等待5秒以上。先调用查单接口,如果没有确切的返回,再调用撤销
+ * 冲正订单(需要证书)
*
- * @param ca
- * 证书文件
+ * @param caFile
+ * 证书文件 (V2版本后缀为*.pfx,V3版本后缀为*.p12)
* @param idQuery
* 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
* transaction_id> out_trade_no
* @return 撤销结果
* @throws WeixinException
*/
- public ApiResult reverse(InputStream ca, IdQuery idQuery)
- throws WeixinException {
- SSLHttpRequest request = new SSLHttpRequest(weixinAccount.getMchId(),
- ca);
- String reverse_uri = getRequestUri("reverse_uri");
- Map map = baseMapV3(idQuery);
- String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
- map.put("sign", sign);
- String param = map2xml(map);
- Response response = request.post(reverse_uri, param);
- return response.getAsObject(new TypeReference() {
- });
- }
-
- /**
- * 冲正撤销:默认采用properties中配置的ca文件
- *
- * @param idQuery
- * transaction_id、out_trade_no 二选一
- * @return 撤销结果
- * @see {@link com.foxinmy.weixin4j.mp.api.PayApi#reverse(InputStream, IdQuery)}
- * @throws WeixinException
- * @throws IOException
- */
- public ApiResult reverse(IdQuery idQuery) throws WeixinException,
- IOException {
- File ca = new File(ConfigUtil.getValue("ca_file"));
- return reverse(new FileInputStream(ca), idQuery);
- }
-
- /**
- * native支付URL转短链接
- *
- * @param url
- * 具有native标识的支付URL
- * @return 转换后的短链接
- * @throws WeixinException
- */
- public String getShorturl(String url) throws WeixinException {
- Map map = baseMapV3(null);
- map.put("long_url", url);
- String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
- map.put("sign", sign);
- try {
- map.put("long_url", URLEncoder.encode(url, Consts.UTF_8.name()));
- } catch (UnsupportedEncodingException ignore) {
- ;
- }
- String param = map2xml(map);
- String shorturl_uri = getRequestUri("p_shorturl_uri");
- Response response = request.post(shorturl_uri, param);
- map = xml2map(response.getAsString());
- return map.get("short_url");
- }
-
- /**
- * 关闭订单 当订单支付失败,调用关单接口后用新订单号重新发起支付,如果关单失败,返回已完
- * 成支付请按正常支付处理。如果出现银行掉单,调用关单成功后,微信后台会主动发起退款。
- *
- * @param outTradeNo
- * 商户系统内部的订单号
- * @return 处理结果
- * @since V3
- * @throws WeixinException
- */
- public XmlResult closeOrder(String outTradeNo) throws WeixinException {
- Map map = baseMapV3(new IdQuery(outTradeNo,
- IdType.TRADENO));
- String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
- map.put("sign", sign);
- String param = map2xml(map);
- String closeorder_uri = getRequestUri("closeorder_uri");
- Response response = request.post(closeorder_uri, param);
- return response.getAsXmlResult();
- }
+ public abstract ApiResult reverse(File caFile, IdQuery idQuery)
+ throws WeixinException;
/**
* 下载对账单
@@ -480,154 +183,30 @@ public class PayApi extends MpApi {
* 下载对账单的类型 ALL,返回当日所有订单信息, 默认值 SUCCESS,返回当日成功支付的订单
* REFUND,返回当日退款订单
* @return excel表格
- * @since V2 & V3
- * @throws WeixinException
- * @throws IOException
- */
- public File downloadbill(Date billDate, BillType billType)
- throws WeixinException {
- if (billDate == null) {
- Calendar now = Calendar.getInstance();
- now.add(Calendar.DAY_OF_MONTH, -1);
- billDate = now.getTime();
- }
- if (billType == null) {
- billType = BillType.ALL;
- }
- String _billDate = DateUtil.fortmat2yyyyMMdd(billDate);
- String bill_path = ConfigUtil.getValue("bill_path");
- String fileName = String.format("%s_%s_%s.xls", _billDate, billType
- .name().toLowerCase(), weixinAccount.getId());
- File file = new File(String.format("%s/%s", bill_path, fileName));
- if (file.exists()) {
- return file;
- }
- 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", DateUtil.timestamp2string());
- 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 = baseMapV3(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);
- } else {
- throw new WeixinException(String.format("unknown version:%d",
- version));
- }
- BufferedReader reader = null;
- OutputStream os = null;
- try {
- reader = new BufferedReader(new InputStreamReader(
- response.getStream(), charset));
- String line = null;
- List bills = new LinkedList();
- while ((line = reader.readLine()) != null) {
- bills.add(line.replaceAll("`", "").split(","));
- }
-
- List headers = Arrays.asList(bills.remove(0));
- List totalDatas = Arrays
- .asList(bills.remove(bills.size() - 1));
- List totalHeaders = Arrays
- .asList(bills.remove(bills.size() - 1));
- HSSFWorkbook wb = new HSSFWorkbook();
- wb.createSheet(_billDate + "对账单");
- ExcelUtil.list2excel(wb, headers, bills);
- ExcelUtil.list2excel(wb, totalHeaders, totalDatas);
- os = new FileOutputStream(file);
- wb.write(os);
- } catch (IOException e) {
- throw new WeixinException(e.getMessage());
- } finally {
- try {
- if (reader != null) {
- reader.close();
- }
- if (os != null) {
- os.close();
- }
- } catch (IOException ignore) {
- ;
- }
- }
- return file;
- }
-
- /**
- * 退款查询 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
- *
- * @param idQuery
- * 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id
- * 四个参数必填一个,优先级为:
- * refund_id>out_refund_no>transaction_id>out_trade_no
- * @return 退款记录
- * @see com.foxinmy.weixin4j.mp.payment.Refund
- * @since V2 & V3
* @throws WeixinException
*/
- public Refund refundQuery(IdQuery idQuery) throws WeixinException {
- 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 = baseMapV3(idQuery);
- String sign = PayUtil
- .paysignMd5(map, weixinAccount.getPaySignKey());
- map.put("sign", sign);
- String param = map2xml(map);
- response = request.post(refundquery_uri, param);
- } else {
- throw new WeixinException(String.format("unknown version:%d",
- version));
- }
- return RefundConverter.fromXML(response.getAsString());
- }
+ public abstract File downloadbill(Date billDate, BillType billType)
+ throws WeixinException;
/**
- * V3接口请求基本数据
+ * 关闭订单 当订单支付失败,调用关单接口后用新订单号重新发起支付,如果关单失败,返回已完
+ * 成支付请按正常支付处理。如果出现银行掉单,调用关单成功后,微信后台会主动发起退款。
*
- * @return
+ * @param outTradeNo
+ * 商户系统内部的订单号
+ * @return 处理结果
+ * @throws WeixinException
*/
- private Map baseMapV3(IdQuery idQuery) {
- Map map = new HashMap();
- map.put("appid", weixinAccount.getId());
- map.put("mch_id", weixinAccount.getMchId());
- map.put("nonce_str", RandomUtil.generateString(16));
- if (StringUtils.isNotBlank(weixinAccount.getDeviceInfo())) {
- map.put("device_info", weixinAccount.getDeviceInfo());
- }
- if (idQuery != null) {
- map.put(idQuery.getType().getName(), idQuery.getId());
- }
- return map;
- }
+ public abstract ApiResult closeOrder(String outTradeNo)
+ throws WeixinException;
+
+ /**
+ * native支付URL转短链接
+ *
+ * @param url
+ * 具有native标识的支付URL
+ * @return 转换后的短链接
+ * @throws WeixinException
+ */
+ public abstract String getShorturl(String url) throws WeixinException;
}
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 ae297722..068e62a3 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
@@ -121,6 +121,8 @@ refund_v3_uri={mch_base_url}/secapi/pay/refund
reverse_uri={mch_base_url}/secapi/pay/reverse
# \u88ab\u626b\u652f\u4ed8
micropay_uri={mch_base_url}/pay/micropay
+# \u63a5\u53e3\u4e0a\u62a5
+pay_report_uri={mch_base_url}/payitil/report
# \u7edf\u4e00\u8ba2\u5355\u751f\u6210
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/CustomRecord.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/CustomRecord.java
index e8f0d226..5c08b040 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/CustomRecord.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/CustomRecord.java
@@ -51,7 +51,7 @@ public class CustomRecord implements Serializable {
}
public void setTime(long time) {
- this.time = new Date(time * 1000);
+ this.time = new Date(time * 1000l);
}
public String getText() {
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/User.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/User.java
index 32909be9..23c77d51 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/User.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/User.java
@@ -1,9 +1,11 @@
package com.foxinmy.weixin4j.mp.model;
import java.io.Serializable;
+import java.util.Date;
import org.apache.commons.lang3.StringUtils;
+import com.alibaba.fastjson.annotation.JSONField;
import com.foxinmy.weixin4j.model.Gender;
import com.foxinmy.weixin4j.mp.type.FaceSize;
import com.foxinmy.weixin4j.mp.type.Lang;
@@ -24,22 +26,20 @@ public class User implements Serializable {
private String openid; // 用户的唯一标识
private String nickname; // 用户昵称
- private int sex; // 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
+ @JSONField(name = "sex")
+ private Gender gender; // 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
private String province; // 用户个人资料填写的省份
private String city; // 普通用户个人资料填写的城市
private String country; // 国家,如中国为CN
private String headimgurl; // 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空
private String privilege; // 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)
- private int subscribe; // 是否关注
- private long subscribe_time; // 关注时间
+ @JSONField(name = "subscribe")
+ private boolean isSubscribe; // 用户是否订阅该公众号标识,值为0时,代表此用户没有关注该公众号,拉取不到其余信息。
+ @JSONField(name = "subscribe_time")
+ private Date subscribeTime; // 关注时间
private Lang language; // 使用语言
private String unionid; // 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段
- public User() {
- this.sex = 0;
- this.language = Lang.zh_CN;
- }
-
public String getOpenid() {
return openid;
}
@@ -56,22 +56,18 @@ public class User implements Serializable {
this.nickname = nickname;
}
- public int getSex() {
- return sex;
- }
-
public Gender getGender() {
- if (sex == 1) {
- return Gender.male;
- } else if (sex == 2) {
- return Gender.female;
- } else {
- return Gender.unknown;
- }
+ return gender;
}
- public void setSex(int sex) {
- this.sex = sex;
+ public void setGender(int sex) {
+ if (sex == 1) {
+ this.gender = Gender.male;
+ } else if (sex == 2) {
+ this.gender = Gender.female;
+ } else {
+ this.gender = Gender.unknown;
+ }
}
public String getProvince() {
@@ -123,14 +119,6 @@ public class User implements Serializable {
this.privilege = privilege;
}
- public int getSubscribe() {
- return subscribe;
- }
-
- public void setSubscribe(int subscribe) {
- this.subscribe = subscribe;
- }
-
public Lang getLanguage() {
return language;
}
@@ -139,12 +127,20 @@ public class User implements Serializable {
this.language = language;
}
- public long getSubscribe_time() {
- return subscribe_time;
+ public boolean isSubscribe() {
+ return isSubscribe;
}
- public void setSubscribe_time(long subscribe_time) {
- this.subscribe_time = subscribe_time;
+ public void setSubscribe(boolean isSubscribe) {
+ this.isSubscribe = isSubscribe;
+ }
+
+ public Date getSubscribeTime() {
+ return (Date) subscribeTime.clone();
+ }
+
+ public void setSubscribeTime(long subscribeTime) {
+ this.subscribeTime = new Date(subscribeTime * 1000l);
}
public String getUnionid() {
@@ -163,46 +159,21 @@ public class User implements Serializable {
return false;
}
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((city == null) ? 0 : city.hashCode());
- result = prime * result + ((country == null) ? 0 : country.hashCode());
- result = prime * result
- + ((headimgurl == null) ? 0 : headimgurl.hashCode());
- result = prime * result
- + ((language == null) ? 0 : language.hashCode());
- result = prime * result
- + ((nickname == null) ? 0 : nickname.hashCode());
- result = prime * result + ((openid == null) ? 0 : openid.hashCode());
- result = prime * result
- + ((privilege == null) ? 0 : privilege.hashCode());
- result = prime * result
- + ((province == null) ? 0 : province.hashCode());
- result = prime * result + sex;
- result = prime * result + subscribe;
- result = prime * result
- + (int) (subscribe_time ^ (subscribe_time >>> 32));
- result = prime * result + ((unionid == null) ? 0 : unionid.hashCode());
- return result;
- }
-
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[User openid=").append(openid);
sb.append(", nickname=").append(nickname);
- sb.append(", sex=").append(sex);
+ sb.append(", gender=").append(gender);
sb.append(", province=").append(province);
sb.append(", city=").append(city);
sb.append(", country=").append(country);
sb.append(", headimgurl=").append(headimgurl);
sb.append(", privilege=").append(privilege);
sb.append(", language=").append(language);
- sb.append(", subscribe_time=").append(subscribe_time);
+ sb.append(", subscribeTime=").append(subscribeTime);
sb.append(", unionid=").append(unionid);
- sb.append(", subscribe=").append(subscribe).append("]");
+ sb.append(", isSubscribe=").append(isSubscribe).append("]");
return sb.toString();
}
}
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 c8adece2..cfe12868 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
@@ -45,6 +45,7 @@ public class PayUtil {
* 订单信息
* @param WeixinMpAccount
* 商户信息
+ * @since V2 & V3
* @return 支付json串
* @throws PayException
*/
@@ -56,8 +57,9 @@ public class PayUtil {
} else if (payPackage instanceof PayPackageV3) {
return createPayJsRequestJsonV3((PayPackageV3) payPackage,
weixinAccount);
+ } else {
+ throw new PayException("unknown pay");
}
- throw new PayException("unknown pay");
}
/**
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/Refund.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/Refund.java
deleted file mode 100644
index 9f784a63..00000000
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/Refund.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.foxinmy.weixin4j.mp.payment;
-
-import java.util.List;
-
-import com.thoughtworks.xstream.annotations.XStreamAlias;
-
-/**
- * 退款记录
- *
- * @className Refund
- * @author jy
- * @date 2014年11月1日
- * @since JDK 1.7
- * @see com.foxinmy.weixin4j.mp.payment.RefundDetail
- */
-@XStreamAlias("xml")
-public class Refund extends ApiResult {
-
- private static final long serialVersionUID = -2971132874939642721L;
-
- @XStreamAlias("transaction_id")
- private String transactionId;// 微信订单号
- @XStreamAlias("out_trade_no")
- private String orderNo;// 商户订单号
- @XStreamAlias("sub_mch_id")
- private String subMchId; //
- @XStreamAlias("refund_count")
- private int count;// 退款笔数
- private String partner; // 商户号V2
- private List details;
-
- public String getTransactionId() {
- return transactionId;
- }
-
- public String getOrderNo() {
- return orderNo;
- }
-
- public String getSubMchId() {
- return subMchId;
- }
-
- public int getCount() {
- return count;
- }
-
- public String getPartner() {
- return partner;
- }
-
- public List getDetails() {
- return details;
- }
-
- public void setDetails(List details) {
- this.details = details;
- }
-
- @Override
- public String toString() {
- return "Refund [transactionId=" + transactionId + ", orderNo="
- + orderNo + ", subMchId=" + subMchId + ", count=" + count
- + ", partner=" + partner + ", details=" + details
- + ", " + super.toString() + "]";
- }
-}
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundConverter.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundConverter.java
index ea1c5faf..85e41127 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundConverter.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundConverter.java
@@ -26,34 +26,39 @@ import com.thoughtworks.xstream.mapper.Mapper;
* @author jy
* @date 2014年11月2日
* @since JDK 1.7
- * @see com.foxinmy.weixin4j.mp.payment.Refund
- * @see com.foxinmy.weixin4j.mp.payment.RefundDetail
+ * @see com.foxinmy.weixin4j.mp.payment.v2.RefundRecord
+ * @see com.foxinmy.weixin4j.mp.payment.v2.RefundDetail
+ * @see com.foxinmy.weixin4j.mp.payment.v3.RefundRecord
+ * @see com.foxinmy.weixin4j.mp.payment.v3.RefundDetail
*/
public class RefundConverter {
private final static XmlStream xStream = XmlStream.get();
private final static Mapper mapper;
private final static ReflectionProvider reflectionProvider;
private final static Pattern pattern = Pattern.compile("(_\\d)$");
+ private static Class> clazz;
+ private final static Class REFUNDRECORD2 = com.foxinmy.weixin4j.mp.payment.v2.RefundRecord.class;
+ private final static Class REFUNDRECORD3 = com.foxinmy.weixin4j.mp.payment.v3.RefundRecord.class;
+
static {
- xStream.processAnnotations(Refund.class);
- xStream.processAnnotations(RefundDetail.class);
+ xStream.processAnnotations(new Class[] { REFUNDRECORD2, REFUNDRECORD3,
+ com.foxinmy.weixin4j.mp.payment.v2.RefundDetail.class,
+ com.foxinmy.weixin4j.mp.payment.v3.RefundDetail.class });
+ xStream.aliasField("refund_state", com.foxinmy.weixin4j.mp.payment.v2.RefundDetail.class, "refundStatus");
xStream.registerConverter(new $());
mapper = xStream.getMapper();
reflectionProvider = xStream.getReflectionProvider();
}
- public static String toXML(Refund refund) {
- return xStream.toXML(refund);
- }
-
- public static Refund fromXML(String xml) {
- return xStream.fromXML(xml, Refund.class);
+ public static T fromXML(String xml, Class clazz) {
+ RefundConverter.clazz = clazz;
+ return xStream.fromXML(xml, clazz);
}
private static class $ implements Converter {
@Override
public boolean canConvert(@SuppressWarnings("rawtypes") Class clazz) {
- return clazz.equals(Refund.class);
+ return clazz.equals(REFUNDRECORD2) || clazz.equals(REFUNDRECORD3);
}
@Override
@@ -63,18 +68,24 @@ public class RefundConverter {
writer, context);
}
- @SuppressWarnings("unchecked")
@Override
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
- Refund refund = new Refund();
+ Object refund = null;
+ try {
+ refund = clazz.newInstance();
+ } catch (InstantiationException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
Matcher matcher = null;
Map> outMap = new HashMap>();
while (reader.hasMoreChildren()) {
reader.moveDown();
String nodeName = reader.getNodeName();
- String fieldName = mapper.realMember(Refund.class, nodeName);
- Field field = reflectionProvider.getFieldOrNull(Refund.class,
+ String fieldName = mapper.realMember(clazz, nodeName);
+ Field field = reflectionProvider.getFieldOrNull(clazz,
fieldName);
if (field != null) {
Object value = context.convertAnother(refund,
@@ -94,7 +105,8 @@ public class RefundConverter {
}
StringBuilder detailXml = new StringBuilder();
detailXml.append("");
- String detailCanonicalName = RefundDetail.class.getCanonicalName();
+ String detailCanonicalName = clazz.getCanonicalName().replaceFirst(
+ "RefundRecord", "RefundDetail");
for (Iterator>> outIt = outMap
.entrySet().iterator(); outIt.hasNext();) {
detailXml.append("<").append(detailCanonicalName).append(">");
@@ -108,7 +120,9 @@ public class RefundConverter {
detailXml.append("").append(detailCanonicalName).append(">");
}
detailXml.append("
");
- refund.setDetails(xStream.fromXML(detailXml.toString(), List.class));
+ reflectionProvider.writeField(refund, "details",
+ xStream.fromXML(detailXml.toString(), List.class),
+ List.class.getDeclaringClass());
return refund;
}
}
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundDetail.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundDetail.java
deleted file mode 100644
index 0c09a2e2..00000000
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundDetail.java
+++ /dev/null
@@ -1,127 +0,0 @@
-package com.foxinmy.weixin4j.mp.payment;
-
-import java.io.Serializable;
-
-import org.apache.commons.lang3.StringUtils;
-
-import com.foxinmy.weixin4j.mp.type.RefundChannel;
-import com.foxinmy.weixin4j.mp.type.RefundStatus;
-import com.thoughtworks.xstream.annotations.XStreamAlias;
-
-/**
- * 退款详细
- *
- * @className RefundDetail
- * @author jy
- * @date 2014年11月2日
- * @since JDK 1.7
- * @see
- */
-public class RefundDetail implements Serializable {
-
- private static final long serialVersionUID = 2828640496307351988L;
-
- @XStreamAlias("out_refund_no")
- private String outRefundNo; // 商户退款单号
- @XStreamAlias("refund_id")
- private String refundId; // 微信退款单号
- @XStreamAlias("refund_channel")
- private String refundChannel; // 退款渠道 ORIGINAL—原路退款 BALANCE—退回到余额
- @XStreamAlias("refund_fee")
- private int refundFee; // 退款总金额,单位为分,可以做部分退款
- @XStreamAlias("coupon_refund_fee")
- private int couponRefundFee; // 现金券退款金额<=退款金额,退款金额-现金券退款金额为现金
- @XStreamAlias("refund_status")
- 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;
- }
-
- public String getRefundId() {
- return refundId;
- }
-
- public String getRefundChannel() {
- 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;
- }
-
- /**
- * 调用接口获取单位为分,get方法转换为元方便使用
- *
- * @return 元单位
- */
- public double getRefundFee() {
- return refundFee / 100d;
- }
-
- /**
- * 调用接口获取单位为分,get方法转换为元方便使用
- *
- * @return 元单位
- */
- public double getCouponRefundFee() {
- return couponRefundFee / 100d;
- }
-
- public RefundStatus getRefundStatus() {
- // 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
- public String toString() {
- return "RefundDetail [outRefundNo=" + outRefundNo + ", refundId="
- + refundId + ", refundChannel=" + refundChannel
- + ", refundFee=" + refundFee + ", couponRefundFee="
- + 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/RefundResult.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundResult.java
deleted file mode 100644
index 28a0b88b..00000000
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundResult.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package com.foxinmy.weixin4j.mp.payment;
-
-import org.apache.commons.lang3.StringUtils;
-
-import com.foxinmy.weixin4j.mp.type.RefundChannel;
-import com.thoughtworks.xstream.annotations.XStreamAlias;
-
-/**
- * 退款申请结果
- *
- * @className RefundResult
- * @author jy
- * @date 2014年11月6日
- * @since JDK 1.7
- * @see
- */
-@XStreamAlias("xml")
-public class RefundResult extends ApiResult {
-
- private static final long serialVersionUID = -3687863914168618620L;
-
- // 微信订单号
- @XStreamAlias("transaction_id")
- private String transactionId;
- // 商户系统内部的订单号
- @XStreamAlias("out_trade_no")
- private String outTradeNo;
- // 商户退款单号
- @XStreamAlias("out_refund_no")
- private String outRefundNo;
- // 微信退款单号
- @XStreamAlias("refund_id")
- private String refundId;
- // 退款渠道 ORIGINAL—原路退款,默认 BALANCE—退回到余额
- @XStreamAlias("refund_channel")
- private String refundChannel;
- // 退款总金额,单位为元,可以做部分退款
- @XStreamAlias("refund_fee")
- private int refundFee;
- // 现金券退款金额<=退款金 额,退款金额-现金券退款金 额为现金
- @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;
- }
-
- public String getOutTradeNo() {
- return outTradeNo;
- }
-
- public String getOutRefundNo() {
- return outRefundNo;
- }
-
- public String getRefundId() {
- return refundId;
- }
-
- public String getRefundChannel() {
- 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;
- }
-
- /**
- * 调用接口获取单位为分,get方法转换为元方便使用
- *
- * @return 元单位
- */
- public double getRefundFee() {
- return refundFee / 100d;
- }
-
- /**
- * 调用接口获取单位为分,get方法转换为元方便使用
- *
- * @return 元单位
- */
- public double getCouponRefundFee() {
- 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 + ", recvUserId=" + recvUserId
- + ", reccvUserName=" + reccvUserName + ", signKeyIndex="
- + signKeyIndex + ", signType=" + signType + ", "
- + super.toString() + "]";
- }
-}
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/ApiResult.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/ApiResult.java
new file mode 100644
index 00000000..200cfb9a
--- /dev/null
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/ApiResult.java
@@ -0,0 +1,107 @@
+package com.foxinmy.weixin4j.mp.payment.v2;
+
+import java.io.Serializable;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.foxinmy.weixin4j.mp.type.SignType;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+
+/**
+ * 调用V2.x接口返回的公用字段
+ *
+ * @className ApiResult
+ * @author jy
+ * @date 2014年12月30日
+ * @since JDK 1.7
+ * @see
+ */
+public class ApiResult implements Serializable {
+
+ private static final long serialVersionUID = -2876899595643466203L;
+ // 是查询结果状态码,0 表明成功,其他表明错误;
+ @JSONField(name = "ret_code")
+ @XStreamAlias("retcode")
+ private int retCode;
+ // 是查询结果出错信息;
+ @JSONField(name = "ret_msg")
+ @XStreamAlias("retmsg")
+ private String retMsg;
+ // 是返回信息中的编码方式;
+ @JSONField(name = "input_charset")
+ @XStreamAlias("input_charset")
+ private String inputCharset;
+ // 是财付通商户号,即前文的 partnerid;
+ private String partner;
+ @XStreamAlias("sign_key_index")
+ @JSONField(name = "sign_key_index")
+ private Integer signKeyIndex; // 多密钥支持的密钥序号,默认 1
+ private String sign;// 签名
+ @JSONField(name = "sign_type")
+ @XStreamAlias("sign_type")
+ private SignType signType; // 签名类型,取值:MD5、RSA
+
+ public int getRetCode() {
+ return retCode;
+ }
+
+ public void setRetCode(int retCode) {
+ this.retCode = retCode;
+ }
+
+ public String getRetMsg() {
+ return StringUtils.isNotBlank(retMsg) ? retMsg : null;
+ }
+
+ public void setRetMsg(String retMsg) {
+ this.retMsg = retMsg;
+ }
+
+ public String getInputCharset() {
+ return inputCharset;
+ }
+
+ public void setInputCharset(String inputCharset) {
+ this.inputCharset = inputCharset;
+ }
+
+ public String getPartner() {
+ return partner;
+ }
+
+ public void setPartner(String partner) {
+ this.partner = partner;
+ }
+
+ public String getSign() {
+ return sign;
+ }
+
+ public void setSign(String sign) {
+ this.sign = sign;
+ }
+
+ public SignType getSignType() {
+ return signType;
+ }
+
+ public void setSignType(SignType signType) {
+ this.signType = signType;
+ }
+
+ public Integer getSignKeyIndex() {
+ return signKeyIndex;
+ }
+
+ public void setSignKeyIndex(Integer signKeyIndex) {
+ this.signKeyIndex = signKeyIndex;
+ }
+
+ @Override
+ public String toString() {
+ return "retCode=" + retCode + ", retMsg=" + retMsg + ", inputCharset="
+ + inputCharset + ", partner=" + partner + ", sign=" + sign
+ + ", signType=" + signType + ", signKeyIndex=" + signKeyIndex;
+ }
+}
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/JsPayRequestV2.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/JsPayRequestV2.java
index 874d2060..19aa5049 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/JsPayRequestV2.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/JsPayRequestV2.java
@@ -10,7 +10,7 @@ import com.foxinmy.weixin4j.mp.payment.PayRequest;
import com.foxinmy.weixin4j.util.MapUtil;
/**
- * 微信JS支付:get_brand_wcpay_request
+ * V2微信JS支付:get_brand_wcpay_request
* 所列参数均为非空字符串
*
* get_brand_wcpay_request:ok 支付成功
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/NativePayNotifyV2.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/NativePayNotifyV2.java
index c85bd833..08178056 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/NativePayNotifyV2.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/NativePayNotifyV2.java
@@ -4,7 +4,7 @@ import com.foxinmy.weixin4j.mp.payment.JsPayNotify;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
- * Native支付回调时POST的信息
+ * V2 Native支付回调时POST的信息
*
* @className PayNativeNotifyV2
* @author jy
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/NativePayResponseV2.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/NativePayResponseV2.java
index 4c1dc57b..93381ef3 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/NativePayResponseV2.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/NativePayResponseV2.java
@@ -4,7 +4,7 @@ import com.foxinmy.weixin4j.model.WeixinMpAccount;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
- * Native支付时的回调响应
+ * V2 Native支付时的回调响应
*
* @className NativePayResponseV2
* @author jy
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/Order.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/Order.java
index c4f5de27..ffef89a8 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/Order.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/Order.java
@@ -1,14 +1,14 @@
package com.foxinmy.weixin4j.mp.payment.v2;
import java.util.Date;
-import java.util.Map;
import com.alibaba.fastjson.annotation.JSONField;
-import com.foxinmy.weixin4j.http.JsonResult;
+import com.foxinmy.weixin4j.mp.type.CurrencyType;
+import com.foxinmy.weixin4j.mp.type.TradeState;
import com.foxinmy.weixin4j.util.DateUtil;
/**
- * 订单信息
+ * V2订单信息
*
* @className Order
* @author jy
@@ -16,27 +16,16 @@ import com.foxinmy.weixin4j.util.DateUtil;
* @since JDK 1.7
* @see
*/
-public class Order extends JsonResult {
+public class Order extends ApiResult {
private static final long serialVersionUID = 4543552984506609920L;
- // 是查询结果状态码,0 表明成功,其他表明错误;
- @JSONField(name = "ret_code")
- private int retCode;
- // 是查询结果出错信息;
- @JSONField(name = "ret_msg")
- private String retMsg;
- // 是返回信息中的编码方式;
- @JSONField(name = "input_charset")
- private String inputCharset;
// 是订单状态,0 为成功,其他为失败;
@JSONField(name = "trade_state")
private int tradeState;
// 是交易模式,1 为即时到帐,其他保留;
@JSONField(name = "trade_mode")
private int tradeMode;
- // 是财付通商户号,即前文的 partnerid;
- private String partner;
// 是银行类型;
@JSONField(name = "bank_type")
private String bankType;
@@ -48,7 +37,7 @@ public class Order extends JsonResult {
private int totalFee;
// 是币种,1 为人民币;
@JSONField(name = "fee_type")
- private String feeType;
+ private int feeType;
// 是财付通订单号;
@JSONField(name = "transaction_id")
private String transactionId;
@@ -76,39 +65,20 @@ public class Order extends JsonResult {
private int discount;
// 换算成人民币之后的总金额,单位为分,一般看 total_fee 即可。
@JSONField(name = "rmb_total_fee")
- private int rmbTotalFee;
-
- @JSONField(serialize = false)
- private Map mapData;
-
- public int getRetCode() {
- return retCode;
- }
-
- public void setRetCode(int retCode) {
- this.retCode = retCode;
- }
-
- public String getRetMsg() {
- return retMsg;
- }
-
- public void setRetMsg(String retMsg) {
- this.retMsg = retMsg;
- }
-
- public String getInputCharset() {
- return inputCharset;
- }
-
- public void setInputCharset(String inputCharset) {
- this.inputCharset = inputCharset;
- }
+ private Integer rmbTotalFee;
public int getTradeState() {
return tradeState;
}
+ @JSONField(serialize = false, deserialize = false)
+ public TradeState getFormatTradeState() {
+ if (tradeState == 0) {
+ return TradeState.SUCCESS;
+ }
+ return null;
+ }
+
public void setTradeState(int tradeState) {
this.tradeState = tradeState;
}
@@ -121,14 +91,6 @@ public class Order extends JsonResult {
this.tradeMode = tradeMode;
}
- public String getPartner() {
- return partner;
- }
-
- public void setPartner(String partner) {
- this.partner = partner;
- }
-
public String getBankType() {
return bankType;
}
@@ -145,12 +107,17 @@ public class Order extends JsonResult {
this.bankBillno = bankBillno;
}
+ public int getTotalFee() {
+ return totalFee;
+ }
+
/**
* 调用接口获取单位为分,get方法转换为元方便使用
*
* @return 元单位
*/
- public double getTotalFee() {
+ @JSONField(serialize = false, deserialize = false)
+ public double getFormatTotalFee() {
return totalFee / 100d;
}
@@ -158,11 +125,19 @@ public class Order extends JsonResult {
this.totalFee = totalFee;
}
- public String getFeeType() {
+ public int getFeeType() {
return feeType;
}
- public void setFeeType(String feeType) {
+ @JSONField(serialize = false, deserialize = false)
+ public CurrencyType getFormatFeeType() {
+ if (feeType == 1) {
+ return CurrencyType.CNY;
+ }
+ return null;
+ }
+
+ public void setFeeType(int feeType) {
this.feeType = feeType;
}
@@ -206,7 +181,12 @@ public class Order extends JsonResult {
this.attach = attach;
}
- public Date getTimeEnd() {
+ public String getTimeEnd() {
+ return timeEnd;
+ }
+
+ @JSONField(serialize = false, deserialize = false)
+ public Date getFormatTimeEnd() {
return DateUtil.parse2yyyyMMddHHmmss(timeEnd);
}
@@ -214,12 +194,17 @@ public class Order extends JsonResult {
this.timeEnd = timeEnd;
}
+ public int getTransportFee() {
+ return transportFee;
+ }
+
/**
* 调用接口获取单位为分,get方法转换为元方便使用
*
* @return 元单位
*/
- public double getTransportFee() {
+ @JSONField(serialize = false, deserialize = false)
+ public double getFormatTransportFee() {
return transportFee / 100d;
}
@@ -227,12 +212,17 @@ public class Order extends JsonResult {
this.transportFee = transportFee;
}
+ public int getProductFee() {
+ return productFee;
+ }
+
/**
* 调用接口获取单位为分,get方法转换为元方便使用
*
* @return 元单位
*/
- public double getProductFee() {
+ @JSONField(serialize = false, deserialize = false)
+ public double getFormatProductFee() {
return productFee / 100d;
}
@@ -240,12 +230,17 @@ public class Order extends JsonResult {
this.productFee = productFee;
}
+ public int getDiscount() {
+ return discount;
+ }
+
/**
* 调用接口获取单位为分,get方法转换为元方便使用
*
* @return 元单位
*/
- public double getDiscount() {
+ @JSONField(serialize = false, deserialize = false)
+ public double getFormatDiscount() {
return discount / 100d;
}
@@ -253,39 +248,42 @@ public class Order extends JsonResult {
this.discount = discount;
}
+ public Integer getRmbTotalFee() {
+ return rmbTotalFee;
+ }
+
/**
* 调用接口获取单位为分,get方法转换为元方便使用
*
* @return 元单位
*/
- public double getRmbTotalFee() {
- return rmbTotalFee / 100d;
+ @JSONField(serialize = false, deserialize = false)
+ public double getFormatRmbTotalFee() {
+ return rmbTotalFee != null ? rmbTotalFee / 100d : 0d;
}
public void setRmbTotalFee(int rmbTotalFee) {
this.rmbTotalFee = rmbTotalFee;
}
- public Map getMapData() {
- return mapData;
- }
-
- public void setMapData(Map mapData) {
- this.mapData = mapData;
- }
-
@Override
public String toString() {
- return "Order [retCode=" + retCode + ", retMsg=" + retMsg
- + ", inputCharset=" + inputCharset + ", tradeState="
- + tradeState + ", tradeMode=" + tradeMode + ", partner="
- + partner + ", bankType=" + bankType + ", bankBillno="
- + bankBillno + ", totalFee=" + totalFee + ", feeType="
- + feeType + ", transactionId=" + transactionId
- + ", outTradeNo=" + outTradeNo + ", isSplit=" + isSplit
- + ", isRefund=" + isRefund + ", attach=" + attach
- + ", timeEnd=" + timeEnd + ", transportFee=" + transportFee
- + ", productFee=" + productFee + ", discount=" + discount
- + ", rmbTotalFee=" + rmbTotalFee + ", mapData=" + mapData + "]";
+ return "Order [tradeState=" + tradeState + ", tradeMode=" + tradeMode
+ + ", bankType=" + bankType + ", bankBillno=" + bankBillno
+ + ", totalFee=" + totalFee + ", feeType=" + feeType
+ + ", transactionId=" + transactionId + ", outTradeNo="
+ + outTradeNo + ", isSplit=" + isSplit + ", isRefund="
+ + isRefund + ", attach=" + attach + ", timeEnd=" + timeEnd
+ + ", transportFee=" + transportFee + ", productFee="
+ + productFee + ", discount=" + discount + ", rmbTotalFee="
+ + rmbTotalFee + ", getFormatTradeState()="
+ + getFormatTradeState() + ", getFormatTotalFee()="
+ + getFormatTotalFee() + ", getFormatFeeType()="
+ + getFormatFeeType() + ", getFormatTimeEnd()="
+ + getFormatTimeEnd() + ", getFormatTransportFee()="
+ + getFormatTransportFee() + ", getFormatProductFee()="
+ + getFormatProductFee() + ", getFormatDiscount()="
+ + getFormatDiscount() + ", getFormatRmbTotalFee()="
+ + getFormatRmbTotalFee() + ", " + super.toString() + "]";
}
}
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/PayFeedback.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/PayFeedback.java
index c3710d07..77a549e3 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/PayFeedback.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/PayFeedback.java
@@ -4,7 +4,7 @@ import com.foxinmy.weixin4j.mp.payment.PayBaseInfo;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
- * 维权POST的数据
+ * V2维权POST的数据
*
* @className PayFeedback
* @author jy
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/PayWarn.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/PayWarn.java
index cf81130a..bc64636d 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/PayWarn.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/PayWarn.java
@@ -3,6 +3,14 @@ package com.foxinmy.weixin4j.mp.payment.v2;
import com.foxinmy.weixin4j.mp.payment.PayBaseInfo;
import com.thoughtworks.xstream.annotations.XStreamAlias;
+/**
+ * V2告警通知
+ * @className PayWarn
+ * @author jy
+ * @date 2014年12月31日
+ * @since JDK 1.7
+ * @see
+ */
@XStreamAlias("xml")
public class PayWarn extends PayBaseInfo {
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/RefundDetail.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/RefundDetail.java
new file mode 100644
index 00000000..94c4d002
--- /dev/null
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/RefundDetail.java
@@ -0,0 +1,123 @@
+package com.foxinmy.weixin4j.mp.payment.v2;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.foxinmy.weixin4j.mp.type.RefundChannel;
+import com.foxinmy.weixin4j.mp.type.RefundStatus;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+
+/**
+ * V2退款详细
+ *
+ * @className RefundDetail
+ * @author jy
+ * @date 2014年11月6日
+ * @since JDK 1.7
+ * @see
+ */
+public class RefundDetail extends ApiResult {
+
+ private static final long serialVersionUID = -3687863914168618620L;
+
+ @XStreamAlias("out_refund_no")
+ @JSONField(name = "out_refund_no")
+ private String outRefundNo; // 商户退款单号
+ @XStreamAlias("refund_id")
+ @JSONField(name = "refund_id")
+ private String refundId; // 微信退款单号
+ @XStreamAlias("refund_channel")
+ @JSONField(name = "refund_channel")
+ private int refundChannel; // 退款渠道 0:退到财付通、1:退到银行;
+ @XStreamAlias("refund_fee")
+ @JSONField(name = "refund_fee")
+ private int refundFee; // 退款总金额,单位为分,可以做部分退款
+ @XStreamAlias("refund_status")
+ @JSONField(name = "refund_status")
+ private int refundStatus; // 退款状态
+ @XStreamAlias("recv_user_id")
+ @JSONField(name = "recv_user_id")
+ private String recvUserId;// 转账退款接收退款的财付通帐号
+ @XStreamAlias("reccv_user_name")
+ @JSONField(name = "reccv_user_name")
+ private String reccvUserName;// 转账退款接收退款的姓名(需与接收退款的财 付通帐号绑定的姓名一致)
+
+ public String getOutRefundNo() {
+ return outRefundNo;
+ }
+
+ public String getRefundId() {
+ return refundId;
+ }
+
+ public int getRefundChannel() {
+ return refundChannel;
+ }
+
+ @JSONField(deserialize = false, serialize = false)
+ public RefundChannel getFormatRefundChannel() {
+ if (refundChannel == 0) {
+ return RefundChannel.TENPAY;
+ } else if (refundChannel == 1) {
+ return RefundChannel.BANK;
+ } else {
+ return null;
+ }
+ }
+
+ public int getRefundFee() {
+ return refundFee;
+ }
+
+ /**
+ * 调用接口获取单位为分,get方法转换为元方便使用
+ *
+ * @return 元单位
+ */
+ @JSONField(deserialize = false, serialize = false)
+ public double getFormatRefundFee() {
+ return refundFee / 100d;
+ }
+
+ public int getRefundStatus() {
+ return refundStatus;
+ }
+
+ @JSONField(deserialize = false, serialize = false)
+ public RefundStatus getFormatRefundStatus() {
+ String refundStatus_ = String.format(",%d,", refundStatus);
+ if (",4,10,".contains(refundStatus_)) {
+ return RefundStatus.SUCCESS;
+ } else if (",3,5,6,".contains(refundStatus_)) {
+ return RefundStatus.FAIL;
+ } else if (",8,9,11,".contains(refundStatus_)) {
+ return RefundStatus.PROCESSING;
+ } else if (",1,2,".contains(refundStatus_)) {
+ return RefundStatus.NOTSURE;
+ } else if (",7,".contains(refundStatus_)) {
+ return RefundStatus.CHANGE;
+ } else {
+ return null;
+ }
+ }
+
+ public String getRecvUserId() {
+ return StringUtils.isNotBlank(recvUserId) ? recvUserId : null;
+ }
+
+ public String getReccvUserName() {
+ return StringUtils.isNotBlank(reccvUserName) ? reccvUserName : null;
+ }
+
+ @Override
+ public String toString() {
+ return "outRefundNo=" + outRefundNo + ", refundId=" + refundId
+ + ", refundChannel=" + refundChannel + ", refundFee="
+ + refundFee + ", refundStatus=" + refundStatus
+ + ", recvUserId=" + recvUserId + ", reccvUserName="
+ + reccvUserName + ", getFormatRefundChannel()="
+ + getFormatRefundChannel() + ", getFormatRefundFee()="
+ + getFormatRefundFee() + ", getFormatRefundStatus()="
+ + getFormatRefundStatus();
+ }
+}
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/RefundRecord.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/RefundRecord.java
new file mode 100644
index 00000000..1073b80f
--- /dev/null
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/RefundRecord.java
@@ -0,0 +1,60 @@
+package com.foxinmy.weixin4j.mp.payment.v2;
+
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamOmitField;
+
+/**
+ * V2退款记录
+ *
+ * @className RefundRecord
+ * @author jy
+ * @date 2014年11月1日
+ * @since JDK 1.7
+ * @see com.foxinmy.weixin4j.mp.payment.v2.RefundDetail
+ */
+@XStreamAlias("xml")
+public class RefundRecord extends ApiResult {
+
+ private static final long serialVersionUID = -2971132874939642721L;
+
+ @XStreamAlias("transaction_id")
+ @JSONField(name = "transaction_id")
+ private String transactionId;// 微信订单号
+ @XStreamAlias("out_trade_no")
+ @JSONField(name = "out_trade_no")
+ private String outTradeNo;// 商户订单号
+ @XStreamAlias("refund_count")
+ @JSONField(name = "refund_count")
+ private int count;// 退款笔数
+ @XStreamOmitField
+ @JSONField(serialize = false, deserialize = false)
+ private List details; // 退款详情
+
+ public String getTransactionId() {
+ return transactionId;
+ }
+
+ public String getOutTradeNo() {
+ return StringUtils.isNotBlank(outTradeNo) ? outTradeNo : null;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public List getDetails() {
+ return details;
+ }
+
+ @Override
+ public String toString() {
+ return "RefundRecord [transactionId=" + transactionId + ", outTradeNo="
+ + outTradeNo + ", count=" + count + ", details=" + details
+ + ", " + super.toString() + "]";
+ }
+}
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/RefundResult.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/RefundResult.java
new file mode 100644
index 00000000..f1206c83
--- /dev/null
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/RefundResult.java
@@ -0,0 +1,40 @@
+package com.foxinmy.weixin4j.mp.payment.v2;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+
+/**
+ * V2退款申请结果
+ *
+ * @className RefundResult
+ * @author jy
+ * @date 2014年11月6日
+ * @since JDK 1.7
+ * @see
+ */
+@XStreamAlias("xml")
+public class RefundResult extends RefundDetail {
+
+ private static final long serialVersionUID = -3687863914168618620L;
+
+ @XStreamAlias("transaction_id")
+ @JSONField(name = "transaction_id")
+ private String transactionId; // 微信订单号
+ @XStreamAlias("out_trade_no")
+ @JSONField(name = "out_trade_no")
+ private String outTradeNo;// 商户系统内部的订单号
+
+ public String getTransactionId() {
+ return transactionId;
+ }
+
+ public String getOutTradeNo() {
+ return outTradeNo;
+ }
+
+ @Override
+ public String toString() {
+ return "RefundResult [transactionId=" + transactionId + ", outTradeNo="
+ + outTradeNo + ", " + super.toString() + "]";
+ }
+}
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/ApiResult.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/ApiResult.java
similarity index 80%
rename from weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/ApiResult.java
rename to weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/ApiResult.java
index e795ea4e..d8c06538 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/ApiResult.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/ApiResult.java
@@ -1,5 +1,8 @@
-package com.foxinmy.weixin4j.mp.payment;
+package com.foxinmy.weixin4j.mp.payment.v3;
+import org.apache.commons.lang3.StringUtils;
+
+import com.alibaba.fastjson.annotation.JSONField;
import com.foxinmy.weixin4j.http.XmlResult;
import com.thoughtworks.xstream.annotations.XStreamAlias;
@@ -17,15 +20,20 @@ public class ApiResult extends XmlResult {
private static final long serialVersionUID = -8430005768959715444L;
@XStreamAlias("appid")
+ @JSONField(name = "appid")
private String appId;// 微信分配的公众账号 ID商户号 非空
@XStreamAlias("mch_id")
+ @JSONField(name = "mch_id")
private String mchId;// 微信支付分配的商户号 非空
@XStreamAlias("sub_mch_id")
+ @JSONField(name = "sub_mch_id")
private String subMchId; // 未知 可能为空
@XStreamAlias("nonce_str")
+ @JSONField(name = "nonce_str")
private String nonceStr;// 随机字符串 非空
private String sign;// 签名 非空
@XStreamAlias("device_info")
+ @JSONField(name = "device_info")
private String deviceInfo;// 微信支付分配的终端设备号 可能为空
private String recall;// 是否需要继续调用接口 Y- 需要,N-不需要
@@ -54,7 +62,7 @@ public class ApiResult extends XmlResult {
}
public String getSubMchId() {
- return subMchId;
+ return StringUtils.isNotBlank(subMchId) ? subMchId : null;
}
public void setSubMchId(String subMchId) {
@@ -89,8 +97,9 @@ public class ApiResult extends XmlResult {
return recall;
}
- public void setRecall(String recall) {
- this.recall = recall;
+ @JSONField(deserialize = false, serialize = false)
+ public boolean getFormatRecall() {
+ return recall != null && recall.equalsIgnoreCase("y");
}
@Override
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/NativePayNotifyV3.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/NativePayNotifyV3.java
index b8b89159..a0203d48 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/NativePayNotifyV3.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/NativePayNotifyV3.java
@@ -1,10 +1,9 @@
package com.foxinmy.weixin4j.mp.payment.v3;
-import com.foxinmy.weixin4j.mp.payment.ApiResult;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
- * Native支付回调时POST的信息
+ * V3 Native支付回调时POST的信息
*
* @className PayNativeNotifyV3
* @author jy
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 d797d5d9..b991d56d 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,14 +3,13 @@ package com.foxinmy.weixin4j.mp.payment.v3;
import com.alibaba.fastjson.annotation.JSONField;
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;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamOmitField;
/**
- * Native支付时的回调响应
+ * V3 Native支付时的回调响应
*
* @className NativePayResponseV3
* @author jy
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/Order.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/Order.java
index b20c96dc..58e972c0 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/Order.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/Order.java
@@ -2,7 +2,9 @@ package com.foxinmy.weixin4j.mp.payment.v3;
import java.util.Date;
-import com.foxinmy.weixin4j.mp.payment.ApiResult;
+import org.apache.commons.lang3.StringUtils;
+
+import com.alibaba.fastjson.annotation.JSONField;
import com.foxinmy.weixin4j.mp.type.CurrencyType;
import com.foxinmy.weixin4j.mp.type.TradeState;
import com.foxinmy.weixin4j.mp.type.TradeType;
@@ -10,7 +12,7 @@ import com.foxinmy.weixin4j.util.DateUtil;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
- * 订单信息
+ * V3订单信息
*
* @className Order
* @author jy
@@ -26,39 +28,52 @@ public class Order extends ApiResult {
// USERPAYING--用户支付中 NOPAY--未支付(输入密码或 确认支付超时) PAYERROR--支付失败(其他 原因,如银行返回失败)
// 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
@XStreamAlias("trade_state")
+ @JSONField(name = "trade_state")
private TradeState tradeState;
// 用户标识ID
@XStreamAlias("openid")
+ @JSONField(name = "openid")
private String openId;
// 用户是否关注公众账号,Y- 关注,N-未关注,仅在公众 账号类型支付有效
@XStreamAlias("is_subscribe")
+ @JSONField(name = "is_subscribe")
private String isSubscribe;
// 交易类型
@XStreamAlias("trade_type")
+ @JSONField(name = "trade_type")
private TradeType tradeType;
// 银行类型
@XStreamAlias("bank_type")
+ @JSONField(name = "bank_type")
private String bankType;
// 订单总金额,单位为分
@XStreamAlias("total_fee")
+ @JSONField(name = "total_fee")
private int totalFee;
// 现金券支付金额<=订单总金 额,订单总金额-现金券金额 为现金支付金额
@XStreamAlias("coupon_fee")
- private int couponFee;
+ @JSONField(name = "coupon_fee")
+ private Integer couponFee;
+ @XStreamAlias("cash_fee")
+ @JSONField(name = "cash_fee")
+ private int cashFee;
// 货币类型,符合 ISO 4217 标准的三位字母代码,默认人民币:CNY
@XStreamAlias("fee_type")
+ @JSONField(name = "fee_type")
private CurrencyType feeType;
// 微信支付订单号
@XStreamAlias("transaction_id")
+ @JSONField(name = "transaction_id")
private String transactionId;
// 商户订单号
- @XStreamAlias("out_rade_no")
+ @XStreamAlias("out_trade_no")
+ @JSONField(name = "out_trade_no")
private String outTradeNo;
// 商家数据包
- @XStreamAlias("attach")
private String attach;
// 支付完成时间,格式为 yyyyMMddhhmmss
@XStreamAlias("time_end")
+ @JSONField(name = "time_end")
private String timeEnd;
public TradeState getTradeState() {
@@ -81,13 +96,8 @@ public class Order extends ApiResult {
return bankType;
}
- /**
- * 调用接口获取单位为分,get方法转换为元方便使用
- *
- * @return 元单位
- */
- public double getTotalFee() {
- return totalFee / 100d;
+ public int getTotalFee() {
+ return totalFee;
}
/**
@@ -95,8 +105,37 @@ public class Order extends ApiResult {
*
* @return 元单位
*/
- public double getCouponFee() {
- return couponFee / 100d;
+ @JSONField(serialize = false, deserialize = false)
+ public double getFormatTotalFee() {
+ return totalFee / 100d;
+ }
+
+ public Integer getCouponFee() {
+ return couponFee;
+ }
+
+ /**
+ * 调用接口获取单位为分,get方法转换为元方便使用
+ *
+ * @return 元单位
+ */
+ @JSONField(serialize = false, deserialize = false)
+ public double getFormatCouponFee() {
+ return couponFee != null ? couponFee / 100d : 0d;
+ }
+
+ public int getCashFee() {
+ return cashFee;
+ }
+
+ /**
+ * 调用接口获取单位为分,get方法转换为元方便使用
+ *
+ * @return 元单位
+ */
+ @JSONField(serialize = false, deserialize = false)
+ public double getFormatCashFee() {
+ return cashFee / 100d;
}
public CurrencyType getFeeType() {
@@ -112,10 +151,15 @@ public class Order extends ApiResult {
}
public String getAttach() {
- return attach;
+ return StringUtils.isBlank(attach) ? null : attach;
}
- public Date getTimeEnd() {
+ public String getTimeEnd() {
+ return timeEnd;
+ }
+
+ @JSONField(serialize = false, deserialize = false)
+ public Date getFormatTimeEnd() {
return DateUtil.parse2yyyyMMddHHmmss(timeEnd);
}
@@ -124,9 +168,13 @@ public class Order extends ApiResult {
return "Order [tradeState=" + tradeState + ", openId=" + openId
+ ", isSubscribe=" + isSubscribe + ", tradeType=" + tradeType
+ ", bankType=" + bankType + ", totalFee=" + totalFee
- + ", couponFee=" + couponFee + ", feeType=" + feeType
- + ", transactionId=" + transactionId + ", outTradeNo="
- + outTradeNo + ", attach=" + attach + ", timeEnd=" + timeEnd
- + ", " + super.toString() + "]";
+ + ", couponFee=" + couponFee + ", cashFee=" + cashFee
+ + ", feeType=" + feeType + ", transactionId=" + transactionId
+ + ", outTradeNo=" + outTradeNo + ", attach=" + attach
+ + ", timeEnd=" + timeEnd + ", getFormatTotalFee()="
+ + getFormatTotalFee() + ", getFormatCouponFee()="
+ + getFormatCouponFee() + ", getFormatCashFee()="
+ + getFormatCashFee() + ", getFormatTimeEnd()="
+ + getFormatTimeEnd() + ", " + super.toString() + "]";
}
}
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 de21e353..cfa8f499 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
@@ -4,7 +4,7 @@ import com.foxinmy.weixin4j.exception.PayException;
import com.foxinmy.weixin4j.mp.payment.PayRequest;
/**
- * JS支付:get_brand_wcpay_request
+ * V3 JS支付:get_brand_wcpay_request
*
* get_brand_wcpay_request:ok 支付成功
* get_brand_wcpay_request:cancel 支付过程中用户取消
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/PrePay.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/PrePay.java
index 9f00b640..2e4121e6 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/PrePay.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/PrePay.java
@@ -1,11 +1,10 @@
package com.foxinmy.weixin4j.mp.payment.v3;
-import com.foxinmy.weixin4j.mp.payment.ApiResult;
import com.foxinmy.weixin4j.mp.type.TradeType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
- * 预订单信息
+ * V3预订单信息
*
* @className PrePay
* @author jy
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/v3/RefundDetail.java
new file mode 100644
index 00000000..b6c08c25
--- /dev/null
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RefundDetail.java
@@ -0,0 +1,112 @@
+package com.foxinmy.weixin4j.mp.payment.v3;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.foxinmy.weixin4j.mp.type.RefundChannel;
+import com.foxinmy.weixin4j.mp.type.RefundStatus;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+
+/**
+ * V3退款详细
+ *
+ * @className RefundDetail
+ * @author jy
+ * @date 2014年11月6日
+ * @since JDK 1.7
+ * @see
+ */
+public class RefundDetail extends ApiResult {
+
+ private static final long serialVersionUID = -3687863914168618620L;
+
+ @XStreamAlias("out_refund_no")
+ @JSONField(name = "out_refund_no")
+ private String outRefundNo; // 商户退款单号
+ @XStreamAlias("refund_id")
+ @JSONField(name = "refund_id")
+ private String refundId; // 微信退款单号
+ @XStreamAlias("refund_channel")
+ @JSONField(name = "refund_channel")
+ private String refundChannel;// 退款渠道:ORIGINAL—原路退款,默认 BALANCE—退回到余额
+ @XStreamAlias("refund_fee")
+ @JSONField(name = "refund_fee")
+ private int refundFee; // 退款总金额,单位为分,可以做部分退款
+ @XStreamAlias("refund_status")
+ @JSONField(name = "refund_status")
+ private String refundStatus; // 退款状态
+ @XStreamAlias("coupon_refund_fee")
+ @JSONField(name = "coupon_refund_fee")
+ private int couponRefundFee; // 现金券退款金额<=退款金额,退款金额-现金券退款金额为现金
+
+ public String getOutRefundNo() {
+ return outRefundNo;
+ }
+
+ public String getRefundId() {
+ return refundId;
+ }
+
+ public String getRefundChannel() {
+ return refundChannel;
+ }
+
+ @JSONField(deserialize = false, serialize = false)
+ public RefundChannel getFormatRefundChannel() {
+ if (StringUtils.isNotBlank(refundChannel)) {
+ return RefundChannel.valueOf(refundChannel.toUpperCase());
+ }
+ return null;
+ }
+
+ public int getRefundFee() {
+ return refundFee;
+ }
+
+ /**
+ * 调用接口获取单位为分,get方法转换为元方便使用
+ *
+ * @return 元单位
+ */
+ @JSONField(deserialize = false, serialize = false)
+ public double getFormatRefundFee() {
+ return refundFee / 100d;
+ }
+
+ public String getRefundStatus() {
+ return refundStatus;
+ }
+
+ @JSONField(deserialize = false, serialize = false)
+ public RefundStatus getFormatRefundStatus() {
+ if (StringUtils.isNotBlank(refundStatus)) {
+ return RefundStatus.valueOf(refundStatus);
+ }
+ return null;
+ }
+
+ public int getCouponRefundFee() {
+ return couponRefundFee;
+ }
+
+ /**
+ * 调用接口获取单位为分,get方法转换为元方便使用
+ *
+ * @return 元单位
+ */
+ @JSONField(deserialize = false, serialize = false)
+ public double getFormatCouponRefundFee() {
+ return couponRefundFee / 100d;
+ }
+
+ @Override
+ public String toString() {
+ return "RefundDetail [outRefundNo=" + outRefundNo + ", refundId="
+ + refundId + ", refundChannel=" + refundChannel
+ + ", refundFee=" + refundFee + ", refundStatus=" + refundStatus
+ + ", couponRefundFee=" + couponRefundFee
+ + ", getFormatRefundChannel()=" + getFormatRefundChannel()
+ + ", getFormatRefundStatus()=" + getFormatRefundStatus()
+ + ", getFormatCouponRefundFee()=" + getFormatCouponRefundFee();
+ }
+}
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RefundRecord.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RefundRecord.java
new file mode 100644
index 00000000..d0a225c2
--- /dev/null
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RefundRecord.java
@@ -0,0 +1,79 @@
+package com.foxinmy.weixin4j.mp.payment.v3;
+
+import java.util.List;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamOmitField;
+
+/**
+ * V3退款记录
+ *
+ * @className RefundRecord
+ * @author jy
+ * @date 2014年11月1日
+ * @since JDK 1.7
+ * @see com.foxinmy.weixin4j.mp.payment.v3.RefundDetail
+ */
+@XStreamAlias("xml")
+public class RefundRecord extends ApiResult {
+
+ private static final long serialVersionUID = -2971132874939642721L;
+
+ @XStreamAlias("transaction_id")
+ @JSONField(name = "transaction_id")
+ private String transactionId;// 微信订单号
+ @XStreamAlias("out_trade_no")
+ @JSONField(name = "out_trade_no")
+ private String outTradeNo;// 商户订单号
+ @XStreamAlias("cash_fee")
+ @JSONField(name = "cash_fee")
+ private int cashFee;
+ @XStreamAlias("refund_count")
+ @JSONField(name = "refund_count")
+ private int count;// 退款笔数
+ @XStreamOmitField
+ @JSONField(serialize = false, deserialize = false)
+ private List details; // 退款详情
+
+ public String getTransactionId() {
+ return transactionId;
+ }
+
+ public String getOutTradeNo() {
+ return outTradeNo;
+ }
+
+ /**
+ * 调用接口获取单位为分,get方法转换为元方便使用
+ *
+ * @return 元单位
+ */
+ @JSONField(serialize = false, deserialize = false)
+ public double getFormatCashFee() {
+ return cashFee / 100d;
+ }
+
+ public int getCashFee() {
+ return cashFee;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public List getDetails() {
+ return details;
+ }
+
+ public void setDetails(List details) {
+ this.details = details;
+ }
+
+ @Override
+ public String toString() {
+ return "RefundRecord [transactionId=" + transactionId + ", outTradeNo="
+ + outTradeNo + ", cashFee=" + cashFee + ", count=" + count
+ + ", details=" + details + ", " + super.toString() + "]";
+ }
+}
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/v3/RefundResult.java
new file mode 100644
index 00000000..c3de1e84
--- /dev/null
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RefundResult.java
@@ -0,0 +1,40 @@
+package com.foxinmy.weixin4j.mp.payment.v3;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+
+/**
+ * V3退款申请结果
+ *
+ * @className RefundResult
+ * @author jy
+ * @date 2014年11月6日
+ * @since JDK 1.7
+ * @see
+ */
+@XStreamAlias("xml")
+public class RefundResult extends RefundDetail {
+
+ private static final long serialVersionUID = -3687863914168618620L;
+
+ @XStreamAlias("transaction_id")
+ @JSONField(name = "transaction_id")
+ private String transactionId;// 微信订单号
+ @XStreamAlias("out_trade_no")
+ @JSONField(name = "out_trade_no")
+ private String outTradeNo;// 商户系统内部的订单号
+
+ public String getTransactionId() {
+ return transactionId;
+ }
+
+ public String getOutTradeNo() {
+ return outTradeNo;
+ }
+
+ @Override
+ public String toString() {
+ return "RefundResult [transactionId=" + transactionId + ", outTradeNo="
+ + outTradeNo + ", " + super.toString() + "]";
+ }
+}
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/IdQuery.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/IdQuery.java
index 0aaca163..a349f176 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/IdQuery.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/IdQuery.java
@@ -37,4 +37,10 @@ public class IdQuery implements Serializable {
this.id = id;
this.type = idType;
}
+
+ @Override
+ public String toString() {
+ return String.format("%s=%s", type.getName(), id);
+ }
+
}
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 9c996b99..e3b5c2d1 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
@@ -10,7 +10,7 @@ package com.foxinmy.weixin4j.mp.type;
* @see
*/
public enum RefundStatus {
- SUCCES, // 退款成功
+ SUCCESS, // 退款成功
FAIL, // 退款失败
PROCESSING, // 退款处理中
NOTSURE, // 未确定,需要商户 原退款单号重新发起
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/RefundType.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/RefundType.java
new file mode 100644
index 00000000..1faceb0d
--- /dev/null
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/type/RefundType.java
@@ -0,0 +1,25 @@
+package com.foxinmy.weixin4j.mp.type;
+
+/**
+ * 退款类型
+ *
+ * @className RefundType
+ * @author jy
+ * @date 2014年12月31日
+ * @since JDK 1.7
+ * @see
+ */
+public enum RefundType {
+ BALANCE(1), // 1:商户号余额退款;
+ CASH(2), // 2:现金帐号 退款;
+ BOTH(3);// 3:优先商户号退款,若商户号余额不足, 再做现金帐号退款。
+ // 使用 2 或 3 时,需联系财 付通开通此功能
+ private int val;
+
+ RefundType(int val) {
+ this.val = val;
+ }
+ public int getVal() {
+ return val;
+ }
+}
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 bd75d8c7..e4de9fe9 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/main/resources/weixin.properties
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/resources/weixin.properties
@@ -17,5 +17,7 @@ qr_path=/tmp/weixin/qr
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 | xxxxx.pfx
\ No newline at end of file
+# ca\u8bc1\u4e66\u5b58\u653e\u7684\u5b8c\u6574\u8def\u5f84 (V2\u7248\u672c\u540e\u7f00\u4e3a*.pfx,V3\u7248\u672c\u540e\u7f00\u4e3a*.p12)
+ca_file=/tmp/weixin/xxxxx.p12
+# classpath\u8def\u5f84\u4e0b\u53ef\u4ee5\u8fd9\u4e48\u5199
+ca_file=classpath:xxxxx.pfx
\ No newline at end of file
diff --git a/weixin4j-mp/weixin4j-mp-api/src/test/java/com/foxinmy/weixin4j/mp/test/UserTest.java b/weixin4j-mp/weixin4j-mp-api/src/test/java/com/foxinmy/weixin4j/mp/test/UserTest.java
index 7f6f2a72..2b375dee 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/test/java/com/foxinmy/weixin4j/mp/test/UserTest.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/test/java/com/foxinmy/weixin4j/mp/test/UserTest.java
@@ -32,7 +32,7 @@ public class UserTest extends TokenTest {
User user = userApi.getUser("owGBft_vbBbOaQOmpEUE4xDLeRSU");
Assert.assertNotNull(user);
System.out.println(user);
- following();
+ // following();
}
@Test
diff --git a/weixin4j-mp/weixin4j-mp-server/src/main/java/com/foxinmy/weixin4j/mp/action/PayAction.java b/weixin4j-mp/weixin4j-mp-server/src/main/java/com/foxinmy/weixin4j/mp/action/PayAction.java
index 888dabf1..58592289 100644
--- a/weixin4j-mp/weixin4j-mp-server/src/main/java/com/foxinmy/weixin4j/mp/action/PayAction.java
+++ b/weixin4j-mp/weixin4j-mp-server/src/main/java/com/foxinmy/weixin4j/mp/action/PayAction.java
@@ -50,8 +50,8 @@ public class PayAction {
JSONObject obj = new JSONObject();
WeixinMpAccount weixinAccount = ConfigUtil.getWeixinMpAccount();
// V3 支付
- PayPackage payPackage = new PayPackageV3(weixinAccount, "用户openid", "商品描述",
- "系统内部订单号", 1d, "IP地址", TradeType.JSAPI);
+ PayPackage payPackage = new PayPackageV3(weixinAccount, "用户openid",
+ "商品描述", "系统内部订单号", 1d, "IP地址", TradeType.JSAPI);
// V2 支付
payPackage = new PayPackageV2("商品描述", weixinAccount.getPartnerId(),
"系统内部订单号", 1d, "回调地址", "IP地址");
@@ -163,7 +163,7 @@ public class PayAction {
if (!sign.equals(valid_sign)) {
return XmlStream.to(new XmlResult(Consts.FAIL, "签名错误"));
}
- return XmlStream.to(new XmlResult());
+ return XmlStream.to(new XmlResult(Consts.SUCCESS, ""));
}
/**
diff --git a/weixin4j-qy/README.md b/weixin4j-qy/README.md
index 0a853053..77ae30b0 100644
--- a/weixin4j-qy/README.md
+++ b/weixin4j-qy/README.md
@@ -91,10 +91,14 @@ weixin4j-qy
* 2014-12-28
- + **weixin4j-qy**: 增加用户进入应用的callback事件
+ + **weixin4j-qy-api**: 增加用户进入应用的callback事件
- + **weixin4j-qy**: 增加批量获取用户详情的接口
+ + **weixin4j-qy-api**: 增加批量获取用户详情的接口
- + **weixin4j-qy**: 新增获取微信服务器IP接口
+ + **weixin4j-qy-api**: 新增获取微信服务器IP接口
- + **weixin4j-qy**: 调整回调模式下的首次验证的签名方式
+ + **weixin4j-qy-server**: 调整回调模式下的首次验证的签名方式
+
+* 2015-01-04
+
+ + **weixin4j-qy-api**: 新增批量删除员工接口
diff --git a/weixin4j-qy/weixin4j-qy-api/README.md b/weixin4j-qy/weixin4j-qy-api/README.md
index 2e1eba04..915be955 100644
--- a/weixin4j-qy/weixin4j-qy-api/README.md
+++ b/weixin4j-qy/weixin4j-qy-api/README.md
@@ -63,3 +63,7 @@ weixin.properties说明
+ 增加`批量获取用户详情`的接口
+ 新增`获取微信服务器IP`接口
+
+* 2015-01-04
+
+ + 新增批量删除员工接口
diff --git a/weixin4j-qy/weixin4j-qy-api/src/main/java/com/foxinmy/weixin4j/qy/WeixinProxy.java b/weixin4j-qy/weixin4j-qy-api/src/main/java/com/foxinmy/weixin4j/qy/WeixinProxy.java
index 548a220b..674029e6 100644
--- a/weixin4j-qy/weixin4j-qy-api/src/main/java/com/foxinmy/weixin4j/qy/WeixinProxy.java
+++ b/weixin4j-qy/weixin4j-qy-api/src/main/java/com/foxinmy/weixin4j/qy/WeixinProxy.java
@@ -231,6 +231,23 @@ public class WeixinProxy {
return userApi.deleteUser(userid);
}
+ /**
+ * 批量删除成员
+ *
+ * @param userIds
+ * 成员列表
+ * @see 批量删除成员说明 userIds)
+ throws WeixinException {
+ return userApi.batchDeleteUser(userIds);
+ }
+
/**
* 创建标签(创建的标签属于管理组;默认为未加锁状态)
*
diff --git a/weixin4j-qy/weixin4j-qy-api/src/main/java/com/foxinmy/weixin4j/qy/api/UserApi.java b/weixin4j-qy/weixin4j-qy-api/src/main/java/com/foxinmy/weixin4j/qy/api/UserApi.java
index 1b114a4a..e7548869 100644
--- a/weixin4j-qy/weixin4j-qy-api/src/main/java/com/foxinmy/weixin4j/qy/api/UserApi.java
+++ b/weixin4j-qy/weixin4j-qy-api/src/main/java/com/foxinmy/weixin4j/qy/api/UserApi.java
@@ -200,6 +200,28 @@ public class UserApi extends QyApi {
return response.getAsJsonResult();
}
+ /**
+ * 批量删除成员
+ *
+ * @param userIds
+ * 成员列表
+ * @see 批量删除成员说明 userIds)
+ throws WeixinException {
+ JSONObject obj = new JSONObject();
+ obj.put("useridlist", userIds);
+ String user_delete_uri = getRequestUri("user_batchdelete_uri");
+ Token token = tokenHolder.getToken();
+ Response response = request.post(String.format(user_delete_uri,
+ token.getAccessToken(), obj.toJSONString()));
+ return response.getAsJsonResult();
+ }
+
/**
* 开启二次验证成功时调用(管理员须拥有userid对应员工的管理权限)
*
diff --git a/weixin4j-qy/weixin4j-qy-api/src/main/java/com/foxinmy/weixin4j/qy/api/weixin.properties b/weixin4j-qy/weixin4j-qy-api/src/main/java/com/foxinmy/weixin4j/qy/api/weixin.properties
index f6a5def3..8df836c3 100644
--- a/weixin4j-qy/weixin4j-qy-api/src/main/java/com/foxinmy/weixin4j/qy/api/weixin.properties
+++ b/weixin4j-qy/weixin4j-qy-api/src/main/java/com/foxinmy/weixin4j/qy/api/weixin.properties
@@ -27,6 +27,8 @@ user_slist_uri={api_base_url}/user/simplelist?access_token=%s&department_id=%d&f
user_list_uri={api_base_url}/user/list?access_token=%s&department_id=%d&fetch_child=%d&status=%d
# \u5220\u9664\u6210\u5458
user_delete_uri={api_base_url}/user/delete?access_token=%s&userid=%s
+# \u6279\u91cf\u5220\u9664\u6210\u5458
+user_batchdelete_uri={api_base_url}/user/batchdelete?access_token=%s
# \u6210\u5458\u4e8c\u6b21\u9a8c\u8bc1\u6210\u529f\u65f6\u8c03\u7528
user_authsucc_uri={api_base_url}/user/authsucc?access_token=%s&userid=%s
# \u521b\u5efa\u6807\u7b7e