diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
index d0e2c1906..b28ce7757 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
@@ -761,6 +761,20 @@ public interface WxMpService {
*/
WxMpPayCallback getJSSDKCallbackData(String xmlData);
+ /**
+ * 微信支付-申请退款
+ * 详见 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
+ * @param parameters 需要传入的退款参数的Map。以下几项为参数的必须项:
+ *
* 计算Map键值对是否和签名相符,
@@ -812,6 +826,7 @@ public interface WxMpService {
*
* @param optionalSignParam 参与签名的参数数组。
* 可以为下列字段:app_id, card_id, card_type, code, openid, location_id
+ * 注意:当做wx.chooseCard调用时,必须传入app_id参与签名,否则会造成签名失败导致拉取卡券列表为空
* @return 卡券Api签名对象
*/
public WxCardApiSignature createCardApiSignature(String... optionalSignParam) throws
@@ -839,10 +854,12 @@ public interface WxMpService {
/**
* 卡券Code核销。核销失败会抛出异常
* @param code 单张卡券的唯一标准
- * @return
- * @throws WxErrorException
- */
- public void consumeCardCode(String code) throws WxErrorException;
+ * @param cardId 当自定义Code卡券时需要传入card_id
+ * @return 调用返回的JSON字符串。
+ *
可用 com.google.gson.JsonParser#parse 等方法直接取JSON串中的errcode等信息。
+ * @throws WxErrorException
+ */
+ public String consumeCardCode(String code, String cardId) throws WxErrorException;
/**
* 卡券Mark接口。
@@ -856,4 +873,15 @@ public interface WxMpService {
*/
public void markCardCode(String code, String cardId, String openId, boolean isMark) throws
WxErrorException;
+
+ /**
+ * 查看卡券详情接口
+ * 详见 https://mp.weixin.qq.com/wiki/14/8dd77aeaee85f922db5f8aa6386d385e.html#.E6.9F.A5.E7.9C.8B.E5.8D.A1.E5.88.B8.E8.AF.A6.E6.83.85
+ * @param cardId 卡券的ID
+ * @return 返回的卡券详情JSON字符串
+ *
[注] 由于返回的JSON格式过于复杂,难以定义其对应格式的Bean并且难以维护,因此只返回String格式的JSON串。
+ *
可由 com.google.gson.JsonParser#parse 等方法直接取JSON串中的某个字段。
+ * @throws WxErrorException
+ */
+ public String getCardDetail(String cardId) throws WxErrorException;
}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
index 25f86e567..e9f6ee0a7 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
@@ -56,6 +56,7 @@ import me.chanjar.weixin.mp.bean.result.WxMpMaterialUploadResult;
import me.chanjar.weixin.mp.bean.result.WxMpMaterialVideoInfoResult;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.result.WxMpPayCallback;
+import me.chanjar.weixin.mp.bean.result.WxMpPayRefundResult;
import me.chanjar.weixin.mp.bean.result.WxMpPayResult;
import me.chanjar.weixin.mp.bean.result.WxMpPrepayIdResult;
import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket;
@@ -97,6 +98,7 @@ import org.slf4j.helpers.MessageFormatter;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
import com.google.gson.internal.Streams;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
@@ -971,6 +973,54 @@ public class WxMpServiceImpl implements WxMpService {
return new WxMpPayCallback();
}
+ @Override
+ public WxMpPayRefundResult refundPay(Map parameters) throws WxErrorException {
+ SortedMap refundParams = new TreeMap(parameters);
+ refundParams.put("appid", wxMpConfigStorage.getAppId());
+ refundParams.put("mch_id", wxMpConfigStorage.getPartnerId());
+ refundParams.put("nonceStr", System.currentTimeMillis() + "");
+ refundParams.put("op_user_id", wxMpConfigStorage.getPartnerId());
+ String sign = WxCryptUtil.createSign(refundParams, wxMpConfigStorage.getPartnerKey());
+ refundParams.put("sign", sign);
+
+ StringBuilder request = new StringBuilder("");
+ for (Entry para : refundParams.entrySet()) {
+ request.append(String.format("<%s>%s%s>", para.getKey(), para.getValue(), para.getKey()));
+ }
+ request.append(" ");
+
+ HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");
+ if (httpProxy != null) {
+ RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build();
+ httpPost.setConfig(config);
+ }
+
+ StringEntity entity = new StringEntity(request.toString(), Consts.UTF_8);
+ httpPost.setEntity(entity);
+ try(
+ CloseableHttpResponse response = getHttpclient().execute(httpPost)) {
+ String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.processAnnotations(WxRedpackResult.class);
+ WxMpPayRefundResult wxMpPayRefundResult = (WxMpPayRefundResult) xstream.fromXML(responseContent);
+
+ if ("FAIL".equals(wxMpPayRefundResult.getResultCode())) {
+ WxError error = new WxError();
+ error.setErrorCode(-1);
+ error.setErrorMsg(wxMpPayRefundResult.getErrCodeDes());
+ throw new WxErrorException(error);
+ }
+
+ return wxMpPayRefundResult;
+ } catch (IOException e) {
+ log.error(MessageFormatter.format("The exception was happened when sending refund '{}'.", request.toString()).getMessage(), e);
+ WxError error = new WxError();
+ error.setErrorCode(-1);
+ error.setErrorMsg("incorrect response.");
+ throw new WxErrorException(error);
+ }
+ }
+
@Override
public boolean checkJSSDKCallbackDataSignature(Map kvm, String signature) {
return signature.equals(WxCryptUtil.createSign(kvm, wxMpConfigStorage.getPartnerKey()));
@@ -1074,6 +1124,7 @@ public class WxMpServiceImpl implements WxMpService {
*
* @param optionalSignParam 参与签名的参数数组。
* 可以为下列字段:app_id, card_id, card_type, code, openid, location_id
+ * 注意:当做wx.chooseCard调用时,必须传入app_id参与签名,否则会造成签名失败导致拉取卡券列表为空
* @return 卡券Api签名对象
*/
@Override
@@ -1148,11 +1199,17 @@ public class WxMpServiceImpl implements WxMpService {
* @throws WxErrorException
*/
@Override
- public void consumeCardCode(String code) throws WxErrorException {
+ public String consumeCardCode(String code, String cardId) throws WxErrorException {
String url = "https://api.weixin.qq.com/card/code/consume";
JsonObject param = new JsonObject();
param.addProperty("code", code);
- post(url, param.toString());
+
+ if (cardId != null && !"".equals(cardId)) {
+ param.addProperty("card_id", cardId);
+ }
+
+ String responseContent = post(url, param.toString());
+ return responseContent;
}
/**
@@ -1183,4 +1240,26 @@ public class WxMpServiceImpl implements WxMpService {
log.warn("朋友的券mark失败:{}", cardResult.getErrorMsg());
}
}
+
+ @Override
+ public String getCardDetail(String cardId) throws WxErrorException {
+ String url = "https://api.weixin.qq.com/card/get";
+ JsonObject param = new JsonObject();
+ param.addProperty("card_id", cardId);
+ String responseContent = post(url, param.toString());
+
+ // 判断返回值
+ JsonObject json = (new JsonParser()).parse(responseContent).getAsJsonObject();
+ String errcode = json.get("errcode").getAsString();
+ if (!"0".equals(errcode)) {
+ String errmsg = json.get("errmsg").getAsString();
+ WxError error = new WxError();
+ error.setErrorCode(Integer.valueOf(errcode));
+ error.setErrorMsg(errmsg);
+ throw new WxErrorException(error);
+ }
+
+ return responseContent;
+ }
+
}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpPayRefundResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpPayRefundResult.java
new file mode 100644
index 000000000..1e8327e30
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpPayRefundResult.java
@@ -0,0 +1,278 @@
+package me.chanjar.weixin.mp.bean.result;
+
+import java.io.Serializable;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+
+/**
+ * 微信支付-申请退款返回结果
+ * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
+ * @author liukaitj
+ *
+ */
+@XStreamAlias("xml")
+public class WxMpPayRefundResult implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @XStreamAlias("return_code")
+ private String returnCode;
+
+ @XStreamAlias("return_msg")
+ private String returnMsg;
+
+ @XStreamAlias("result_code")
+ private String resultCode;
+
+ @XStreamAlias("err_code")
+ private String errCode;
+
+ @XStreamAlias("err_code_des")
+ private String errCodeDes;
+
+ @XStreamAlias("appid")
+ private String appid;
+
+ @XStreamAlias("mch_id")
+ private String mchId;
+
+ @XStreamAlias("device_info")
+ private String deviceInfo;
+
+ @XStreamAlias("nonce_str")
+ private String nonceStr;
+
+ @XStreamAlias("sign")
+ private String sign;
+
+ @XStreamAlias("transaction_id")
+ private String transactionId;
+
+ @XStreamAlias("out_trade_no")
+ private String outTradeNo;
+
+ @XStreamAlias("out_refund_no")
+ private String outRefundNo;
+
+ @XStreamAlias("refund_id")
+ private String refundId;
+
+ @XStreamAlias("refund_channel")
+ private String refundChannel;
+
+ @XStreamAlias("refund_fee")
+ private String refundFee;
+
+ @XStreamAlias("total_fee")
+ private String totalFee;
+
+ @XStreamAlias("fee_type")
+ private String feeType;
+
+ @XStreamAlias("cash_fee")
+ private String cashFee;
+
+ @XStreamAlias("cash_refund_fee")
+ private String cashRefundfee;
+
+ @XStreamAlias("coupon_refund_fee")
+ private String couponRefundFee;
+
+ @XStreamAlias("coupon_refund_count")
+ private String couponRefundCount;
+
+ @XStreamAlias("coupon_refund_id")
+ private String couponRefundId;
+
+ public String getReturnCode() {
+ return returnCode;
+ }
+
+ public void setReturnCode(String returnCode) {
+ this.returnCode = returnCode;
+ }
+
+ public String getReturnMsg() {
+ return returnMsg;
+ }
+
+ public void setReturnMsg(String returnMsg) {
+ this.returnMsg = returnMsg;
+ }
+
+ public String getResultCode() {
+ return resultCode;
+ }
+
+ public void setResultCode(String resultCode) {
+ this.resultCode = resultCode;
+ }
+
+ public String getErrCode() {
+ return errCode;
+ }
+
+ public void setErrCode(String errCode) {
+ this.errCode = errCode;
+ }
+
+ public String getErrCodeDes() {
+ return errCodeDes;
+ }
+
+ public void setErrCodeDes(String errCodeDes) {
+ this.errCodeDes = errCodeDes;
+ }
+
+ public String getAppid() {
+ return appid;
+ }
+
+ public void setAppid(String appid) {
+ this.appid = appid;
+ }
+
+ public String getMchId() {
+ return mchId;
+ }
+
+ public void setMchId(String mchId) {
+ this.mchId = mchId;
+ }
+
+ public String getDeviceInfo() {
+ return deviceInfo;
+ }
+
+ public void setDeviceInfo(String deviceInfo) {
+ this.deviceInfo = deviceInfo;
+ }
+
+ public String getNonceStr() {
+ return nonceStr;
+ }
+
+ public void setNonceStr(String nonceStr) {
+ this.nonceStr = nonceStr;
+ }
+
+ public String getSign() {
+ return sign;
+ }
+
+ public void setSign(String sign) {
+ this.sign = sign;
+ }
+
+ 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 getOutRefundNo() {
+ return outRefundNo;
+ }
+
+ public void setOutRefundNo(String outRefundNo) {
+ this.outRefundNo = outRefundNo;
+ }
+
+ public String getRefundId() {
+ return refundId;
+ }
+
+ public void setRefundId(String refundId) {
+ this.refundId = refundId;
+ }
+
+ public String getRefundChannel() {
+ return refundChannel;
+ }
+
+ public void setRefundChannel(String refundChannel) {
+ this.refundChannel = refundChannel;
+ }
+
+ public String getRefundFee() {
+ return refundFee;
+ }
+
+ public void setRefundFee(String refundFee) {
+ this.refundFee = refundFee;
+ }
+
+ public String getTotalFee() {
+ return totalFee;
+ }
+
+ public void setTotalFee(String totalFee) {
+ this.totalFee = totalFee;
+ }
+
+ public String getFeeType() {
+ return feeType;
+ }
+
+ public void setFeeType(String feeType) {
+ this.feeType = feeType;
+ }
+
+ public String getCashFee() {
+ return cashFee;
+ }
+
+ public void setCashFee(String cashFee) {
+ this.cashFee = cashFee;
+ }
+
+ public String getCashRefundfee() {
+ return cashRefundfee;
+ }
+
+ public void setCashRefundfee(String cashRefundfee) {
+ this.cashRefundfee = cashRefundfee;
+ }
+
+ public String getCouponRefundFee() {
+ return couponRefundFee;
+ }
+
+ public void setCouponRefundFee(String couponRefundFee) {
+ this.couponRefundFee = couponRefundFee;
+ }
+
+ public String getCouponRefundCount() {
+ return couponRefundCount;
+ }
+
+ public void setCouponRefundCount(String couponRefundCount) {
+ this.couponRefundCount = couponRefundCount;
+ }
+
+ public String getCouponRefundId() {
+ return couponRefundId;
+ }
+
+ public void setCouponRefundId(String couponRefundId) {
+ this.couponRefundId = couponRefundId;
+ }
+
+ @Override
+ public String toString() {
+ return "[" +
+ "return_code:" + returnCode + ";" +
+ "return_msg" + returnMsg + ";";
+ }
+
+}