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 d0e2c190..b28ce775 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 25f86e56..e9f6ee0a 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(Mapparameters) 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 "); + + 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(Mappara : refundParams.entrySet()) { + request.append(String.format("<%s>%s%s>", para.getKey(), para.getValue(), para.getKey())); + } + request.append(" 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 00000000..1e8327e3 --- /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 + ";"; + } + +}