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 e362e64b..c91e8ac0 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 e3692931..e7a7ea90 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 c2b4e22e..cd948dfb 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 a411cd2c..54657ac6 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 00000000..392c1014 --- /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 00000000..6688d602 --- /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 00000000..d1ebdee0 --- /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 00000000..3eb0e374 --- /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 92c98bda..d8ab2ab6 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 dc8ce23b..64842b14 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 0e375bd6..fe35ed21 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 bf599ba1..0605edfc 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,11 +115,24 @@ public interface WxPayService { throws WxPayException; /** - * 读取支付结果通知 - * 详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 + * @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 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 27d4d2dd..f0b22d19 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; } + publicT 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 389b374d..384de4c9 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 00000000..cd1777f8 --- /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 = "" + + " "; + + WxPayRefundNotifyResult refundNotifyResult = WxPayRefundNotifyResult.fromXML(xmlString, this.wxPayConfig.getMchKey()); + + assertNotNull(refundNotifyResult); + System.out.println(refundNotifyResult); + } + + public void encodeReqInfo() throws Exception { + String xml = "SUCCESS " + + "" + + " " + + " " + + " \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 de0fe10e..8d419be8 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)} . */\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "