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(""); + 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(""); + } + couponXml.append(""); } - detailXml.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(""); + } + 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(""); + } + detailXml + .append(""); + couponIT.remove(); + } + } + detailXml.append(""); + } + detailXml.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 { + +}