关于微信退款
一、官方文档
申请退款:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_4&index=6
二、退款流程图
注意:微信退款时候,需要证书服务,这里可以参见官方文档中的说明,包括证书的具体下载,解析等。(后面我也补充个证书解析的文件,方便理解)
一个百度文库的文档:https://wenku.baidu.com/view/754c78e93186bceb19e8bbcc.html
三、项目中使用的代码
拿来作个参考吧,方便记忆和理解
/** * 微信支付申请退款 * * @param weChatPayRefund * @return */ public boolean weChatPayRefundRequest(WeChatPayRefund weChatPayRefund, String source) { try { if (null == weChatPayRefund) { throw new ArgumentException("参数异常"); } if (Strings.isNullOrEmpty(weChatPayRefund.getRefund_fee())) { throw new ArgumentException("退款金额为空"); } if (Strings.isNullOrEmpty(weChatPayRefund.getOut_trade_no())) { throw new ArgumentException("原交易编号为空"); } //退款金额,微信金额单位为分,系统为元,做换算 int refundFee = new BigDecimal(weChatPayRefund.getRefund_fee()) .multiply(new BigDecimal(100)).intValue(); weChatPayRefund.setRefund_fee(String.valueOf(refundFee)); //订单总金额单位为分 int totalFee = new BigDecimal(weChatPayRefund.getTotal_fee()) .multiply(new BigDecimal(100)).intValue(); weChatPayRefund.setTotal_fee(String.valueOf(totalFee)); //生成随机流水号 String timeStr = System.currentTimeMillis() + ""; weChatPayRefund.setOut_refund_no("TK" + timeStr); //退款单号为UUID weChatPayRefund.setNonce_str(UUIDUtil.randrom()); //生成退款参数XML String xml = this.createRefundXml(weChatPayRefund, source); String refundURL = "https://api.mch.weixin.qq.com/secapi/pay/refund"; ClientCustomSSL clientCustomSSL = new ClientCustomSSL(); String result = ""; if ("01".equals(source)) { result = clientCustomSSL.doRefund(refundURL, xml, weChatPkcs12, "B2B"); } else if ("02".equals(source)) { result = clientCustomSSL.doRefund(refundURL, xml, b2cWeChatPkcs12, "B2C"); } else if ("03".equals(source)) { result = clientCustomSSL.doRefund(refundURL, xml, b2cWeChatPkcs12_new, "B2C_NEW"); } //请求返回结果 result = result.replaceAll(" ", "").replaceAll(" ", "").replaceAll(" ", ""); Document doc = DocumentHelper.parseText(result); Map<String, Object> resultMap = XmlMapHandle.Dom2Map(doc); String applyId = weChatPayRefund.getApplyId(); StringHashMapper stringHashMapper = new StringHashMapper(WeChatPayRefund.class); WeChatPayRefund weChatPay = (WeChatPayRefund) stringHashMapper.fromHash(resultMap); ConvenienceRefund refund = createRefundInfo(weChatPayRefund);
//根据返回结果,处理一些自己的业务 if (weChatPay.getReturn_code().equalsIgnoreCase("SUCCESS") && weChatPay.getResult_code().equalsIgnoreCase("SUCCESS")) { weChatPay.setApplyId(applyId); refund.setRefundStatus("1"); this.rechargeOrderWriteDao.saveRefundInfo(refund);//保存退款信息 int updateFlag = rechargeOrderWriteDao.updateOrderPayStatus(RedisType.PAY_STATUS2.getIndex(), weChatPayRefund.getOut_trade_no()); if (updateFlag >= 1) { //退款成功 //订单日志保存 ConvenienceOrderLog orderLog = new ConvenienceOrderLog(); orderLog.setOrderId(String.valueOf(weChatPayRefund.getOut_trade_no())); orderLog.setStatusCode(50); orderLog.setStatusName("退款成功"); this.saveConvenienceOrderLog(orderLog); return true; } else { //退款失败 return false; } } else { refund.setRefundStatus("0"); this.rechargeOrderWriteDao.saveRefundInfo(refund); ConvenienceOrderLog orderLog = new ConvenienceOrderLog(); orderLog.setOrderId(String.valueOf(weChatPayRefund.getOut_trade_no())); orderLog.setStatusCode(99); orderLog.setStatusName("退款失败"); throw new BusinessException("退款失败:" + weChatPayRefund.getReturn_msg()); } } catch (Exception e) { throw new BusinessException("退款失败:请联系管理员"); } } /** * 根据订单来源生成退款参数XML * * @param weChatPayRefund * @param source * @return */ public String createRefundXml(WeChatPayRefund weChatPayRefund, String source) { String appId = ""; String mchId = ""; String key = ""; if ("01".equals(source)) { appId = weChatPayRefund.getB2b_appid_new(); mchId = weChatPayRefund.getB2b_mch_id_new(); key = weChatPayRefund.getB2b_appKey_new(); } else if ("02".equals(source)) { appId = weChatPayRefund.getB2c_appid(); mchId = weChatPayRefund.getB2c_mch_id(); key = weChatPayRefund.getB2c_appKey(); } else if ("03".equals(source)) { appId = weChatPayRefund.getB2c_appid_new(); mchId = weChatPayRefund.getB2c_mch_id_new(); key = weChatPayRefund.getB2c_appKey_new(); } //生成退款签名 SortedMap<String, String> packageParams = new TreeMap<String, String>(); packageParams.put("appid", appId); packageParams.put("mch_id", mchId); packageParams.put("nonce_str", weChatPayRefund.getNonce_str()); packageParams.put("out_trade_no", weChatPayRefund.getOut_trade_no()); packageParams.put("out_refund_no", weChatPayRefund.getOut_refund_no()); packageParams.put("total_fee", weChatPayRefund.getTotal_fee()); packageParams.put("refund_fee", weChatPayRefund.getRefund_fee()); packageParams.put("op_user_id", mchId); RequestHandler reqHandler = new RequestHandler(null, null); reqHandler.init(appId, "", key); String sign = reqHandler.createSign(packageParams); String xml = org.apache.commons.lang3.StringUtils.join("<xml>", "<appid>", appId, "</appid>", "<mch_id>", mchId, "</mch_id>", "<nonce_str>", weChatPayRefund.getNonce_str(), "</nonce_str>", "<sign><![CDATA[", sign, "]]></sign>", "<out_trade_no>", weChatPayRefund.getOut_trade_no(), "</out_trade_no>", "<out_refund_no>" + weChatPayRefund.getOut_refund_no(), "</out_refund_no>", "<total_fee>", weChatPayRefund.getTotal_fee(), "</total_fee>", "<refund_fee>" + weChatPayRefund.getRefund_fee(), "</refund_fee>", "<op_user_id>" + mchId, "</op_user_id>", "</xml>"); return xml; }
四、引申与其他
(待续..)