关于微信退款

一、官方文档

申请退款: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. eclipse maven 刷新报错

    问题描述: An internal error occurred during: "Loading descriptor for cmbc_wms.".java.lang.Null ...

  2. python发布IIS

    参考文档 https://segmentfault.com/a/1190000008909201 http://blog.51cto.com/anngle/1922041 https://www.cn ...

  3. SPOJ LCS2 - Longest Common Substring II 后缀自动机 多个串的LCS

    LCS2 - Longest Common Substring II no tags  A string is finite sequence of characters over a non-emp ...

  4. VI带行号查看

        :set nu         带行号查看,并不改变文件内容 :set nonu     取消带行号查看 在每个用户的主目录下,都有一个 vi 的配置文件".vimrc"或 ...

  5. Asp.net MVC3 异常全局过滤器处理

    1.建立异常全局过滤器处理机制,在Gloabal.asax.cs文件中,有如下代码块: public static void RegisterGlobalFilters(GlobalFilterCol ...

  6. c++中的基本知识点

    1 class和struct的区别和联系 在c++中,class和struct只有一点不同,它们是可以完全替代使用的.唯一的不同在于,class中的成员默认是private的,而struct中默认是p ...

  7. ios导航栏问题

    http://m.blog.csdn.net/article/details?id=47395605

  8. lasso the moon

  9. [持续集成]Jenkins 自动化部署 Maven 工程

    一.Jenkins 持续部署原理图 基础服务: 1 SVN 服务 SVN是Subversion的简称,是一个开放源代码的版本控制系统.说得简单一点SVN就是用于多个人共同开发同一个项目,共用资源的目的 ...

  10. 开发的第一个PHP扩展

    下载php源码php-5.4.23.tar.gz,解压,进入/home/hubo/php-5.4.23/ext/扩展目录 wget http://cn2.php.net/get/php-5.4.23. ...