From 1bc2a5c7b3f55638b1e328ff70c0c51005fdf321 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Thu, 24 Nov 2016 20:08:05 +0800 Subject: [PATCH] =?UTF-8?q?=E9=83=A8=E5=88=86=E5=AE=9E=E7=8E=B0=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E6=94=AF=E4=BB=98=E6=9F=A5=E8=AF=A2=E9=80=80=E6=AC=BE?= =?UTF-8?q?=E7=9A=84=E6=8E=A5=E5=8F=A3=20for=20#59?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chanjar/weixin/mp/api/WxMpPayService.java | 54 +- .../mp/api/impl/WxMpPayServiceImpl.java | 92 ++-- .../pay/request/WxPayRefundQueryRequest.java | 135 +++++ .../pay/result/WxPayRefundQueryResult.java | 488 ++++++++++++++++++ .../mp/api/impl/WxMpPayServiceImplTest.java | 24 +- 5 files changed, 743 insertions(+), 50 deletions(-) create mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRefundQueryRequest.java create mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResult.java diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java index 2fcd4cb9..6cde5950 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java @@ -2,7 +2,6 @@ package me.chanjar.weixin.mp.api; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.mp.bean.pay.WxPayJsSDKCallback; -import me.chanjar.weixin.mp.bean.pay.result.WxPayOrderCloseResult; import me.chanjar.weixin.mp.bean.pay.request.WxEntPayRequest; import me.chanjar.weixin.mp.bean.pay.request.WxPayRefundRequest; import me.chanjar.weixin.mp.bean.pay.request.WxPaySendRedpackRequest; @@ -13,8 +12,9 @@ import java.io.File; import java.util.Map; /** - * 微信支付相关接口 - * Created by Binary Wang on 2016/7/28. + * 微信支付相关接口 + * Created by Binary Wang on 2016/7/28. + * * @author binarywang (https://github.com/binarywang) */ public interface WxMpPayService { @@ -24,14 +24,15 @@ public interface WxMpPayService { * 查询订单(详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2) * 该接口提供所有微信支付订单的查询,商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。 * 需要调用查询接口的情况: - ◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知; - ◆ 调用支付接口后,返回系统错误或未知交易状态情况; - ◆ 调用被扫支付API,返回USERPAYING的状态; - ◆ 调用关单或撤销接口API之前,需确认支付状态; + * ◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知; + * ◆ 调用支付接口后,返回系统错误或未知交易状态情况; + * ◆ 调用被扫支付API,返回USERPAYING的状态; + * ◆ 调用关单或撤销接口API之前,需确认支付状态; * 接口地址:https://api.mch.weixin.qq.com/pay/orderquery * + * * @param transactionId 微信支付分配的商户号 - * @param outTradeNo 商户系统内部的订单号,当没提供transaction_id时需要传这个。 + * @param outTradeNo 商户系统内部的订单号,当没提供transaction_id时需要传这个。 * @throws WxErrorException */ WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException; @@ -47,6 +48,7 @@ public interface WxMpPayService { * 接口地址:https://api.mch.weixin.qq.com/pay/closeorder * 是否需要证书: 不需要。 * + * * @param outTradeNo 商户系统内部的订单号,当没提供transaction_id时需要传这个。 * @throws WxErrorException */ @@ -56,15 +58,16 @@ public interface WxMpPayService { * 统一下单(详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1) * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识" * 接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder - * @throws WxErrorException - * @param request 请求对象 * + * @param request 请求对象 + * @throws WxErrorException */ WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws WxErrorException; /** * 该接口调用“统一下单”接口,并拼装发起支付请求需要的参数 * 详见http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN + * * @param request 请求对象 */ Map getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException; @@ -75,16 +78,33 @@ public interface WxMpPayService { * 详见 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4 * 接口链接:https://api.mch.weixin.qq.com/secapi/pay/refund * + * * @param request 请求对象 - * @param keyFile 证书文件对象 + * @param keyFile 证书文件对象 * @return 退款操作结果 */ WxPayRefundResult refund(WxPayRefundRequest request, File keyFile) throws WxErrorException; + /** + *
+   * 微信支付-查询退款
+   * 应用场景:
+   *  提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
+   * 详见 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5
+   * 接口链接:https://api.mch.weixin.qq.com/pay/refundquery
+   * 
+ * 以下四个参数四选一 + * @param transactionId 微信订单号 + * @param outTradeNo 商户订单号 + * @param outRefundNo 商户退款单号 + * @param refundId 微信退款单号 + * @return 退款信息 + */ + WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) throws WxErrorException; + /** * 读取支付结果通知 * 详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 - * */ WxPayJsSDKCallback getJSSDKCallbackData(String xmlData) throws WxErrorException; @@ -93,7 +113,6 @@ public interface WxMpPayService { * 计算Map键值对是否和签名相符, * 按照字段名的 ASCII 码从小到大排序(字典序)后,使用 URL 键值对的 格式(即 key1=value1&key2=value2...)拼接成字符串 * - * */ boolean checkJSSDKCallbackDataSignature(Map kvm, String signature); @@ -104,8 +123,9 @@ public interface WxMpPayService { * 发送普通红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3 * 发送裂变红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_5&index=4 * + * * @param request 请求对象 - * @param keyFile 证书文件对象 + * @param keyFile 证书文件对象 */ WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request, File keyFile) throws WxErrorException; @@ -118,8 +138,9 @@ public interface WxMpPayService { * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2 * 接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers * + * * @param request 请求对象 - * @param keyFile 证书文件对象 + * @param keyFile 证书文件对象 */ WxEntPayResult entPay(WxEntPayRequest request, File keyFile) throws WxErrorException; @@ -130,8 +151,9 @@ public interface WxMpPayService { * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_3 * 接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo * + * * @param partnerTradeNo 商户订单号 - * @param keyFile 证书文件对象 + * @param keyFile 证书文件对象 */ WxEntPayQueryResult queryEntPay(String partnerTradeNo, File keyFile) throws WxErrorException; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java index 3273cd2b..e49891f1 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java @@ -8,7 +8,6 @@ import me.chanjar.weixin.common.util.xml.XStreamInitializer; import me.chanjar.weixin.mp.api.WxMpPayService; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.pay.WxPayJsSDKCallback; -import me.chanjar.weixin.mp.bean.pay.result.WxPayOrderCloseResult; import me.chanjar.weixin.mp.bean.pay.request.*; import me.chanjar.weixin.mp.bean.pay.result.*; import org.apache.commons.codec.digest.DigestUtils; @@ -42,13 +41,11 @@ import java.util.*; */ public class WxMpPayServiceImpl implements WxMpPayService { - protected final Logger log = LoggerFactory.getLogger(this.getClass()); - private static final String PAY_BASE_URL = "https://api.mch.weixin.qq.com"; - private static final String[] TRADE_TYPES = new String[]{"JSAPI","NATIVE", "APP"}; - private static final String[] REFUND_ACCOUNT = new String[]{"REFUND_SOURCE_RECHARGE_FUNDS", + private static final String[] TRADE_TYPES = new String[]{"JSAPI", "NATIVE", "APP"}; + private static final String[] REFUND_ACCOUNT = new String[]{"REFUND_SOURCE_RECHARGE_FUNDS", "REFUND_SOURCE_UNSETTLED_FUNDS"}; - + protected final Logger log = LoggerFactory.getLogger(this.getClass()); private WxMpService wxMpService; public WxMpPayServiceImpl(WxMpService wxMpService) { @@ -57,7 +54,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { @Override public WxPayRefundResult refund(WxPayRefundRequest request, File keyFile) - throws WxErrorException { + throws WxErrorException { checkParameters(request); XStream xstream = XStreamInitializer.getInstance(); @@ -67,7 +64,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { request.setAppid(this.wxMpService.getWxMpConfigStorage().getAppId()); String partnerId = this.wxMpService.getWxMpConfigStorage().getPartnerId(); request.setMchId(partnerId); - request.setNonceStr( System.currentTimeMillis() + ""); + request.setNonceStr(System.currentTimeMillis() + ""); request.setOpUserId(partnerId); String sign = this.createSign(BeanUtils.xmlBean2Map(request), this.wxMpService.getWxMpConfigStorage().getPartnerKey()); request.setSign(sign); @@ -79,11 +76,45 @@ public class WxMpPayServiceImpl implements WxMpPayService { return result; } + @Override + public WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) throws WxErrorException { + if ((StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo) && StringUtils.isBlank(outRefundNo) && StringUtils.isBlank(refundId)) || + (StringUtils.isNotBlank(transactionId) && StringUtils.isNotBlank(outTradeNo) && StringUtils.isNotBlank(outRefundNo) && StringUtils.isNotBlank(refundId))) { + throw new IllegalArgumentException("transaction_id , out_trade_no,out_refund_no, refund_id 必须四选一"); + } + + XStream xstream = XStreamInitializer.getInstance(); + xstream.processAnnotations(WxPayRefundQueryRequest.class); + xstream.processAnnotations(WxPayRefundQueryResult.class); + + WxPayRefundQueryRequest request = new WxPayRefundQueryRequest(); + request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); + request.setTransactionId(StringUtils.trimToNull(transactionId)); + request.setOutRefundNo(StringUtils.trimToNull(outRefundNo)); + request.setRefundId(StringUtils.trimToNull(refundId)); + + request.setAppid(this.wxMpService.getWxMpConfigStorage().getAppId()); + request.setMchId(this.wxMpService.getWxMpConfigStorage().getPartnerId()); + request.setNonceStr(System.currentTimeMillis() + ""); + + String sign = this.createSign(BeanUtils.xmlBean2Map(request), + this.wxMpService.getWxMpConfigStorage().getPartnerKey()); + request.setSign(sign); + + String url = PAY_BASE_URL + "/pay/refundquery"; + + String responseContent = this.executeRequest(url, xstream.toXML(request)); + WxPayRefundQueryResult result = (WxPayRefundQueryResult) xstream.fromXML(responseContent); + result.composeRefundRecords(responseContent); + this.checkResult(result); + return result; + } + private void checkResult(WxPayBaseResult result) throws WxErrorException { if (!"SUCCESS".equalsIgnoreCase(result.getReturnCode()) || !"SUCCESS".equalsIgnoreCase(result.getResultCode())) { throw new WxErrorException(WxError.newBuilder().setErrorCode(-1) - .setErrorMsg("返回代码:" + result.getReturnCode() + ", 返回信息: " + .setErrorMsg("返回代码: " + result.getReturnCode() + ", 返回信息: " + result.getReturnMsg() + ", 结果代码: " + result.getResultCode() + ", 错误代码: " + result.getErrCode() + ", 错误详情: " + result.getErrCodeDes()) .build()); @@ -94,7 +125,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { BeanUtils.checkRequiredFields(request); if (StringUtils.isNotBlank(request.getRefundAccount())) { - if(!ArrayUtils.contains(REFUND_ACCOUNT, request.getRefundAccount())){ + if (!ArrayUtils.contains(REFUND_ACCOUNT, request.getRefundAccount())) { throw new IllegalArgumentException("refund_account目前必须为" + Arrays.toString(REFUND_ACCOUNT) + "其中之一"); } } @@ -118,14 +149,14 @@ public class WxMpPayServiceImpl implements WxMpPayService { @Override public boolean checkJSSDKCallbackDataSignature(Map kvm, - String signature) { + String signature) { return signature.equals(this.createSign(kvm, - this.wxMpService.getWxMpConfigStorage().getPartnerKey())); + this.wxMpService.getWxMpConfigStorage().getPartnerKey())); } @Override public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request, File keyFile) - throws WxErrorException { + throws WxErrorException { XStream xstream = XStreamInitializer.getInstance(); xstream.processAnnotations(WxPaySendRedpackRequest.class); xstream.processAnnotations(WxPaySendRedpackResult.class); @@ -136,7 +167,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { request.setNonceStr(System.currentTimeMillis() + ""); String sign = this.createSign(BeanUtils.xmlBean2Map(request), - this.wxMpService.getWxMpConfigStorage().getPartnerKey()); + this.wxMpService.getWxMpConfigStorage().getPartnerKey()); request.setSign(sign); String url = PAY_BASE_URL + "/mmpaymkttransfers/sendredpack"; @@ -147,15 +178,16 @@ public class WxMpPayServiceImpl implements WxMpPayService { String responseContent = this.executeRequestWithKeyFile(url, keyFile, xstream.toXML(request), mchId); WxPaySendRedpackResult result = (WxPaySendRedpackResult) xstream - .fromXML(responseContent); + .fromXML(responseContent); this.checkResult(result); return result; } /** * 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) + * * @param packageParams 原始参数 - * @param signKey 加密Key(即 商户Key) + * @param signKey 加密Key(即 商户Key) * @return 签名字符串 */ private String createSign(Map packageParams, String signKey) { @@ -165,7 +197,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { for (String key : sortedMap.keySet()) { String value = packageParams.get(key); if (null != value && !"".equals(value) && !"sign".equals(key) - && !"key".equals(key)) { + && !"key".equals(key)) { toSign.append(key + "=" + value + "&"); } } @@ -237,7 +269,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { @Override public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) - throws WxErrorException { + throws WxErrorException { checkParameters(request); XStream xstream = XStreamInitializer.getInstance(); @@ -249,14 +281,14 @@ public class WxMpPayServiceImpl implements WxMpPayService { request.setNonceStr(System.currentTimeMillis() + ""); String sign = this.createSign(BeanUtils.xmlBean2Map(request), - this.wxMpService.getWxMpConfigStorage().getPartnerKey()); + this.wxMpService.getWxMpConfigStorage().getPartnerKey()); request.setSign(sign); String url = PAY_BASE_URL + "/pay/unifiedorder"; String responseContent = this.executeRequest(url, xstream.toXML(request)); WxPayUnifiedOrderResult result = (WxPayUnifiedOrderResult) xstream - .fromXML(responseContent); + .fromXML(responseContent); this.checkResult(result); return result; } @@ -264,7 +296,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { private void checkParameters(WxPayUnifiedOrderRequest request) throws WxErrorException { BeanUtils.checkRequiredFields(request); - if (! ArrayUtils.contains(TRADE_TYPES, request.getTradeType())) { + if (!ArrayUtils.contains(TRADE_TYPES, request.getTradeType())) { throw new IllegalArgumentException("trade_type目前必须为" + Arrays.toString(TRADE_TYPES) + "其中之一"); } @@ -283,7 +315,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { String prepayId = unifiedOrderResult.getPrepayId(); if (StringUtils.isBlank(prepayId)) { throw new RuntimeException(String.format("Failed to get prepay id due to error code '%s'(%s).", - unifiedOrderResult.getErrCode(), unifiedOrderResult.getErrCodeDes())); + unifiedOrderResult.getErrCode(), unifiedOrderResult.getErrCodeDes())); } Map payInfo = new HashMap<>(); @@ -347,7 +379,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { return result; } - private String executeRequest( String url, String requestStr) throws WxErrorException { + private String executeRequest(String url, String requestStr) throws WxErrorException { HttpPost httpPost = new HttpPost(url); if (this.wxMpService.getHttpProxy() != null) { httpPost.setConfig(RequestConfig.custom().setProxy(this.wxMpService.getHttpProxy()).build()); @@ -358,25 +390,25 @@ public class WxMpPayServiceImpl implements WxMpPayService { try (CloseableHttpResponse response = httpclient.execute(httpPost)) { String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8); - this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}",url, requestStr, result); + this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, requestStr, result); return result; } } catch (IOException e) { this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", url, requestStr, e.getMessage()); throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg(e.getMessage()).build(), e); - }finally { + } finally { httpPost.releaseConnection(); } } - private String executeRequestWithKeyFile( String url, File keyFile, String requestStr, String mchId) throws WxErrorException { + private String executeRequestWithKeyFile(String url, File keyFile, String requestStr, String mchId) throws WxErrorException { try (FileInputStream inputStream = new FileInputStream(keyFile)) { KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(inputStream, mchId.toCharArray()); SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build(); - SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null, - new DefaultHostnameVerifier()); + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, + new DefaultHostnameVerifier()); HttpPost httpPost = new HttpPost(url); if (this.wxMpService.getHttpProxy() != null) { @@ -387,10 +419,10 @@ public class WxMpPayServiceImpl implements WxMpPayService { httpPost.setEntity(new StringEntity(new String(requestStr.getBytes("UTF-8"), "ISO-8859-1"))); try (CloseableHttpResponse response = httpclient.execute(httpPost)) { String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8); - this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}",url, requestStr, result); + this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, requestStr, result); return result; } - }finally { + } finally { httpPost.releaseConnection(); } } catch (Exception e) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRefundQueryRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRefundQueryRequest.java new file mode 100644 index 00000000..373b0917 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRefundQueryRequest.java @@ -0,0 +1,135 @@ +package me.chanjar.weixin.mp.bean.pay.request; + +import com.thoughtworks.xstream.annotations.XStreamAlias; + +/** + *
+ * Created by Binary Wang on 2016-11-24.
+ * @author binarywang(Binary Wang)
+ * 
+ */ +@XStreamAlias("xml") +public class WxPayRefundQueryRequest extends WxPayBaseRequest { + /** + *
+   * 设备号
+   * device_info
+   * 否
+   * String(32)
+   * 013467007045764
+   * 商户自定义的终端设备号,如门店编号、设备的ID等
+   * 
+ */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
+   * 签名类型
+   * sign_type
+   * 否
+   * String(32)
+   * HMAC-SHA256
+   * 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
+   * 
+ */ + @XStreamAlias("sign_type") + private String signType; + + //************以下四选一************ + /** + *
+   * 微信订单号
+   * transaction_id
+   * String(32)
+   * 1217752501201407033233368018
+   * 微信订单号
+   * 
+ */ + @XStreamAlias("transaction_id") + private String transactionId; + + /** + *
+   * 商户订单号
+   * out_trade_no
+   * String(32)
+   * 1217752501201407033233368018
+   * 商户系统内部的订单号
+   * 
+ */ + @XStreamAlias("out_trade_no") + private String outTradeNo; + + /** + *
+   * 商户退款单号
+   * out_refund_no
+   * String(32)
+   * 1217752501201407033233368018
+   * 商户侧传给微信的退款单号
+   * 
+ */ + @XStreamAlias("out_refund_no") + private String outRefundNo; + + /** + *
+   * 微信退款单号
+   * refund_id
+   * String(28)
+   * 1217752501201407033233368018
+   * 微信生成的退款单号,在申请退款接口有返回
+   * 
+ */ + @XStreamAlias("refund_id") + private String refundId; + + public String getDeviceInfo() { + return deviceInfo; + } + + public void setDeviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + } + + public String getSignType() { + return signType; + } + + public void setSignType(String signType) { + this.signType = signType; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + public String getOutTradeNo() { + return outTradeNo; + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public String getOutRefundNo() { + return outRefundNo; + } + + public void setOutRefundNo(String outRefundNo) { + this.outRefundNo = outRefundNo; + } + + public String getRefundId() { + return refundId; + } + + public void setRefundId(String refundId) { + this.refundId = refundId; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResult.java new file mode 100644 index 00000000..efc21139 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResult.java @@ -0,0 +1,488 @@ +package me.chanjar.weixin.mp.bean.pay.result; + +import com.google.common.collect.Lists; +import com.thoughtworks.xstream.annotations.XStreamAlias; + +import java.util.List; + +/** + *
+ * Created by Binary Wang on 2016-11-24.
+ * @author binarywang(Binary Wang)
+ * 
+ */ +@XStreamAlias("xml") +public class WxPayRefundQueryResult extends WxPayBaseResult { + /** + *
+   * 设备号
+   * device_info
+   * 否
+   * String(32)
+   * 013467007045764
+   * 终端设备号
+   */
+  @XStreamAlias("device_info")
+  private String deviceInfo;
+
+  /**
+   * 
+   * 微信订单号
+   * transaction_id
+   * 是
+   * String(32)
+   * 1217752501201407033233368018
+   * 微信订单号
+   */
+  @XStreamAlias("transaction_id")
+  private String transactionId;
+
+  /**
+   * 
+   * 商户订单号
+   * out_trade_no
+   * 是
+   * String(32)
+   * 1217752501201407033233368018
+   * 商户系统内部的订单号
+   */
+  @XStreamAlias("out_trade_no")
+  private String outTradeNo;
+
+  /**
+   * 
+   * 订单金额
+   * total_fee
+   * 是
+   * Int
+   * 100
+   * 订单总金额,单位为分,只能为整数,详见支付金额
+   */
+  @XStreamAlias("total_fee")
+  private Integer totalFee;
+
+  /**
+   * 
+   * 应结订单金额
+   * settlement_total_fee
+   * 否
+   * Int
+   * 100
+   * 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
+   */
+  @XStreamAlias("settlement_total_fee")
+  private Integer settlementTotalFee;
+
+  /**
+   * 
+   * 货币种类
+   * fee_type
+   * 否
+   * String(8)
+   * CNY
+   * 订单金额货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
+   */
+  @XStreamAlias("fee_type")
+  private String feeType;
+
+  /**
+   * 
+   * 现金支付金额
+   * cash_fee
+   * 是
+   * Int
+   * 100
+   * 现金支付金额,单位为分,只能为整数,详见支付金额
+   */
+  @XStreamAlias("cash_fee")
+  private Integer cashFee;
+
+  /**
+   * 
+   * 退款笔数
+   * refund_count
+   * 是
+   * Int
+   * 1
+   * 退款记录数
+   */
+  @XStreamAlias("refund_count")
+  private Integer refundCount;
+
+  private List refundRecords;
+
+  public String getDeviceInfo() {
+    return deviceInfo;
+  }
+
+  public void setDeviceInfo(String deviceInfo) {
+    this.deviceInfo = deviceInfo;
+  }
+
+  public String getTransactionId() {
+    return transactionId;
+  }
+
+  public void setTransactionId(String transactionId) {
+    this.transactionId = transactionId;
+  }
+
+  public String getOutTradeNo() {
+    return outTradeNo;
+  }
+
+  public void setOutTradeNo(String outTradeNo) {
+    this.outTradeNo = outTradeNo;
+  }
+
+  public Integer getTotalFee() {
+    return totalFee;
+  }
+
+  public void setTotalFee(Integer totalFee) {
+    this.totalFee = totalFee;
+  }
+
+  public Integer getSettlementTotalFee() {
+    return settlementTotalFee;
+  }
+
+  public void setSettlementTotalFee(Integer settlementTotalFee) {
+    this.settlementTotalFee = settlementTotalFee;
+  }
+
+  public String getFeeType() {
+    return feeType;
+  }
+
+  public void setFeeType(String feeType) {
+    this.feeType = feeType;
+  }
+
+  public Integer getCashFee() {
+    return cashFee;
+  }
+
+  public void setCashFee(Integer cashFee) {
+    this.cashFee = cashFee;
+  }
+
+  public Integer getRefundCount() {
+    return refundCount;
+  }
+
+  public void setRefundCount(Integer refundCount) {
+    this.refundCount = refundCount;
+  }
+
+  public List getRefundRecords() {
+    return refundRecords;
+  }
+
+  public void setRefundRecords(List refundRecords) {
+    this.refundRecords = refundRecords;
+  }
+
+  public static class RefundRecord {
+    /**
+     * 
+     * 商户退款单号
+     * out_refund_no_$n
+     * 是
+     * String(32)
+     * 1217752501201407033233368018
+     * 商户退款单号
+     * 
+ */ + @XStreamAlias("out_refund_no") + private String outRefundNo; + + /** + *
+     * 微信退款单号
+     * refund_id_$n
+     * 是
+     * String(28)
+     * 1217752501201407033233368018
+     * 微信退款单号
+     * 
+ */ + @XStreamAlias("refund_id") + private String refundId; + + /** + *
+     * 退款渠道
+     * refund_channel_$n
+     * 否
+     * String(16)
+     * ORIGINAL
+     * ORIGINAL—原路退款 BALANCE—退回到余额
+     * 
+ */ + @XStreamAlias("refund_channel") + private String refundChannel; + + /** + *
+     * 申请退款金额
+     * refund_fee_$n
+     * 是
+     * Int
+     * 100
+     * 退款总金额,单位为分,可以做部分退款
+     * 
+ */ + @XStreamAlias("refund_fee") + private String refundFee; + + /** + *
+     * 退款金额
+     * settlement_refund_fee_$n
+     * 否
+     * Int
+     * 100
+     * 退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额
+     * 
+ */ + @XStreamAlias("settlement_refund_fee") + private String settlementRefundFee; + + /** + *
+     * 退款资金来源
+     * refund_account
+     * 否
+     * String(30)
+     * REFUND_SOURCE_RECHARGE_FUNDS
+     * REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款/基本账户, REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款
+     * 
+ */ + @XStreamAlias("refund_account") + private String refundAccount; + + /** + *
+     * 代金券类型
+     * coupon_type_$n
+     * 否
+     * Int
+     * CASH
+     * CASH--充值代金券 , NO_CASH---非充值代金券。订单使用代金券时有返回(取值:CASH、NO_CASH)。$n为下标,从0开始编号,举例:coupon_type_$0
+     * 
+ */ + @XStreamAlias("coupon_type") + private String couponType; + + /** + *
+     * 代金券退款金额
+     * coupon_refund_fee_$n
+     * 否
+     * Int
+     * 100
+     * 代金券退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠
+     * 
+ */ + @XStreamAlias("coupon_refund_fee") + private String couponRefundFee; + + /** + *
+     * 退款代金券使用数量
+     * coupon_refund_count_$n
+     * 否
+     * Int
+     * 1
+     * 退款代金券使用数量 ,$n为下标,从0开始编号
+     * 
+ */ + @XStreamAlias("coupon_refund_count") + private String couponRefundCount; + + private List refundCoupons; + + /** + *
+     * 退款状态
+     * refund_status_$n
+     * 是
+     * String(16)
+     * SUCCESS
+     * 退款状态:
+     *  SUCCESS—退款成功,
+     *  FAIL—退款失败,
+     *  PROCESSING—退款处理中,
+     *  CHANGE—转入代发,
+     * 退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,资金回流到商户的现金帐号,需要商户人工干预,通过线下或者财付通转账的方式进行退款。
+     * 
+ */ + @XStreamAlias("refund_status") + private String refundStatus; + /** + *
+     * 退款入账账户
+     * refund_recv_accout_$n
+     * 是
+     * String(64)
+     * 招商银行信用卡0403
+     * 取当前退款单的退款入账方,1)退回银行卡:{银行名称}{卡类型}{卡尾号},2)退回支付用户零钱:支付用户零钱
+     * 
+ */ + @XStreamAlias("refund_recv_accout") + private String refundRecvAccout; + + public String getOutRefundNo() { + return outRefundNo; + } + + public void setOutRefundNo(String outRefundNo) { + this.outRefundNo = outRefundNo; + } + + public String getRefundId() { + return refundId; + } + + public void setRefundId(String refundId) { + this.refundId = refundId; + } + + public String getRefundChannel() { + return refundChannel; + } + + public void setRefundChannel(String refundChannel) { + this.refundChannel = refundChannel; + } + + public String getRefundFee() { + return refundFee; + } + + public void setRefundFee(String refundFee) { + this.refundFee = refundFee; + } + + public String getSettlementRefundFee() { + return settlementRefundFee; + } + + public void setSettlementRefundFee(String settlementRefundFee) { + this.settlementRefundFee = settlementRefundFee; + } + + public String getRefundAccount() { + return refundAccount; + } + + public void setRefundAccount(String refundAccount) { + this.refundAccount = refundAccount; + } + + public String getCouponType() { + return couponType; + } + + public void setCouponType(String couponType) { + this.couponType = couponType; + } + + public String getCouponRefundFee() { + return couponRefundFee; + } + + public void setCouponRefundFee(String couponRefundFee) { + this.couponRefundFee = couponRefundFee; + } + + public String getCouponRefundCount() { + return couponRefundCount; + } + + public void setCouponRefundCount(String couponRefundCount) { + this.couponRefundCount = couponRefundCount; + } + + public List getRefundCoupons() { + return refundCoupons; + } + + public void setRefundCoupons(List refundCoupons) { + this.refundCoupons = refundCoupons; + } + + public String getRefundStatus() { + return refundStatus; + } + + public void setRefundStatus(String refundStatus) { + this.refundStatus = refundStatus; + } + + public String getRefundRecvAccout() { + return refundRecvAccout; + } + + public void setRefundRecvAccout(String refundRecvAccout) { + this.refundRecvAccout = refundRecvAccout; + } + + public static class RefundCoupon { + /** + *
+       * 退款代金券批次ID
+       * coupon_refund_batch_id_$n_$m
+       * 否
+       * String(20)
+       * 100
+       * 退款代金券批次ID ,$n为下标,$m为下标,从0开始编号
+       * 
+ */ + @XStreamAlias("coupon_refund_batch_id") + private String couponRefundBatchId; + + /** + *
+       * 退款代金券ID
+       * coupon_refund_id_$n_$m
+       * 否
+       * String(20)
+       * 10000
+       * 退款代金券ID, $n为下标,$m为下标,从0开始编号
+       * 
+ */ + @XStreamAlias("coupon_refund_id") + private String couponRefundId; + + /** + *
+       * 单个退款代金券支付金额
+       * coupon_refund_fee_$n_$m
+       * 否
+       * Int
+       * 100
+       * 单个退款代金券支付金额, $n为下标,$m为下标,从0开始编号
+       * 
+ */ + @XStreamAlias("coupon_refund_fee") + private String couponRefundFee; + + public RefundCoupon(String couponRefundBatchId, String couponRefundId, String couponRefundFee) { + this.couponRefundBatchId = couponRefundBatchId; + this.couponRefundId = couponRefundId; + this.couponRefundFee = couponRefundFee; + } + } + + } + + public void composeRefundRecords(String xmlString){ + if(this.refundCount != null && this.refundCount > 0 ){ + this.refundRecords = Lists.newArrayList(); + //TODO 暂时待实现 + } + } +} + diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java index b4e22f11..5f155862 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java @@ -9,6 +9,7 @@ import me.chanjar.weixin.mp.bean.pay.request.WxEntPayRequest; import me.chanjar.weixin.mp.bean.pay.request.WxPayRefundRequest; import me.chanjar.weixin.mp.bean.pay.request.WxPaySendRedpackRequest; import me.chanjar.weixin.mp.bean.pay.request.WxPayUnifiedOrderRequest; +import me.chanjar.weixin.mp.bean.pay.result.WxPayRefundQueryResult; import me.chanjar.weixin.mp.bean.pay.result.WxPayRefundResult; import me.chanjar.weixin.mp.bean.pay.result.WxPaySendRedpackResult; import me.chanjar.weixin.mp.bean.pay.result.WxPayUnifiedOrderResult; @@ -20,6 +21,7 @@ import java.io.File; /** * 测试支付相关接口 * Created by Binary Wang on 2016/7/28. + * * @author binarywang (https://github.com/binarywang) */ @Test @@ -46,6 +48,20 @@ public class WxMpPayServiceImplTest { System.err.println(result); } + @Test + public void testRefundQuery() throws Exception { + WxPayRefundQueryResult result = this.wxService.getPayService().refundQuery("1", "", "", ""); + result = this.wxService.getPayService().refundQuery("", "2", "", ""); + System.err.println(result); + result = this.wxService.getPayService().refundQuery("", "", "3", ""); + System.err.println(result); + result = this.wxService.getPayService().refundQuery("", "", "", "4"); + System.err.println(result); + //测试四个参数都填的情况,应该报异常的 + result = this.wxService.getPayService().refundQuery("1", "2", "3", "4"); + System.err.println(result); + } + @Test public void testCheckJSSDKCallbackDataSignature() throws Exception { @@ -58,7 +74,7 @@ public class WxMpPayServiceImplTest { request.setClientIp("aaa"); request.setMchBillno("aaaa"); request - .setReOpenid(((WxXmlMpInMemoryConfigStorage) this.wxService.getWxMpConfigStorage()).getOpenid()); + .setReOpenid(((WxXmlMpInMemoryConfigStorage) this.wxService.getWxMpConfigStorage()).getOpenid()); File keyFile = new File("E:\\dlt.p12"); WxPaySendRedpackResult redpackResult = this.wxService.getPayService().sendRedpack(request, keyFile); System.err.println(redpackResult); @@ -70,9 +86,9 @@ public class WxMpPayServiceImplTest { @Test public void testUnifiedOrder() throws WxErrorException { WxPayUnifiedOrderResult result = this.wxService.getPayService() - .unifiedOrder(WxPayUnifiedOrderRequest.builder().body("1111111") - .totalFee(1).spbillCreateIp("111111").notifyURL("111111") - .tradeType("JSAPI").openid("122").outTradeNo("111111").build()); + .unifiedOrder(WxPayUnifiedOrderRequest.builder().body("1111111") + .totalFee(1).spbillCreateIp("111111").notifyURL("111111") + .tradeType("JSAPI").openid("122").outTradeNo("111111").build()); System.err.println(result); }