From 3f4cdb7bf5568de7226e22610571cfb6d2e3ba0c Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Fri, 24 Mar 2017 11:11:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=92=A4=E9=94=80=E8=AE=A2=E5=8D=95API?= =?UTF-8?q?=EF=BC=8C=E5=B9=B6=E9=87=8D=E6=9E=84=E7=9B=B8=E5=85=B3=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=EF=BC=8C=E7=AE=80=E5=8C=96=E5=BC=80=E5=8F=91=20#101?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bean/request/WxEntPayQueryRequest.java | 5 + .../wxpay/bean/request/WxEntPayRequest.java | 5 + .../wxpay/bean/request/WxPayBaseRequest.java | 58 +++ .../request/WxPayDownloadBillRequest.java | 18 + .../bean/request/WxPayMicropayRequest.java | 5 + .../bean/request/WxPayOrderCloseRequest.java | 4 + .../bean/request/WxPayOrderQueryRequest.java | 9 + .../request/WxPayOrderReverseRequest.java | 163 +++++++++ .../request/WxPayRedpackQueryRequest.java | 5 + .../bean/request/WxPayRefundQueryRequest.java | 12 + .../bean/request/WxPayRefundRequest.java | 34 +- .../bean/request/WxPayReportRequest.java | 5 + .../bean/request/WxPaySendRedpackRequest.java | 5 + .../request/WxPayUnifiedOrderRequest.java | 36 ++ .../wxpay/bean/result/WxPayBaseResult.java | 44 +++ .../bean/result/WxPayOrderReverseResult.java | 34 ++ .../wxpay/service/WxPayService.java | 94 +---- .../wxpay/service/impl/WxPayServiceImpl.java | 329 ++++-------------- .../binarywang/wxpay/util/SignUtils.java | 84 +++++ 19 files changed, 607 insertions(+), 342 deletions(-) create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseResult.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayQueryRequest.java index 30ff0a87..f9135c34 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayQueryRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayQueryRequest.java @@ -43,6 +43,11 @@ public class WxEntPayQueryRequest extends WxPayBaseRequest { this.partnerTradeNo = partnerTradeNo; } + @Override + protected void checkConstraints() { + //do nothing + } + @Override public String toString() { return ToStringUtils.toSimpleString(this); diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayRequest.java index 56783883..fd43fb1a 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayRequest.java @@ -154,6 +154,11 @@ public class WxEntPayRequest extends WxPayBaseRequest { private String spbillCreateIp; + @Override + protected void checkConstraints() { + + } + @Override public String getAppid() { return this.mchAppid; diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java index 2e34f0a2..4d5cbbaa 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java @@ -1,9 +1,14 @@ package com.github.binarywang.wxpay.bean.request; +import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.util.SignUtils; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.util.BeanUtils; import me.chanjar.weixin.common.util.ToStringUtils; import me.chanjar.weixin.common.util.xml.XStreamInitializer; +import org.apache.commons.lang3.StringUtils; import java.math.BigDecimal; @@ -23,6 +28,22 @@ import java.math.BigDecimal; * @author binarywang(Binary Wang) */ public abstract class WxPayBaseRequest { + /** + * 检查请求参数内容,包括必填参数以及特殊约束 + */ + protected void checkFields() throws WxErrorException { + //check required fields + BeanUtils.checkRequiredFields(this); + + //check other parameters + this.checkConstraints(); + } + + /** + * 检查约束情况 + */ + protected abstract void checkConstraints(); + /** *
    * 公众账号ID
@@ -180,4 +201,41 @@ public abstract class WxPayBaseRequest {
     xstream.processAnnotations(this.getClass());
     return xstream.toXML(this);
   }
+
+  /**
+   * 
+   * 检查参数,并设置签名
+   * 1、检查参数(注意:子类实现需要检查参数的而外功能时,请在调用父类的方法前进行相应判断)
+   * 2、补充系统参数,如果未传入则从配置里读取
+   * 3、生成签名,并设置进去
+   * 
+ * @param config 支付配置对象,用于读取相应系统配置信息 + */ + public void checkAndSign(WxPayConfig config) throws WxErrorException { + this.checkFields(); + + if (StringUtils.isBlank(getAppid())) { + this.setAppid(config.getAppId()); + } + + if (StringUtils.isBlank(getMchId())) { + this.setMchId(config.getMchId()); + } + + if (StringUtils.isBlank(getSubAppId())) { + this.setSubAppId(config.getSubAppId()); + } + + if (StringUtils.isBlank(getSubMchId())) { + this. setSubMchId(config.getSubMchId()); + } + + if (StringUtils.isBlank(getNonceStr())) { + this.setNonceStr(String.valueOf(System.currentTimeMillis())); + } + + //设置签名字段的值 + this.setSign(SignUtils.createSign(this, config.getMchKey())); + } + } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java index fac28aba..ed02060f 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java @@ -2,6 +2,10 @@ package com.github.binarywang.wxpay.bean.request; import com.thoughtworks.xstream.annotations.XStreamAlias; import me.chanjar.weixin.common.annotation.Required; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; /** *
@@ -12,6 +16,8 @@ import me.chanjar.weixin.common.annotation.Required;
  */
 @XStreamAlias("xml")
 public class WxPayDownloadBillRequest extends WxPayBaseRequest {
+  private static final String[] BILL_TYPE = new String[]{"ALL", "REFUND", "SUCCESS"};
+
   /**
    * 
    * 设备号
@@ -120,4 +126,16 @@ public class WxPayDownloadBillRequest extends WxPayBaseRequest {
   public void setTarType(String tarType) {
     this.tarType = tarType;
   }
+
+  @Override
+  protected void checkConstraints() {
+    if (StringUtils.isNotBlank(this.getTarType()) && !"GZIP".equals(this.getTarType())) {
+      throw new IllegalArgumentException("tar_type值如果存在,只能为GZIP");
+    }
+
+    if (!ArrayUtils.contains(BILL_TYPE, this.getBillType())) {
+      throw new IllegalArgumentException(String.format("bill_tpye目前必须为%s其中之一,实际值:%s",
+        Arrays.toString(BILL_TYPE), this.getBillType()));
+    }
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java
index 10aac649..a0253378 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java
@@ -261,6 +261,11 @@ public class WxPayMicropayRequest extends WxPayBaseRequest {
     this.authCode = authCode;
   }
 
+  @Override
+  protected void checkConstraints() {
+    //do nothing
+  }
+
   public static final class Builder {
     private String signType;
     private String body;
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseRequest.java
index 43206e36..21968ac9 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseRequest.java
@@ -33,4 +33,8 @@ public class WxPayOrderCloseRequest extends WxPayBaseRequest {
     this.outTradeNo = outTradeNo;
   }
 
+  @Override
+  protected void checkConstraints() {
+
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java
index a0982764..49972799 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java
@@ -1,6 +1,7 @@
 package com.github.binarywang.wxpay.bean.request;
 
 import com.thoughtworks.xstream.annotations.XStreamAlias;
+import org.apache.commons.lang3.StringUtils;
 
 /**
  * 
@@ -61,4 +62,12 @@ public class WxPayOrderQueryRequest extends WxPayBaseRequest {
   public void setOutTradeNo(String outTradeNo) {
     this.outTradeNo = outTradeNo;
   }
+
+  @Override
+  protected void checkConstraints() {
+    if ((StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo)) ||
+      (StringUtils.isNotBlank(transactionId) && StringUtils.isNotBlank(outTradeNo))) {
+      throw new IllegalArgumentException("transaction_id 和 out_trade_no 不能同时存在或同时为空,必须二选一");
+    }
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java
new file mode 100644
index 00000000..6aef2aa0
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java
@@ -0,0 +1,163 @@
+package com.github.binarywang.wxpay.bean.request;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * 
+ * 撤销订单请求类
+ * Created by Binary Wang on 2017-3-23.
+ * @author binarywang(Binary Wang)
+ * 
+ */ +@XStreamAlias("xml") +public class WxPayOrderReverseRequest extends WxPayBaseRequest { + + /** + *
+   * 微信订单号
+   * transaction_id
+   * String(28)
+   * 1217752501201400000000000000
+   * 微信的订单号,优先使用
+   * 
+ */ + @XStreamAlias("transaction_id") + private String transactionId; + + /** + *
+   * 商户订单号
+   * out_trade_no
+   * String(32)
+   * 1217752501201400000000000000
+   * 商户系统内部的订单号
+   * transaction_id、out_trade_no二选一,如果同时存在优先级:transaction_id> out_trade_no
+   * 
+ */ + @XStreamAlias("out_trade_no") + private String outTradeNo; + + /** + *
+   * 签名类型
+   * sign_type
+   * 否
+   * String(32)
+   * HMAC-SHA256
+   * 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
+   **/
+  @XStreamAlias("sign_type")
+  private String signType;
+
+  private WxPayOrderReverseRequest(Builder builder) {
+    setTransactionId(builder.transactionId);
+    setAppid(builder.appid);
+    setOutTradeNo(builder.outTradeNo);
+    setMchId(builder.mchId);
+    setSignType(builder.signType);
+    setSubAppId(builder.subAppId);
+    setSubMchId(builder.subMchId);
+    setNonceStr(builder.nonceStr);
+    setSign(builder.sign);
+  }
+
+  public static Builder newBuilder() {
+    return new Builder();
+  }
+
+  public String getTransactionId() {
+    return this.transactionId;
+  }
+
+  public void setTransactionId(String transactionId) {
+    this.transactionId = transactionId;
+  }
+
+  public String getOutTradeNo() {
+    return this.outTradeNo;
+  }
+
+  public void setOutTradeNo(String outTradeNo) {
+    this.outTradeNo = outTradeNo;
+  }
+
+  public String getSignType() {
+    return this.signType;
+  }
+
+  public void setSignType(String signType) {
+    this.signType = signType;
+  }
+
+  @Override
+  protected void checkConstraints() {
+    if (StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo)) {
+      throw new IllegalArgumentException("transaction_id 和 out_trade_no不能同时为空!");
+    }
+  }
+
+  public static final class Builder {
+    private String transactionId;
+    private String appid;
+    private String outTradeNo;
+    private String mchId;
+    private String signType;
+    private String subAppId;
+    private String subMchId;
+    private String nonceStr;
+    private String sign;
+
+    private Builder() {
+    }
+
+    public Builder transactionId(String transactionId) {
+      this.transactionId = transactionId;
+      return this;
+    }
+
+    public Builder appid(String appid) {
+      this.appid = appid;
+      return this;
+    }
+
+    public Builder outTradeNo(String outTradeNo) {
+      this.outTradeNo = outTradeNo;
+      return this;
+    }
+
+    public Builder mchId(String mchId) {
+      this.mchId = mchId;
+      return this;
+    }
+
+    public Builder signType(String signType) {
+      this.signType = signType;
+      return this;
+    }
+
+    public Builder subAppId(String subAppId) {
+      this.subAppId = subAppId;
+      return this;
+    }
+
+    public Builder subMchId(String subMchId) {
+      this.subMchId = subMchId;
+      return this;
+    }
+
+    public Builder nonceStr(String nonceStr) {
+      this.nonceStr = nonceStr;
+      return this;
+    }
+
+    public Builder sign(String sign) {
+      this.sign = sign;
+      return this;
+    }
+
+    public WxPayOrderReverseRequest build() {
+      return new WxPayOrderReverseRequest(this);
+    }
+  }
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRedpackQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRedpackQueryRequest.java
index ae7ec352..191db469 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRedpackQueryRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRedpackQueryRequest.java
@@ -54,4 +54,9 @@ public class WxPayRedpackQueryRequest extends WxPayBaseRequest {
   public void setMchBillNo(String mchBillNo) {
     this.mchBillNo = mchBillNo;
   }
+
+  @Override
+  protected void checkConstraints() {
+
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java
index 06687c36..1efa23db 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java
@@ -1,6 +1,7 @@
 package com.github.binarywang.wxpay.bean.request;
 
 import com.thoughtworks.xstream.annotations.XStreamAlias;
+import org.apache.commons.lang3.StringUtils;
 
 /**
  * 
@@ -132,4 +133,15 @@ public class WxPayRefundQueryRequest extends WxPayBaseRequest {
   public void setRefundId(String refundId) {
     this.refundId = refundId;
   }
+
+  @Override
+  protected void checkConstraints() {
+    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 必须四选一");
+    }
+
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java
index 36fb76d6..c7fc8c26 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java
@@ -1,7 +1,13 @@
 package com.github.binarywang.wxpay.bean.request;
 
+import com.github.binarywang.wxpay.config.WxPayConfig;
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import me.chanjar.weixin.common.annotation.Required;
+import me.chanjar.weixin.common.exception.WxErrorException;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Arrays;
 
 /**
  * 
@@ -13,13 +19,25 @@ import me.chanjar.weixin.common.annotation.Required;
  * 
  • 类型 *
  • 示例值 *
  • 描述 + * Created by Binary Wang on 2016-10-08. *
  • * * @author binarywang(Binary Wang) - * Created by Binary Wang on 2016-10-08. */ @XStreamAlias("xml") public class WxPayRefundRequest extends WxPayBaseRequest { + private static final String[] REFUND_ACCOUNT = new String[]{"REFUND_SOURCE_RECHARGE_FUNDS", + "REFUND_SOURCE_UNSETTLED_FUNDS"}; + + @Override + public void checkAndSign(WxPayConfig config) throws WxErrorException { + if (StringUtils.isBlank(this.getOpUserId())) { + this.setOpUserId(config.getMchId()); + } + + super.checkAndSign(config); + } + /** *
        * 设备号
    @@ -240,6 +258,20 @@ public class WxPayRefundRequest extends WxPayBaseRequest {
         this.refundAccount = refundAccount;
       }
     
    +  @Override
    +  protected void checkConstraints() {
    +    if (StringUtils.isNotBlank(this.getRefundAccount())) {
    +      if (!ArrayUtils.contains(REFUND_ACCOUNT, this.getRefundAccount())) {
    +        throw new IllegalArgumentException(String.format("refund_account目前必须为%s其中之一,实际值:%s",
    +          Arrays.toString(REFUND_ACCOUNT), this.getRefundAccount()));
    +      }
    +    }
    +
    +    if (StringUtils.isBlank(this.getOutTradeNo()) && StringUtils.isBlank(this.getTransactionId())) {
    +      throw new IllegalArgumentException("transaction_id 和 out_trade_no 不能同时为空,必须提供一个");
    +    }
    +  }
    +
       public static final class Builder {
         private String deviceInfo;
         private String appid;
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java
    index 6795536f..8b3f88b9 100644
    --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java
    @@ -270,4 +270,9 @@ public class WxPayReportRequest extends WxPayBaseRequest {
       public void setTime(String time) {
         this.time = time;
       }
    +
    +  @Override
    +  protected void checkConstraints() {
    +    //do nothing
    +  }
     }
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.java
    index b49253a4..f7082fc3 100644
    --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.java
    @@ -217,6 +217,11 @@ public class WxPaySendRedpackRequest extends WxPayBaseRequest {
         this.remark = remark;
       }
     
    +  @Override
    +  protected void checkConstraints() {
    +
    +  }
    +
       @Override
       public String getAppid() {
         return this.wxAppid;
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java
    index 4e1d5a5d..48b059d3 100644
    --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java
    @@ -1,7 +1,13 @@
     package com.github.binarywang.wxpay.bean.request;
     
    +import com.github.binarywang.wxpay.config.WxPayConfig;
     import com.thoughtworks.xstream.annotations.XStreamAlias;
     import me.chanjar.weixin.common.annotation.Required;
    +import me.chanjar.weixin.common.exception.WxErrorException;
    +import org.apache.commons.lang3.ArrayUtils;
    +import org.apache.commons.lang3.StringUtils;
    +
    +import java.util.Arrays;
     
     /**
      * 
    @@ -21,6 +27,7 @@ import me.chanjar.weixin.common.annotation.Required;
      */
     @XStreamAlias("xml")
     public class WxPayUnifiedOrderRequest extends WxPayBaseRequest {
    +  private static final String[] TRADE_TYPES = new String[]{"JSAPI", "NATIVE", "APP"};
     
       /**
        * 
    @@ -409,6 +416,35 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest {
         this.openid = openid;
       }
     
    +  @Override
    +  protected void checkConstraints() {
    +    if (!ArrayUtils.contains(TRADE_TYPES, this.getTradeType())) {
    +      throw new IllegalArgumentException(String.format("trade_type目前必须为%s其中之一,实际值:%s",
    +        Arrays.toString(TRADE_TYPES), this.getTradeType()));
    +    }
    +
    +    if ("JSAPI".equals(this.getTradeType()) && this.getOpenid() == null) {
    +      throw new IllegalArgumentException("当 trade_type是'JSAPI'时未指定openid");
    +    }
    +
    +    if ("NATIVE".equals(this.getTradeType()) && this.getProductId() == null) {
    +      throw new IllegalArgumentException("当 trade_type是'NATIVE'时未指定product_id");
    +    }
    +  }
    +
    +  @Override
    +  public void checkAndSign(WxPayConfig config) throws WxErrorException {
    +    if (StringUtils.isBlank(this.getNotifyURL())) {
    +      this.setNotifyURL(config.getNotifyUrl());
    +    }
    +
    +    if (StringUtils.isBlank(this.getTradeType())) {
    +      this.setTradeType(config.getTradeType());
    +    }
    +
    +    super.checkAndSign(config);
    +  }
    +
       public static class WxUnifiedOrderRequestBuilder {
         private String appid;
         private String mchId;
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java
    index fed9544c..899703c8 100644
    --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java
    @@ -1,9 +1,13 @@
     package com.github.binarywang.wxpay.bean.result;
     
    +import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
    +import com.github.binarywang.wxpay.util.SignUtils;
     import com.google.common.base.Joiner;
     import com.google.common.collect.Maps;
     import com.thoughtworks.xstream.XStream;
     import com.thoughtworks.xstream.annotations.XStreamAlias;
    +import me.chanjar.weixin.common.bean.result.WxError;
    +import me.chanjar.weixin.common.exception.WxErrorException;
     import me.chanjar.weixin.common.util.ToStringUtils;
     import me.chanjar.weixin.common.util.xml.XStreamInitializer;
     import org.apache.commons.lang3.StringUtils;
    @@ -300,4 +304,44 @@ public abstract class WxPayBaseResult {
     
         return Integer.valueOf(result);
       }
    +
    +  /**
    +   * 校验返回结果签名
    +   */
    +  public void checkResult(WxPayServiceImpl wxPayService) throws WxErrorException {
    +    //校验返回结果签名
    +    Map map = toMap();
    +    if (getSign() != null && !SignUtils.checkSign(map, wxPayService.getConfig().getMchKey())) {
    +      this.getLogger().debug("校验结果签名失败,参数:{}", map);
    +      throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg("参数格式校验错误!").build());
    +    }
    +
    +    //校验结果是否成功
    +    if (!"SUCCESS".equalsIgnoreCase(getReturnCode())
    +      || !"SUCCESS".equalsIgnoreCase(getResultCode())) {
    +      StringBuilder errorMsg = new StringBuilder();
    +      if (getReturnCode() != null) {
    +        errorMsg.append("返回代码:").append(getReturnCode());
    +      }
    +      if (getReturnMsg() != null) {
    +        errorMsg.append(",返回信息:").append(getReturnMsg());
    +      }
    +      if (getResultCode() != null) {
    +        errorMsg.append(",结果代码:").append(getResultCode());
    +      }
    +      if (getErrCode() != null) {
    +        errorMsg.append(",错误代码:").append(getErrCode());
    +      }
    +      if (getErrCodeDes() != null) {
    +        errorMsg.append(",错误详情:").append(getErrCodeDes());
    +      }
    +
    +      WxError error = WxError.newBuilder()
    +        .setErrorCode(-1)
    +        .setErrorMsg(errorMsg.toString())
    +        .build();
    +      this.getLogger().error("\n结果业务代码异常,返回結果:{},\n{}", map, error);
    +      throw new WxErrorException(error);
    +    }
    +  }
     }
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseResult.java
    new file mode 100644
    index 00000000..7732a962
    --- /dev/null
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseResult.java
    @@ -0,0 +1,34 @@
    +package com.github.binarywang.wxpay.bean.result;
    +
    +import com.thoughtworks.xstream.annotations.XStreamAlias;
    +
    +/**
    + * 
    + * 撤销订单响应结果类
    + * Created by Binary Wang on 2017-3-23.
    + * @author binarywang(Binary Wang)
    + * 
    + */ +public class WxPayOrderReverseResult extends WxPayBaseResult { + + /** + *
    +   * 是否重调
    +   * recall
    +   * 是
    +   * String(1)
    +   * Y
    +   * 是否需要继续调用撤销,Y-需要,N-不需要
    +   * 
    + **/ + @XStreamAlias("recall") + private String isRecall; + + public String getIsRecall() { + return this.isRecall; + } + + public void setIsRecall(String isRecall) { + this.isRecall = isRecall; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java index 0473d9ba..902669e7 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java @@ -116,84 +116,6 @@ public interface WxPayService { */ WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxErrorException; - /** - * 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) - * - * @param xmlbean Bean需要标记有XML注解,默认使用配置中的PartnerKey进行签名 - * @return 签名字符串 - * @see #createSign(Map, String) - * @since 2.5.0 - */ - String createSign(Object xmlbean); - - /** - * 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) - * - * @param xmlbean Bean需要标记有XML注解 - * @param signKey 签名Key - * @return 签名字符串 - * @see #createSign(Map, String) - */ - String createSign(Object xmlbean, String signKey); - - /** - * 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) - * - * @param prams 参数信息,默认使用配置中的PartnerKey进行签名 - * @return 签名字符串 - * @see #createSign(Map, String) - */ - String createSign(Map prams); - - - /** - * 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) - * - * @param prams 参数信息 - * @param signKey 签名Key - * @return 签名字符串 - */ - String createSign(Map prams, String signKey); - - - /** - * 校验签名是否正确,默认使用配置中的PartnerKey进行签名 - * - * @param xmlbean Bean需要标记有XML注解 - * @return true - 签名校验成功,false - 签名校验失败 - * @see #checkSign(Map, String) - */ - boolean checkSign(Object xmlbean); - - /** - * 校验签名是否正确 - * - * @param xmlbean Bean需要标记有XML注解 - * @param signKey 校验的签名Key - * @return true - 签名校验成功,false - 签名校验失败 - * @see #checkSign(Map, String) - */ - boolean checkSign(Object xmlbean, String signKey); - - /** - * 校验签名是否正确,默认使用配置中的PartnerKey进行签名 - * - * @param prams 需要校验的参数Map - * @return true - 签名校验成功,false - 签名校验失败 - * @see #checkSign(Map, String) - */ - boolean checkSign(Map prams); - - /** - * 校验签名是否正确 - * - * @param params 需要校验的参数Map - * @param signKey 校验的签名Key - * @return true - 签名校验成功,false - 签名校验失败 - * @see #checkSign(Map, String) - */ - boolean checkSign(Map params, String signKey); - /** * 发送微信红包给个人用户 *
    @@ -271,6 +193,7 @@ public interface WxPayService {
        * 其中XXXXX为商户需要填写的内容,商户将该链接生成二维码,如需要打印发布二维码,需要采用此格式。商户可调用第三方库生成二维码图片。
        * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_4
        * 
    + * * @param productId 产品Id * @return 生成的二维码URL连接 */ @@ -328,6 +251,7 @@ public interface WxPayService { /** *
        * 提交刷卡支付
    +   * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1
        * 应用场景:
        * 收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,由商户收银台或者商户后台调用该接口发起支付。
        * 提醒1:提交支付请求后微信会同步返回支付结果。当返回结果为“系统错误”时,商户系统等待5秒后调用【查询订单API】,查询支付实际交易结果;当返回结果为“USERPAYING”时,商户系统可设置间隔时间(建议10秒)重新查询支付结果,直到支付成功或超时(建议30秒);
    @@ -337,4 +261,18 @@ public interface WxPayService {
        * 
    */ WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxErrorException; + + /** + *
    +   * 撤销订单API
    +   * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_11&index=3
    +   * 应用场景:
    +   *  支付交易返回失败或支付系统超时,调用该接口撤销交易。如果此订单用户支付失败,微信支付系统会将此订单关闭;如果用户支付成功,微信支付系统会将此订单资金退还给用户。
    +   *  注意:7天以内的交易单可调用撤销,其他正常支付的单如需实现相同功能请调用申请退款API。提交支付交易后调用【查询订单API】,没有明确的支付结果再调用【撤销订单API】。
    +   *  调用支付接口后请勿立即调用撤销订单API,建议支付后至少15s后再调用撤销订单接口。
    +   *  接口链接 :https://api.mch.weixin.qq.com/secapi/pay/reverse
    +   *  是否需要证书:请求需要双向证书。
    +   *
    + */ + WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxErrorException; } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java index 371b50c8..7ed530be 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java @@ -5,14 +5,12 @@ import com.github.binarywang.wxpay.bean.request.*; import com.github.binarywang.wxpay.bean.result.*; import com.github.binarywang.wxpay.config.WxPayConfig; import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.util.SignUtils; import com.google.common.collect.Maps; import jodd.http.HttpRequest; import jodd.http.HttpResponse; import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.common.util.BeanUtils; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.CharEncoding; import org.apache.commons.lang3.StringUtils; import org.apache.http.Consts; @@ -30,7 +28,8 @@ import org.slf4j.LoggerFactory; import javax.net.ssl.SSLContext; import java.io.File; import java.io.UnsupportedEncodingException; -import java.util.*; +import java.util.HashMap; +import java.util.Map; /** * Created by Binary Wang on 2016/7/28. @@ -38,12 +37,7 @@ import java.util.*; * @author binarywang (https://github.com/binarywang) */ public class WxPayServiceImpl implements WxPayService { - 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", "REFUND_SOURCE_UNSETTLED_FUNDS"}; - private static final String[] BILL_TYPE = new String[]{"ALL", "REFUND", "SUCCESS"}; - private final Logger log = LoggerFactory.getLogger(this.getClass()); private WxPayConfig config; @@ -62,126 +56,47 @@ public class WxPayServiceImpl implements WxPayService { if (this.getConfig().useSandboxForWxPay()) { return PAY_BASE_URL + "/sandboxnew"; } + return PAY_BASE_URL; } @Override public WxPayRefundResult refund(WxPayRefundRequest request) throws WxErrorException { - this.initRequest(request); - if (StringUtils.isBlank(request.getOpUserId())) { - request.setOpUserId(this.getConfig().getMchId()); - } - - this.checkParameters(request); - request.setSign(this.createSign(request)); + request.checkAndSign(this.getConfig()); String url = this.getPayBaseUrl() + "/secapi/pay/refund"; String responseContent = this.postWithKey(url, request.toXML()); - WxPayRefundResult result = WxPayRefundResult.fromXML(responseContent, WxPayRefundResult.class); - this.checkResult(result); + WxPayRefundResult result = WxPayBaseResult.fromXML(responseContent, WxPayRefundResult.class); + result.checkResult(this); return result; } @Override - public WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, - String outRefundNo, String refundId) + 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 必须四选一"); - } - WxPayRefundQueryRequest request = new WxPayRefundQueryRequest(); - this.initRequest(request); request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); request.setTransactionId(StringUtils.trimToNull(transactionId)); request.setOutRefundNo(StringUtils.trimToNull(outRefundNo)); request.setRefundId(StringUtils.trimToNull(refundId)); - request.setSign(this.createSign(request)); + + request.checkAndSign(this.getConfig()); String url = this.getPayBaseUrl() + "/pay/refundquery"; String responseContent = this.post(url, request.toXML()); - WxPayRefundQueryResult result = WxPayRefundQueryResult.fromXML(responseContent, WxPayRefundQueryResult.class); + WxPayRefundQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayRefundQueryResult.class); result.composeRefundRecords(); - this.checkResult(result); + result.checkResult(this); return result; } - private void checkResult(WxPayBaseResult result) throws WxErrorException { - //校验返回结果签名 - Map map = result.toMap(); - if (result.getSign() != null && !this.checkSign(map)) { - log.debug("校验结果签名失败,参数:{}", map); - throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg("参数格式校验错误!").build()); - } - - //校验结果是否成功 - if (!"SUCCESS".equalsIgnoreCase(result.getReturnCode()) - || !"SUCCESS".equalsIgnoreCase(result.getResultCode())) { - StringBuilder errorMsg = new StringBuilder(); - if (result.getReturnCode() != null) { - errorMsg.append("返回代码:").append(result.getReturnCode()); - } - if (result.getReturnMsg() != null) { - errorMsg.append(",返回信息:").append(result.getReturnMsg()); - } - if (result.getResultCode() != null) { - errorMsg.append(",结果代码:").append(result.getResultCode()); - } - if (result.getErrCode() != null) { - errorMsg.append(",错误代码:").append(result.getErrCode()); - } - if (result.getErrCodeDes() != null) { - errorMsg.append(",错误详情:").append(result.getErrCodeDes()); - } - - WxError error = WxError.newBuilder() - .setErrorCode(-1) - .setErrorMsg(errorMsg.toString()) - .build(); - log.error("\n结果业务代码异常,返回結果:{},\n{}", map, error); - throw new WxErrorException(error); - } - } - - private void checkParameters(WxPayDownloadBillRequest request) throws WxErrorException { - BeanUtils.checkRequiredFields(request); - - if (StringUtils.isNotBlank(request.getTarType()) && !"GZIP".equals(request.getTarType())) { - throw new IllegalArgumentException("tar_type值如果存在,只能为GZIP"); - } - - if (!ArrayUtils.contains(BILL_TYPE, request.getBillType())) { - throw new IllegalArgumentException("bill_tpye目前必须为" + Arrays.toString(BILL_TYPE) - + "其中之一,实际值:" + request.getBillType()); - } - - } - - private void checkParameters(WxPayRefundRequest request) throws WxErrorException { - BeanUtils.checkRequiredFields(request); - - if (StringUtils.isNotBlank(request.getRefundAccount())) { - if (!ArrayUtils.contains(REFUND_ACCOUNT, request.getRefundAccount())) { - throw new IllegalArgumentException("refund_account目前必须为" + Arrays.toString(REFUND_ACCOUNT) - + "其中之一,实际值:" + request.getRefundAccount()); - } - } - - if (StringUtils.isBlank(request.getOutTradeNo()) && StringUtils.isBlank(request.getTransactionId())) { - throw new IllegalArgumentException("transaction_id 和 out_trade_no 不能同时为空,必须提供一个"); - } - } - @Override public WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxErrorException { try { log.debug("微信支付回调参数详细:{}", xmlData); WxPayOrderNotifyResult result = WxPayOrderNotifyResult.fromXML(xmlData); log.debug("微信支付回调结果对象:{}", result); - this.checkResult(result); + result.checkResult(this); return result; } catch (WxErrorException e) { log.error(e.getMessage(), e); @@ -193,18 +108,17 @@ public class WxPayServiceImpl implements WxPayService { } @Override - public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) - throws WxErrorException { - this.initRequest(request); - request.setSign(this.createSign(request)); + public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throws WxErrorException { + request.checkAndSign(this.getConfig()); String url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendredpack"; if (request.getAmtType() != null) { //裂变红包 url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendgroupredpack"; } + String responseContent = this.postWithKey(url, request.toXML()); - WxPaySendRedpackResult result = WxPaySendRedpackResult.fromXML(responseContent, WxPaySendRedpackResult.class); + WxPaySendRedpackResult result = WxPayBaseResult.fromXML(responseContent, WxPaySendRedpackResult.class); //毋须校验,因为没有返回签名信息 // this.checkResult(result); return result; @@ -215,28 +129,21 @@ public class WxPayServiceImpl implements WxPayService { WxPayRedpackQueryRequest request = new WxPayRedpackQueryRequest(); request.setMchBillNo(mchBillNo); request.setBillType("MCHT"); - initRequest(request); - request.setSign(this.createSign(request)); + request.checkAndSign(this.getConfig()); String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gethbinfo"; String responseContent = this.postWithKey(url, request.toXML()); - WxPayRedpackQueryResult result = WxPayRedpackQueryResult.fromXML(responseContent, WxPayRedpackQueryResult.class); - this.checkResult(result); + WxPayRedpackQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayRedpackQueryResult.class); + result.checkResult(this); return result; } @Override public WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException { - if ((StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo)) || - (StringUtils.isNotBlank(transactionId) && StringUtils.isNotBlank(outTradeNo))) { - throw new IllegalArgumentException("transaction_id 和 out_trade_no 不能同时存在或同时为空,必须二选一"); - } - WxPayOrderQueryRequest request = new WxPayOrderQueryRequest(); request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); request.setTransactionId(StringUtils.trimToNull(transactionId)); - initRequest(request); - request.setSign(this.createSign(request)); + request.checkAndSign(this.getConfig()); String url = this.getPayBaseUrl() + "/pay/orderquery"; String responseContent = this.post(url, request.toXML()); @@ -244,9 +151,9 @@ public class WxPayServiceImpl implements WxPayService { throw new WxErrorException(WxError.newBuilder().setErrorMsg("无响应结果").build()); } - WxPayOrderQueryResult result = WxPayOrderQueryResult.fromXML(responseContent, WxPayOrderQueryResult.class); + WxPayOrderQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderQueryResult.class); result.composeCoupons(); - this.checkResult(result); + result.checkResult(this); return result; } @@ -258,77 +165,27 @@ public class WxPayServiceImpl implements WxPayService { WxPayOrderCloseRequest request = new WxPayOrderCloseRequest(); request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); - this.initRequest(request); - request.setSign(this.createSign(request)); + request.checkAndSign(this.getConfig()); String url = this.getPayBaseUrl() + "/pay/closeorder"; String responseContent = this.post(url, request.toXML()); WxPayOrderCloseResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderCloseResult.class); - this.checkResult(result); + result.checkResult(this); return result; } @Override - public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) - throws WxErrorException { - this.initRequest(request); - if (StringUtils.isBlank(request.getNotifyURL())) { - request.setNotifyURL(getConfig().getNotifyUrl()); - } - - if (StringUtils.isBlank(request.getTradeType())) { - request.setTradeType(getConfig().getTradeType()); - } - - this.checkParameters(request);//校验参数 - request.setSign(this.createSign(request)); + public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws WxErrorException { + request.checkAndSign(this.getConfig()); String url = this.getPayBaseUrl() + "/pay/unifiedorder"; String responseContent = this.post(url, request.toXML()); - WxPayUnifiedOrderResult result = WxPayUnifiedOrderResult.fromXML(responseContent, WxPayUnifiedOrderResult.class); - this.checkResult(result); + WxPayUnifiedOrderResult result = WxPayBaseResult.fromXML(responseContent, WxPayUnifiedOrderResult.class); + result.checkResult(this); return result; } - private void initRequest(WxPayBaseRequest request) { - if (StringUtils.isBlank(request.getAppid())) { - request.setAppid(this.getConfig().getAppId()); - } - - if (StringUtils.isBlank(request.getMchId())) { - request.setMchId(this.getConfig().getMchId()); - } - - if (StringUtils.isBlank(request.getSubAppId())) { - request.setSubAppId(this.getConfig().getSubAppId()); - } - - if (StringUtils.isBlank(request.getSubMchId())) { - request.setSubMchId(this.getConfig().getSubMchId()); - } - - if (StringUtils.isBlank(request.getNonceStr())) { - request.setNonceStr(String.valueOf(System.currentTimeMillis())); - } - } - - private void checkParameters(WxPayUnifiedOrderRequest request) throws WxErrorException { - BeanUtils.checkRequiredFields(request); - - if (!ArrayUtils.contains(TRADE_TYPES, request.getTradeType())) { - throw new IllegalArgumentException("trade_type目前必须为" + Arrays.toString(TRADE_TYPES) + "其中之一,实际值:" + request.getTradeType()); - } - - if ("JSAPI".equals(request.getTradeType()) && request.getOpenid() == null) { - throw new IllegalArgumentException("当 trade_type是'JSAPI'时未指定openid"); - } - - if ("NATIVE".equals(request.getTradeType()) && request.getProductId() == null) { - throw new IllegalArgumentException("当 trade_type是'NATIVE'时未指定product_id"); - } - } - @Override public Map getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException { WxPayUnifiedOrderResult unifiedOrderResult = this.unifiedOrder(request); @@ -349,21 +206,18 @@ public class WxPayServiceImpl implements WxPayService { if ("NATIVE".equals(request.getTradeType())) { payInfo.put("codeUrl", unifiedOrderResult.getCodeURL()); } - payInfo.put("paySign", this.createSign(payInfo)); + payInfo.put("paySign", SignUtils.createSign(payInfo, this.getConfig().getMchKey())); return payInfo; } @Override public WxEntPayResult entPay(WxEntPayRequest request) throws WxErrorException { - this.initRequest(request); - BeanUtils.checkRequiredFields(request); - request.setSign(this.createSign(request)); - + request.checkAndSign(this.getConfig()); String url = this.getPayBaseUrl() + "/mmpaymkttransfers/promotion/transfers"; String responseContent = this.postWithKey(url, request.toXML()); - WxEntPayResult result = WxEntPayResult.fromXML(responseContent, WxEntPayResult.class); - this.checkResult(result); + WxEntPayResult result = WxPayBaseResult.fromXML(responseContent, WxEntPayResult.class); + result.checkResult(this); return result; } @@ -371,25 +225,24 @@ public class WxPayServiceImpl implements WxPayService { public WxEntPayQueryResult queryEntPay(String partnerTradeNo) throws WxErrorException { WxEntPayQueryRequest request = new WxEntPayQueryRequest(); request.setPartnerTradeNo(partnerTradeNo); - this.initRequest(request); - request.setSign(this.createSign(request)); + request.checkAndSign(this.getConfig()); String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gettransferinfo"; String responseContent = this.postWithKey(url, request.toXML()); - WxEntPayQueryResult result = WxEntPayQueryResult.fromXML(responseContent, WxEntPayQueryResult.class); - this.checkResult(result); + WxEntPayQueryResult result = WxPayBaseResult.fromXML(responseContent, WxEntPayQueryResult.class); + result.checkResult(this); return result; } @Override public byte[] createScanPayQrcodeMode1(String productId, File logoFile, Integer sideLength) { - String content = createScanPayQrcodeMode1(productId); - return createQrcode(content, logoFile, sideLength); + String content = this.createScanPayQrcodeMode1(productId); + return this.createQrcode(content, logoFile, sideLength); } @Override - public String createScanPayQrcodeMode1(String productId){ - //weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX + public String createScanPayQrcodeMode1(String productId) { + //weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX StringBuilder codeUrl = new StringBuilder("weixin://wxpay/bizpayurl?"); Map params = Maps.newHashMap(); params.put("appid", this.getConfig().getAppId()); @@ -398,7 +251,7 @@ public class WxPayServiceImpl implements WxPayService { params.put("time_stamp", String.valueOf(System.currentTimeMillis() / 1000));//这里需要秒,10位数字 params.put("nonce_str", String.valueOf(System.currentTimeMillis())); - String sign = this.createSign(params); + String sign = SignUtils.createSign(params, this.getConfig().getMchKey()); params.put("sign", sign); @@ -407,72 +260,80 @@ public class WxPayServiceImpl implements WxPayService { } String content = codeUrl.toString().substring(0, codeUrl.length() - 1); - log.debug("扫码支付模式一生成二维码的URL:{}",content); - return content; + log.debug("扫码支付模式一生成二维码的URL:{}", content); + return content; } @Override public byte[] createScanPayQrcodeMode2(String codeUrl, File logoFile, Integer sideLength) { - return createQrcode(codeUrl, logoFile, sideLength); + return this.createQrcode(codeUrl, logoFile, sideLength); } private byte[] createQrcode(String content, File logoFile, Integer sideLength) { - if (sideLength == null || sideLength < 1) { + if (sideLength == null || sideLength < 1) { return QrcodeUtils.createQrcode(content, logoFile); } + return QrcodeUtils.createQrcode(content, sideLength, logoFile); } public void report(WxPayReportRequest request) throws WxErrorException { - BeanUtils.checkRequiredFields(request); - this.initRequest(request); - request.setSign(this.createSign(request)); + request.checkAndSign(this.getConfig()); String url = this.getPayBaseUrl() + "/payitil/report"; String responseContent = this.post(url, request.toXML()); WxPayCommonResult result = WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class); - this.checkResult(result); + result.checkResult(this); } @Override public File downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxErrorException { WxPayDownloadBillRequest request = new WxPayDownloadBillRequest(); - this.initRequest(request); request.setBillType(billType); request.setBillDate(billDate); request.setTarType(tarType); request.setDeviceInfo(deviceInfo); - this.checkParameters(request); - request.setSign(this.createSign(request)); + + request.checkAndSign(this.getConfig()); String url = this.getPayBaseUrl() + "/pay/downloadbill"; //TODO 返回的内容可能是文件流,也有可能是xml,需要区分对待 String responseContent = this.post(url, request.toXML()); WxPayCommonResult result = WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class); - this.checkResult(result); + result.checkResult(this); //TODO 待实现,暂时无测试帐号,无法调试 return null; } @Override public WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxErrorException { - this.initRequest(request); - BeanUtils.checkRequiredFields(request); - request.setSign(this.createSign(request)); + request.checkAndSign(this.getConfig()); String url = this.getPayBaseUrl() + "/pay/micropay"; String responseContent = this.post(url, request.toXML()); WxPayMicropayResult result = WxPayBaseResult.fromXML(responseContent, WxPayMicropayResult.class); - this.checkResult(result); + result.checkResult(this); + return result; + } + + @Override + public WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxErrorException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/secapi/pay/reverse"; + String responseContent = this.postWithKey(url, request.toXML()); + WxPayOrderReverseResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderReverseResult.class); + result.checkResult(this); return result; } private String post(String url, String xmlParam) { - String requestString = null; + String requestString = xmlParam; try { requestString = new String(xmlParam.getBytes(CharEncoding.UTF_8), CharEncoding.ISO_8859_1); } catch (UnsupportedEncodingException e) { + //实际上不会发生该异常 e.printStackTrace(); } @@ -520,62 +381,4 @@ public class WxPayServiceImpl implements WxPayService { } } - @Override - public String createSign(Object xmlBean) { - return this.createSign(BeanUtils.xmlBean2Map(xmlBean), this.getConfig().getMchKey()); - } - - @Override - public String createSign(Object xmlBean, String signKey) { - return this.createSign(BeanUtils.xmlBean2Map(xmlBean), signKey); - } - - @Override - public String createSign(Map params) { - return this.createSign(params, this.getConfig().getMchKey()); - } - - @Override - public String createSign(Map params, String signKey) { - if (this.getConfig().useSandboxForWxPay()) { - //使用仿真测试环境 - //TODO 目前测试发现,以下两行代码都会出问题,所以暂不建议使用仿真测试环境 - signKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456"; - //return "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456"; - } - - SortedMap sortedMap = new TreeMap<>(params); - - StringBuilder toSign = new StringBuilder(); - for (String key : sortedMap.keySet()) { - String value = params.get(key); - if (StringUtils.isNotEmpty(value) && !"sign".equals(key) && !"key".equals(key)) { - toSign.append(key + "=" + value + "&"); - } - } - - toSign.append("key=" + signKey); - return DigestUtils.md5Hex(toSign.toString()).toUpperCase(); - } - - @Override - public boolean checkSign(Object xmlBean) { - return this.checkSign(BeanUtils.xmlBean2Map(xmlBean), getConfig().getMchKey()); - } - - @Override - public boolean checkSign(Object xmlBean, String signKey) { - return this.checkSign(BeanUtils.xmlBean2Map(xmlBean), signKey); - } - - @Override - public boolean checkSign(Map params) { - return this.checkSign(params, getConfig().getMchKey()); - } - - @Override - public boolean checkSign(Map params, String signKey) { - String sign = this.createSign(params, signKey); - return sign.equals(params.get("sign")); - } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java new file mode 100644 index 00000000..2a6030f3 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java @@ -0,0 +1,84 @@ +package com.github.binarywang.wxpay.util; + +import me.chanjar.weixin.common.util.BeanUtils; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + *
    + * 签名相关工具类
    + * Created by Binary Wang on 2017-3-23.
    + * @author binarywang(Binary Wang)
    + * 
    + */ +public class SignUtils { + + /** + * 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) + * + * @param xmlBean Bean需要标记有XML注解 + * @param signKey 签名Key + * @return 签名字符串 + */ + public static String createSign(Object xmlBean, String signKey) { + return createSign(BeanUtils.xmlBean2Map(xmlBean), signKey); + } + + /** + * 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) + * + * @param params 参数信息 + * @param signKey 签名Key + * @return 签名字符串 + */ + public static String createSign(Map params, String signKey) { +// if (this.getConfig().useSandboxForWxPay()) { +// //使用仿真测试环境 +// //TODO 目前测试发现,以下两行代码都会出问题,所以暂不建议使用仿真测试环境 +// signKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456"; +// //return "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456"; +// } + + SortedMap sortedMap = new TreeMap<>(params); + + StringBuilder toSign = new StringBuilder(); + for (String key : sortedMap.keySet()) { + String value = params.get(key); + if (StringUtils.isNotEmpty(value) && !"sign".equals(key) && !"key".equals(key)) { + toSign.append(key + "=" + value + "&"); + } + } + + toSign.append("key=" + signKey); + return DigestUtils.md5Hex(toSign.toString()).toUpperCase(); + } + + /** + * 校验签名是否正确 + * + * @param xmlBean Bean需要标记有XML注解 + * @param signKey 校验的签名Key + * @return true - 签名校验成功,false - 签名校验失败 + * @see #checkSign(Map, String) + */ + public static boolean checkSign(Object xmlBean, String signKey) { + return checkSign(BeanUtils.xmlBean2Map(xmlBean), signKey); + } + + /** + * 校验签名是否正确 + * + * @param params 需要校验的参数Map + * @param signKey 校验的签名Key + * @return true - 签名校验成功,false - 签名校验失败 + * @see #checkSign(Map, String) + */ + public static boolean checkSign(Map params, String signKey) { + String sign = createSign(params, signKey); + return sign.equals(params.get("sign")); + } +}