JAVA实现微信小程序支付退款功能

JAVA实现微信小程序支付退款功能

  • 本如亲测有效(代码复制直接可以用的),退款的前提是必须有小程序的appid、商户号、商户密匙、和证书、
  • 这个是微信小程序退款的官网大家可以去看看:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_9.shtml
  • 这个是微信官网里面的签名信息和API证书如何申请可以去看看:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml
  • 吧上面4个东西全部弄到保存好,并吧证书放在项目的resources层下,一般情况下证书名称就叫:apiclient_cert.p12 .
  • 全部填写好了之后就可以将下面代码复制到你的项目里面就可直接运行,返回类型根据你的项目需求进行修改就行了。
  • 注意:这里退款金额和订单金额都是以分为单位的
 // 有需要的话可以取这段代码进行金额转换,将金额转换成分
BigDecimal payMoney = new BigDecimal(BigDecimal.valueOf(Double.parseDouble(BigDecimalUtil.mulBig(money, new BigDecimal(100)).toString())).stripTrailingZeros().toPlainString());
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import javax.net.ssl.SSLContext;
import java.io.*;
import java.security.KeyStore;
import java.util.*;

public class PayUtil {
    private static String APPID = "";  //小程序的appid
    private static String MCH_ID = "";  //商户号
    private static String KEY = "";   //商户秘钥
    private byte [] certData;

    private String getRandomStringByLength(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }

    private static String mapToXml(Map param) {
        StringBuffer sb = new StringBuffer();
        sb.append("");
        for (Map.Entry entry : param.entrySet()) {
            sb.append("");
            sb.append(entry.getValue());
            sb.append("");
        }
        sb.append("");
        return sb.toString();
    }

    private static Map xmlToMap(String strxml) throws Exception {
        Map map = new HashMap<>();
        if (null == strxml || "".equals(strxml)) {
            return null;
        }
        InputStream in = String2Inputstream(strxml);
        SAXReader read = new SAXReader();
        Document doc = read.read(in);
        //得到xml根元素
        Element root = doc.getRootElement();
        //遍历  得到根元素的所有子节点
        @SuppressWarnings("unchecked")
        List list = root.elements();
        for (Element element : list) {
            //装进map
            map.put(element.getName(), element.getText());
        }
        //关闭流
        in.close();
        return map;
    }

    private static InputStream String2Inputstream(String strxml) throws IOException {
        return new ByteArrayInputStream(strxml.getBytes("UTF-8"));
    }

    /**
     * 把数组所有元素排序,并按照"参数=参数值"的模式用"&"字符拼接成字符串
     *
     * @param params 需要排序并参与字符拼接的参数组
     * @return 拼接后字符串
     */
    private static String createLinkString(Map params) {
        List keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);
        String preStr = "";
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
                preStr = preStr + key + "=" + value;
            } else {
                preStr = preStr + key + "=" + value + "&";
            }
        }
        return preStr;
    }

    /**
     * 签名字符串
     *
     * @param text          需要签名的字符串
     * @param key           密钥
     * @param input_charset 编码格式
     * @return 签名结果
     */
    public static String sign(String text, String key, String input_charset) {
        text = text + "&key=" + key;
        return DigestUtils.md5Hex(getContentBytes(text, input_charset));
    }

    private static byte[] getContentBytes(String content, String charset) {
        if (charset == null || "".equals(charset)) {
            return content.getBytes();
        }
        try {
            return content.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
        }
    }

    /**
     * 调用微信退款接口
     */
    private String doRefund(String url, String data) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("PKCS12"); //证书格式
        try {
           InputStream certStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("apiclient_cert.p12");
            this.certData = IOUtils.toByteArray(certStream);
            certStream.close();
          } catch (Exception e) {
                e.printStackTrace();
             }
        ByteArrayInputStream is = new ByteArrayInputStream(this.certData);
           try {
            keyStore.load(is, PayUtil.MCH_ID.toCharArray());
        } finally {
            is.close();
        }
        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(
                keyStore,
                PayUtil.MCH_ID.toCharArray())
                .build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext,
                new String[]{"TLSv1"},
                null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
        );
        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        try {
            HttpPost httpost = new HttpPost(url); // 设置响应头信息
            httpost.addHeader("Connection", "keep-alive");
            httpost.addHeader("Accept", "*/*");
            httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            httpost.addHeader("Host", "api.mch.weixin.qq.com");
            httpost.addHeader("X-Requested-With", "XMLHttpRequest");
            httpost.addHeader("Cache-Control", "max-age=0");
            httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
            httpost.setEntity(new StringEntity(data, "UTF-8"));
            CloseableHttpResponse response = httpclient.execute(httpost);
            try {
                HttpEntity entity = response.getEntity();

                String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                EntityUtils.consume(entity);
                return jsonStr;
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }

    /**
     * 申请退款
     *
     * @param orderId       商户订单号
     * @param refundId      商户退款单号
     * @param totalFee      订单金额
     * @param refundFee     退款金额
     * @param refundAccount 退款资金来源(默认传 "REFUND_SOURCE_UNSETTLED_FUNDS")
     * 注: 退款金额不能大于订单金额
     */
    public Map refund(String orderId, String refundId, String totalFee,
                                      String refundFee, String refundAccount) {

        Map params = new HashMap<>();
        params.put("appid", PayUtil.APPID);
        params.put("mch_id", PayUtil.MCH_ID);
        params.put("nonce_str", getRandomStringByLength(32));
        params.put("out_trade_no", orderId); //商户订单号和微信订单号二选一(我这里选的是商户订单号)
        params.put("out_refund_no", refundId);
        params.put("total_fee", totalFee);
        params.put("refund_fee", refundFee);
        params.put("refund_account", refundAccount);
        params.put("sign_type", "MD5");
        String preStr = PayUtil.createLinkString(params);

        //签名算法
        String sign = (sign(preStr, PayUtil.KEY, "utf-8")).toUpperCase();
        params.put("sign", sign);

        Map map = new HashMap<>();
        try {
            String xml = mapToXml(params);
            String xmlStr = doRefund("https://api.mch.weixin.qq.com/secapi/pay/refund", xml);
            map = xmlToMap(xmlStr);
            // 这里是将返回类型转换成了 map 如果要判断是否退款成功可以获取 map里面的 result_code 值,如果这个值等于 SUCCESS就代表退款成功,这里就可以写退款之后的逻辑,比如修改订单状态之类的,
               if ("SUCCESS".equals(status)){
                // 退款成功
                // 这里就写逻辑,当然,如果不需要写逻辑的话,可以直接吧if else 删掉也是可以直接跑起来的

              }else {
                // 退款失败
                return Result.fail("退款失败");
               }

        } catch (Exception e) {
             }
        return map;
    }
}

