关于微信退款

一、官方文档

申请退款: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. 提高Interface Builder高效工作的8个技巧

    本文转载至 http://www.cocoachina.com/ios/20141106/10151.html iOS开发Interface Builder 本文译自:8 Tips for worki ...

  2. coreseek中文搜索

    coreseek的安装和使用 准备软件包 coreseek-3.2.14.tar.gz 其他汁源 coreseek中文索引-示例文件.zip sphinx配置文件详解.txt 1.安装组件 yum - ...

  3. 常用BAPI list

    2017-03-25 MD 主数据 1.创建物料主数据 CALL FUNCTION 'BAPI_MATERIAL_SAVEDATA' 2.创建供应商 CALL METHOD VMD_EI_API=&g ...

  4. Appium 1.9.1 启动报错 Fatal TypeError: Class constructor BaseDriver cannot be invoked without 'new'

    安装了appium 1.9.1后一直报错Fatal TypeError: Class constructor BaseDriver cannot be invoked without 'new',无法 ...

  5. 【linux】让普通用户执行root的程序

    再有些时候,比如zabbix监控中,需要使用netstat命令查看当前网络链接状态,但是zabbix用户没有权限执行netstat,会导致监控失败,为此使用如下即可解决 chmod +s /bin/n ...

  6. 使用 Spring 容器管理 Filter

    当我们用Filter时,往往需要使用一些辅助的service,在普通的java中,只要声明(set,get方法)后在spring-application配置文件中配置就可以了,但是由于Filter与L ...

  7. 爬虫前戏(回顾掌握) -- HTTP和HTTPS

    一.HTTP协议 1.官方概念: HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文 ...

  8. 吴恩达机器学习笔记(十一) —— Large Scale Machine Learning

    主要内容: 一.Batch gradient descent 二.Stochastic gradient descent 三.Mini-batch gradient descent 四.Online ...

  9. BZOJ 1041 [HAOI2008]圆上的整点:数学【费马平方和定理】

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1041 题意: 给定n(n <= 2*10^9),问你在圆x^2 + y^2 = n^ ...

  10. linux命令学习笔记(24):Linux文件类型与扩展名

    Linux文件类型和Linux文件的文件名所代表的意义是两个不同的概念.我们通过一般应用程序 而创建的比如file.txt.file.tar.gz ,这些文件虽然要用不同的程序来打开,但放在Linux ...