微信支付-退款(v3版微信支付)

2024-05-31 17:18

本文主要是介绍微信支付-退款(v3版微信支付),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

做完微信支付,如果遇到顾客需要退款的情况,我们就要调用微信的退款接口进行对款操作。下面大致介绍下微信支付中退款的流程、主要代码以及一些我测出的bug解决方法。

先说下我们需要哪些jar包以及微信给我们的证书。

证书:apiclient_cert.p12。

jar包:commons-codec-1.6.jar   commons-logging-1.1.3.jar     fluent-hc-4.3.4.jar     httpclient-4.3.4.jar    httpclient-cache-4.3.4.jar     httpcore-4.3.2.jar    httpmime-4.3.4.jar

上面7个jar包,微信官网文档对应的demo里面都有。官网下载地址:https://mp.weixin.qq.com/paymch/readtemplate?t=mp/business/course3_tmpl&lang=zh_CN。

不想去官网找的,下列地址直接下载即可(象征性要一分下载分):http://download.csdn.net/detail/u011160656/8262955

注意:
1.交易时间超过 1 年的订单无法提交退款;
2.支持部分退款, 部分退需要设置相同的订单号和不同的 out_refund_no。一笔退款失败后重新提交,要采用原来的 out_refund_no。总退款金额丌能超过用户实际支付金额。

看看退款接口中需要哪些参数。


特别注意:图中的refund_fee和total_fee官方文档写的类型为Int,其实我用string也是可以,其中原因不明。

代码如下:代码需要稍作修改,具体修改地方已在main方法中做了文字描述

