业务场景:由于做的是C端的产品,社交电商。负责的是订单售后部分,微信部分是一个单独项目,另外一个项目基本都是业务实现,二个项目也是通过htpps 方式来交互。这里主要介绍微信退款,查询退款记录部分。不展开介绍订单售后部分的逻辑实现(如下图,已经上线)。


**
微信小程序查询退款与申请退款操作**
查询退款:官方文档
申请退款:官方文档
查询退款
1.主要是通过商户订单号 out_trade_no 也就是订单号来进行退款
2.四个必传的参数:appid,mch_id,nonce_str,sign
3.微信返回都是xml格式,将xml转换为map并用json形式返回
4.官方文档的参数

查询退款接口设计(如下):
查询退款接口
url: /weix/queryRefund
请求方式: POST
参数:
参数 | 类型 | 是否必传 | 说明 | 是否请求头 |
---|---|---|---|---|
orderNo | String | yes | 订单号 | n |
返回结果:
参数 | 类型 | 说明 | 示例 |
---|---|---|---|
openId | String | 退款人的openid | oii6K5fN9K5Q4wvuF8-hlA2q7TpY |
outTradeNo | String | 订单号 | 154840066566078463640 |
refundFee | int | 退款金额 | 3(分) |
totalFee | int | 订单总金额 | 3 |
controller 层
/**
* 查询退款
* @param params
* @return
*/
@RequestMapping("queryRefund")
@ResponseBody
public Object queryRefund(@RequestBody String params){
String s = wxApiService.queryRefund(JSONObject.parseObject(params));
Map<String, String> stringStringMap = null;
try {
stringStringMap = WXPayUtil.xmlToMap(s);
} catch (Exception e) {
e.printStackTrace();
}
return RespResult.respOK(stringStringMap);
}
Service层的queryRefund 方法
/**
* 查询退款
*
* @param jsonObject
* @return
*/
public String queryRefund(JSONObject jsonObject) {
try {
Map<String, String> map = new HashMap<>();
map.put("appid", ConfigUtil.getProperty("appid"));
map.put("mch_id", ConfigUtil.getProperty("mch_id"));
map.put("nonce_str", StringUtils.getUUID());
map.put("out_trade_no", jsonObject.getString("orderNo")); //商户生成的订单号
map.put("sign", WXPayUtil.generateSignature(map, ConfigUtil.getProperty("key"), WXPayConstants.SignType.MD5));
String xml = WXPayUtil.mapToXml(map);
String s = HttpTools.httpClientByPostXmlWithCert("https://api.mch.weixin.qq.com/pay/refundquery", xml, "utf-8");
return s;
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
申请退款 ---
退款接口
1.与付款相比,退款就简单多了,有6个必传参数如上图,付款是通过 out_trade_no 商户订单号 来进行,退款也根据商户订单号来退款。
2.total_fee 订单金额(也就是支付金额), refund_fee 退款金额,都是以分为单位。一般付款与退款都会有事物的,退款时退款金额是不允许超过支付金额的,可以小于支付金额。
3.这里也会有一个小坑,已经暴露在代码中。在测试环境商户余额不足的情况下,退款就会失败。
4.官方的退款接口参数如下图:

5.我厂设计的接口如下:
url: /weix/refund
请求方式: POST
参数:
参数 | 类型 | 是否必传 | 说明 | 是否请求头 |
---|---|---|---|---|
orderNo | String | Y | 订单号 | N |
total_fee | int | Y | 退款金额 | N |
Controller 层
/**
* 退款
* @return
*/
@RequestMapping("refund")
@ResponseBody
public Object refund(@RequestBody String params, HttpServletRequest request){
boolean check = wxIpInterfaceService.check(request);
if(check){
return RespResult.respFailureMsg("ip不对哦 小伙子");
}
JSONObject jsonObject = JSONObject.parseObject(params);
String orderNo = jsonObject.getString("orderNo");
String ipAddress = getIpAddress(request);
logger.info("退款ip=================="+ipAddress+"退款单号====="+ orderNo);
WxOrder wxOrder = new WxOrder();
wxOrder.setOutTradeNo(orderNo);
List<WxOrder> orders = wxOrderService.queryRefuseByOutTradeNo(wxOrder);
if(orders.isEmpty()){
return RespResult.respFailureMsg("订单不存在");
}
String refund = "";
for (WxOrder order: orders) {
orderNo = order.getOutTradeNo();
jsonObject.put("out_trade_no",orderNo);
jsonObject.put("refund_fee",jsonObject.getString("total_fee"));
jsonObject.put("total_fee", order.getTotalFee());
refund = wxApiService.sendWxPayRefund(jsonObject);
if(null == refund){
return RespResult.respFailureMsg("商户号余额不足,请充值");
}
WxRefund wxRefund = new WxRefund();
wxRefund.setOutTradeNo(orderNo);
wxRefund.setRefundFee(order.getTotalFee());
wxRefund.setTotalFee(jsonObject.getInteger("total_fee"));
wxRefund.setOpenId(order.getOpenid());
wxRefundService.save(wxRefund);
}
return RespResult.respOK();
}
Service 层
/**
* 退款
*
* @param jsonObject
* @return
*/
public String sendWxPayRefund(JSONObject jsonObject) {
try {
Map<String, String> map = new HashMap<>();
map.put("appid", ConfigUtil.getProperty("appid"));
map.put("mch_id", ConfigUtil.getProperty("mch_id"));
map.put("nonce_str", StringUtils.getUUID());
map.put("out_trade_no", jsonObject.getString("out_trade_no")); //商户生成的订单号
map.put("out_refund_no", StringUtils.getUUID()); //退款单号
map.put("total_fee", jsonObject.getString("total_fee")); //订单总额
map.put("refund_fee", jsonObject.getString("refund_fee")); //退款金额
map.put("sign", WXPayUtil.generateSignature(map, ConfigUtil.getProperty("key"), WXPayConstants.SignType.MD5));
String xml = WXPayUtil.mapToXml(map);
String s = HttpTools.httpClientByPostXmlWithCert("https://api.mch.weixin.qq.com/secapi/pay/refund", xml, "utf-8");
if (s.contains("基本账号余额不足")) {
return null;
}
return s;
} catch (Exception e) {
e.printStackTrace();
}
return "";
}