mirror of
				https://gitee.com/binary/weixin-java-tools.git
				synced 2025-10-31 18:46:10 +08:00 
			
		
		
		
	#319 增加“退款结果通知“处理方法,并优化调整微信支付相关代码
This commit is contained in:
		| @ -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); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|  | ||||
| @ -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); | ||||
| @ -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; | ||||
| @ -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; | ||||
| 
 | ||||
|   /** | ||||
| @ -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; | ||||
|  | ||||
| /** | ||||
|  * <pre> | ||||
|  *  退款结果通知对象 | ||||
|  *  Created by BinaryWang on 2017/8/27. | ||||
|  * </pre> | ||||
|  * | ||||
|  * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
|  */ | ||||
| @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; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * <pre> | ||||
|    * 字段名:加密信息 | ||||
|    * 变量名:req_info | ||||
|    * 是否必填:是 | ||||
|    * 类型:String(1024) | ||||
|    * 描述:加密信息请用商户证书与商户秘钥进行解密 | ||||
|    * </pre> | ||||
|    */ | ||||
|   @XStreamAlias("req_info") | ||||
|   private String reqInfoString; | ||||
|  | ||||
|   private ReqInfo reqInfo; | ||||
|  | ||||
|   /** | ||||
|    * 加密信息字段解密后的内容 | ||||
|    */ | ||||
|   @XStreamAlias("root") | ||||
|   public static class ReqInfo { | ||||
|     @Override | ||||
|     public String toString() { | ||||
|       return ToStringUtils.toSimpleString(this); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * <pre> | ||||
|      * 字段名:微信订单号 | ||||
|      * 变量名:transaction_id | ||||
|      * 是否必填:是 | ||||
|      * 类型:String(32) | ||||
|      * 示例值:1217752501201407033233368018 | ||||
|      * 描述:微信订单号 | ||||
|      * </pre> | ||||
|      */ | ||||
|     @XStreamAlias("transaction_id") | ||||
|     private String transactionId; | ||||
|  | ||||
|     /** | ||||
|      * <pre> | ||||
|      * 字段名:商户订单号 | ||||
|      * 变量名:out_trade_no | ||||
|      * 是否必填:是 | ||||
|      * 类型:String(32) | ||||
|      * 示例值:1217752501201407033233368018 | ||||
|      * 描述:商户系统内部的订单号 | ||||
|      * </pre> | ||||
|      */ | ||||
|     @XStreamAlias("out_trade_no") | ||||
|     private String outTradeNo; | ||||
|  | ||||
|     /** | ||||
|      * <pre> | ||||
|      * 字段名:微信退款单号 | ||||
|      * 变量名:refund_id | ||||
|      * 是否必填:是 | ||||
|      * 类型:String(28) | ||||
|      * 示例值:1217752501201407033233368018 | ||||
|      * 描述:微信退款单号 | ||||
|      * </pre> | ||||
|      */ | ||||
|     @XStreamAlias("refund_id") | ||||
|     private String refundId; | ||||
|  | ||||
|     /** | ||||
|      * <pre> | ||||
|      * 字段名:商户退款单号 | ||||
|      * 变量名:out_refund_no | ||||
|      * 是否必填:是 | ||||
|      * 类型:String(64) | ||||
|      * 示例值:1217752501201407033233368018 | ||||
|      * 描述:商户退款单号 | ||||
|      * </pre> | ||||
|      */ | ||||
|     @XStreamAlias("out_refund_no") | ||||
|     private String outRefundNo; | ||||
|  | ||||
|     /** | ||||
|      * <pre> | ||||
|      * 字段名:订单金额 | ||||
|      * 变量名:total_fee | ||||
|      * 是否必填:是 | ||||
|      * 类型:Int | ||||
|      * 示例值:100 | ||||
|      * 描述:订单总金额,单位为分,只能为整数,详见支付金额 | ||||
|      * </pre> | ||||
|      */ | ||||
|     @XStreamAlias("total_fee") | ||||
|     private Integer totalFee; | ||||
|  | ||||
|     /** | ||||
|      * <pre> | ||||
|      * 字段名:结订单金额 | ||||
|      * 变量名:settlement_total_fee | ||||
|      * 是否必填:否 | ||||
|      * 类型:Int | ||||
|      * 示例值:100 | ||||
|      * 描述:当该订单有使用非充值券时,返回此字段。应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。 | ||||
|      * </pre> | ||||
|      */ | ||||
|     @XStreamAlias("settlement_total_fee") | ||||
|     private Integer settlementTotalFee; | ||||
|  | ||||
|     /** | ||||
|      * <pre> | ||||
|      * 字段名:申请退款金额 | ||||
|      * 变量名:refund_fee | ||||
|      * 是否必填:是 | ||||
|      * 类型:Int | ||||
|      * 示例值:100 | ||||
|      * 描述:退款总金额,单位为分 | ||||
|      * </pre> | ||||
|      */ | ||||
|     @XStreamAlias("refund_fee") | ||||
|     private Integer refundFee; | ||||
|  | ||||
|     /** | ||||
|      * <pre> | ||||
|      * 字段名:退款金额 | ||||
|      * 变量名:settlement_refund_fee | ||||
|      * 是否必填:是 | ||||
|      * 类型:Int | ||||
|      * 示例值:100 | ||||
|      * 描述:退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额 | ||||
|      * </pre> | ||||
|      */ | ||||
|     @XStreamAlias("settlement_refund_fee") | ||||
|     private Integer settlementRefundFee; | ||||
|  | ||||
|     /** | ||||
|      * <pre> | ||||
|      * 字段名:退款状态 | ||||
|      * 变量名:refund_status | ||||
|      * 是否必填:是 | ||||
|      * 类型:String(16) | ||||
|      * 示例值:SUCCESS | ||||
|      * 描述:SUCCESS-退款成功,CHANGE-退款异常,REFUNDCLOSE—退款关闭 | ||||
|      * </pre> | ||||
|      */ | ||||
|     @XStreamAlias("refund_status") | ||||
|     private String refundStatus; | ||||
|  | ||||
|     /** | ||||
|      * <pre> | ||||
|      * 字段名:退款成功时间 | ||||
|      * 变量名:success_time | ||||
|      * 是否必填:否 | ||||
|      * 类型: String(20) | ||||
|      * 示例值:20160725152626 | ||||
|      * 描述:- | ||||
|      */ | ||||
|     @XStreamAlias("success_time") | ||||
|     private String successTime; | ||||
|  | ||||
|     /** | ||||
|      * <pre> | ||||
|      * 字段名:退款入账账户 | ||||
|      * 变量名:refund_recv_accout | ||||
|      * 是否必填:是 | ||||
|      * 类型:String(64) | ||||
|      * 示例值:招商银行信用卡0403 | ||||
|      * 描述:取当前退款单的退款入账方,1)退回银行卡:{银行名称}{卡类型}{卡尾号},2)退回支付用户零钱:支付用户零钱 ,3)退还商户: 商户基本账户,商户结算银行账户,4)退回支付用户零钱通: 支付用户零钱通 | ||||
|      * </pre> | ||||
|      */ | ||||
|     @XStreamAlias("refund_recv_accout") | ||||
|     private String refundRecvAccout; | ||||
|  | ||||
|     /** | ||||
|      * <pre> | ||||
|      * 字段名:退款资金来源 | ||||
|      * 变量名:refund_account | ||||
|      * 是否必填:是 | ||||
|      * 类型:String(30) | ||||
|      * 示例值:REFUND_SOURCE_RECHARGE_FUNDS | ||||
|      * 描述:REFUND_SOURCE_RECHARGE_FUNDS 可用余额退款/基本账户,REFUND_SOURCE_UNSETTLED_FUNDS 未结算资金退款 | ||||
|      * </pre> | ||||
|      */ | ||||
|     @XStreamAlias("refund_account") | ||||
|     private String refundAccount; | ||||
|  | ||||
|     /** | ||||
|      * <pre> | ||||
|      * 字段名:退款发起来源 | ||||
|      * 变量名:refund_request_source | ||||
|      * 是否必填:是 | ||||
|      * 类型:String(30) | ||||
|      * 示例值:API | ||||
|      * 描述:API接口,VENDOR_PLATFORM商户平台 | ||||
|      * </pre> | ||||
|      */ | ||||
|     @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; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,145 @@ | ||||
| package com.github.binarywang.wxpay.bean.order; | ||||
|  | ||||
| /** | ||||
|  * <pre> | ||||
|  * APP支付调用统一下单接口后的组装所需参数的实现类 | ||||
|  * 参考 https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_12 | ||||
|  * Created by Binary Wang on 2017-9-1. | ||||
|  * </pre> | ||||
|  * | ||||
|  * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
|  */ | ||||
| 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); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,133 @@ | ||||
| package com.github.binarywang.wxpay.bean.order; | ||||
|  | ||||
| /** | ||||
|  * <pre> | ||||
|  * 微信公众号支付进行统一下单后组装所需参数的类 | ||||
|  * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6 | ||||
|  * Created by Binary Wang on 2017-9-1. | ||||
|  * </pre> | ||||
|  * | ||||
|  * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
|  */ | ||||
| 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); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,48 @@ | ||||
| package com.github.binarywang.wxpay.bean.order; | ||||
|  | ||||
| /** | ||||
|  * <pre> | ||||
|  * 微信扫码支付统一下单后发起支付拼接所需参数实现类 | ||||
|  * Created by Binary Wang on 2017-9-1. | ||||
|  * </pre> | ||||
|  * | ||||
|  * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
|  */ | ||||
| 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); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -5,7 +5,7 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
| /** | ||||
|  * <pre> | ||||
|  * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"返回的结果 | ||||
|  * 统一下单(详见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) | ||||
|  * </pre> | ||||
|  * | ||||
|  * @author chanjarster | ||||
|  | ||||
| @ -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"; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
|   /** | ||||
|    * <pre> | ||||
|    * 查询订单(详见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; | ||||
|  | ||||
|   /** | ||||
|    * 发送微信红包给个人用户 | ||||
|  | ||||
| @ -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> 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<String, String> 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(); | ||||
|  | ||||
| @ -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.*; | ||||
| 
 | ||||
| @ -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; | ||||
|  | ||||
| /** | ||||
|  * <pre> | ||||
|  *  Created by BinaryWang on 2017/8/27. | ||||
|  * </pre> | ||||
|  * | ||||
|  * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
|  */ | ||||
| @Test | ||||
| @Guice(modules = ApiTestModule.class) | ||||
| public class WxPayRefundNotifyResultTest { | ||||
|   @Inject | ||||
|   private WxPayConfig wxPayConfig; | ||||
|  | ||||
|   public void testFromXML() throws WxPayException { | ||||
|     String xmlString = "<xml>" + | ||||
|       "<return_code>SUCCESS</return_code>" + | ||||
|       "<appid><![CDATA[****]]></appid>" + | ||||
|       "<mch_id><![CDATA[****]]></mch_id>" + | ||||
|       "<nonce_str><![CDATA[1ee38e38b04990449808688cf3a763b7]]></nonce_str>" + | ||||
|       "<req_info><![CDATA[q1QZlV5j/4I7CsJ3voq1zDgVAuzNM/Gg5JYHcpMZCLtg9KQlB6vShzsh8tgK60dU6yG2WVa0zeSDlK4B7wJCad1lUUP8Ar0Hm18M1ZEjw5vQU17wMzypRM0M9A4CcRLBezRZYzCka9CAH90E2FZ74y6VRe4DNR87t5n3DWVtSbWTBoaFUexHtNs6pyrqX77VvbilIyLZMv5ZYQYOobbQ1U3kime5He7ShEWZ0GPI3gq+z/ZOLsnIdJ5bsT4kokhq/531hSoZ5006vxRGGXnhJt8IYiG7R+oSQxZOYqYR5SKWF+0z2/g0zzM2QQlT2ynLWvBKvdVCLlgCjFN1DF4z/7IEK5FAISFP0GGF51hYw/LofL3ftlD7h7jvjOIgH5viJ0yFGmGCEFHcLKqg0DPXmzwXIrkgQSSQPsuZ6UbHUUG0L8YTRgLnl2FwNFskJIaNx0179Il6xveR1sCXbwSDGvGN78sFuQMztbnx+gFu6VYgv7C+5pFr87wHFAeuDXGTkVM6ucAwSanP7HuxSVvf7SrSrcovKslyqj869pSqn/AB0atiQ4eoq3kWaOqx87NHOV1st9SQW1SYH7SKz4jd9uhrQyDuPb6KJSg1Z2B4sU4187NjPzL4NpzZySgiYk2yXpWKhCLIz6BdZuWX79zgqxLbGxJJnhyy3tOzRWIlMkDOppGJyh8LO0LOqhXzwyrCYzPA+h2xcr7xN5WIW1IGJSZqHdURUtlemcB+yZivuzARNH0LE2MGUfuoNgZ5j1Osn7K88IrkAyKupcIEmG3ktVnPOd1A9RQ9eWbU+C7yKrl6u5ZRZOX0eElVszKfBFy4tu3XHlT7hd/zMFK5NJt8sE89k5m7M8KCGSgJ+Y90ZnUclQvDVtoR5CFkfqsP9fSpA1L+aKYsl2ESq5+fzcqsYRL3YLEhIipBKKrvg6Gy698oNeG+9oCIyuiFexJDq8ycBZ/AWiR+pFQVbNRaFbfKPR9zCW8gHwYOGnENNY9gABuuENqxxXDx9tEYkACd0H9ezLnu9psC6AuR41ACfo6wGKUA1TnpVEHsDbdvJBWDcw60l1hkmHQN2lYFy+eMusEX]]></req_info></xml>"; | ||||
|  | ||||
|     WxPayRefundNotifyResult refundNotifyResult = WxPayRefundNotifyResult.fromXML(xmlString, this.wxPayConfig.getMchKey()); | ||||
|  | ||||
|     assertNotNull(refundNotifyResult); | ||||
|     System.out.println(refundNotifyResult); | ||||
|   } | ||||
|  | ||||
|   public void encodeReqInfo() throws Exception { | ||||
|     String xml = "<root>\n" + | ||||
|       "<out_refund_no><![CDATA[R4001312001201707262674894706_4]]></out_refund_no>\n" + | ||||
|       "<out_trade_no><![CDATA[201707260201501501005710775]]></out_trade_no>\n" + | ||||
|       "<refund_account><![CDATA[REFUND_SOURCE_UNSETTLED_FUNDS]]></refund_account>\n" + | ||||
|       "<refund_fee><![CDATA[15]]></refund_fee>\n" + | ||||
|       "<refund_id><![CDATA[50000203702017072601461713166]]></refund_id>\n" + | ||||
|       "<refund_recv_accout><![CDATA[用户零钱]]></refund_recv_accout>\n" + | ||||
|       "<refund_request_source><![CDATA[API]]></refund_request_source>\n" + | ||||
|       "<refund_status><![CDATA[SUCCESS]]></refund_status>\n" + | ||||
|       "<settlement_refund_fee><![CDATA[15]]></settlement_refund_fee>\n" + | ||||
|       "<settlement_total_fee><![CDATA[100]]></settlement_total_fee>\n" + | ||||
|       "<success_time><![CDATA[2017-07-26 02:45:49]]></success_time>\n" + | ||||
|       "<total_fee><![CDATA[100]]></total_fee>\n" + | ||||
|       "<transaction_id><![CDATA[4001312001201707262674894706]]></transaction_id>\n" + | ||||
|       "</root>"; | ||||
|  | ||||
|     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()))); | ||||
|   } | ||||
| } | ||||
| @ -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)} . | ||||
|    */ | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Binary Wang
					Binary Wang