diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java index e362e64b2..c91e8ac08 100755 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java @@ -47,7 +47,6 @@ public class WxCryptUtil { protected String appidOrCorpid; public WxCryptUtil() { - super(); } /** @@ -106,7 +105,7 @@ public class WxCryptUtil { private static String genRandomStr() { String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); for (int i = 0; i < 16; i++) { int number = random.nextInt(base.length()); sb.append(base.charAt(number)); @@ -148,12 +147,11 @@ public class WxCryptUtil { String encryptedXml = encrypt(genRandomStr(), plainText); // 生成安全签名 - String timeStamp = Long.toString(System.currentTimeMillis() / 1000l); + String timeStamp = Long.toString(System.currentTimeMillis() / 1000L); String nonce = genRandomStr(); String signature = SHA1.gen(this.token, timeStamp, nonce, encryptedXml); - String result = generateXml(encryptedXml, signature, timeStamp, nonce); - return result; + return generateXml(encryptedXml, signature, timeStamp, nonce); } /** @@ -194,9 +192,7 @@ public class WxCryptUtil { byte[] encrypted = cipher.doFinal(unencrypted); // 使用BASE64对加密后的字符串进行编码 - String base64Encrypted = base64.encodeToString(encrypted); - - return base64Encrypted; + return base64.encodeToString(encrypted); } catch (Exception e) { throw new RuntimeException(e); } @@ -228,8 +224,7 @@ public class WxCryptUtil { } // 解密 - String result = decrypt(cipherText); - return result; + return decrypt(cipherText); } /** diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyResponse.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayNotifyResponse.java similarity index 79% rename from weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyResponse.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayNotifyResponse.java index e36929319..e7a7ea909 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyResponse.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayNotifyResponse.java @@ -1,4 +1,4 @@ -package com.github.binarywang.wxpay.bean; +package com.github.binarywang.wxpay.bean.notify; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; @@ -7,8 +7,11 @@ import com.thoughtworks.xstream.annotations.XStreamOmitField; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; import me.chanjar.weixin.common.util.xml.XStreamInitializer; +/** + * 微信支付订单和退款的异步通知共用的响应类 + */ @XStreamAlias("xml") -public class WxPayOrderNotifyResponse { +public class WxPayNotifyResponse { @XStreamOmitField private transient static final String FAIL = "FAIL"; @XStreamOmitField @@ -21,25 +24,25 @@ public class WxPayOrderNotifyResponse { @XStreamAlias("return_msg") private String returnMsg; - public WxPayOrderNotifyResponse() { + public WxPayNotifyResponse() { super(); } - public WxPayOrderNotifyResponse(String returnCode, String returnMsg) { + public WxPayNotifyResponse(String returnCode, String returnMsg) { super(); this.returnCode = returnCode; this.returnMsg = returnMsg; } public static String fail(String msg) { - WxPayOrderNotifyResponse response = new WxPayOrderNotifyResponse(FAIL, msg); + WxPayNotifyResponse response = new WxPayNotifyResponse(FAIL, msg); XStream xstream = XStreamInitializer.getInstance(); xstream.autodetectAnnotations(true); return xstream.toXML(response); } public static String success(String msg) { - WxPayOrderNotifyResponse response = new WxPayOrderNotifyResponse(SUCCESS, msg); + WxPayNotifyResponse response = new WxPayNotifyResponse(SUCCESS, msg); XStream xstream = XStreamInitializer.getInstance(); xstream.autodetectAnnotations(true); return xstream.toXML(response); diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyCoupon.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyCoupon.java similarity index 96% rename from weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyCoupon.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyCoupon.java index c2b4e22e7..cd948dfba 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyCoupon.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyCoupon.java @@ -1,4 +1,4 @@ -package com.github.binarywang.wxpay.bean; +package com.github.binarywang.wxpay.bean.notify; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResult.java similarity index 98% rename from weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResult.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResult.java index a411cd2c7..54657ac6a 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResult.java @@ -1,6 +1,6 @@ -package com.github.binarywang.wxpay.bean.result; +package com.github.binarywang.wxpay.bean.notify; -import com.github.binarywang.wxpay.bean.WxPayOrderNotifyCoupon; +import com.github.binarywang.wxpay.bean.result.WxPayBaseResult; import com.github.binarywang.wxpay.converter.WxPayOrderNotifyResultConverter; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; @@ -20,7 +20,6 @@ import java.util.Map; */ @XStreamAlias("xml") public class WxPayOrderNotifyResult extends WxPayBaseResult implements Serializable { - private static final long serialVersionUID = 5389718115223345496L; /** diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java new file mode 100644 index 000000000..392c10144 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java @@ -0,0 +1,372 @@ +package com.github.binarywang.wxpay.bean.notify; + +import com.github.binarywang.wxpay.bean.result.WxPayBaseResult; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import me.chanjar.weixin.common.util.ToStringUtils; +import me.chanjar.weixin.common.util.xml.XStreamInitializer; +import org.apache.commons.codec.binary.Base64; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.io.Serializable; +import java.math.BigInteger; +import java.security.MessageDigest; + +/** + *
+ * 退款结果通知对象 + * Created by BinaryWang on 2017/8/27. + *+ * + * @author Binary Wang + */ +@XStreamAlias("xml") +public class WxPayRefundNotifyResult extends WxPayBaseResult implements Serializable { + private static final long serialVersionUID = 4651725860079259186L; + + /** + * 从xml字符串创建bean对象 + * + * @param xmlString xml字符串 + * @param mchKey 商户密钥 + */ + public static WxPayRefundNotifyResult fromXML(String xmlString, String mchKey) throws WxPayException { + WxPayRefundNotifyResult result = WxPayBaseResult.fromXML(xmlString, WxPayRefundNotifyResult.class); + String reqInfoString = result.getReqInfoString(); + try { + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + + final MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(mchKey.getBytes()); + final String keyMd5String = new BigInteger(1, md5.digest()).toString(16).toLowerCase(); + SecretKeySpec key = new SecretKeySpec(keyMd5String.getBytes(), "AES"); + cipher.init(Cipher.DECRYPT_MODE, key); + result.setReqInfo(ReqInfo.fromXML(new String(cipher.doFinal(Base64.decodeBase64(reqInfoString))))); + } catch (Exception e) { + throw new WxPayException("解密退款通知加密信息时出错", e); + } + + return result; + } + + /** + *
+ * 字段名:加密信息 + * 变量名:req_info + * 是否必填:是 + * 类型:String(1024) + * 描述:加密信息请用商户证书与商户秘钥进行解密 + *+ */ + @XStreamAlias("req_info") + private String reqInfoString; + + private ReqInfo reqInfo; + + /** + * 加密信息字段解密后的内容 + */ + @XStreamAlias("root") + public static class ReqInfo { + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + /** + *
+ * 字段名:微信订单号 + * 变量名:transaction_id + * 是否必填:是 + * 类型:String(32) + * 示例值:1217752501201407033233368018 + * 描述:微信订单号 + *+ */ + @XStreamAlias("transaction_id") + private String transactionId; + + /** + *
+ * 字段名:商户订单号 + * 变量名:out_trade_no + * 是否必填:是 + * 类型:String(32) + * 示例值:1217752501201407033233368018 + * 描述:商户系统内部的订单号 + *+ */ + @XStreamAlias("out_trade_no") + private String outTradeNo; + + /** + *
+ * 字段名:微信退款单号 + * 变量名:refund_id + * 是否必填:是 + * 类型:String(28) + * 示例值:1217752501201407033233368018 + * 描述:微信退款单号 + *+ */ + @XStreamAlias("refund_id") + private String refundId; + + /** + *
+ * 字段名:商户退款单号 + * 变量名:out_refund_no + * 是否必填:是 + * 类型:String(64) + * 示例值:1217752501201407033233368018 + * 描述:商户退款单号 + *+ */ + @XStreamAlias("out_refund_no") + private String outRefundNo; + + /** + *
+ * 字段名:订单金额 + * 变量名:total_fee + * 是否必填:是 + * 类型:Int + * 示例值:100 + * 描述:订单总金额,单位为分,只能为整数,详见支付金额 + *+ */ + @XStreamAlias("total_fee") + private Integer totalFee; + + /** + *
+ * 字段名:结订单金额 + * 变量名:settlement_total_fee + * 是否必填:否 + * 类型:Int + * 示例值:100 + * 描述:当该订单有使用非充值券时,返回此字段。应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。 + *+ */ + @XStreamAlias("settlement_total_fee") + private Integer settlementTotalFee; + + /** + *
+ * 字段名:申请退款金额 + * 变量名:refund_fee + * 是否必填:是 + * 类型:Int + * 示例值:100 + * 描述:退款总金额,单位为分 + *+ */ + @XStreamAlias("refund_fee") + private Integer refundFee; + + /** + *
+ * 字段名:退款金额 + * 变量名:settlement_refund_fee + * 是否必填:是 + * 类型:Int + * 示例值:100 + * 描述:退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额 + *+ */ + @XStreamAlias("settlement_refund_fee") + private Integer settlementRefundFee; + + /** + *
+ * 字段名:退款状态 + * 变量名:refund_status + * 是否必填:是 + * 类型:String(16) + * 示例值:SUCCESS + * 描述:SUCCESS-退款成功,CHANGE-退款异常,REFUNDCLOSE—退款关闭 + *+ */ + @XStreamAlias("refund_status") + private String refundStatus; + + /** + *
+ * 字段名:退款成功时间
+ * 变量名:success_time
+ * 是否必填:否
+ * 类型: String(20)
+ * 示例值:20160725152626
+ * 描述:-
+ */
+ @XStreamAlias("success_time")
+ private String successTime;
+
+ /**
+ *
+ * 字段名:退款入账账户
+ * 变量名:refund_recv_accout
+ * 是否必填:是
+ * 类型:String(64)
+ * 示例值:招商银行信用卡0403
+ * 描述:取当前退款单的退款入账方,1)退回银行卡:{银行名称}{卡类型}{卡尾号},2)退回支付用户零钱:支付用户零钱 ,3)退还商户: 商户基本账户,商户结算银行账户,4)退回支付用户零钱通: 支付用户零钱通
+ *
+ */
+ @XStreamAlias("refund_recv_accout")
+ private String refundRecvAccout;
+
+ /**
+ *
+ * 字段名:退款资金来源
+ * 变量名:refund_account
+ * 是否必填:是
+ * 类型:String(30)
+ * 示例值:REFUND_SOURCE_RECHARGE_FUNDS
+ * 描述:REFUND_SOURCE_RECHARGE_FUNDS 可用余额退款/基本账户,REFUND_SOURCE_UNSETTLED_FUNDS 未结算资金退款
+ *
+ */
+ @XStreamAlias("refund_account")
+ private String refundAccount;
+
+ /**
+ *
+ * 字段名:退款发起来源
+ * 变量名:refund_request_source
+ * 是否必填:是
+ * 类型:String(30)
+ * 示例值:API
+ * 描述:API接口,VENDOR_PLATFORM商户平台
+ *
+ */
+ @XStreamAlias("refund_request_source")
+ private String refundRequestSource;
+
+ 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 getRefundId() {
+ return refundId;
+ }
+
+ public void setRefundId(String refundId) {
+ this.refundId = refundId;
+ }
+
+ public String getOutRefundNo() {
+ return outRefundNo;
+ }
+
+ public void setOutRefundNo(String outRefundNo) {
+ this.outRefundNo = outRefundNo;
+ }
+
+ 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 Integer getRefundFee() {
+ return refundFee;
+ }
+
+ public void setRefundFee(Integer refundFee) {
+ this.refundFee = refundFee;
+ }
+
+ public Integer getSettlementRefundFee() {
+ return settlementRefundFee;
+ }
+
+ public void setSettlementRefundFee(Integer settlementRefundFee) {
+ this.settlementRefundFee = settlementRefundFee;
+ }
+
+ public String getRefundStatus() {
+ return refundStatus;
+ }
+
+ public void setRefundStatus(String refundStatus) {
+ this.refundStatus = refundStatus;
+ }
+
+ public String getSuccessTime() {
+ return successTime;
+ }
+
+ public void setSuccessTime(String successTime) {
+ this.successTime = successTime;
+ }
+
+ public String getRefundRecvAccout() {
+ return refundRecvAccout;
+ }
+
+ public void setRefundRecvAccout(String refundRecvAccout) {
+ this.refundRecvAccout = refundRecvAccout;
+ }
+
+ public String getRefundAccount() {
+ return refundAccount;
+ }
+
+ public void setRefundAccount(String refundAccount) {
+ this.refundAccount = refundAccount;
+ }
+
+ public String getRefundRequestSource() {
+ return refundRequestSource;
+ }
+
+ public void setRefundRequestSource(String refundRequestSource) {
+ this.refundRequestSource = refundRequestSource;
+ }
+
+ public static ReqInfo fromXML(String xmlString) {
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.processAnnotations(ReqInfo.class);
+ return (ReqInfo) xstream.fromXML(xmlString);
+ }
+ }
+
+ public String getReqInfoString() {
+ return reqInfoString;
+ }
+
+ public void setReqInfoString(String reqInfoString) {
+ this.reqInfoString = reqInfoString;
+ }
+
+ public ReqInfo getReqInfo() {
+ return reqInfo;
+ }
+
+ public void setReqInfo(ReqInfo reqInfo) {
+ this.reqInfo = reqInfo;
+ }
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayAppOrderResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayAppOrderResult.java
new file mode 100644
index 000000000..6688d602c
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayAppOrderResult.java
@@ -0,0 +1,145 @@
+package com.github.binarywang.wxpay.bean.order;
+
+/**
+ *
+ * APP支付调用统一下单接口后的组装所需参数的实现类
+ * 参考 https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_12
+ * Created by Binary Wang on 2017-9-1.
+ *
+ *
+ * @author Binary Wang
+ */
+public class WxPayAppOrderResult {
+ private String sign;
+ private String prepayId;
+ private String partnerId;
+ private String appId;
+ private String packageValue;
+ private String timeStamp;
+ private String nonceStr;
+
+ public WxPayAppOrderResult() {
+ }
+
+ private WxPayAppOrderResult(Builder builder) {
+ setSign(builder.sign);
+ setPrepayId(builder.prepayId);
+ setPartnerId(builder.partnerId);
+ setAppId(builder.appId);
+ setPackageValue(builder.packageValue);
+ setTimeStamp(builder.timeStamp);
+ setNonceStr(builder.nonceStr);
+ }
+
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ public String getSign() {
+ return this.sign;
+ }
+
+ public void setSign(String sign) {
+ this.sign = sign;
+ }
+
+ public String getPrepayId() {
+ return this.prepayId;
+ }
+
+ public void setPrepayId(String prepayId) {
+ this.prepayId = prepayId;
+ }
+
+ public String getPartnerId() {
+ return this.partnerId;
+ }
+
+ public void setPartnerId(String partnerId) {
+ this.partnerId = partnerId;
+ }
+
+ public String getAppId() {
+ return this.appId;
+ }
+
+ public void setAppId(String appId) {
+ this.appId = appId;
+ }
+
+ public String getPackageValue() {
+ return this.packageValue;
+ }
+
+ public void setPackageValue(String packageValue) {
+ this.packageValue = packageValue;
+ }
+
+ public String getTimeStamp() {
+ return this.timeStamp;
+ }
+
+ public void setTimeStamp(String timeStamp) {
+ this.timeStamp = timeStamp;
+ }
+
+ public String getNonceStr() {
+ return this.nonceStr;
+ }
+
+ public void setNonceStr(String nonceStr) {
+ this.nonceStr = nonceStr;
+ }
+
+ public static final class Builder {
+ private String sign;
+ private String prepayId;
+ private String partnerId;
+ private String appId;
+ private String packageValue;
+ private String timeStamp;
+ private String nonceStr;
+
+ private Builder() {
+ }
+
+ public Builder sign(String sign) {
+ this.sign = sign;
+ return this;
+ }
+
+ public Builder prepayId(String prepayId) {
+ this.prepayId = prepayId;
+ return this;
+ }
+
+ public Builder partnerId(String partnerId) {
+ this.partnerId = partnerId;
+ return this;
+ }
+
+ public Builder appId(String appId) {
+ this.appId = appId;
+ return this;
+ }
+
+ public Builder packageValue(String packageValue) {
+ this.packageValue = packageValue;
+ return this;
+ }
+
+ public Builder timeStamp(String timeStamp) {
+ this.timeStamp = timeStamp;
+ return this;
+ }
+
+ public Builder nonceStr(String nonceStr) {
+ this.nonceStr = nonceStr;
+ return this;
+ }
+
+ public WxPayAppOrderResult build() {
+ return new WxPayAppOrderResult(this);
+ }
+ }
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayMpOrderResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayMpOrderResult.java
new file mode 100644
index 000000000..d1ebdee05
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayMpOrderResult.java
@@ -0,0 +1,133 @@
+package com.github.binarywang.wxpay.bean.order;
+
+/**
+ *
+ * 微信公众号支付进行统一下单后组装所需参数的类
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
+ * Created by Binary Wang on 2017-9-1.
+ *
+ *
+ * @author Binary Wang
+ */
+public class WxPayMpOrderResult {
+ private String appId;
+ private String timeStamp;
+ private String nonceStr;
+ /**
+ * 由于package为java保留关键字,因此改为packageValue
+ */
+ private String packageValue;
+ private String signType;
+ private String paySign;
+
+ private WxPayMpOrderResult(Builder builder) {
+ setAppId(builder.appId);
+ setTimeStamp(builder.timeStamp);
+ setNonceStr(builder.nonceStr);
+ setPackageValue(builder.packageValue);
+ setSignType(builder.signType);
+ setPaySign(builder.paySign);
+ }
+
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ public String getAppId() {
+ return this.appId;
+ }
+
+ public void setAppId(String appId) {
+ this.appId = appId;
+ }
+
+ public String getTimeStamp() {
+ return this.timeStamp;
+ }
+
+ public void setTimeStamp(String timeStamp) {
+ this.timeStamp = timeStamp;
+ }
+
+ public String getNonceStr() {
+ return this.nonceStr;
+ }
+
+ public void setNonceStr(String nonceStr) {
+ this.nonceStr = nonceStr;
+ }
+
+ public String getPackageValue() {
+ return this.packageValue;
+ }
+
+ public void setPackageValue(String packageValue) {
+ this.packageValue = packageValue;
+ }
+
+ public String getSignType() {
+ return this.signType;
+ }
+
+ public void setSignType(String signType) {
+ this.signType = signType;
+ }
+
+ public String getPaySign() {
+ return this.paySign;
+ }
+
+ public void setPaySign(String paySign) {
+ this.paySign = paySign;
+ }
+
+ public WxPayMpOrderResult() {
+ }
+
+
+ public static final class Builder {
+ private String appId;
+ private String timeStamp;
+ private String nonceStr;
+ private String packageValue;
+ private String signType;
+ private String paySign;
+
+ private Builder() {
+ }
+
+ public Builder appId(String appId) {
+ this.appId = appId;
+ return this;
+ }
+
+ public Builder timeStamp(String timeStamp) {
+ this.timeStamp = timeStamp;
+ return this;
+ }
+
+ public Builder nonceStr(String nonceStr) {
+ this.nonceStr = nonceStr;
+ return this;
+ }
+
+ public Builder packageValue(String packageValue) {
+ this.packageValue = packageValue;
+ return this;
+ }
+
+ public Builder signType(String signType) {
+ this.signType = signType;
+ return this;
+ }
+
+ public Builder paySign(String paySign) {
+ this.paySign = paySign;
+ return this;
+ }
+
+ public WxPayMpOrderResult build() {
+ return new WxPayMpOrderResult(this);
+ }
+ }
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayNativeOrderResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayNativeOrderResult.java
new file mode 100644
index 000000000..3eb0e3748
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayNativeOrderResult.java
@@ -0,0 +1,48 @@
+package com.github.binarywang.wxpay.bean.order;
+
+/**
+ *
+ * 微信扫码支付统一下单后发起支付拼接所需参数实现类
+ * Created by Binary Wang on 2017-9-1.
+ *
+ *
+ * @author Binary Wang
+ */
+public class WxPayNativeOrderResult {
+ private String codeUrl;
+
+ private WxPayNativeOrderResult(Builder builder) {
+ setCodeUrl(builder.codeUrl);
+ }
+
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ public String getCodeUrl() {
+ return this.codeUrl;
+ }
+
+ public void setCodeUrl(String codeUrl) {
+ this.codeUrl = codeUrl;
+ }
+
+ public WxPayNativeOrderResult() {
+ }
+
+ public static final class Builder {
+ private String codeUrl;
+
+ private Builder() {
+ }
+
+ public Builder codeUrl(String codeUrl) {
+ this.codeUrl = codeUrl;
+ return this;
+ }
+
+ public WxPayNativeOrderResult build() {
+ return new WxPayNativeOrderResult(this);
+ }
+ }
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java
index 92c98bda4..d8ab2ab6d 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java
@@ -5,7 +5,7 @@ import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
*
* 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"返回的结果
- * 统一下单(详见http://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1)
+ * 统一下单(详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1)
*
*
* @author chanjarster
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java
index dc8ce23b8..64842b145 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java
@@ -71,6 +71,11 @@ public class WxPayConstants {
*/
public static final String JSAPI = "JSAPI";
+ /**
+ * H5支付
+ */
+ public static final String MWEB = "MWEB";
+
/**
* 刷卡支付,刷卡支付有单独的支付接口,不调用统一下单接口
*/
@@ -84,4 +89,15 @@ public class WxPayConstants {
public static final String HMAC_SHA256 = "HMAC-SHA256";
public static final String MD5 = "MD5";
}
+
+
+ /**
+ * 限定支付方式
+ */
+ public static class LimitPay {
+ /**
+ * no_credit--指定不能使用信用卡支付
+ */
+ public static final String NO_CREDIT = "no_credit";
+ }
}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java
index 0e375bd62..fe35ed212 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java
@@ -1,7 +1,7 @@
package com.github.binarywang.wxpay.converter;
-import com.github.binarywang.wxpay.bean.WxPayOrderNotifyCoupon;
-import com.github.binarywang.wxpay.bean.result.WxPayOrderNotifyResult;
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyCoupon;
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import com.thoughtworks.xstream.annotations.XStreamAlias;
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 bf599ba17..0605edfc2 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
@@ -2,6 +2,8 @@ package com.github.binarywang.wxpay.service;
import com.github.binarywang.wxpay.bean.WxPayApiData;
import com.github.binarywang.wxpay.bean.coupon.*;
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
+import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.request.*;
import com.github.binarywang.wxpay.bean.result.*;
import com.github.binarywang.wxpay.config.WxPayConfig;
@@ -22,7 +24,7 @@ public interface WxPayService {
/**
*
- * 查询订单(详见https://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2)
+ * 查询订单(详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2)
* 该接口提供所有微信支付订单的查询,商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。
* 需要调用查询接口的情况:
* ◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知;
@@ -54,7 +56,7 @@ public interface WxPayService {
WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxPayException;
/**
- * 统一下单(详见http://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1)
+ * 统一下单(详见https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1)
* 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"
* 接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder
*
@@ -64,7 +66,7 @@ public interface WxPayService {
/**
* 该接口调用“统一下单”接口,并拼装发起支付请求需要的参数
- * 详见http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN
+ * 详见https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5
*
* @param request 请求对象,注意一些参数如appid、mchid等不用设置,方法内会自动从配置对象中获取到(前提是对应配置中已经设置)
*/
@@ -113,10 +115,23 @@ public interface WxPayService {
throws WxPayException;
/**
- * 读取支付结果通知
+ * @deprecated use WxPayService#parseOrderNotifyResult(String) instead
+ * @see WxPayService#parseOrderNotifyResult(String)
+ */
+ @Deprecated
+ WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxPayException;
+
+ /**
+ * 解析支付结果通知
* 详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
*/
- WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxPayException;
+ WxPayOrderNotifyResult parseOrderNotifyResult(String xmlData) throws WxPayException;
+
+ /**
+ * 解析退款结果通知
+ * 详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_16&index=9
+ */
+ WxPayRefundNotifyResult parseRefundNotifyResult(String xmlData) throws WxPayException;
/**
* 发送微信红包给个人用户
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImpl.java
index 27d4d2dd6..f0b22d194 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImpl.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImpl.java
@@ -3,6 +3,11 @@ package com.github.binarywang.wxpay.service.impl;
import com.github.binarywang.utils.qrcode.QrcodeUtils;
import com.github.binarywang.wxpay.bean.WxPayApiData;
import com.github.binarywang.wxpay.bean.coupon.*;
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
+import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
+import com.github.binarywang.wxpay.bean.order.WxPayAppOrderResult;
+import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
+import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
import com.github.binarywang.wxpay.bean.request.*;
import com.github.binarywang.wxpay.bean.result.*;
import com.github.binarywang.wxpay.config.WxPayConfig;
@@ -97,11 +102,17 @@ public abstract class WxPayServiceAbstractImpl implements WxPayService {
}
@Override
+ @Deprecated
public WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxPayException {
+ return this.parseOrderNotifyResult(xmlData);
+ }
+
+ @Override
+ public WxPayOrderNotifyResult parseOrderNotifyResult(String xmlData) throws WxPayException {
try {
- log.debug("微信支付回调参数详细:{}", xmlData);
+ log.debug("微信支付异步通知请求参数:{}", xmlData);
WxPayOrderNotifyResult result = WxPayOrderNotifyResult.fromXML(xmlData);
- log.debug("微信支付回调结果对象:{}", result);
+ log.debug("微信支付异步通知请求解析后的对象:{}", result);
result.checkResult(this);
return result;
} catch (WxPayException e) {
@@ -113,6 +124,19 @@ public abstract class WxPayServiceAbstractImpl implements WxPayService {
}
}
+ @Override
+ public WxPayRefundNotifyResult parseRefundNotifyResult(String xmlData) throws WxPayException {
+ try {
+ log.debug("微信支付退款异步通知参数:{}", xmlData);
+ WxPayRefundNotifyResult result = WxPayRefundNotifyResult.fromXML(xmlData, this.getConfig().getMchKey());
+ log.debug("微信支付退款异步通知解析后的对象:{}", result);
+ return result;
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ throw new WxPayException("发生异常," + e.getMessage(), e);
+ }
+ }
+
@Override
public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throws WxPayException {
request.checkAndSign(this.getConfig());
@@ -181,6 +205,65 @@ public abstract class WxPayServiceAbstractImpl implements WxPayService {
return result;
}
+ public T createOrder(WxPayUnifiedOrderRequest request) throws WxPayException {
+ WxPayUnifiedOrderResult unifiedOrderResult = this.unifiedOrder(request);
+ String prepayId = unifiedOrderResult.getPrepayId();
+ if (StringUtils.isBlank(prepayId)) {
+ throw new RuntimeException(String.format("无法获取prepay id,错误代码: '%s',信息:%s。",
+ unifiedOrderResult.getErrCode(), unifiedOrderResult.getErrCodeDes()));
+ }
+
+ String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
+ String nonceStr = String.valueOf(System.currentTimeMillis());
+ Object payResult = null;
+ switch (request.getTradeType()) {
+ case TradeType.NATIVE: {
+ payResult = WxPayNativeOrderResult.newBuilder().codeUrl(unifiedOrderResult.getCodeURL())
+ .build();
+ break;
+ }
+ case TradeType.APP: {
+ // APP支付绑定的是微信开放平台上的账号,APPID为开放平台上绑定APP后发放的参数
+ String appId = this.getConfig().getAppId();
+ Map configMap = new HashMap<>();
+ // 此map用于参与调起sdk支付的二次签名,格式全小写,timestamp只能是10位,格式固定,切勿修改
+ String partnerId = getConfig().getMchId();
+ configMap.put("prepayid", prepayId);
+ configMap.put("partnerid", partnerId);
+ String packageValue = "Sign=WXPay";
+ configMap.put("package", packageValue);
+ configMap.put("timestamp", timestamp);
+ configMap.put("noncestr", nonceStr);
+ configMap.put("appid", appId);
+
+ payResult = WxPayAppOrderResult.newBuilder()
+ .sign(SignUtils.createSign(configMap, this.getConfig().getMchKey()))
+ .prepayId(prepayId)
+ .partnerId(partnerId)
+ .appId(appId)
+ .packageValue(packageValue)
+ .timeStamp(timestamp)
+ .nonceStr(nonceStr)
+ .build();
+ break;
+ }
+ case TradeType.JSAPI: {
+ payResult = WxPayMpOrderResult.newBuilder()
+ .appId(unifiedOrderResult.getAppid())
+ .timeStamp(timestamp)
+ .nonceStr(nonceStr)
+ .packageValue("prepay_id=" + prepayId)
+ .signType(SignType.MD5)
+ .build();
+ ((WxPayMpOrderResult) payResult)
+ .setPaySign(SignUtils.createSign(payResult, this.getConfig().getMchKey()));
+ break;
+ }
+ }
+
+ return (T) payResult;
+ }
+
@Override
public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws WxPayException {
request.checkAndSign(this.getConfig());
@@ -508,7 +591,7 @@ public abstract class WxPayServiceAbstractImpl implements WxPayService {
public WxPayApiData getWxApiData() {
try {
return wxApiData.get();
- }finally {
+ } finally {
//一般来说,接口请求会在一个线程内进行,这种情况下,每个线程get的会是之前所存入的数据,
// 但以防万一有同一线程多次请求的问题,所以每次获取完数据后移除对应数据
wxApiData.remove();
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResultTest.java
similarity index 95%
rename from weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResultTest.java
rename to weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResultTest.java
index 389b374d9..384de4c98 100644
--- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResultTest.java
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResultTest.java
@@ -1,5 +1,6 @@
-package com.github.binarywang.wxpay.bean.result;
+package com.github.binarywang.wxpay.bean.notify;
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import org.testng.*;
import org.testng.annotations.*;
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java
new file mode 100644
index 000000000..cd1777f83
--- /dev/null
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java
@@ -0,0 +1,70 @@
+package com.github.binarywang.wxpay.bean.notify;
+
+import com.github.binarywang.wxpay.config.WxPayConfig;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.testbase.ApiTestModule;
+import org.apache.commons.codec.binary.Base64;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+import javax.inject.Inject;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+
+import static org.testng.Assert.assertNotNull;
+
+/**
+ *
+ * Created by BinaryWang on 2017/8/27.
+ *
+ *
+ * @author Binary Wang
+ */
+@Test
+@Guice(modules = ApiTestModule.class)
+public class WxPayRefundNotifyResultTest {
+ @Inject
+ private WxPayConfig wxPayConfig;
+
+ public void testFromXML() throws WxPayException {
+ String xmlString = "" +
+ "SUCCESS " +
+ " " +
+ " " +
+ " " +
+ " ";
+
+ WxPayRefundNotifyResult refundNotifyResult = WxPayRefundNotifyResult.fromXML(xmlString, this.wxPayConfig.getMchKey());
+
+ assertNotNull(refundNotifyResult);
+ System.out.println(refundNotifyResult);
+ }
+
+ public void encodeReqInfo() throws Exception {
+ String xml = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+
+ Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+ final MessageDigest md5 = MessageDigest.getInstance("MD5");
+ md5.update(this.wxPayConfig.getMchKey().getBytes());
+ final String keyMd5String = new BigInteger(1, md5.digest()).toString(16).toLowerCase();
+ SecretKeySpec key = new SecretKeySpec(keyMd5String.getBytes(), "AES");
+ cipher.init(Cipher.ENCRYPT_MODE, key);
+ System.out.println(Base64.encodeBase64String(cipher.doFinal(xml.getBytes())));
+ }
+}
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImplTest.java
index de0fe10e2..8d419be8a 100644
--- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImplTest.java
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImplTest.java
@@ -2,6 +2,7 @@ package com.github.binarywang.wxpay.service.impl;
import com.github.binarywang.utils.qrcode.QrcodeUtils;
import com.github.binarywang.wxpay.bean.coupon.*;
+import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.request.*;
import com.github.binarywang.wxpay.bean.result.*;
import com.github.binarywang.wxpay.constant.WxPayConstants;
@@ -147,6 +148,11 @@ public class WxPayServiceAbstractImplTest {
this.logger.info(result.toString());
}
+ @Test
+ public void testParseRefundNotifyResult() throws Exception {
+ // 请参考com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResultTest里的单元测试
+ }
+
/**
* Test method for {@link WxPayService#sendRedpack(WxPaySendRedpackRequest)} .
*/