diff --git a/CHANGE.md b/CHANGE.md
index 92716e4a..5c52da03 100644
--- a/CHANGE.md
+++ b/CHANGE.md
@@ -193,4 +193,10 @@
+ **weixin-mp**: 新增素材管理多个接口
+ **weixin-mp**: 新增多客服会话管理多个接口
+
+* 2015-03-25
+
+ + **weixin-mp**: 根据《微信商户平台文档》修缮[Pay3Api](./weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/Pay3Api.java)类
+
+ + **weixin-mp**: 新增客服创建、关闭、转接会话事件
\ No newline at end of file
diff --git a/README.md b/README.md
index c7143274..5ffa01f1 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ weixin4j
`公众平台API封装`
- `微信支付(公众号)`
+ `微信支付(刷卡/扫码/公众号)`
`netty服务器&消息分发`
@@ -71,6 +71,8 @@ netty的代码没有放到maven中心仓库,也没什么意义,因为最终需
接下来
------
+* 代金券 & 红包接口
+
* 公众号服务应用
* 企业号第三方应用
diff --git a/weixin4j-mp/README.md b/weixin4j-mp/README.md
index 6fa033b6..14ca6ef9 100644
--- a/weixin4j-mp/README.md
+++ b/weixin4j-mp/README.md
@@ -188,4 +188,10 @@ weixin4j-mp
+ **weixin-mp-api**: 新增素材管理多个接口
- + **weixin-mp-api**: 新增多客服会话管理多个接口
\ No newline at end of file
+ + **weixin-mp-api**: 新增多客服会话管理多个接口
+
+* 2015-03-25
+
+ + **weixin-mp-api**: 根据《微信商户平台文档》修缮[Pay3Api](./weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/Pay3Api.java)类
+
+ + **weixin-mp-server**: 新增客服创建、关闭、转接会话事件
\ 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 2548a858..177d2a29 100644
--- a/weixin4j-mp/weixin4j-mp-api/README.md
+++ b/weixin4j-mp/weixin4j-mp-api/README.md
@@ -167,4 +167,8 @@ weixin.properties说明
+ 新增素材管理多个接口
- + 新增多客服会话管理多个接口
\ No newline at end of file
+ + 新增多客服会话管理多个接口
+
+* 2015-03-25
+
+ + 根据《微信商户平台文档》修缮[Pay3Api](./weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/Pay3Api.java)类
\ 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
index 227ca767..0faa3a32 100644
--- 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
@@ -12,6 +12,7 @@ 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.CurrencyType;
import com.foxinmy.weixin4j.mp.type.IdQuery;
import com.foxinmy.weixin4j.mp.type.IdType;
import com.foxinmy.weixin4j.mp.type.RefundType;
@@ -30,6 +31,7 @@ import com.foxinmy.weixin4j.util.ConfigUtil;
* @since JDK 1.7
* @see com.foxinmy.weixin4j.mp.api.Pay2Api
* @see com.foxinmy.weixin4j.mp.api.Pay3Api
+ * @see 商户平台支付API
*/
public class WeixinPayProxy {
private final PayApi payApi;
@@ -122,6 +124,10 @@ public class WeixinPayProxy {
/**
* V3订单查询
+ *
+ * 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知; 调用支付接口后,返回系统错误或未知交易状态情况;
+ * 调用被扫支付API,返回USERPAYING的状态; 调用关单或撤销接口API之前,需确认支付状态;
+ *
*
* @param idQuery
* 商户系统内部的订单号, transaction_id、out_trade_no 二 选一,如果同时存在优先级:
@@ -130,6 +136,8 @@ public class WeixinPayProxy {
* @see com.foxinmy.weixin4j.mp.payment.v3.Order
* @see com.foxinmy.weixin4j.mp.api.PayApi
* @see com.foxinmy.weixin4j.mp.api.Pay3Api
+ * @see 订单查询API
* @return 订单详情
* @throws WeixinException
*/
@@ -253,9 +261,14 @@ public class WeixinPayProxy {
/**
* V3申请退款(请求需要双向证书)
+ *
+ * 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,
+ * 按照退款规则将支付款按原路退到买家帐号上。
+ *
*
- * 交易时间超过 1 年的订单无法提交退款; 支持部分退款,部分退需要设置相同的订单号和不同的 out_refund_no。一笔退款失
- * 败后重新提交,要采用原来的 out_refund_no。总退款金额不能超过用户实际支付金额。
+ * 1.交易时间超过半年的订单无法提交退款;
+ * 2.微信支付退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。一笔退款失败后重新提交
+ * ,要采用原来的退款单号。总退款金额不能超过用户实际支付金额。
*
*
* @param caFile
@@ -269,6 +282,8 @@ public class WeixinPayProxy {
* 订单总金额,单位为元
* @param refundFee
* 退款总金额,单位为元,可以做部分退款
+ * @param refundFeeType
+ * 货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY
* @param opUserId
* 操作员帐号, 默认为商户号
*
@@ -276,31 +291,37 @@ public class WeixinPayProxy {
* @see com.foxinmy.weixin4j.mp.payment.v3.RefundResult
* @see com.foxinmy.weixin4j.mp.api.PayApi
* @see com.foxinmy.weixin4j.mp.api.Pay3Api
- * @since V3
+ * @see 申请退款API
+ * @since V3 TODO
* @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 {
+ double refundFee, CurrencyType refundFeeType, String opUserId)
+ throws WeixinException {
return pay3Api.refund(caFile, idQuery, outRefundNo, totalFee,
- refundFee, opUserId);
+ refundFee, refundFeeType, opUserId);
}
/**
* V3退款申请采用properties中配置的ca文件
*
- * @see {@link com.foxinmy.weixin4j.mp.WeixinPayProxy#refundV3(File, IdQuery, String, double, double, String)}
+ * @see {@link com.foxinmy.weixin4j.mp.WeixinPayProxy#refundV3(File, IdQuery, String, double, double,CurrencyType, 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);
+ refundFee, CurrencyType.CNY, opUserId);
}
/**
- * V3退款查询 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
+ * V3退款查询
+ *
+ * 提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
+ *
*
* @param idQuery
* 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id
@@ -310,6 +331,8 @@ public class WeixinPayProxy {
* @see com.foxinmy.weixin4j.mp.api.PayApi
* @see com.foxinmy.weixin4j.mp.api.Pay3Api
* @see com.foxinmy.weixin4j.mp.payment.v3.RefundRecord
+ * @see 退款查询API
* @since V3
* @throws WeixinException
*/
@@ -333,6 +356,8 @@ public class WeixinPayProxy {
* @return excel表格
* @since V2 & V3
* @see com.foxinmy.weixin4j.mp.api.PayApi
+ * @see 下载对账单API
* @throws WeixinException
*/
public File downloadbill(Date billDate, BillType billType)
@@ -378,8 +403,11 @@ public class WeixinPayProxy {
}
/**
- * 关闭订单 当订单支付失败,调用关单接口后用新订单号重新发起支付,如果关单失败,返回已完
- * 成支付请按正常支付处理。如果出现银行掉单,调用关单成功后,微信后台会主动发起退款。
+ * 关闭订单
+ *
+ * 商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;系统下单后,用户支付超时,系统退出不再受理,避免用户继续
+ * ,请调用关单接口,如果关单失败,返回已完 成支付请按正常支付处理。如果出现银行掉单,调用关单成功后,微信后台会主动发起退款。
+ *
*
* @param outTradeNo
* 商户系统内部的订单号
@@ -388,13 +416,16 @@ public class WeixinPayProxy {
* @see com.foxinmy.weixin4j.mp.api.Pay3Api
* @since V3
* @throws WeixinException
+ * @see 关闭订单API
*/
public ApiResult closeOrder(String outTradeNo) throws WeixinException {
return payApi.closeOrder(outTradeNo);
}
/**
- * native支付URL转短链接
+ * native支付URL转短链接:用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX),减小二维码数据量
+ * ,提升扫描速度和精确度。
*
* @param url
* 具有native标识的支付URL
@@ -402,6 +433,8 @@ public class WeixinPayProxy {
* @see com.foxinmy.weixin4j.mp.api.PayApi
* @see com.foxinmy.weixin4j.mp.api.Pay2Api
* @see com.foxinmy.weixin4j.mp.api.Pay3Api
+ * @see 转换短链接API
* @since V2 & V3
* @throws WeixinException
*/
@@ -428,6 +461,8 @@ public class WeixinPayProxy {
* @return 处理结果
* @see com.foxinmy.weixin4j.mp.api.PayApi
* @see com.foxinmy.weixin4j.mp.api.Pay3Api
+ * @see 接口测试上报API
* @throws WeixinException
*/
public XmlResult interfaceReport(String interfaceUrl, int executeTime,
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
index b1afc759..f614df44 100644
--- 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
@@ -39,7 +39,7 @@ import com.foxinmy.weixin4j.http.SSLHttpRequest;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.model.WeixinMpAccount;
import com.foxinmy.weixin4j.mp.payment.PayUtil;
-import com.foxinmy.weixin4j.mp.payment.RefundConverter;
+import com.foxinmy.weixin4j.mp.payment.conver.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;
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
index 667abf1b..0c7ff081 100644
--- 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
@@ -30,12 +30,14 @@ import com.foxinmy.weixin4j.http.SSLHttpRequest;
import com.foxinmy.weixin4j.http.XmlResult;
import com.foxinmy.weixin4j.model.WeixinMpAccount;
import com.foxinmy.weixin4j.mp.payment.PayUtil;
-import com.foxinmy.weixin4j.mp.payment.RefundConverter;
+import com.foxinmy.weixin4j.mp.payment.conver.CouponConverter;
+import com.foxinmy.weixin4j.mp.payment.conver.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.CurrencyType;
import com.foxinmy.weixin4j.mp.type.IdQuery;
import com.foxinmy.weixin4j.mp.type.IdType;
import com.foxinmy.weixin4j.mp.util.ExcelUtil;
@@ -51,7 +53,7 @@ import com.foxinmy.weixin4j.util.RandomUtil;
* @author jy
* @date 2014年10月28日
* @since JDK 1.7
- * @see
+ * @see 公众号支付API
*/
public class Pay3Api extends PayApi {
@@ -61,12 +63,18 @@ public class Pay3Api extends PayApi {
/**
* 订单查询
+ *
+ * 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知; 调用支付接口后,返回系统错误或未知交易状态情况;
+ * 调用被扫支付API,返回USERPAYING的状态; 调用关单或撤销接口API之前,需确认支付状态;
+ *
*
* @param idQuery
* 商户系统内部的订单号, transaction_id、out_trade_no 二 选一,如果同时存在优先级:
* transaction_id> out_trade_no
* @return 订单信息
* @see com.foxinmy.weixin4j.mp.payment.v3.Order
+ * @see 订单查询API
* @since V3
* @throws WeixinException
*/
@@ -77,15 +85,19 @@ public class Pay3Api extends PayApi {
String param = map2xml(map);
String orderquery_uri = getRequestUri("orderquery_v3_uri");
Response response = request.post(orderquery_uri, param);
- return response.getAsObject(new TypeReference() {
- });
+ return CouponConverter.fromXML(response.getAsString(), Order.class);
}
/**
- * 申请退款(请求需要双向证书)
+ * 申请退款(请求需要双向证书)
+ *
+ * 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,
+ * 按照退款规则将支付款按原路退到买家帐号上。
+ *
*
- * 交易时间超过 1 年的订单无法提交退款; 支持部分退款,部分退需要设置相同的订单号和不同的 out_refund_no。一笔退款失
- * 败后重新提交,要采用原来的 out_refund_no。总退款金额不能超过用户实际支付金额。
+ * 1.交易时间超过半年的订单无法提交退款;
+ * 2.微信支付退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。一笔退款失败后重新提交
+ * ,要采用原来的退款单号。总退款金额不能超过用户实际支付金额。
*
*
* @param caFile
@@ -104,6 +116,8 @@ public class Pay3Api extends PayApi {
*
* @return 退款申请结果
* @see com.foxinmy.weixin4j.mp.payment.v3.RefundResult
+ * @see 申请退款API
* @since V3
* @throws WeixinException
*/
@@ -147,15 +161,15 @@ public class Pay3Api extends PayApi {
}
}
}
- return response.getAsObject(new TypeReference() {
- });
+ return CouponConverter.fromXML(response.getAsString(),
+ RefundResult.class);
}
/**
* 退款申请
*
* @param caFile
- * 证书文件(V2版本后缀为*.pfx)
+ * 证书文件(V3版本后缀为*.p12)
* @param idQuery
* 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
* transaction_id> out_trade_no
@@ -165,15 +179,22 @@ public class Pay3Api extends PayApi {
* 订单总金额,单位为元
* @param refundFee
* 退款总金额,单位为元,可以做部分退款
+ * @param refundFeeType
+ * 货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY
* @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 {
+ CurrencyType refundFeeType, String opUserId) throws WeixinException {
+ Map mopara = new HashMap();
+ if (refundFeeType == null) {
+ refundFeeType = CurrencyType.CNY;
+ }
+ mopara.put("refund_fee_type", refundFeeType.name());
return refund(caFile, idQuery, outRefundNo, totalFee, refundFee,
- opUserId, null);
+ opUserId, mopara);
}
/**
@@ -221,12 +242,15 @@ public class Pay3Api extends PayApi {
}
/**
- * native支付URL转短链接
+ * native支付URL转短链接:用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX),减小二维码数据量
+ * ,提升扫描速度和精确度。
*
* @param url
* 具有native标识的支付URL
* @return 转换后的短链接
* @throws WeixinException
+ * @see 转换短链接API
*/
public String getShorturl(String url) throws WeixinException {
Map map = baseMap(null);
@@ -245,14 +269,19 @@ public class Pay3Api extends PayApi {
}
/**
- * 关闭订单 当订单支付失败,调用关单接口后用新订单号重新发起支付,如果关单失败,返回已完
- * 成支付请按正常支付处理。如果出现银行掉单,调用关单成功后,微信后台会主动发起退款。
+ * 关闭订单
+ *
+ * 商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;系统下单后,用户支付超时,系统退出不再受理,避免用户继续
+ * ,请调用关单接口,如果关单失败,返回已完 成支付请按正常支付处理。如果出现银行掉单,调用关单成功后,微信后台会主动发起退款。
+ *
*
* @param outTradeNo
* 商户系统内部的订单号
* @return 处理结果
* @since V3
* @throws WeixinException
+ * @see 关闭订单API
*/
public ApiResult closeOrder(String outTradeNo) throws WeixinException {
Map map = baseMap(new IdQuery(outTradeNo,
@@ -280,6 +309,8 @@ public class Pay3Api extends PayApi {
* REFUND,返回当日退款订单
* @return excel表格
* @since V3
+ * @see 下载对账单API
* @throws WeixinException
*/
public File downloadbill(Date billDate, BillType billType)
@@ -349,7 +380,11 @@ public class Pay3Api extends PayApi {
}
/**
- * 退款查询 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
+ * 退款查询
+ *
+ *
+ * 提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
+ *
*
* @param idQuery
* 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id
@@ -358,6 +393,8 @@ public class Pay3Api extends PayApi {
* @return 退款记录
* @see com.foxinmy.weixin4j.mp.payment.v3.RefundRecord
* @see com.foxinmy.weixin4j.mp.payment.v3.RefundDetail
+ * @see 退款查询API
* @since V3
* @throws WeixinException
*/
@@ -390,6 +427,8 @@ public class Pay3Api extends PayApi {
* 调用接口返回的基本数据
* @return 处理结果
* @throws WeixinException
+ * @see 接口测试上报API
*/
@SuppressWarnings("unchecked")
public XmlResult interfaceReport(String interfaceUrl, int executeTime,
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/PayPackage.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/PayPackage.java
index 4243618e..26f7fb06 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/PayPackage.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/PayPackage.java
@@ -21,6 +21,7 @@ public class PayPackage implements Serializable {
private static final long serialVersionUID = 3450161267802545790L;
private String body; // 商品描述 必须
+ private String detail; // 商品详情 非必须
private String attach; // 附加数据,原样返回 非必须
@XStreamAlias("out_trade_no")
@JSONField(name = "out_trade_no")
@@ -56,6 +57,14 @@ public class PayPackage implements Serializable {
this.body = body;
}
+ public String getDetail() {
+ return detail;
+ }
+
+ public void setDetail(String detail) {
+ this.detail = detail;
+ }
+
public String getAttach() {
return attach;
}
@@ -151,10 +160,10 @@ public class PayPackage implements Serializable {
@Override
public String toString() {
- return "PayPackage [body=" + body + ", attach=" + attach
- + ", outTradeNo=" + outTradeNo + ", totalFee=" + totalFee
- + ", spbillCreateIp=" + spbillCreateIp + ", timeStart="
- + timeStart + ", timeExpire=" + timeExpire + ", goodsTag="
- + goodsTag + ", notifyUrl=" + notifyUrl + "]";
+ return "PayPackage [body=" + body + ", detail=" + detail + ", attach="
+ + attach + ", outTradeNo=" + outTradeNo + ", totalFee="
+ + totalFee + ", spbillCreateIp=" + spbillCreateIp
+ + ", timeStart=" + timeStart + ", timeExpire=" + timeExpire
+ + ", goodsTag=" + goodsTag + ", notifyUrl=" + notifyUrl + "]";
}
}
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 824a239c..7149774d 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
@@ -208,7 +208,9 @@ public class PayUtil {
}
/**
- * 创建预支付对象
+ * 统一下单接口
+ * 除被扫支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易回话标识后再按扫码、JSAPI
+ * 、APP等不同场景生成交易串调起支付。
*
* @param payPackage
* 包含订单信息的对象
@@ -216,6 +218,8 @@ public class PayUtil {
* 如果sign为空 则拿paysignkey进行签名
* @see com.foxinmy.weixin4j.mp.payment.v3.PayPackageV3
* @see com.foxinmy.weixin4j.mp.payment.v3.PrePay
+ * @see 统一下单接口
* @return 预支付对象
*/
public static PrePay createPrePay(PayPackageV3 payPackage, String paySignKey)
@@ -306,13 +310,14 @@ public class PayUtil {
}
/**
- * 创建V3.x NativePay支付链接
+ * 创建V3.x NativePay支付(扫码支付)链接
*
* @param weixinAccount
* 支付配置信息
* @param productId
* 与订单ID等价
* @return
+ * @see 扫码支付
*/
public static String createNativePayRequestURLV3(
WeixinMpAccount weixinAccount, String productId) {
@@ -330,7 +335,7 @@ public class PayUtil {
}
/**
- * NATIVE回调时的响应
+ * 创建V2.x NATIVE回调时的响应字符串
*
* @param weixinAccount
* 商户信息
@@ -360,7 +365,7 @@ public class PayUtil {
* 提交被扫支付
*
* @param authCode
- * 扫码支付授权码 ,设备读取用 户微信中的条码或者二维码 信息
+ * 扫码支付授权码 ,设备读取用户微信中的条码或者二维码信息
* @param body
* 商品描述
* @param attach
@@ -373,7 +378,7 @@ public class PayUtil {
* 订单生成的机器 IP
* @param weixinAccount
* 商户信息
- * @return 返回数据
+ * @return 支付的订单信息
* @see {@link com.foxinmy.weixin4j.mp.payment.PayUtil#createMicroPay(MicroPayPackage, WeixinMpAccount)}
* @throws WeixinException
*/
@@ -387,14 +392,17 @@ public class PayUtil {
}
/**
- * 提交被扫支付
+ * 提交被扫支付:收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,由商户收银台或者商户后台调用该接口发起支付.
*
* @param payPackage
* 订单信息
* @param weixinAccount
* 商户信息
- * @return 返回数据
+ * @return 支付的订单信息
* @throws WeixinException
+ * @see com.foxinmy.weixin4j.mp.payment.v3.Order
+ * @see 提交被扫支付API
*/
public static com.foxinmy.weixin4j.mp.payment.v3.Order createMicroPay(
MicroPayPackage payPackage, WeixinMpAccount weixinAccount)
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/conver/CouponConverter.java
similarity index 54%
rename from weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/RefundConverter.java
rename to weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/conver/CouponConverter.java
index 85e41127..cf06f65b 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/conver/CouponConverter.java
@@ -1,4 +1,4 @@
-package com.foxinmy.weixin4j.mp.payment;
+package com.foxinmy.weixin4j.mp.payment.conver;
import java.lang.reflect.Field;
import java.util.HashMap;
@@ -9,6 +9,9 @@ import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import com.foxinmy.weixin4j.mp.payment.coupon.CouponInfo;
+import com.foxinmy.weixin4j.mp.payment.v3.Order;
+import com.foxinmy.weixin4j.mp.payment.v3.RefundResult;
import com.foxinmy.weixin4j.xml.XmlStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
@@ -20,45 +23,41 @@ import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.mapper.Mapper;
/**
- * 退款查询接口调用结果转换类
+ * V3订单详情转换类
*
- * @className RefundConverter
+ * @className OrderConverter
* @author jy
- * @date 2014年11月2日
+ * @date 2015年3月24日
* @since JDK 1.7
- * @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
+ * @see
*/
-public class RefundConverter {
+public class CouponConverter {
+
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 COUPON_CLASS = CouponInfo.class;
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(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.processAnnotations(new Class[] { COUPON_CLASS });
xStream.registerConverter(new $());
mapper = xStream.getMapper();
reflectionProvider = xStream.getReflectionProvider();
}
public static T fromXML(String xml, Class clazz) {
- RefundConverter.clazz = clazz;
+ CouponConverter.clazz = clazz;
+ xStream.processAnnotations(clazz);
return xStream.fromXML(xml, clazz);
}
private static class $ implements Converter {
@Override
public boolean canConvert(@SuppressWarnings("rawtypes") Class clazz) {
- return clazz.equals(REFUNDRECORD2) || clazz.equals(REFUNDRECORD3);
+ return clazz.equals(Order.class)
+ || clazz.equals(RefundResult.class);
}
@Override
@@ -71,9 +70,9 @@ public class RefundConverter {
@Override
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
- Object refund = null;
+ Object object = null;
try {
- refund = clazz.newInstance();
+ object = clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
@@ -88,9 +87,9 @@ public class RefundConverter {
Field field = reflectionProvider.getFieldOrNull(clazz,
fieldName);
if (field != null) {
- Object value = context.convertAnother(refund,
+ Object value = context.convertAnother(object,
field.getType());
- reflectionProvider.writeField(refund, fieldName, value,
+ reflectionProvider.writeField(object, fieldName, value,
field.getDeclaringClass());
} else if ((matcher = pattern.matcher(nodeName)).find()) {
String key = matcher.group();
@@ -103,27 +102,34 @@ public class RefundConverter {
}
reader.moveUp();
}
- StringBuilder detailXml = new StringBuilder();
- detailXml.append("");
- String detailCanonicalName = clazz.getCanonicalName().replaceFirst(
- "RefundRecord", "RefundDetail");
- for (Iterator>> outIt = outMap
- .entrySet().iterator(); outIt.hasNext();) {
- detailXml.append("<").append(detailCanonicalName).append(">");
- for (Iterator> innerIt = outIt.next()
- .getValue().entrySet().iterator(); innerIt.hasNext();) {
- Entry entry = innerIt.next();
- detailXml.append("<").append(entry.getKey()).append(">");
- detailXml.append(entry.getValue());
- detailXml.append("").append(entry.getKey()).append(">");
+ if (!outMap.isEmpty()) {
+ StringBuilder couponXml = new StringBuilder();
+ couponXml.append("");
+ for (Iterator>> outIt = outMap
+ .entrySet().iterator(); outIt.hasNext();) {
+ couponXml.append("<")
+ .append(COUPON_CLASS.getCanonicalName())
+ .append(">");
+ for (Iterator> innerIt = outIt.next()
+ .getValue().entrySet().iterator(); innerIt
+ .hasNext();) {
+ Entry entry = innerIt.next();
+ couponXml.append("<").append(entry.getKey())
+ .append(">");
+ couponXml.append(entry.getValue());
+ couponXml.append("").append(entry.getKey())
+ .append(">");
+ }
+ couponXml.append("")
+ .append(COUPON_CLASS.getCanonicalName())
+ .append(">");
}
- detailXml.append("").append(detailCanonicalName).append(">");
+ couponXml.append("
");
+ reflectionProvider.writeField(object, "couponList",
+ xStream.fromXML(couponXml.toString(), List.class),
+ List.class.getDeclaringClass());
}
- detailXml.append("
");
- reflectionProvider.writeField(refund, "details",
- xStream.fromXML(detailXml.toString(), List.class),
- List.class.getDeclaringClass());
- return refund;
+ return object;
}
}
-}
\ No newline at end of file
+}
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/conver/RefundConverter.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/conver/RefundConverter.java
new file mode 100644
index 00000000..ed614ac0
--- /dev/null
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/conver/RefundConverter.java
@@ -0,0 +1,190 @@
+package com.foxinmy.weixin4j.mp.payment.conver;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.foxinmy.weixin4j.mp.payment.coupon.CouponInfo;
+import com.foxinmy.weixin4j.xml.XmlStream;
+import com.thoughtworks.xstream.converters.Converter;
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
+import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+import com.thoughtworks.xstream.mapper.Mapper;
+
+/**
+ * 退款查询接口调用结果转换类
+ *
+ * @className RefundConverter
+ * @author jy
+ * @date 2014年11月2日
+ * @since JDK 1.7
+ * @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 REFUND_PATTERN = Pattern.compile("_\\d{1,}$");
+ private final static Pattern COUPON_PATTERN = Pattern
+ .compile("_\\d{1,}_\\d{1,}$");
+ private static Class> clazz;
+ private final static Class COUPON_CLASS = CouponInfo.class;
+ 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(new Class[] { REFUNDRECORD2, REFUNDRECORD3 });
+ xStream.aliasField("refund_state",
+ com.foxinmy.weixin4j.mp.payment.v2.RefundDetail.class,
+ "refundStatus");
+ xStream.registerConverter(new $());
+ mapper = xStream.getMapper();
+ reflectionProvider = xStream.getReflectionProvider();
+ }
+
+ public static T fromXML(String xml, Class clazz) {
+ RefundConverter.clazz = clazz;
+ xStream.processAnnotations(clazz);
+ return xStream.fromXML(xml, clazz);
+ }
+
+ private static class $ implements Converter {
+ @Override
+ public boolean canConvert(@SuppressWarnings("rawtypes") Class clazz) {
+ return clazz.equals(REFUNDRECORD2) || clazz.equals(REFUNDRECORD3);
+ }
+
+ @Override
+ public void marshal(Object source, HierarchicalStreamWriter writer,
+ MarshallingContext context) {
+ new ReflectionConverter(mapper, reflectionProvider).marshal(source,
+ writer, context);
+ }
+
+ @Override
+ public Object unmarshal(HierarchicalStreamReader reader,
+ UnmarshallingContext context) {
+ Object refund = null;
+ try {
+ refund = clazz.newInstance();
+ } catch (InstantiationException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ Matcher matcher = null;
+ Map> refundMap = new HashMap>();
+ Map> couponMap = new HashMap>();
+ while (reader.hasMoreChildren()) {
+ reader.moveDown();
+ String nodeName = reader.getNodeName();
+ String fieldName = mapper.realMember(clazz, nodeName);
+ Field field = reflectionProvider.getFieldOrNull(clazz,
+ fieldName);
+ if (field != null) {
+ Object value = context.convertAnother(refund,
+ field.getType());
+ reflectionProvider.writeField(refund, fieldName, value,
+ field.getDeclaringClass());
+ } else if ((matcher = REFUND_PATTERN.matcher(nodeName)).find()) {
+ String key = matcher.group();
+ Map innerMap = null;
+ if ((matcher = COUPON_PATTERN.matcher(nodeName)).find()) {
+ key = matcher.group();
+ if ((innerMap = couponMap.get(key)) == null) {
+ innerMap = new HashMap();
+ couponMap.put(key, innerMap);
+ }
+ } else {
+ if ((innerMap = refundMap
+ .get(String.format("%s_", key))) == null) {
+ innerMap = new HashMap();
+ refundMap.put(String.format("%s_", key), innerMap);
+ }
+ }
+ innerMap.put(nodeName.replaceFirst(key, ""),
+ reader.getValue());
+ }
+ reader.moveUp();
+ }
+ if (!refundMap.isEmpty()) {
+ StringBuilder detailXml = new StringBuilder();
+ detailXml.append("");
+ String detailCanonicalName = clazz.getCanonicalName()
+ .replaceFirst("RefundRecord", "RefundDetail");
+ for (Iterator>> refundIT = refundMap
+ .entrySet().iterator(); refundIT.hasNext();) {
+ detailXml.append("<").append(detailCanonicalName)
+ .append(">");
+ Entry> refundEntry = refundIT
+ .next();
+ for (Iterator> innerIt = refundEntry
+ .getValue().entrySet().iterator(); innerIt
+ .hasNext();) {
+ Entry entry = innerIt.next();
+ detailXml.append("<").append(entry.getKey())
+ .append(">");
+ detailXml.append(entry.getValue());
+ detailXml.append("").append(entry.getKey())
+ .append(">");
+ }
+ if (!couponMap.isEmpty()) {
+ detailXml.append("");
+ Iterator>> couponIT = couponMap
+ .entrySet().iterator();
+ while (couponIT.hasNext()) {
+ Entry> couponEntry = couponIT
+ .next();
+ if (couponEntry.getKey().startsWith(
+ refundEntry.getKey())) {
+ detailXml
+ .append("<")
+ .append(COUPON_CLASS.getCanonicalName())
+ .append(">");
+ for (Iterator> innerIt = couponEntry
+ .getValue().entrySet().iterator(); innerIt
+ .hasNext();) {
+ Entry entry = innerIt
+ .next();
+ detailXml.append("<")
+ .append(entry.getKey()).append(">");
+ detailXml.append(entry.getValue());
+ detailXml.append("")
+ .append(entry.getKey()).append(">");
+ }
+ detailXml
+ .append("")
+ .append(COUPON_CLASS.getCanonicalName())
+ .append(">");
+ couponIT.remove();
+ }
+ }
+ detailXml.append("");
+ }
+ detailXml.append("").append(detailCanonicalName)
+ .append(">");
+ }
+ detailXml.append("
");
+ reflectionProvider.writeField(refund, "details",
+ xStream.fromXML(detailXml.toString(), List.class),
+ List.class.getDeclaringClass());
+ }
+ return refund;
+ }
+ }
+}
\ No newline at end of file
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/coupon/CouponInfo.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/coupon/CouponInfo.java
new file mode 100644
index 00000000..8e6c32fd
--- /dev/null
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/coupon/CouponInfo.java
@@ -0,0 +1,65 @@
+package com.foxinmy.weixin4j.mp.payment.coupon;
+
+import java.io.Serializable;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+
+/**
+ * 代金券信息
+ *
+ * @className CouponBase
+ * @author jy
+ * @date 2015年3月24日
+ * @since JDK 1.7
+ * @see
+ */
+public class CouponInfo implements Serializable {
+
+ private static final long serialVersionUID = -8744999305258786901L;
+
+ // 代金券或立减优惠批次ID
+ @XStreamAlias("coupon_batch_id")
+ @JSONField(name = "coupon_batch_id")
+ private String couponBatchId;
+ // 代金券或立减优惠ID
+ @XStreamAlias("coupon_id")
+ @JSONField(name = "coupon_id")
+ private String couponId;
+ // 单个代金券或立减优惠支付金额
+ @XStreamAlias("coupon_fee")
+ @JSONField(name = "coupon_fee")
+ private Integer couponFee;
+
+ public String getCouponBatchId() {
+ return couponBatchId;
+ }
+
+ public String getCouponId() {
+ return couponId;
+ }
+
+ public Integer getCouponFee() {
+ return couponFee;
+ }
+
+ /**
+ * 调用接口获取单位为分,get方法转换为元方便使用
+ *
+ * @return 元单位
+ */
+ @JSONField(serialize = false, deserialize = false)
+ public double getFormatCouponFee() {
+ return couponFee / 100d;
+ }
+
+ public void setCouponId(String couponId) {
+ this.couponId = couponId;
+ }
+
+ @Override
+ public String toString() {
+ return "couponBatchId=" + couponBatchId + ", couponId=" + couponId
+ + ", couponFee=" + couponFee;
+ }
+}
diff --git a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/ApiResult.java b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/ApiResult.java
index ffc6f8ce..bb5a36b8 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/ApiResult.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/ApiResult.java
@@ -110,6 +110,6 @@ public class ApiResult extends XmlResult {
public String toString() {
return "appId=" + appId + ", mchId=" + mchId + ", subMchId=" + subMchId
+ ", nonceStr=" + nonceStr + ", sign=" + sign + ", deviceInfo="
- + deviceInfo + ", recall=" + recall + ", " + super.toString();
+ + deviceInfo + ", recall=" + getFormatRecall() + ", " + super.toString();
}
}
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 58e972c0..806408b9 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
@@ -1,15 +1,16 @@
package com.foxinmy.weixin4j.mp.payment.v3;
import java.util.Date;
-
-import org.apache.commons.lang3.StringUtils;
+import java.util.List;
import com.alibaba.fastjson.annotation.JSONField;
+import com.foxinmy.weixin4j.mp.payment.coupon.CouponInfo;
import com.foxinmy.weixin4j.mp.type.CurrencyType;
import com.foxinmy.weixin4j.mp.type.TradeState;
import com.foxinmy.weixin4j.mp.type.TradeType;
import com.foxinmy.weixin4j.util.DateUtil;
import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamOmitField;
/**
* V3订单信息
@@ -54,6 +55,14 @@ public class Order extends ApiResult {
@XStreamAlias("coupon_fee")
@JSONField(name = "coupon_fee")
private Integer couponFee;
+ // 代金券或立减优惠使用数量
+ @XStreamAlias("coupon_count")
+ @JSONField(name = "coupon_count")
+ private Integer couponCount;
+ // 代金券信息
+ @XStreamOmitField
+ @JSONField(serialize = false)
+ private List couponList;
@XStreamAlias("cash_fee")
@JSONField(name = "cash_fee")
private int cashFee;
@@ -75,6 +84,10 @@ public class Order extends ApiResult {
@XStreamAlias("time_end")
@JSONField(name = "time_end")
private String timeEnd;
+ // 交易状态描述
+ @XStreamAlias("trade_state_desc")
+ @JSONField(name = "trade_state_desc")
+ private String tradeStateDesc;
public TradeState getTradeState() {
return tradeState;
@@ -88,6 +101,11 @@ public class Order extends ApiResult {
return isSubscribe;
}
+ @JSONField(serialize = false, deserialize = false)
+ public boolean getFormatIsSubscribe() {
+ return isSubscribe != null && isSubscribe.equalsIgnoreCase("y");
+ }
+
public TradeType getTradeType() {
return tradeType;
}
@@ -124,6 +142,15 @@ public class Order extends ApiResult {
return couponFee != null ? couponFee / 100d : 0d;
}
+ public Integer getCouponCount() {
+ return couponCount;
+ }
+
+ @JSONField(serialize = false, deserialize = false)
+ public int getFormatCouponCount() {
+ return couponCount != null ? couponCount.intValue() : 0;
+ }
+
public int getCashFee() {
return cashFee;
}
@@ -151,7 +178,7 @@ public class Order extends ApiResult {
}
public String getAttach() {
- return StringUtils.isBlank(attach) ? null : attach;
+ return attach;
}
public String getTimeEnd() {
@@ -163,18 +190,30 @@ public class Order extends ApiResult {
return DateUtil.parse2yyyyMMddHHmmss(timeEnd);
}
+ public String getTradeStateDesc() {
+ return tradeStateDesc;
+ }
+
+ public List getCouponList() {
+ return couponList;
+ }
+
+ public void setCouponList(List couponList) {
+ this.couponList = couponList;
+ }
+
@Override
public String toString() {
return "Order [tradeState=" + tradeState + ", openId=" + openId
- + ", isSubscribe=" + isSubscribe + ", tradeType=" + tradeType
- + ", bankType=" + bankType + ", totalFee=" + totalFee
- + ", couponFee=" + couponFee + ", cashFee=" + cashFee
- + ", feeType=" + feeType + ", transactionId=" + transactionId
- + ", outTradeNo=" + outTradeNo + ", attach=" + attach
- + ", timeEnd=" + timeEnd + ", getFormatTotalFee()="
- + getFormatTotalFee() + ", getFormatCouponFee()="
- + getFormatCouponFee() + ", getFormatCashFee()="
- + getFormatCashFee() + ", getFormatTimeEnd()="
- + getFormatTimeEnd() + ", " + super.toString() + "]";
+ + ", isSubscribe=" + getFormatIsSubscribe() + ", tradeType="
+ + tradeType + ", bankType=" + bankType + ", feeType=" + feeType
+ + ", transactionId=" + transactionId + ", outTradeNo="
+ + outTradeNo + ", attach=" + attach + ", timeEnd=" + timeEnd
+ + ", totalFee=" + getFormatTotalFee() + ", couponFee="
+ + getFormatCouponFee() + ", couponCount="
+ + getFormatCouponCount() + ", couponList=" + couponList
+ + ", cashFee=" + getFormatCashFee() + ", timeEnd="
+ + getFormatTimeEnd() + ", tradeStateDesc=" + tradeStateDesc
+ + ", " + super.toString() + "]";
}
}
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 2e4121e6..d6f06dc3 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
@@ -18,12 +18,20 @@ public class PrePay extends ApiResult {
private static final long serialVersionUID = -8430005768959715444L;
@XStreamAlias("trade_type")
- private TradeType tradeType;// 交易类型JSAPI、NATIVE、APP 非空
+ /**
+ * 调用接口提交的交易类型,取值如下:JSAPI,NATIVE,APP,
+ */
+ private TradeType tradeType;
@XStreamAlias("prepay_id")
- private String prepayId;// 微信生成的预支付 ID,用于后续接口调用中使用二维码链接 非空
+ /**
+ * 微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时
+ */
+ private String prepayId;
@XStreamAlias("code_url")
- private String codeUrl;// trade_type 为 NATIVE 是有 返回,此参数可直接生成二 维码展示出来进行扫码支付
- // 可能为空
+ /**
+ * trade_type 为 NATIVE 是有 返回,此参数可直接生成二 维码展示出来进行扫码支付 可能为空
+ */
+ private String codeUrl;
public PrePay() {
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
index b6c08c25..b6774f13 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RefundDetail.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RefundDetail.java
@@ -1,8 +1,12 @@
package com.foxinmy.weixin4j.mp.payment.v3;
+import java.util.List;
+
import org.apache.commons.lang3.StringUtils;
import com.alibaba.fastjson.annotation.JSONField;
+import com.foxinmy.weixin4j.mp.payment.coupon.CouponInfo;
+import com.foxinmy.weixin4j.mp.type.CurrencyType;
import com.foxinmy.weixin4j.mp.type.RefundChannel;
import com.foxinmy.weixin4j.mp.type.RefundStatus;
import com.thoughtworks.xstream.annotations.XStreamAlias;
@@ -32,12 +36,43 @@ public class RefundDetail extends ApiResult {
@XStreamAlias("refund_fee")
@JSONField(name = "refund_fee")
private int refundFee; // 退款总金额,单位为分,可以做部分退款
+ @XStreamAlias("refund_fee_type")
+ @JSONField(name = "refund_fee_type")
+ private CurrencyType refundFeeType; // 退款货币种类
+ @XStreamAlias("total_fee")
+ @JSONField(name = "total_fee")
+ private int totalFee; // 订单总金额
+ @XStreamAlias("fee_type")
+ @JSONField(name = "fee_type")
+ private CurrencyType feeType; // 订单金额货币种类
+ @XStreamAlias("cash_fee")
+ @JSONField(name = "cash_fee")
+ private int cashFee; // 现金支付金额
+ @XStreamAlias("cash_fee_type")
+ @JSONField(name = "cash_fee_type")
+ private CurrencyType cashFeeType; // 现金支付货币种类
+ @XStreamAlias("cash_refund_fee")
+ @JSONField(name = "cash_refund_fee")
+ private Integer cashRefundFee; // 现金退款金额
+ @XStreamAlias("cash_refund_fee_type")
+ @JSONField(name = "cash_refund_fee_type")
+ private CurrencyType cashRefundFeeType; // 现金退款货币类型
@XStreamAlias("refund_status")
@JSONField(name = "refund_status")
private String refundStatus; // 退款状态
@XStreamAlias("coupon_refund_fee")
@JSONField(name = "coupon_refund_fee")
- private int couponRefundFee; // 现金券退款金额<=退款金额,退款金额-现金券退款金额为现金
+ private Integer couponRefundFee; // 现金券退款金额<=退款金额,退款金额-现金券退款金额为现金
+ /**
+ * 微信支付文档上写的coupon_count,而实际测试拿到的是coupon_refund_count,做个记号。
+ *
+ */
+ @XStreamAlias("coupon_refund_count")
+ @JSONField(name = "coupon_refund_count")
+ private Integer couponRefundCount; // 代金券或立减优惠使用数量
+ @JSONField(serialize = false)
+ private List couponList; // 代金券信息
public String getOutRefundNo() {
return outRefundNo;
@@ -63,6 +98,10 @@ public class RefundDetail extends ApiResult {
return refundFee;
}
+ public CurrencyType getFeeType() {
+ return feeType;
+ }
+
/**
* 调用接口获取单位为分,get方法转换为元方便使用
*
@@ -85,7 +124,7 @@ public class RefundDetail extends ApiResult {
return null;
}
- public int getCouponRefundFee() {
+ public Integer getCouponRefundFee() {
return couponRefundFee;
}
@@ -96,17 +135,92 @@ public class RefundDetail extends ApiResult {
*/
@JSONField(deserialize = false, serialize = false)
public double getFormatCouponRefundFee() {
- return couponRefundFee / 100d;
+ return couponRefundFee != null ? couponRefundFee.intValue() / 100d : 0d;
+ }
+
+ public CurrencyType getRefundFeeType() {
+ return refundFeeType;
+ }
+
+ public int getTotalFee() {
+ return totalFee;
+ }
+
+ /**
+ * 调用接口获取单位为分,get方法转换为元方便使用
+ *
+ * @return 元单位
+ */
+ @JSONField(deserialize = false, serialize = false)
+ public double getFormatTotalFee() {
+ return totalFee / 100d;
+ }
+
+ public int getCashFee() {
+ return cashFee;
+ }
+
+ /**
+ * 调用接口获取单位为分,get方法转换为元方便使用
+ *
+ * @return 元单位
+ */
+ @JSONField(deserialize = false, serialize = false)
+ public double getFormatCashFee() {
+ return cashFee / 100d;
+ }
+
+ public CurrencyType getCashFeeType() {
+ return cashFeeType;
+ }
+
+ public Integer getCashRefundFee() {
+ return cashRefundFee;
+ }
+
+ /**
+ * 调用接口获取单位为分,get方法转换为元方便使用
+ *
+ * @return 元单位
+ */
+ @JSONField(deserialize = false, serialize = false)
+ public double getFormatCashRefundFee() {
+ return cashRefundFee != null ? cashRefundFee.intValue() / 100d : 0d;
+ }
+
+ public CurrencyType getCashRefundFeeType() {
+ return cashRefundFeeType;
+ }
+
+ public Integer getCouponRefundCount() {
+ return couponRefundCount;
+ }
+
+ @JSONField(deserialize = false, serialize = false)
+ public int getFormatCouponRefundCount() {
+ return couponRefundCount != null ? couponRefundCount.intValue() : 0;
+ }
+
+ public List getCouponList() {
+ return couponList;
+ }
+
+ public void setCouponList(List couponList) {
+ this.couponList = couponList;
}
@Override
public String toString() {
return "RefundDetail [outRefundNo=" + outRefundNo + ", refundId="
+ refundId + ", refundChannel=" + refundChannel
- + ", refundFee=" + refundFee + ", refundStatus=" + refundStatus
- + ", couponRefundFee=" + couponRefundFee
- + ", getFormatRefundChannel()=" + getFormatRefundChannel()
- + ", getFormatRefundStatus()=" + getFormatRefundStatus()
- + ", getFormatCouponRefundFee()=" + getFormatCouponRefundFee();
+ + ", refundFee=" + getFormatRefundFee() + ", refundFeeType="
+ + refundFeeType + ", totalFee=" + getFormatTotalFee()
+ + ", feeType=" + feeType + ", cashFee=" + getFormatCashFee()
+ + ", cashFeeType=" + cashFeeType + ", cashRefundFee="
+ + getFormatCashRefundFee() + ", cashRefundFeeType="
+ + cashRefundFeeType + ", refundStatus=" + refundStatus
+ + ", couponRefundFee=" + getFormatCouponRefundFee()
+ + ", couponCount=" + getCouponRefundCount() + ", couponList="
+ + couponList + ", " + super.toString() + "]";
}
}
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
index d0a225c2..d27dee75 100644
--- 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
@@ -3,6 +3,7 @@ package com.foxinmy.weixin4j.mp.payment.v3;
import java.util.List;
import com.alibaba.fastjson.annotation.JSONField;
+import com.foxinmy.weixin4j.mp.type.CurrencyType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamOmitField;
@@ -26,9 +27,24 @@ public class RefundRecord extends ApiResult {
@XStreamAlias("out_trade_no")
@JSONField(name = "out_trade_no")
private String outTradeNo;// 商户订单号
+ @XStreamAlias("total_fee")
+ @JSONField(name = "total_fee")
+ private int totalFee; // 订单总金额
+ @XStreamAlias("fee_type")
+ @JSONField(name = "fee_type")
+ private CurrencyType feeType; // 订单金额货币种类
@XStreamAlias("cash_fee")
@JSONField(name = "cash_fee")
- private int cashFee;
+ private int cashFee; // 现金支付金额
+ @XStreamAlias("cash_fee_type")
+ @JSONField(name = "cash_fee_type")
+ private CurrencyType cashFeeType; // 现金支付金额货币种类
+ @XStreamAlias("refund_fee")
+ @JSONField(name = "refund_fee")
+ private int refundFee; // 退款总金额
+ @XStreamAlias("coupon_refund_fee")
+ @JSONField(name = "coupon_refund_fee")
+ private Integer couponRefundFee; // 代金券或立减优惠退款金额=订单金额-现金退款金额,注意:满立减金额不会退回
@XStreamAlias("refund_count")
@JSONField(name = "refund_count")
private int count;// 退款笔数
@@ -58,6 +74,42 @@ public class RefundRecord extends ApiResult {
return cashFee;
}
+ public CurrencyType getFeeType() {
+ return feeType;
+ }
+
+ public CurrencyType getCashFeeType() {
+ return cashFeeType;
+ }
+
+ /**
+ * 调用接口获取单位为分,get方法转换为元方便使用
+ *
+ * @return 元单位
+ */
+ @JSONField(serialize = false, deserialize = false)
+ public double getFormatCouponRefundFee() {
+ return couponRefundFee != null ? couponRefundFee.intValue() / 100d : 0d;
+ }
+
+ public Integer getCouponRefundFee() {
+ return couponRefundFee;
+ }
+
+ /**
+ * 调用接口获取单位为分,get方法转换为元方便使用
+ *
+ * @return 元单位
+ */
+ @JSONField(serialize = false, deserialize = false)
+ public double getFormatTotalFee() {
+ return totalFee / 100d;
+ }
+
+ public int getTotalFee() {
+ return totalFee;
+ }
+
public int getCount() {
return count;
}
@@ -70,10 +122,28 @@ public class RefundRecord extends ApiResult {
this.details = details;
}
+ public int getRefundFee() {
+ return refundFee;
+ }
+
+ /**
+ * 调用接口获取单位为分,get方法转换为元方便使用
+ *
+ * @return 元单位
+ */
+ @JSONField(serialize = false, deserialize = false)
+ public double getFormatRefundFee() {
+ return refundFee / 100d;
+ }
+
@Override
public String toString() {
return "RefundRecord [transactionId=" + transactionId + ", outTradeNo="
- + outTradeNo + ", cashFee=" + cashFee + ", count=" + count
+ + outTradeNo + ", totalFee=" + getFormatTotalFee()
+ + ", feeType=" + feeType + ", cashFee=" + getFormatCashFee()
+ + ", cashFeeType=" + cashFeeType + ", refundFee="
+ + getFormatRefundFee() + ", couponRefundFee="
+ + getFormatCouponRefundFee() + ", count=" + count
+ ", details=" + details + ", " + super.toString() + "]";
}
}
diff --git a/weixin4j-mp/weixin4j-mp-api/src/test/java/com/foxinmy/weixin4j/mp/test/PayTest.java b/weixin4j-mp/weixin4j-mp-api/src/test/java/com/foxinmy/weixin4j/mp/test/PayTest.java
index 1c1511b8..ee391768 100644
--- a/weixin4j-mp/weixin4j-mp-api/src/test/java/com/foxinmy/weixin4j/mp/test/PayTest.java
+++ b/weixin4j-mp/weixin4j-mp-api/src/test/java/com/foxinmy/weixin4j/mp/test/PayTest.java
@@ -25,151 +25,160 @@ import com.foxinmy.weixin4j.token.WeixinTokenCreator;
import com.foxinmy.weixin4j.type.AccountType;
public class PayTest {
- private final static WeixinPayProxy PAY2;
- private final static WeixinPayProxy PAY3;
- private final static WeixinMpAccount ACCOUNT2;
- private final static WeixinMpAccount ACCOUNT3;
- static {
- ACCOUNT2 = new WeixinMpAccount(
- "wxba294f2c6f330361",
- "8e33f5371a1afea1f7bce88088cb4bba",
- "gADrKITv3qYWu9JEg1NS0WPaU5yFgTwS9WfPueskfPpt3OZGpnUN1uBom36G2tP701vi2pPRJLZF9dEDFj9pqxidPn10Y91Lj8kK37Svz6S4MfeAHo9svFZmHkIKScGb",
- "1221928801", "8d1b26231827a965ef54fe6a3a151551");
- PAY2 = new WeixinPayProxy(ACCOUNT2, new FileTokenHolder(
- new WeixinTokenCreator(ACCOUNT2.getId(), ACCOUNT2.getSecret(),
- AccountType.MP)));
- ACCOUNT3 = new WeixinMpAccount("wx0d1d598c0c03c999",
- "2513ac683f1beabdb6b98d9ddd9e5755",
- "GATFzDwbQdbbci3QEQxX2rUBvwTrsMiZ", "10020674");
- PAY3 = new WeixinPayProxy(ACCOUNT3, new FileTokenHolder(
- new WeixinTokenCreator(ACCOUNT3.getId(), ACCOUNT3.getSecret(),
- AccountType.MP)));
- }
+ private final static WeixinPayProxy PAY2;
+ private final static WeixinPayProxy PAY3;
+ private final static WeixinMpAccount ACCOUNT2;
+ private final static WeixinMpAccount ACCOUNT3;
+ static {
+ ACCOUNT2 = new WeixinMpAccount(
+ "请填入v2版本的appid",
+ "请填入v2版本的appsecret",
+ "请填入v2版本的paysignkey",
+ "请填入v2版本的partnerId", "请填入v2版本的partnerKey");
+ PAY2 = new WeixinPayProxy(ACCOUNT2, new FileTokenHolder(
+ new WeixinTokenCreator(ACCOUNT2.getId(), ACCOUNT2.getSecret(),
+ AccountType.MP)));
+ ACCOUNT3 = new WeixinMpAccount("请填入v3版本的appid",
+ "请填入v3版本的appSecret",
+ "请填入v3版本的paysignkey", "请填入v3版本的mchid");
+ PAY3 = new WeixinPayProxy(ACCOUNT3, new FileTokenHolder(
+ new WeixinTokenCreator(ACCOUNT3.getId(), ACCOUNT3.getSecret(),
+ AccountType.MP)));
+ }
- @Test
- public void orderQueryV2() throws WeixinException {
- System.err.println(PAY2.orderQueryV2("D14110500021"));
- }
+ @Test
+ public void orderQueryV2() throws WeixinException {
+ System.err.println(PAY2.orderQueryV2("D14110500021"));
+ }
- @Test
- public void refundV2() throws WeixinException {
- File caFile = new File(
- "/Users/jy/workspace/feican/canyi-weixin-parent/canyi-weixin-service/src/main/resources/1221928801.pfx");
- IdQuery idQuery = new IdQuery("D15020300005", IdType.TRADENO);
- System.err.println(PAY2.refundV2(caFile, idQuery, "1422925555037", 16d, 16d,
- "1221928801", "111111", null, null, null));
- }
+ @Test
+ public void refundV2() throws WeixinException {
+ File caFile = new File(
+ "签名文件,如12333.pfx");
+ IdQuery idQuery = new IdQuery("D15020300005", IdType.TRADENO);
+ System.err.println(PAY2.refundV2(caFile, idQuery, "1422925555037", 16d,
+ 16d, "1221928801", "111111", null, null, null));
+ }
- @Test
- public void refundQueryV2() throws WeixinException {
- System.err.println(PAY2.refundQueryV2(new IdQuery("D14123000004",
- IdType.TRADENO)));
- refundQueryV3();
- }
+ @Test
+ public void refundQueryV2() throws WeixinException {
+ System.err.println(PAY2.refundQueryV2(new IdQuery("D14123000004",
+ IdType.TRADENO)));
+ refundQueryV3();
+ }
- @Test
- public void downbillV2() throws WeixinException {
- Calendar c = Calendar.getInstance();
- c.set(Calendar.YEAR, 2014);
- c.set(Calendar.MONTH, 11);
- c.set(Calendar.DAY_OF_MONTH, 22);
- File file = PAY2.downloadbill(c.getTime(), null);
- System.err.println(file);
- }
+ @Test
+ public void downbillV2() throws WeixinException {
+ Calendar c = Calendar.getInstance();
+ c.set(Calendar.YEAR, 2014);
+ c.set(Calendar.MONTH, 11);
+ c.set(Calendar.DAY_OF_MONTH, 22);
+ File file = PAY2.downloadbill(c.getTime(), null);
+ System.err.println(file);
+ }
- @Test
- public void orderQueryV3() throws WeixinException {
- Order order = PAY3.orderQueryV3(new IdQuery("T0002", IdType.TRADENO));
- System.err.println(order);
- String sign = order.getSign();
- order.setSign(null);
- String valiSign = PayUtil.paysignMd5(order, ACCOUNT3.getPaySignKey());
- System.err
- .println(String.format("sign=%s,valiSign=%s", sign, valiSign));
- Assert.assertEquals(valiSign, sign);
- }
+ @Test
+ public void orderQueryV3() throws WeixinException {
+ Order order = PAY3.orderQueryV3(new IdQuery("T0002", IdType.TRADENO));
+ System.err.println(order);
+ String sign = order.getSign();
+ order.setSign(null);
+ String valiSign = PayUtil.paysignMd5(order, ACCOUNT3.getPaySignKey());
+ System.err
+ .println(String.format("sign=%s,valiSign=%s", sign, valiSign));
+ Assert.assertEquals(valiSign, sign);
+ }
- @Test
- public void refundQueryV3() throws WeixinException {
- System.err.println(PAY3.refundQueryV3(new IdQuery("T00015",
- IdType.TRADENO)));
- }
+ @Test
+ public void refundQueryV3() throws WeixinException {
+ com.foxinmy.weixin4j.mp.payment.v3.RefundRecord record = PAY3.refundQueryV3(new IdQuery("TT_1427183696238",
+ IdType.TRADENO));
+ System.err.println(record);
+ // 这里的验证签名需要把details循环拼接
+ String sign = record.getSign();
+ record.setSign(null);
+ String valiSign = PayUtil.paysignMd5(record, ACCOUNT3.getPaySignKey());
+ System.err
+ .println(String.format("sign=%s,valiSign=%s", sign, valiSign));
+ Assert.assertEquals(valiSign, sign);
+ }
- @Test
- public void downbillV3() throws WeixinException {
- Calendar c = Calendar.getInstance();
- System.err.println(c.getTime());
- c.set(Calendar.YEAR, 2014);
- c.set(Calendar.MONTH, 9);
- c.set(Calendar.DAY_OF_MONTH, 22);
- System.err.println(c.getTime());
- File file = PAY3.downloadbill(c.getTime(), null);
- System.err.println(file);
- }
+ @Test
+ public void downbillV3() throws WeixinException {
+ Calendar c = Calendar.getInstance();
+ System.err.println(c.getTime());
+ c.set(Calendar.YEAR, 2015);
+ c.set(Calendar.MONTH, 2);
+ c.set(Calendar.DAY_OF_MONTH, 24);
+ System.err.println(c.getTime());
+ File file = PAY3.downloadbill(c.getTime(), null);
+ System.err.println(file);
+ }
- @Test
- public void refundV3() throws WeixinException {
- File caFile = new File(
- "/Users/jy/workspace/feican/canyi-weixin-parent/canyi-weixin-service/src/main/resources/10020674.p12");
- IdQuery idQuery = new IdQuery("T00015", IdType.TRADENO);
- com.foxinmy.weixin4j.mp.payment.v3.RefundResult result = PAY3.refundV3(
- caFile, idQuery, "R0002", 1d, 1d, "10020674");
- System.err.println(result);
- String sign = result.getSign();
- result.setSign(null);
- String valiSign = PayUtil.paysignMd5(result, ACCOUNT3.getPaySignKey());
- System.err
- .println(String.format("sign=%s,valiSign=%s", sign, valiSign));
- Assert.assertEquals(valiSign, sign);
- }
+ @Test
+ public void refundV3() throws WeixinException {
+ File caFile = new File(
+ "签名文件如123.p12");
+ IdQuery idQuery = new IdQuery("TT_1427183696238", IdType.TRADENO);
+ com.foxinmy.weixin4j.mp.payment.v3.RefundResult result = PAY3.refundV3(
+ caFile, idQuery, "TT_R" + System.currentTimeMillis(), 0.01d, 0.01d,
+ null, "10020674");
+ System.err.println(result);
+ String sign = result.getSign();
+ result.setSign(null);
+ String valiSign = PayUtil.paysignMd5(result, ACCOUNT3.getPaySignKey());
+ System.err
+ .println(String.format("sign=%s,valiSign=%s", sign, valiSign));
+ Assert.assertEquals(valiSign, sign);
+ }
- @Test
- public void nativeV3() throws WeixinException {
- PayPackageV3 payPackageV3 = new PayPackageV3(ACCOUNT3,
- "oyFLst1bqtuTcxK-ojF8hOGtLQao", "native测试", "T0001", 0.1d,
- "127.0.0.1", TradeType.NATIVE);
- payPackageV3.setProductId("0001");
- payPackageV3.setNotifyUrl("xxxx");
- PrePay prePay = null;
- try {
- prePay = PayUtil.createPrePay(payPackageV3,
- ACCOUNT3.getPaySignKey());
- } catch (PayException e) {
- e.printStackTrace();
- }
- System.err.println(prePay);
- }
+ @Test
+ public void nativeV3() throws WeixinException {
+ PayPackageV3 payPackageV3 = new PayPackageV3(ACCOUNT3,
+ "oyFLst1bqtuTcxK-ojF8hOGtLQao", "native测试", "T0001", 0.1d,
+ "127.0.0.1", TradeType.NATIVE);
+ payPackageV3.setProductId("0001");
+ payPackageV3.setNotifyUrl("xxxx");
+ PrePay prePay = null;
+ try {
+ prePay = PayUtil.createPrePay(payPackageV3,
+ ACCOUNT3.getPaySignKey());
+ } catch (PayException e) {
+ e.printStackTrace();
+ }
+ System.err.println(prePay);
+ }
- @Test
- public void closeOrder() throws WeixinException {
- ApiResult result = PAY3.closeOrder("D111");
- System.err.println(result);
- String sign = result.getSign();
- result.setSign(null);
- String valiSign = PayUtil.paysignMd5(result, ACCOUNT3.getPaySignKey());
- System.err
- .println(String.format("sign=%s,valiSign=%s", sign, valiSign));
- Assert.assertEquals(valiSign, sign);
- }
+ @Test
+ public void closeOrder() throws WeixinException {
+ ApiResult result = PAY3.closeOrder("D111");
+ System.err.println(result);
+ String sign = result.getSign();
+ result.setSign(null);
+ String valiSign = PayUtil.paysignMd5(result, ACCOUNT3.getPaySignKey());
+ System.err
+ .println(String.format("sign=%s,valiSign=%s", sign, valiSign));
+ Assert.assertEquals(valiSign, sign);
+ }
- @Test
- public void shortUrl() throws WeixinException {
- String url = "weixin://wxpay/bizpayurl?xxxxxx";
- String shortUrl = PAY3.getPayShorturl(url);
- System.err.println(shortUrl);
- }
+ @Test
+ public void shortUrl() throws WeixinException {
+ String url = "weixin://wxpay/bizpayurl?xxxxxx";
+ String shortUrl = PAY3.getPayShorturl(url);
+ System.err.println(shortUrl);
+ }
- @Test
- public void interfaceReport() throws WeixinException {
- String interfaceUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
- int executeTime = 2500;
- String outTradeNo = null;
- String ip = "127.0.0.1";
- Date time = new Date();
- XmlResult returnXml = new XmlResult("SUCCESS", "");
- returnXml.setResultCode("SUCCESS");
- returnXml = PAY3.interfaceReport(interfaceUrl, executeTime, outTradeNo,
- ip, time, returnXml);
- System.err.println(returnXml);
- }
+ @Test
+ public void interfaceReport() throws WeixinException {
+ String interfaceUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
+ int executeTime = 2500;
+ String outTradeNo = null;
+ String ip = "127.0.0.1";
+ Date time = new Date();
+ XmlResult returnXml = new XmlResult("SUCCESS", "");
+ returnXml.setResultCode("SUCCESS");
+ returnXml = PAY3.interfaceReport(interfaceUrl, executeTime, outTradeNo,
+ ip, time, returnXml);
+ System.err.println(returnXml);
+ }
}
diff --git a/weixin4j-mp/weixin4j-mp-server/README.md b/weixin4j-mp/weixin4j-mp-server/README.md
index 1ea63e6d..767c3b0f 100644
--- a/weixin4j-mp/weixin4j-mp-server/README.md
+++ b/weixin4j-mp/weixin4j-mp-server/README.md
@@ -67,4 +67,8 @@ weixin4j-mp-server
* 2014-11-23
- + `WeixinServerBootstrap`重命名为`WeixinMpServerBootstrap`
\ No newline at end of file
+ + `WeixinServerBootstrap`重命名为`WeixinMpServerBootstrap`
+
+* 2015-03-25
+
+ + 新增客服创建、关闭、转接会话事件
\ No newline at end of file
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 a12a9ff6..77f1cf7b 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
@@ -5,6 +5,8 @@ import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
+import org.dom4j.DocumentException;
+import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -16,6 +18,7 @@ import com.foxinmy.weixin4j.model.WeixinMpAccount;
import com.foxinmy.weixin4j.mp.payment.JsPayNotify;
import com.foxinmy.weixin4j.mp.payment.PayPackage;
import com.foxinmy.weixin4j.mp.payment.PayUtil;
+import com.foxinmy.weixin4j.mp.payment.conver.CouponConverter;
import com.foxinmy.weixin4j.mp.payment.v2.NativePayNotifyV2;
import com.foxinmy.weixin4j.mp.payment.v2.NativePayResponseV2;
import com.foxinmy.weixin4j.mp.payment.v2.PayFeedback;
@@ -149,16 +152,22 @@ public class PayAction {
* <return_code>SUCCESS/FAIL</return_code>
* <return_msg>如非空,为错误 原因签名失败参数格式校验错误</return_msg>
* </xml>
+ * @throws DocumentException
+ * @see 支付结果通知
*/
- public String jsNotifyV3(InputStream inputStream) {
- com.foxinmy.weixin4j.mp.payment.v3.Order order = XmlStream.get(
- inputStream, com.foxinmy.weixin4j.mp.payment.v3.Order.class);
+ public String jsNotifyV3(InputStream inputStream) throws DocumentException {
+ SAXReader sax = new SAXReader();
+ String orderXml = sax.read(inputStream).asXML();
+ com.foxinmy.weixin4j.mp.payment.v3.Order order = CouponConverter
+ .fromXML(orderXml, com.foxinmy.weixin4j.mp.payment.v3.Order.class);
log.info("jsapi_notify_order_info:", order);
String sign = order.getSign();
order.setSign(null);
WeixinMpAccount weixinAccount = ConfigUtil.getWeixinMpAccount();
String valid_sign = PayUtil.paysignMd5(order,
weixinAccount.getPaySignKey());
+ // 如果订单中存在代金券的情况并不适用
log.info("微信签名----->sign={},vaild_sign={}", sign, valid_sign);
if (!sign.equals(valid_sign)) {
return XmlStream.to(new XmlResult(Consts.FAIL, "签名错误"));
diff --git a/weixin4j-mp/weixin4j-mp-server/src/main/java/com/foxinmy/weixin4j/mp/action/event/KfCloseAction.java b/weixin4j-mp/weixin4j-mp-server/src/main/java/com/foxinmy/weixin4j/mp/action/event/KfCloseAction.java
new file mode 100644
index 00000000..aed5ede4
--- /dev/null
+++ b/weixin4j-mp/weixin4j-mp-server/src/main/java/com/foxinmy/weixin4j/mp/action/event/KfCloseAction.java
@@ -0,0 +1,21 @@
+package com.foxinmy.weixin4j.mp.action.event;
+
+import com.foxinmy.weixin4j.action.DebugAction;
+import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
+import com.foxinmy.weixin4j.msg.event.KfCloseEventMessage;
+import com.foxinmy.weixin4j.type.EventType;
+import com.foxinmy.weixin4j.type.MessageType;
+
+/**
+ * 客服关闭会话消息
+ *
+ * @className KfCloseAction
+ * @author jy
+ * @date 2015年3月25日
+ * @since JDK 1.7
+ * @see com.foxinmy.weixin4j.msg.event.KfCloseEventMessage
+ */
+@ActionAnnotation(msgType = MessageType.event, eventType = { EventType.kf_close_session })
+public class KfCloseAction extends DebugAction {
+
+}
diff --git a/weixin4j-mp/weixin4j-mp-server/src/main/java/com/foxinmy/weixin4j/mp/action/event/KfCreateAction.java b/weixin4j-mp/weixin4j-mp-server/src/main/java/com/foxinmy/weixin4j/mp/action/event/KfCreateAction.java
new file mode 100644
index 00000000..3a049670
--- /dev/null
+++ b/weixin4j-mp/weixin4j-mp-server/src/main/java/com/foxinmy/weixin4j/mp/action/event/KfCreateAction.java
@@ -0,0 +1,21 @@
+package com.foxinmy.weixin4j.mp.action.event;
+
+import com.foxinmy.weixin4j.action.DebugAction;
+import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
+import com.foxinmy.weixin4j.msg.event.KfCreateEventMessage;
+import com.foxinmy.weixin4j.type.EventType;
+import com.foxinmy.weixin4j.type.MessageType;
+
+/**
+ * 客服接入会话消息
+ *
+ * @className KfCreateAction
+ * @author jy
+ * @date 2015年3月25日
+ * @since JDK 1.7
+ * @see com.foxinmy.weixin4j.msg.event.KfCreateEventMessage
+ */
+@ActionAnnotation(msgType = MessageType.event, eventType = { EventType.kf_create_session })
+public class KfCreateAction extends DebugAction {
+
+}
diff --git a/weixin4j-mp/weixin4j-mp-server/src/main/java/com/foxinmy/weixin4j/mp/action/event/KfSwitchAction.java b/weixin4j-mp/weixin4j-mp-server/src/main/java/com/foxinmy/weixin4j/mp/action/event/KfSwitchAction.java
new file mode 100644
index 00000000..ac8ca836
--- /dev/null
+++ b/weixin4j-mp/weixin4j-mp-server/src/main/java/com/foxinmy/weixin4j/mp/action/event/KfSwitchAction.java
@@ -0,0 +1,21 @@
+package com.foxinmy.weixin4j.mp.action.event;
+
+import com.foxinmy.weixin4j.action.DebugAction;
+import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
+import com.foxinmy.weixin4j.msg.event.KfSwitchEventMessage;
+import com.foxinmy.weixin4j.type.EventType;
+import com.foxinmy.weixin4j.type.MessageType;
+
+/**
+ * 客服转接会话消息
+ *
+ * @className KfSwitchAction
+ * @author jy
+ * @date 2015年3月25日
+ * @since JDK 1.7
+ * @see com.foxinmy.weixin4j.msg.event.KfSwitchEventMessage
+ */
+@ActionAnnotation(msgType = MessageType.event, eventType = { EventType.kf_switch_session })
+public class KfSwitchAction extends DebugAction {
+
+}