关于微信退款

一、官方文档

申请退款: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("\n", "").replaceAll("\r", "").replaceAll("\t", "");
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;
}

四、引申与其他

(待续..)

<正则吃饺子> :关于微信支付的简单总结说明(二)的更多相关文章

  1. <正则吃饺子>:关于集合的简单整理总结

    项目中用到的集合不可谓不多,对于自己的一次面试,要求说下自己用过的集合,自己开始说的并不系统也不完整,一直耿耿于怀,特整理一下,以备后期之用和帮助后来者. package com.love.malin ...

  2. <正则吃饺子>:关于java中垃圾回收技术的简单学习总结

    知识介绍来自网络,后面会根据继续学习进行补充和适当的修改,谢谢!原文地址:http://www.importnew.com/26821.html#comment-578355 java中的垃圾回收机制 ...

  3. <正则吃饺子> :关于微信支付的简单总结说明(一)

    关于支付,一直想参与开发,现在根据项目中已有及参见的微信开发文档,将自己对于微信开发的流程进行简单的总结,以备后用和帮助后来者. 一.相关官方文档 微信支付官方文档:https://pay.weixi ...

  4. 微信支付生成带logo的二维码

    利用到一个qrcode类 比较简洁 原作者没有加入二维码嵌入logo的功能 在这里我进行了小小的修改 可以实现生成微信支付二维码时打上logo 生成png格式的利用到该类中的png方法(我已经改好了) ...

  5. <正则吃饺子>:关于java中对内存部分的简单总结整理

    在项目和一些群讨论中,经常看到对内存的处理,但是,自己确是一知半解的,基于此,就把这部分的知识简单的整理了下,知识点来源于网络博文,也一一标明出处,谢谢. package com.love.malin ...

  6. <正则吃饺子> :关于 Matcher 的 replaceAll 的简单使用

    在线文档地址:http://tool.oschina.net/apidocs/apidoc?api=jdk-zh replaceAll public String replaceAll(String  ...

  7. <正则吃饺子> :关于oracle 中 with的简单使用

    oracle中 with的简单使用介绍,具体可以参见其他的博文介绍,在这里只是简单的介绍: with 构建了一个临时表,类似于存储过程中的游标,我是这么理解的. 一.数据准备: select * fr ...

  8. <正则吃饺子> :关于oracle 中 exists 、not exists 的简单使用

    话不多说,简单的总结而已.网络上很多很详细介绍. 例如,博文:http://blog.csdn.net/zhiweianran/article/details/7868894  当然这篇也是转载的,原 ...

  9. <正则吃饺子> :关于Guava中 Joiner 和 Splitter 的简单使用

    在现在项目中经常看到 这两个类的使用,开始时候不明白具体是做的什么事情,就单独拿出来学习下了,参照了网上的博文,这里主要是简单的讲讲用法. 具体对这两个类,不做过多介绍,有个在线文档,需要的可以自己去 ...

随机推荐

  1. View数据呈现相关技术

    一.了解Razor语法 1.Razor基本语法 a)输出单一变量时不需要加分号做结尾.如: <p>现在时刻:@DateTime.Now</p> b)输出一段含有空白字元或运算子 ...

  2. VC++ 非托管代码 & 托管代码

    #pragma managed #pragma unmanaged 看了好多好多非托管代码和托管代码之间相互调用,感觉都没有说在重点上,到底怎么用才是关键,理论的东西我们到微软官网上就可以找到,毕竟这 ...

  3. 高性能流媒体服务器EasyDSS前端重构(三)- webpack + vue + AdminLTE 多页面引入 element-ui

    接上篇 接上篇<高性能流媒体服务器EasyDSS前端重构(二) webpack + vue + AdminLTE 多页面提取共用文件, 优化编译时间> 本文围绕着实现EasyDSS高性能流 ...

  4. python venv虚拟环境

    1 目的 给python应用一个独立的运行环境,独立于其它的python应用也独立于系统的python环境. 环境升级不影响其它应用. 避免包冲突. 2 创建方式 2.1 pycharm中创建 pro ...

  5. Bloom Filters

    http://pages.cs.wisc.edu/~cao/papers/summary-cache/node8.html A Bloom filter is a method for represe ...

  6. js实现随机选取[10,100)中的10个整数,存入一个数组,并排序。 另考虑(10,100]和[10,100]两种情况。

    1.js实现随机选取[10,100)中的10个整数,存入一个数组,并排序. <!DOCTYPE html> <html lang="en"> <hea ...

  7. 我的Java开发学习之旅------>工具类:将播放器的进度值转换成相应的时间格式

    在我的博客<我的Java开发学习之旅------>Java 格式化类(java.util.Formatter)基本用法,地址:http://blog.csdn.net/ouyang_pen ...

  8. 畅游HttpCore

    欢迎 非常欢迎阅读本文,本文主要介绍HttpCore是如何工作的. 你应该知道HTTP是一种用于在客户端与服务端进行消息交换的协议.它使用的特别广泛,它通常运行在TCP/IP或者安全的TLS/SSL之 ...

  9. 验证控件 .net

    检查Page.IsValid   if (typeof (Page_ClientValidate) == 'function') {                Page_ClientValidat ...

  10. CodeForces - 540D Bad Luck Island —— 求概率

    题目链接:https://vjudge.net/contest/226823#problem/D The Bad Luck Island is inhabited by three kinds of ...