微信支付之微信退款


官方文档

无论刷卡支付、微信公众号支付、扫码支付、H5支付、APP支付等都有申请退款API并且接口一样,本篇文章就拿刷卡支付中的申请退款API文档举例

应用场景

当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上

注意事项

  1. 交易时间超过一年的订单无法提交退款
  2. 微信支付退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。申请退款总金额不能超过订单金额。一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号
  3. 请求频率限制:150qps,即每秒钟正常的申请退款请求次数不超过150次
  4. 错误或无效请求频率限制:6qps,即每秒钟异常或错误的退款申请请求不超过6次
  5. 每个支付订单的部分退款次数不能超过50次

下载证书

参考安全规范-3.商户证书

下载:微信商户平台(pay.weixin.qq.com)-->账户中心-->账户设置-->API安全-->证书下载

IJPay 中的接口

https://github.com/Javen205/IJPay https://githee.com/Javen205/IJPay

com.jpay.weixin.api.WxPayApi.java

    // 申请退款
    private static final String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";
    // 查询退款
    private static final String REFUNDQUERY_URL = "https://api.mch.weixin.qq.com/pay/refundquery";
    // 申请退款
    private static final String REFUND_SANDBOXNEW_URL = "https://api.mch.weixin.qq.com/sandboxnew/secapi/pay/refund";
    // 查询退款
    private static final String REFUNDQUERY_SANDBOXNEW_URL = "https://api.mch.weixin.qq.com/sandboxnew/pay/refundquery";

    /**
     * 申请退款
     * 服务商模式接入文档:https://pay.weixin.qq.com/wiki/doc/api/micropay_sl.php?chapter=9_4
     * 商户模式接入文档:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_4
     * 
     * @param isSandbox
     *            是否是沙盒环境
     * @param params
     *            请求参数
     * @param certPath
     *            证书文件目录
     * @param certPass
     *            证书密码
     * @return {String}
     */
    public static String orderRefund(boolean isSandbox, Map<String, String> params, String certPath, String certPass) {
        if (isSandbox)
            return doPostSSL(REFUND_SANDBOXNEW_URL, params, certPath, certPass);
        return doPostSSL(REFUND_URL, params, certPath, certPass);
    }

    /**
     * 查询退款
     * 服务商模式接入文档:https://pay.weixin.qq.com/wiki/doc/api/micropay_sl.php?chapter=9_5
     * 商户模式接入文档:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_5
     * 
     * @param isSandbox
     *            是否是沙盒环境
     * @param params
     *            请求参数
     * @return {String}
     */
    public static String orderRefundQuery(boolean isSandbox, Map<String, String> params) {
        if (isSandbox)
            return doPost(REFUNDQUERY_SANDBOXNEW_URL, params);
        return doPost(REFUNDQUERY_URL, params);
    }

示例代码

https://github.com/Javen205/IJPay-Demo https://githee.com/Javen205/IJPay-Demo

com.ijpay.controller.weixin.WxPayController.java

    /**
     * 微信退款
     */
    public void refund() {
        String transaction_id = getPara("transactionId");
        String out_trade_no = getPara("out_trade_no");
        
        if (StrKit.isBlank(out_trade_no) && StrKit.isBlank(transaction_id)) {
            renderText("transactionId、out_trade_no二选一");
            return;
        }
        
        Map<String, String> params = new HashMap<String, String>();
        params.put("appid", appid);
        params.put("mch_id", mch_id);
        params.put("nonce_str", System.currentTimeMillis()+"");
        if (StrKit.notBlank(transaction_id)) {
            params.put("transaction_id", transaction_id);
        }else {
            params.put("out_trade_no", out_trade_no);
        }
        params.put("out_refund_no", System.currentTimeMillis()+"");
        params.put("total_fee", "1");
        params.put("refund_fee", "1");
        params.put("sign", PaymentKit.createSign(params, partnerKey));
        String refund = WxPayApi.orderRefund(false, params , certPath, mch_id);
        renderText(refund);
    }
    
    /**
     * 微信退款查询
     */
    public void refundQuery() {
        String transaction_id = getPara("transactionId");
        String out_trade_no = getPara("out_trade_no");
        String out_refund_no = getPara("out_refund_no");
        String refund_id = getPara("refund_id");
        
        Map<String, String> params = new HashMap<String, String>();
        params.put("appid", appid);
        params.put("mch_id", mch_id);
        params.put("nonce_str", System.currentTimeMillis()+"");
        params.put("transaction_id", transaction_id);
        params.put("out_trade_no", out_trade_no);
        params.put("out_refund_no", out_refund_no);
        params.put("refund_id", refund_id);
        params.put("out_refund_no", System.currentTimeMillis()+"");
        params.put("sign", PaymentKit.createSign(params, partnerKey));
        String refund = WxPayApi.orderRefundQuery(false, params);
        renderText(refund);
    }

同步返回结果XML参考

