微信退款工具

微信退款功能 需要证书

微信开放文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4

代码中  keyStore.load(is, PayUtil.MCH_ID.toCharArray()); 会报错

报错信息:DerInputStream.getLength(): lengthTag=111, too big. 

在pom.xml(parent)中加入如下配置,过滤后缀为pkcs12、jks,p12的证书文件。如果还加载其他文件,可以自行加入




 <!--解决 DerInputStream.getLength(): lengthTag=111, too big.   BEGIN-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration><encoding>UTF-8</encoding>
                    <!-- 过滤后缀为pem、pfx,p12的证书文件 -->
                    <nonFilteredFileExtensions>
                        <nonFilteredFileExtension>cer</nonFilteredFileExtension>
                        <nonFilteredFileExtension>pem</nonFilteredFileExtension>
                        <nonFilteredFileExtension>pfx</nonFilteredFileExtension>
                        <nonFilteredFileExtension>p12</nonFilteredFileExtension>
                    </nonFilteredFileExtensions>
                </configuration>
            </plugin>
            <!--解决 DerInputStream.getLength(): lengthTag=111, too big.    END-->
package io.geekidea.springbootplus.system.util;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
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 org.springframework.beans.factory.annotation.Value;

import javax.net.ssl.SSLContext;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
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;
    @Value("${}")

    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<String, String> param) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        for (Map.Entry<String, String> entry : param.entrySet()) {
            sb.append("<" + entry.getKey() + ">");
            sb.append(entry.getValue());
            sb.append("</" + entry.getKey() + ">");
        }
        sb.append("</xml>");
        return sb.toString();
    }

    private static Map xmlToMap(String strxml) throws Exception {
        Map<String, String> 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<Element> 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<String, String> params) {
        List<String> 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("static/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());
        }catch (Exception e){
            e.printStackTrace();
        }
        finally {
            is.close();
        }
        System.out.println("ceshi----------------->");
        // 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<String, String> refund(String orderId, String refundId, Integer totalFee,
                                      Integer refundFee, String refundAccount) {

        Map<String, String> 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.toString());//订单金额
        params.put("refund_fee", refundFee.toString());//退款金额
        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<String, String> map = new HashMap<>();
        try {
            String xml = mapToXml(params);
            String xmlStr = doRefund("https://api.mch.weixin.qq.com/secapi/pay/refund", xml);
            map = xmlToMap(xmlStr);
        } catch (Exception e) {
        }
        return map;
    }

//
}

以下代码存在签名错误问题 待解决【微信签名验证工具检验通过,代码中不好使】

//企业支付到个人
    public Map<String,String> CorporatePayment(String orderId, String openid, Integer totalFee,
                                                String refundAccount){
        Map<String, String> params = new HashMap<>();

        params.put("amount", totalFee.toString());//订单金额 单位分
        params.put("check_name","NO_CHECK");
        params.put("desc", refundAccount); //备注   必须
        params.put("mch_appid", PayUtil.APPID);
        params.put("mchid", PayUtil.MCH_ID);
        params.put("nonce_str", getRandomStringByLength(32));
        params.put("openid",openid);//用户在此小程序中的唯一标识 appid
        params.put("partner_trade_no", orderId); //商户订单号

        params.put("sign_type", "MD5");
        String preStr = PayUtil.createLinkString(params);
        //签名算法
        String sign = (sign(preStr, PayUtil.KEY, "utf-8")).toUpperCase();
        params.put("sign", sign);
        Map<String, String> map = new HashMap<>();
        try {
            String xml = mapToXml(params);
            String xmlStr = doRefund("https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers", xml);
            map = xmlToMap(xmlStr);
        } catch (Exception e) {
        }
        return map;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值