1. 增加微信退款接口 2. 增加取得卡券详情接口 3. 修改取卡券签名接口的说明 4. 将卡券核销接口的返回值由Void改为String Change-Id: I2bac2d090871a4988f7279a9794eca5722451cdd Signed-off-by: Liu Kai <liukai@tinkers.com.cn>master
@@ -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。以下几项为参数的必须项:<br/> | |||
* <li/> transaction_id | |||
* <li/> out_trade_no (仅在上述transaction_id为空时是必须项) | |||
* <li/> out_refund_no | |||
* <li/> total_fee | |||
* <li/> refund_fee | |||
* @return 退款操作结果 | |||
* @throws WxErrorException | |||
*/ | |||
public WxMpPayRefundResult refundPay(Map<String, String> parameters) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 计算Map键值对是否和签名相符, | |||
@@ -812,6 +826,7 @@ public interface WxMpService { | |||
* | |||
* @param optionalSignParam 参与签名的参数数组。 | |||
* 可以为下列字段:app_id, card_id, card_type, code, openid, location_id | |||
* </br>注意:当做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字符串。 | |||
* <br>可用 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字符串 | |||
* <br> [注] 由于返回的JSON格式过于复杂,难以定义其对应格式的Bean并且难以维护,因此只返回String格式的JSON串。 | |||
* <br> 可由 com.google.gson.JsonParser#parse 等方法直接取JSON串中的某个字段。 | |||
* @throws WxErrorException | |||
*/ | |||
public String getCardDetail(String cardId) throws WxErrorException; | |||
} |
@@ -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<String, String> parameters) throws WxErrorException { | |||
SortedMap<String, String> refundParams = new TreeMap<String, String>(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("<xml>"); | |||
for (Entry<String, String> para : refundParams.entrySet()) { | |||
request.append(String.format("<%s>%s</%s>", para.getKey(), para.getValue(), para.getKey())); | |||
} | |||
request.append("</xml>"); | |||
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<String, String> 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 | |||
* </br>注意:当做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; | |||
} | |||
} |
@@ -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 + ";"; | |||
} | |||
} |