Ver código fonte

#319 增加“退款结果通知“处理方法,并优化调整微信支付相关代码

master
Binary Wang 7 anos atrás
committed by Binary Wang
pai
commit
b9262c93c4
16 arquivos alterados com 918 adições e 32 exclusões
  1. +5
    -10
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java
  2. +9
    -6
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayNotifyResponse.java
  3. +1
    -1
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyCoupon.java
  4. +2
    -3
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResult.java
  5. +372
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java
  6. +145
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayAppOrderResult.java
  7. +133
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayMpOrderResult.java
  8. +48
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayNativeOrderResult.java
  9. +1
    -1
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java
  10. +16
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java
  11. +2
    -2
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java
  12. +20
    -5
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java
  13. +86
    -3
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImpl.java
  14. +2
    -1
      weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResultTest.java
  15. +70
    -0
      weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java
  16. +6
    -0
      weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImplTest.java

+ 5
- 10
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java Ver arquivo

@@ -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);
}

/**


weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyResponse.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayNotifyResponse.java Ver arquivo

@@ -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);

weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyCoupon.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyCoupon.java Ver arquivo

@@ -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;

weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResult.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResult.java Ver arquivo

@@ -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;

/**

+ 372
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java Ver arquivo

@@ -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;
}
}

+ 145
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayAppOrderResult.java Ver arquivo

@@ -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);
}
}
}

+ 133
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayMpOrderResult.java Ver arquivo

@@ -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);
}
}
}

+ 48
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayNativeOrderResult.java Ver arquivo

@@ -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);
}
}
}

+ 1
- 1
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java Ver arquivo

@@ -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


+ 16
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java Ver arquivo

@@ -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";
}
}

+ 2
- 2
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java Ver arquivo

@@ -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;


+ 20
- 5
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java Ver arquivo

@@ -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,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;

/**
* 发送微信红包给个人用户
* <pre>


+ 86
- 3
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImpl.java Ver arquivo

@@ -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();


weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResultTest.java → weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResultTest.java Ver arquivo

@@ -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.*;


+ 70
- 0
weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java Ver arquivo

@@ -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())));
}
}

+ 6
- 0
weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImplTest.java Ver arquivo

@@ -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)} .
*/


Carregando…
Cancelar
Salvar