//退款
<xml><return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[wxxx]]></appid>
<mch_id><![CDATA[xxxx]]></mch_id>
<nonce_str><![CDATA[22hvoyIn52VVVksU]]></nonce_str>
<sign><![CDATA[ED121D6583909150308C76ACFB6FC74B]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<transaction_id><![CDATA[4200000100201801133414066xxx]]></transaction_id>
<out_trade_no><![CDATA[1515845954891]]></out_trade_no>
<out_refund_no><![CDATA[1519535580802]]></out_refund_no>
<refund_id><![CDATA[50000405502018022503594217469]]></refund_id>
<refund_channel><![CDATA[]]></refund_channel>
<refund_fee>1</refund_fee>
<coupon_refund_fee>0</coupon_refund_fee>
<total_fee>1</total_fee>
<cash_fee>1</cash_fee>
<coupon_refund_count>0</coupon_refund_count>
<cash_refund_fee>1</cash_refund_fee>
</xml>


//退款查询错误示例
<xml>
<appid><![CDATA[wxxxx]]></appid>
<err_code><![CDATA[REFUNDNOTEXIST]]></err_code>
<err_code_des><![CDATA[not exist]]></err_code_des>
<mch_id><![CDATA[xxx]]></mch_id>
<nonce_str><![CDATA[SG1IAYtK0apvnHpF]]></nonce_str>
<result_code><![CDATA[FAIL]]></result_code>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<sign><![CDATA[7323459E0DE45DBB8C87932C4C99BBF3]]></sign>
</xml>

//退款查询

<xml>
<appid><![CDATA[wxxxx]]></appid>
<cash_fee><![CDATA[1]]></cash_fee>
<mch_id><![CDATA[xxx]]></mch_id>
<nonce_str><![CDATA[KptCSXZh1qBjK8wb]]></nonce_str>
<out_refund_no_0><![CDATA[1519535580802]]></out_refund_no_0>
<out_trade_no><![CDATA[1515845954891]]></out_trade_no>
<refund_account_0><![CDATA[REFUND_SOURCE_UNSETTLED_FUNDS]]></refund_account_0>
<refund_channel_0><![CDATA[ORIGINAL]]></refund_channel_0>
<refund_count>1</refund_count>
<refund_fee>1</refund_fee>
<refund_fee_0>1</refund_fee_0>
<refund_id_0><![CDATA[50000405502018022503594217469]]></refund_id_0>
<refund_recv_accout_0><![CDATA[支付用户的零钱]]></refund_recv_accout_0>
<refund_status_0><![CDATA[SUCCESS]]></refund_status_0>
<refund_success_time_0><![CDATA[2018-02-25 13:13:03]]></refund_success_time_0>
<result_code><![CDATA[SUCCESS]]></result_code>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<sign><![CDATA[E8A30F02296C6169860A92C2D52AD5A8]]></sign>
<total_fee><![CDATA[1]]></total_fee>
<transaction_id><![CDATA[4200000100201801133414066940]]></transaction_id>
</xml>

微信支付、支付宝支付博客专栏

如有疑问欢迎留言交流讨论

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏移动端周边技术扩展

Axios跨域处理方案

生产与开发环境都配置完成了,开发环境可直接进行跨域请求,生产环境还需要后端进行配合处理

12520
来自专栏后端技术探索

获取客户端的真实IP(而非代理服务器IP) 的一个实例

在api程序中,某个业务场景需要通过获取用户app的真实ip,因为中间通过代理代理服务器,用 REMOTE_ADDR 头信息肯定获取不到app的ip,从goog...

40410
来自专栏编程微刊

百度地图API的使用示例

刚刚工作的时候写过百度地图API文档,那时候没有记录到技术博客里面,今天在群里看见有个姑娘在问这个问题,重温了一遍,这个API还算好用。百度地图API,集成简单...

14040
来自专栏Python

根据ip查询经纬度的接口

13020
来自专栏后端技术探索

2016 PHP大会,可以受教php之父Rasmus Lerdorf

PHP全球开发者大会是DevLink每年一度的,特别针对PHP开发者的专题活动。每次活动均会请到该领域内的资深开发者、技术专家来分享具体、有针对性、具操作性的内...

22430
来自专栏后端技术探索

后端Api设计的一些注意项

App所有数据都来源于服务器,App和服务器交互普遍是采用http请求接口的方式,那么在搭建和维护一个后端Api项目时候需要注意哪些问题呢?

19430
来自专栏后端技术探索

nginx实现内容重定向的两种方式:rewrite和反向代理

当访问test.cc/uri,如果uri资源在项目中不存在,就会重定向 http://www.yuhaiwei.net/blog/Api_element 网页。

9510
来自专栏Java后端技术栈

分布式之消息队列复习精讲!

小A,工作于传统软件行业(某社保局的软件外包公司),每天工作内容就是和产品聊聊需求,改改业务逻辑。再不然就是和运营聊聊天,写几个SQL,生成下报表。又或者接到客...

8830
来自专栏Java后端技术栈

前后端分离后API交互如何保证数据安全性?

前后端分离的开发方式,我们以接口为标准来进行推动,定义好接口,各自开发自己的功能,最后进行联调整合。无论是开发原生的APP还是webapp还是PC端的软件,只要...

57710
来自专栏程序你好

REST API和SOAP API之间的区别

The Representational State Transfer (REST)架构风格不是可以购买的技术,也不是可以添加到软件开发项目中的库。REST是一...

19510

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励