public static void main(String[] args) throws Exception {
              SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
             parameters.put("appid", "wx0953bae287adfeee");
             parameters.put("mch_id", "你的微信支付商户号");
             parameters.put("nonce_str", CreateNoncestr());
            //在notify_url中解析微信返回的信息获取到 transaction_id,此项不是必填,详细请看上图文档
             parameters.put("transaction_id", "微信支付订单中调用统一接口后微信返回的 transaction_id");
             parameters.put("out_trade_no", "微信支付订单中的out_trade_no");
             parameters.put("out_refund_no", "No.QM20141215002");                              //我们自己设定的退款申请号,约束为UK
             parameters.put("total_fee", "1") ;                                                                          //单位为分
             parameters.put("refund_fee", "1");                                                                      //单位为分
             parameters.put("op_user_id", "你的微信支付商户号");
             String sign = createSign("utf-8", parameters);
             parameters.put("sign", sign);
             
             String reuqestXml = getRequestXml(parameters);
            KeyStore keyStore  = KeyStore.getInstance("PKCS12");
            FileInputStream instream = new FileInputStream(new File("D:/apiclient_cert.p12"));//放退款证书的路径
            try {
                keyStore.load(instream, "你的微信支付商户号".toCharArray());
            } finally {
                instream.close();
            }

            SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, "你的微信支付商户号".toCharArray()).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                    sslcontext,
                    new String[] { "TLSv1" },
                    null,
                    SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
            CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
            try {

                HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");//退款接口
                
                System.out.println("executing request" + httpPost.getRequestLine());
                StringEntity  reqEntity  = new StringEntity(reuqestXml);
                // 设置类型
                reqEntity.setContentType("application/x-www-form-urlencoded");
                httpPost.setEntity(reqEntity);
                CloseableHttpResponse response = httpclient.execute(httpPost);
                try {
                    HttpEntity entity = response.getEntity();

                    System.out.println("----------------------------------------");
                    System.out.println(response.getStatusLine());
                    if (entity != null) {
                        System.out.println("Response content length: " + entity.getContentLength());
                        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(),"UTF-8"));
                        String text;
                        while ((text = bufferedReader.readLine()) != null) {
                            System.out.println(text);
                        }
                       
                    }
                    EntityUtils.consume(entity);
                } finally {
                    response.close();
                }
            } finally {
                httpclient.close();
            }
}
public static String CreateNoncestr() {
            String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            String res = "";
            for (int i = 0; i < 16; i++) {
                Random rd = new Random();
                res += chars.charAt(rd.nextInt(chars.length() - 1));
            }
            return res;
}
public static String createSign(String charSet,SortedMap<Object,Object> parameters){
            StringBuffer sb = new StringBuffer();
            Set es = parameters.entrySet();
            Iterator it = es.iterator();
            while(it.hasNext()) {
                Map.Entry entry = (Map.Entry)it.next();
                String k = (String)entry.getKey();
                Object v = entry.getValue();
                if(null != v && !"".equals(v)
                        && !"sign".equals(k) && !"key".equals(k)) {
                    sb.append(k + "=" + v + "&");
                }
            }
            sb.append("key=" + "zhuhaizhongyuele4008866060liuboz");
            String sign = MD5Util.MD5Encode(sb.toString(), charSet).toUpperCase();
            return sign;
}
public static String getRequestXml(SortedMap<Object,Object> parameters){
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            String v = (String)entry.getValue();
            if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) {
                sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");
            }else {
                sb.append("<"+k+">"+v+"</"+k+">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
}
我们可以获得调用接口后返回的信息(上面main方法中,打印的text就是返回的信息)具体信息如下图:

退款成功的返回信息如下:
<xml><return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[wx0953bae287adfeee]]></appid>
<mch_id><![CDATA[10032574]]></mch_id>
<sub_mch_id><![CDATA[]]></sub_mch_id>
<nonce_str><![CDATA[3ILl3mJupKAVrPdU]]></nonce_str>
<sign><![CDATA[1B213B3EFC73C40DFFAC5AAC777DF08D]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<transaction_id><![CDATA[1008010446201412020006419926]]></transaction_id>
<out_trade_no><![CDATA[1706467782]]></out_trade_no>
<out_refund_no><![CDATA[No.QM20141205002]]></out_refund_no>
<refund_id><![CDATA[2008010446201412050000255624]]></refund_id>
<refund_channel><![CDATA[]]></refund_channel>
<refund_fee>1</refund_fee>
<coupon_refund_fee>0</coupon_refund_fee>
</xml>


下面罗列下我测试遇到的bug:
同一账单进行二次退款操作返回信息如下
<xml><return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[wx0953bae287adfeee]]></appid>
<mch_id><![CDATA[10032574]]></mch_id>
<sub_mch_id><![CDATA[]]></sub_mch_id>
<nonce_str><![CDATA[H7gbRloPIJq5zk6S]]></nonce_str>
<sign><![CDATA[295234A53534EB345C7189E31A2C422D]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<transaction_id><![CDATA[1008010446201412030006453329]]></transaction_id>
<out_trade_no><![CDATA[1406539982]]></out_trade_no>
<out_refund_no><![CDATA[20141205001]]></out_refund_no>
<refund_id><![CDATA[2008010446201412050000262217]]></refund_id>
<refund_channel><![CDATA[]]></refund_channel>
<refund_fee>1</refund_fee>
<coupon_refund_fee>0</coupon_refund_fee>
</xml>
<span style="color:#FF0000;">(仍然显示退款成功,但是只退款一次),所以我们要根据自己的订单退款状态进行判断是否进行数据库操作。</span>



退款金额( refund_fee)大于总金额( total_fee)的返回信息
<xml><return_code><![CDATA[FAIL]]></return_code>
<return_msg><![CDATA[invalid refund_fee]]></return_msg>
</xml>
退款中的总金额( total_fee)与微信支付订单总金额不一致
<xml><return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[wx0953bae287adfeee]]></appid>
<mch_id><![CDATA[10032574]]></mch_id>
<sub_mch_id><![CDATA[]]></sub_mch_id>
<nonce_str><![CDATA[pt4JUHwVzYEtLzyh]]></nonce_str>
<sign><![CDATA[6193D0EECEE231BEDA427AABAA8A20DF]]></sign>
<result_code><![CDATA[FAIL]]></result_code>
<err_code><![CDATA[REFUND_FEE_MISMATCH]]></err_code>
<err_code_des><![CDATA[同一个out_refund_no退款金额要一致]]></err_code_des>
</xml>

退款单号( out_refund_no)不唯一
<xml><return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[wx0953bae287adfeee]]></appid>
<mch_id><![CDATA[10032574]]></mch_id>
<sub_mch_id><![CDATA[]]></sub_mch_id>
<nonce_str><![CDATA[T4tZ3GhGasjEszIl]]></nonce_str>
<sign><![CDATA[2A38E0FAF22FEAB10049C9D020EECE02]]></sign>
<result_code><![CDATA[FAIL]]></result_code>
<err_code><![CDATA[REFUND_ID_INVALID]]></err_code>
<err_code_des><![CDATA[退款单号非法]]></err_code_des>
</xml>
仍有不明之处,可留言给我。