Original: https://www.cnblogs.com/gongss/p/16729064.html
Author: Gongss
Title: JAVA实现微信小程序支付退款功能

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/582530/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Dockerfile

    Docker可以通过Dockerfile构建镜像。Dockerfile是一个文本文档,它包含用户可以在命令行上调用的所有命令来组装镜像。使用 docker build用户可以创建一…

    Linux 2023年6月13日
    089
  • 解决端口被占用问题

    在 Linux 里查看端口被哪个进程占用(以Apache服务80端口为例,其余的端口一样方法处理) [root@localhost /]# lsof -i:80 #查看进程 COM…

    Linux 2023年6月7日
    0136
  • 美团笔试(22.03.19)

    代码题 一共五道代码题,看了前面三道,ac了三道,后面两道题没有时间看,此处将对前三题进行记录总结,后附代码。 题意:给定一组n个商品的价格,下单购买商品时,必须购买前i个商品,即…

    Linux 2023年6月13日
    0102
  • 搭建NFS文件共享系统

    1、概述: NFS(Network File System)意为网络文件系统,它最大的功能就是可以通过网络,让不同的机器不同的操作系统可以共享彼此的文件。简单的讲就是可以挂载远程主…

    Linux 2023年6月7日
    097
  • 三款优秀的替代Xshell的SSH软件

    在之前的文章介绍个, 由于公司禁止使用xshell, 让我很是难受了一阵, 因为一直无法找到好的工具来替代xshell, 前面文章中提到的那些对我来时功能还是太单一了, 界面也不够…

    Linux 2023年5月28日
    0855
  • DDoS攻击–TCP攻击概述

    https://blog.csdn.net/qq_34777600/article/details/81945594 posted @2020-12-10 18:07 珠峰之梦 阅…

    Linux 2023年6月7日
    0101
  • redis高级

    1 redis高可用 主从复制存在的问题: 1 主从复制,主节点发生故障,需要做故障转移,可以手动转移:让其中一个slave变成master—>哨兵 2 主从复制,只能主…

    Linux 2023年6月14日
    091
  • mysql内连接查询之自连接

    连接查询:当查询数据时,通过连接操作查询出存放在多个表中的不同数据,当两个或者多个表中存在相同意义的字段时,便可以通过这些字段对不同的表进行连接查询。自连接:如果在一个连接查询中,…

    Linux 2023年6月7日
    091
  • 云笔记本:一个Laxcus应用软件

    给大家展示一个第三方开发的应用软件:云笔记本。 这个作品来自一位Laxcus分布式应用软件开发者,目前已经通过Laxcus集群操作系统的兼容性测试。云笔记本的界面和功能,类似Win…

    Linux 2023年6月6日
    0134
  • redis

    ./redis-cli -a 111 KEYS "key*" | xargs ./redis-cli -a 111 DEL Original: https://…

    Linux 2023年5月28日
    096
  • MyCAT实现MySQL读写分离

    用户连接到MySQL的中间件(代理),中间件接收用户的访问转发给后端的mysql数据库。 是MySQL的一个中间件软件,Mycat是一个开源的分布式数据库系统,是一个实现了MySQ…

    Linux 2023年6月7日
    088
  • Android进阶技术之——一文吃透Android的消息机制

    前言 为什么要老药换新汤 作为Android中 至关重要 的机制之一,十多年来,分析它的文章不断,大量的内容已经被挖掘过了。所以: 已经对这一机制熟稔于心的读者,在这篇文章中,看不…

    Linux 2023年6月13日
    0100
  • 浅谈kali : aircrack-ng套件

    aircrack-ng 套件包含有: Name Description aircrack-ng 破解WEP以及WPA(字典攻击)密钥 airdecap-ng airmon-ng 将…

    Linux 2023年6月14日
    075
  • 【Jmeter】jmeter提取response中的返回值,并保存到本地文件–BeanShell后置处理器

    有个需求,需要在压测环境中,创建几十万的账号数据,然后再根据创建结果,查询到某些账号信息。 按照之前我的做法,直接Python调用API,然后再数据库查询; 但是近期所有开发人员的…

    Linux 2023年5月28日
    088
  • 给亨亨猫软件配置系统代理下载国外视频

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Linux 2023年6月7日
    097
  • Docker清理日志脚本

    Docker清理日志脚本 #!/bin/sh 此脚本为日常清理docker日志 docker 容器的路劲日志为 /var/lib/docker/containers/ 下-json…

    Linux 2023年6月8日
    088
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球