这篇关于微信支付-退款(v3版微信支付)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1018548

相关文章

W外链微信推广短连接怎么做?

制作微信推广链接的难点分析 一、内容创作难度 制作微信推广链接时,首先需要创作有吸引力的内容。这不仅要求内容本身有趣、有价值,还要能够激起人们的分享欲望。对于许多企业和个人来说,尤其是那些缺乏创意和写作能力的人来说,这是制作微信推广链接的一大难点。 二、精准定位难度 微信用户群体庞大,不同用户的需求和兴趣各异。因此,制作推广链接时需要精准定位目标受众,以便更有效地吸引他们点击并分享链接

uniapp设置微信小程序的交互反馈

链接:uni.showToast(OBJECT) | uni-app官网 (dcloud.net.cn) 设置操作成功的弹窗: title是我们弹窗提示的文字 showToast是我们在加载的时候进入就会弹出的提示。 2.设置失败的提示窗口和标签 icon:'error'是设置我们失败的logo 设置的文字上限是7个文字,如果需要设置的提示文字过长就需要设置icon并给

基于微信小程序与嵌入式系统的智能小车开发(详细流程)

一、项目概述 本项目旨在开发一款智能小车,结合微信小程序与嵌入式系统,提供实时图像处理与控制功能。用户可以通过微信小程序远程操控小车,并实时接收摄像头采集的图像。该项目解决了传统遥控小车在图像反馈和控制延迟方面的问题,提升了小车的智能化水平,适用于教育、科研和娱乐等多个领域。 二、系统架构 1. 系统架构设计 本项目的系统架构主要分为以下几个部分: 微信小程序:负责用户界面、控制指令的

微信小程序uniappvue3版本-控制tabbar某一个的显示与隐藏

1. 首先在pages.json中配置tabbar信息 2. 在代码根目录下添加 tabBar 代码文件 直接把微信小程序文档里面的四个文件复制到自己项目中就可以了   3. 根据自己的需求更改index.js文件 首先我这里需要判断什么时候隐藏某一个元素,需要引入接口 然后在切换tabbar时,改变tabbar当前点击的元素 import getList from '../

微信小程序(一)数据流与数据绑定

一、单向数据流和双向数据流 1、单项数据流:指的是我们先把模板写好,然后把模板和数据(数据可能来自后台)整合到一起形成HTML代码,然后把这段HTML代码插入到文档流里面 优点:数据跟踪方便,流向单一,追寻问题比较方便【主要体现:微信小程序】。 缺点:就是写起来不太方便,如果修改UI界面数据需要维护对应的model对象 2、双向数据流:值和UI是双向绑定的,大家都知道,只要UI里面的值发生

微信小程序学习网站

小程序--柯神博客 http://www.cnblogs.com/nosqlcoco 案例地址: https://github.com/cocoli/weixin_smallexe/tree/master/weixin_demo/pages/component/uploadfile

分享一个基于uniapp科技馆服务微信小程序 博物馆管理小程序(源码、调试、LW、开题、PPT)

💕💕作者:计算机源码社 💕💕个人简介:本人 八年开发经验,擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等,大家有这一块的问题可以一起交流! 💕💕学习资料、程序开发、技术解答、文档报告 💕💕如需要源码,可以扫取文章下方二维码联系咨询 💕💕Java项目 💕💕微信小程序项目 💕💕Android项目 �

flutter开发实战-flutter build web微信无法识别二维码及小程序码问题

flutter开发实战-flutter build web微信无法识别二维码及小程序码问题 GitHub Pages是一个直接从GitHub存储库托管的静态站点服务,‌它允许用户通过简单的配置,‌将个人的代码项目转化为一个可以在线访问的网站。‌这里使用flutter build web来构建web发布到GitHub Pages。 最近通过flutter build web,通过发布到GitHu

1-3 微信小程序协同工作和发布

协同工作和发布 🥟🥞以权限管理需求为例 一个项目组,一般有不同的岗位,不同角色的员工同时参与项目成员 流程 成员管理的两个方面 不同项目成员对应的权限 版本

Deepin Linux 下安装微信

在下载和运行这个项目之前,你需要在电脑上安装Git和Node.js (来自npm)。在命令行中输入: 安装Git和nodejs 命令:sudo apt-get install git  sudo apt-get install nodejs 执行完上面命令之后运行下面命令 # 下载仓库 git clone https://github.com/geeeeeeeeek/elec