微信支付开发V2版本 springboot详细版

一、配置

1、开发文档链接

v2版本官方链接:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1

2、公众号后台配置

1、

2、

3、

二、流程

商户系统和微信支付系统主要交互:

1、商户server调用统一下单接口请求订单,api参见公共api【统一下单API

2、商户server可通过【JSAPI调起支付API】调起微信支付,发起支付请求。

3、商户server接收支付通知,api参见公共api【支付结果通知API

4、商户server查询支付结果,api参见公共api【查询订单API】(查单实现可参考:支付回调和查单实现指引

三、代码

v2版本代码可以直接复制使用

1、添加Maven

   <!-- 微信支付 SDK -->
        <dependency>
            <groupId>com.github.wxpay</groupId>
            <artifactId>wxpay-sdk</artifactId>
            <version>0.0.3</version>
        </dependency>

2、统一下单接口

@GetMapping("/place")
    public AjaxResult place(PayVo payVo) {
        System.out.println("money:"+money.toString());
        System.out.println("openid:"+payVo.getOpenid());
        Map<String,String> weChatPayMap = new HashMap<>();
        String wechat_appid = payConfig.getAppid();//小程序appid 或者 公众号appid, 必须与商户绑定
        String wechat_mchid = payConfig.getMch_id(); //商户号id
        String wechat_seckey = payConfig.getApi_key();//商户秘钥
        String out_trade_no = UUIDUtils.getUUID36(); //订单号
        String ip = payConfig.getIp();
        String openid = payVo.getOpenid();
        String trade_type = "JSAPI";
        Double d = new Double(money.toString());
        Double a=d*100;
        String total_fee =a.intValue()+"";
        try {
//            YmyXlyAct ymyXlyAct = new YmyXlyAct();
//            List<YmyXlyAct> list = ymyXlyActService.selectYmyXlyActList(ymyXlyAct);
//            String body = qishu;
            String product_id = actId;
            YmyXlyDingdan XlyDingdan=new YmyXlyDingdan();
            XlyDingdan.setOpenid(openid);
            XlyDingdan.setMoney(money.toString());
            XlyDingdan.setPayType("未支付");
            XlyDingdan.setProductId(product_id);
            List<YmyXlyDingdan> dingdans= ymyXlyDingdanService.selectYmyXlyDingdanList(XlyDingdan);
            if (StringUtils.isNotEmpty(dingdans)){
//                JSONObject jsonObject = JSON.parseObject(dingdans.get(0).getParam());
                /*JSON对象转map常用*/
                Map<String,Object> jsonToMap =  JSONObject.parseObject(dingdans.get(0).getParam());
              return AjaxResult.success(jsonToMap);

            }
            //微信统一下单
            Map<String, String> paraMap = new HashMap<String, String>();
            paraMap.put("appid", wechat_appid);
            paraMap.put("mch_id", wechat_mchid);
            String new_body=new String(body.getBytes(),"utf-8");
            paraMap.put("body", body);//商品描述
            paraMap.put("product_id", product_id);//商品id
            paraMap.put("nonce_str", UUIDUtils.getUUID36());
            paraMap.put("out_trade_no", out_trade_no);//订单号
            paraMap.put("spbill_create_ip", ip);
            paraMap.put("total_fee", total_fee);
            paraMap.put("trade_type", trade_type);//交易类型   JSAPI支付
            paraMap.put("notify_url", WeChatConstants.NOTIFY_URL);// 此路径是微信服务器调用支付结果通知路径随意写,---回调地址
            //生成签名, 统一下单

            if (StringUtils.equals(WeChatConstants.TRADE_TYPE_JSAPI, trade_type)) {
                paraMap.put("openid", openid);//trade_type=JSAPI时(即JSAPI支付),此参数必传
                weChatPayMap = WXPayUtil.unifiedorderJSAPI(paraMap, wechat_appid, wechat_seckey, out_trade_no);
                System.out.println(weChatPayMap);

                if ("200".equals(weChatPayMap.get("paycode"))){
                    YmyXlyDingdan ymyXlyDingdan=new YmyXlyDingdan();
                    ymyXlyDingdan.setOpenid(openid);
                    ymyXlyDingdan.setOutTradeNo(out_trade_no);
                    ymyXlyDingdan.setPayType("待支付");
                    ymyXlyDingdan.setMoney(money.toString());
                    ymyXlyDingdan.setProductId(product_id);
                    ymyXlyDingdan.setBody(body);
                    JSONObject JSONObj = JSONObject.parseObject(JSON.toJSONString(weChatPayMap));
                    ymyXlyDingdan.setParam(String.valueOf(weChatPayMap));
                    String str = JSONObj.toString();
                    ymyXlyDingdan.setParam(str);
                    ymyXlyDingdanService.insertYmyXlyDingdan(ymyXlyDingdan);
                }
                return AjaxResult.success(weChatPayMap);
            }


        } catch (Exception e) {

            e.printStackTrace();
        }

        return AjaxResult.success(weChatPayMap);
    }

3、查询订单

 /**
     * 订单支付状态查询
     *
     * @param wechat_appid
     * @param wechat_mchid
     * @param wechat_seckey
     * @param out_trade_no
     * @return
     * @throws Exception
     */
    @GetMapping("/orderquery")
    public Map<String, String> orderquery(String wechat_appid, String wechat_mchid, String wechat_seckey, String out_trade_no) throws Exception {
        //拼接 参数
        Map<String, String> paraMap = new HashMap<String, String>();
        paraMap.put("appid", wechat_appid);
        paraMap.put("mch_id", wechat_mchid);
        paraMap.put("nonce_str", UUIDUtils.getUUID36());
        paraMap.put("out_trade_no", out_trade_no);//订单号
        String sign = WXPayUtil.generateSignature(paraMap, wechat_seckey);
        paraMap.put("sign", sign);
        String xml = WXPayUtil.mapToXml(paraMap);//将所有参数(map)转xml格式
        String xmlStr = HttpUtils.sendPost(WeChatConstants.ORDERQUERY_URL, xml);//发送post请求"统一下单接口"返回预支付id:prepay_id
        Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);
        if(map!=null && "SUCCESS".equalsIgnoreCase(map.get("trade_state"))){
            //支付成功
            //2.修改订单状态为“待发货/已支付”
            String orderId = out_trade_no;
//            String orderId = map.get("out_trade_no");
//            int i =1;
            YmyXlyDingdan ymyXlyDingdan = new YmyXlyDingdan();
            ymyXlyDingdan.setOutTradeNo(orderId);
            ymyXlyDingdan.setPayType("已支付");
            ymyXlyDingdanService.updateYmyXlyDingdan(ymyXlyDingdan);

            List<YmyXlyDingdan> list = ymyXlyDingdanService.selectYmyXlyDingdanList(ymyXlyDingdan);
            String[] actIds=list.get(0).getProductId().split(",");
            for (String actId:actIds){
                YmyXly ymyXly = new YmyXly();
                ymyXly.setOpenid(list.get(0).getOpenid());
                ymyXly.setActId(Long.valueOf(actId));
                ymyXly.setMoney(list.get(0).getMoney());
                List<YmyXly> ymyXlies = ymyXlyService.selectYmyXlyList(ymyXly);
                YmyXly xly=new YmyXly();
                BeanUtils.copyBeanProp(xly,ymyXlies.get(0));
                xly.setIsPay("已付款");
                ymyXlyService.updateYmyXly(xly);
            }

            //3.响应微信支付平台


        }
        return map;
    }

4、下单回调接口

  /**
     * 1.接收微信支付平台传递的数据(使用request的输入流接收)
     */
    @PostMapping("/callback")
    public String success(HttpServletRequest request) throws Exception {
        System.out.println("进入回调----------------------------------------------------");
        ServletInputStream is = request.getInputStream();
        byte[] bs = new byte[1024];
        int len = -1;
        StringBuilder builder = new StringBuilder();
        while ((len = is.read(bs)) != -1) {
            builder.append(new String(bs, 0, len));
        }
        String s = builder.toString();
        System.out.println("s="+s);
        //使用帮助类将xml接口的字符串转换成map
        Map<String, String> map = WXPayUtil.xmlToMap(s);
System.out.println("map:"+map);
        if(map!=null && "SUCCESS".equalsIgnoreCase(map.get("return_code"))){
            //支付成功
            //2.修改订单状态为“待发货/已支付”
            String orderId = map.get("out_trade_no");
//            int i =1;
            try {
                YmyXlyDingdan ymyXlyDingdan = new YmyXlyDingdan();
                ymyXlyDingdan.setOutTradeNo(orderId);
                ymyXlyDingdan.setPayType("已支付");
                int i = ymyXlyDingdanService.updateYmyXlyDingdan(ymyXlyDingdan);

                List<YmyXlyDingdan> list = ymyXlyDingdanService.selectYmyXlyDingdanList(ymyXlyDingdan);
                String[] actIds=list.get(0).getProductId().split(",");
                for (String actId:actIds){
                    YmyXly ymyXly = new YmyXly();
                    ymyXly.setOpenid(list.get(0).getOpenid());
                    ymyXly.setActId(Long.valueOf(actId));
                    ymyXly.setMoney(list.get(0).getMoney());
                    List<YmyXly> ymyXlies = ymyXlyService.selectYmyXlyList(ymyXly);
                    YmyXly xly=new YmyXly();
                    BeanUtils.copyBeanProp(xly,ymyXlies.get(0));
                    xly.setIsPay("已付款");
                    ymyXlyService.updateYmyXly(xly);
                }



                System.out.println("--orderId:" + orderId);
                //3.响应微信支付平台
                if (i > 0) {
                    HashMap<String, String> resp = new HashMap<>();
                    resp.put("return_code", "success");
                    resp.put("return_msg", "OK");
                    resp.put("appid", map.get("appid"));
                    resp.put("result_code", "success");
                    return WXPayUtil.mapToXml(resp);
                }
            }catch (Exception e){
                e.printStackTrace();
            }

        }
        //支付失败
        return null;
    }

5、工具类util

 1、HttpUtils

import com.alibaba.fastjson2.JSONObject;
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.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.*;
import java.net.ConnectException;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyStore;

public class HttpUtils {


    public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {
        JSONObject jsonObject = null;
        StringBuffer buffer = new StringBuffer();
        try {
            // 创建SSLContext对象,并使用我们指定的信任管理器初始化
            TrustManager[] tm = { new MyX509TrustManager() };
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new java.security.SecureRandom());
            // 从上述SSLContext对象中得到SSLSocketFactory对象
            SSLSocketFactory ssf = sslContext.getSocketFactory();

            URL url = new URL(requestUrl);
            HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
            httpUrlConn.setSSLSocketFactory(ssf);

            httpUrlConn.setDoOutput(true);
            httpUrlConn.setDoInput(true);
            httpUrlConn.setUseCaches(false);
            // 设置请求方式(GET/POST)
            httpUrlConn.setRequestMethod(requestMethod);

            if ("GET".equalsIgnoreCase(requestMethod))
                httpUrlConn.connect();

            // 当有数据需要提交时
            if (null != outputStr) {
                OutputStream outputStream = httpUrlConn.getOutputStream();
                // 注意编码格式,防止中文乱码
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }

            // 将返回的输入流转换成字符串
            InputStream inputStream = httpUrlConn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

            String str = null;
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            bufferedReader.close();
            inputStreamReader.close();
            // 释放资源
            inputStream.close();
            inputStream = null;
            httpUrlConn.disconnect();
            jsonObject = JSONObject.parseObject(buffer.toString());
            return jsonObject;
        } catch (ConnectException ce) {
            System.out.println("Weixin server connection timed out.");
        } catch (Exception e) {
            System.out.println("error."+ e.getMessage());
        }
        return jsonObject;
    }

    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url
     *            发送请求的 URL
     * @param param
     *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String param) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            // 设置通用的请求属性
            conn.setRequestProperty("Charsert", "UTF-8");
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            out = new PrintWriter(conn.getOutputStream());
            // 发送请求参数
            out.print(param);
            // flush输出流的缓冲
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 使用finally块来关闭输出流、输入流
        finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 需要使用证书请求接口
     */
    public static String requestWithCert(String url,String pay_cert,String mchid, String data) throws Exception {

        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        FileInputStream is = new FileInputStream(new File(pay_cert));
        try {
            keyStore.load(is, mchid.toCharArray());// 这里写密码..默认是你的MCHID
        } finally {
            is.close();
        }
        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchid.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();
        }
    }
}
2、WeChatConstants

/**
 * 常量
 * @author voayc
 *
 */
public class WeChatConstants {

	public enum SignType {
		MD5, HMACSHA256
	}

	public static final String FIELD_SIGN = "sign";

	/** 支付成功回调地址 */
	public static String NOTIFY_URL= "http://8.142.116.233:8001/pay/wxpay/callback";微信支付回调地址


	/** 支付证书路径 */
	public static String PAY_CERT_LOC = "C:\\CTO\\java\\apache-tomcat-8.0.50\\wxcert\\";

	/** 微信 trade_type 参数  */
	public static final String TRADE_TYPE_JSAPI = "JSAPI";//JSAPI支付  例如 : 直接调用微信支付
	public static final String TRADE_TYPE_NATIVE = "NATIVE";//Native支付  例如 : 扫码支付

	/**
	 * 统一下单
	 */
	public static String UNIFIEDORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
	/**
	 * 订单支付状态查询
	 */
	public static String ORDERQUERY_URL = "https://api.mch.weixin.qq.com/pay/orderquery";
	/**
	 * 退款
	 */
	public static String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";
	/**
	 * 提现
	 */
	public static String TRANSFERS_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";

}
3、WXPayUtil

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.utils.sign.Md5Utils;
import com.ruoyi.garden.controller.wxpay.constants.WeChatConstants;
import com.ruoyi.garden.controller.wxpay.dto.*;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.*;



public class WXPayUtil {

    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    private static final Random RANDOM = new SecureRandom();
    /**
     * JSAPI 下单
     * @Title: unifiedorderJSAPI
     * @param paraMap
     * @param wechat_seckey
     * @return
     * @author: voayc
     * @date:  2020年3月17日 上午10:00:49
     */
    public static Map<String,String> unifiedorderJSAPI(Map<String, String> paraMap, String wechat_appid, String wechat_seckey, String out_trade_no) throws Exception {
        Map<String,String> weChatPayMap = new HashMap<>();
    	//生成签名, 统一下单
        System.out.println("paraMap------------"+paraMap);
		String sign = WXPayUtil.generateSignature(paraMap, wechat_seckey);
		System.out.println("sign:"+sign);
		paraMap.put("sign", sign);
		String xml = WXPayUtil.mapToXml(paraMap);//将所有参数(map)转xml格式
        String new_xml=new String(xml.getBytes("utf-8"));

        System.out.println("xml:"+new_xml);
		String xmlStr = HttpUtils.sendPost(WeChatConstants.UNIFIEDORDER_URL, new_xml);//发送post请求"统一下单接口"返回预支付id:prepay_id
        System.out.println("xmlStr:"+xmlStr);

        Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);

		//以下内容是返回前端页面的json数据
		if (xmlStr.indexOf("SUCCESS") == -1) {
            weChatPayMap.put("message",CommonText.UNIFIEDORDER_ERR+":"+map + ",paraMap:"+paraMap +",return_msg" + map.get("return_msg") +",err_code_des" + map.get("err_code_des"));
//			b.setDefultFailInfo();
//			b.setMessage(CommonText.UNIFIEDORDER_ERR+":"+map + ",paraMap:"+paraMap +",return_msg" + map.get("return_msg") +",err_code_des" + map.get("err_code_des"));
//
		return weChatPayMap;
		}

		//返回前台参数
		Map<String,String> payMap = new HashMap<>();
		String prepay_id = MapUtils.getString(map, "prepay_id");
		if (StringUtils.isBlank(prepay_id)) {
            weChatPayMap.put("message",CommonText.UNIFIEDORDER_ERR_PREPAY_ID_NOT_FIND + "," + map.get("err_code_des"));

//			b.setDefultFailInfo();
//			b.setMessage(CommonText.UNIFIEDORDER_ERR_PREPAY_ID_NOT_FIND + "," + map.get("err_code_des"));
			return weChatPayMap;
		}
        weChatPayMap.put("appId", wechat_appid);
        weChatPayMap.put("timeStamp", WXPayUtil.getCurrentTimestamp()+"");
        weChatPayMap.put("nonceStr", WXPayUtil.generateNonceStr());
        weChatPayMap.put("signType", "MD5");
        weChatPayMap.put("package", "prepay_id=" + prepay_id);

		String paySign = WXPayUtil.generateSignature(weChatPayMap, wechat_seckey);
        weChatPayMap.put("paySign", paySign);
        weChatPayMap.put("out_trade_no", out_trade_no);
        weChatPayMap.put("paycode", "200");
//		b.put("data",payMap);

		return weChatPayMap;
	}

    /**
     * NATIVE 下单
     * @Title: unifiedorderJSAPI
     * @param paraMap
     * @param wechat_seckey
     * @return
     * @author: voayc
     * @date:  2020年3月17日 上午10:00:49
     */
    public static BasicResponse unifiedorderNATIVE(Map<String, String> paraMap,String wechat_appid, String wechat_seckey,String out_trade_no) throws Exception {
    	BasicResponse b = new BasicResponse<>();
    	//生成签名, 统一下单
    	String sign = WXPayUtil.generateSignature(paraMap, wechat_seckey);
    	paraMap.put("sign", sign);
    	String xml = WXPayUtil.mapToXml(paraMap);//将所有参数(map)转xml格式
    	String xmlStr = HttpUtils.sendPost(WeChatConstants.UNIFIEDORDER_URL, xml);//发送post请求"统一下单接口"返回预支付id:prepay_id
    	Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);

    	//以下内容是返回前端页面的json数据
		if (xmlStr.indexOf("SUCCESS") == -1) {
			b.setDefultFailInfo();
			b.setMessage(CommonText.UNIFIEDORDER_ERR+":"+map + ",paraMap:"+paraMap +",return_msg" + map.get("return_msg"));
			return b;
		}

		//将未支付状态 放入redis缓存, 用于前端循环查询, 扫码支付是否成功
		//RedisClientUtils.save(SystemConstant.WECHAT_PAY_NATIVE_STATE+out_trade_no, "0",SystemConstant.WECHAT_PAY_NATIVE_STATE_EXPIRE);

		Map<String,String> resultMap = new HashMap<>();
		resultMap.put("code_url", MapUtils.getString(map, "code_url"));
		resultMap.put("out_trade_no", out_trade_no);
    	b.setObject(resultMap);
    	b.setMessage(map.get("err_code_des"));

    	return b;
    }

    /**
	 * 微信退款
	 * @Title: wxRefund
	 * @param wechat_appid  	公众号/小程序  appid
	 * @param wechat_mchid		微信支付商户号
	 * @param wechat_seckey		微信支付商户号
	 * @param wechat_pay_cert	微信支付商户号证书路径
	 * @param out_trade_no		订单编码
	 * @param service_code		售后编码
	 * @param total_price		订单总金额
	 * @param refund_fee 		退款金额
	 * @author: voayc
	 * @throws Exception
	 * @date:  2020年4月18日 下午4:53:58
	 */
	public static BasicResponse wxRefund(String wechat_appid, String wechat_mchid, String wechat_seckey, String wechat_pay_cert, String out_trade_no, String service_code, BigDecimal total_price, BigDecimal refund_fee) throws Exception {
		BasicResponse b = new BasicResponse();
		//拼接 退款 参数
		Map<String, String> paraMap = new HashMap<String, String>();
		paraMap.put("appid", wechat_appid);
		paraMap.put("mch_id", wechat_mchid);
		paraMap.put("nonce_str", UUIDUtils.getUUID36());
		paraMap.put("out_trade_no", out_trade_no);//订单号
		paraMap.put("out_refund_no", service_code);//商户退款单号, 必须为不同, 相同的话, 只会退一笔
		paraMap.put("total_fee",total_price.multiply(new BigDecimal("100")).setScale(0).toString());//单位为分
		paraMap.put("refund_fee",refund_fee.multiply(new BigDecimal("100")).setScale(0).toString());//单位为分
		String sign = WXPayUtil.generateSignature(paraMap, wechat_seckey);
		paraMap.put("sign", sign);
		String xml = WXPayUtil.mapToXml(paraMap);//将所有参数(map)转xml格式

		//退款
		String xmlStr = HttpUtils.requestWithCert(WeChatConstants.REFUND_URL,(WeChatConstants.PAY_CERT_LOC+wechat_pay_cert),wechat_mchid, xml);
		Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);
		if (xmlStr.indexOf("SUCCESS") == -1) {
			b.setDefultFailInfo();
			b.setMessage(CommonText.UNIFIEDORDER_ERR+":"+map + ",paraMap:"+paraMap +",return_msg" + map.get("return_msg"));
			return b;
		}
		return b;
	}

	/**
	 * 微信提现
	 * @Title: wxTransfers
	 * @param wechat_appid  	公众号/小程序  appid
	 * @param wechat_mchid		微信支付商户号
	 * @param wechat_seckey		微信支付商户号
	 * @param wechat_pay_cert	微信支付商户号证书路径
	 * @param out_trade_no		订单编码
	 * @param openid			openid
	 * @param transfers_price	提现金额
	 * @author: voayc
	 * @throws Exception
	 * @date:  2020年4月18日 下午4:53:58
	 */
	public static BasicResponse wxTransfers(String wechat_appid, String wechat_mchid, String wechat_seckey, String wechat_pay_cert, String out_trade_no, String openid, BigDecimal transfers_price) throws Exception {
		BasicResponse b = new BasicResponse();
		//拼接 退款 参数
		Map<String, String> paraMap = new HashMap<String, String>();
		paraMap.put("mch_appid", wechat_appid);
		paraMap.put("mchid", wechat_mchid);
		paraMap.put("nonce_str", UUIDUtils.getUUID36());
		paraMap.put("partner_trade_no", out_trade_no);//订单号
		paraMap.put("openid", openid);//退款会员id
		paraMap.put("check_name", "NO_CHECK");//NO_CHECK:不校验真实姓名
		paraMap.put("desc", "提现");//企业付款备注,必填
		paraMap.put("amount",transfers_price.multiply(new BigDecimal("100")).setScale(0).toString());//单位为分
		String sign = WXPayUtil.generateSignature(paraMap, wechat_seckey);
		paraMap.put("sign", sign);
		String xml = WXPayUtil.mapToXml(paraMap);//将所有参数(map)转xml格式

		//提现
		String xmlStr = HttpUtils.requestWithCert(WeChatConstants.TRANSFERS_URL,(WeChatConstants.PAY_CERT_LOC+wechat_pay_cert),wechat_mchid, xml);

		Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);
		String return_code = MapUtils.getString(map, "return_code");
		String result_code = MapUtils.getString(map, "result_code");
		if ( !(StringUtils.equals("SUCCESS", return_code) && StringUtils.equals("SUCCESS", result_code)) ) {
			b.setDefultFailInfo();
			b.setMessage(CommonText.UNIFIEDORDER_ERR+":"+map + ",paraMap:"+paraMap +",return_msg" + map.get("return_msg"));
			return b;
		}
		return b;
	}

	 /**
     * 订单支付状态查询
     * @param wechat_appid
     * @param wechat_mchid
     * @param wechat_seckey
     * @param out_trade_no
     * @return
     * @throws Exception
     */
    public static Map<String, String> orderquery(String wechat_appid, String wechat_mchid, String wechat_seckey, String out_trade_no) throws Exception {
    	//拼接 参数
		Map<String, String> paraMap = new HashMap<String, String>();
		paraMap.put("appid", wechat_appid);
		paraMap.put("mch_id", wechat_mchid);
		paraMap.put("nonce_str", UUIDUtils.getUUID36());
		paraMap.put("out_trade_no", out_trade_no);//订单号
    	String sign = WXPayUtil.generateSignature(paraMap, wechat_seckey);
    	paraMap.put("sign", sign);
    	String xml = WXPayUtil.mapToXml(paraMap);//将所有参数(map)转xml格式
    	String xmlStr = HttpUtils.sendPost(WeChatConstants.ORDERQUERY_URL, xml);//发送post请求"统一下单接口"返回预支付id:prepay_id
    	Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);
    	return map;
    }

	/**
     * XML格式字符串转换为Map
     *
     * @param strXML XML字符串
     * @return XML数据转换后的Map
     * @throws Exception
     */
    public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<String, String>();
            DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();
//            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
            InputStream stream = new ByteArrayInputStream(strXML.getBytes("GBK"));
            Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    Element element = (Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // do nothing
            }
            return data;
        } catch (Exception ex) {
            throw ex;
        }

    }


    /**
     * 将Map转换为XML格式的字符串
     *
     * @param data Map类型数据
     * @return XML格式的字符串
     * @throws Exception
     */
    public static String mapToXml(Map<String, String> data) throws Exception {
        Document document = WXPayXmlUtil.newDocument();
        Element root = document.createElement("xml");
        document.appendChild(root);
        for (String key: data.keySet()) {
            String value = data.get(key);
            if (value == null) {
                value = "";
            }
            value = value.trim();
            Element filed = document.createElement(key);
            filed.appendChild(document.createTextNode(value));
            root.appendChild(filed);
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMSource source = new DOMSource(document);
        transformer.setOutputProperty(OutputKeys.ENCODING, "GBK");
//        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        transformer.transform(source, result);
        String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
        try {
            writer.close();
        }
        catch (Exception ex) {
        }
        return output;
    }


    /**
     * 生成带有 sign 的 XML 格式字符串
     *
     * @param data Map类型数据
     * @param key API密钥
     * @return 含有sign字段的XML
     */
    public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {
        return generateSignedXml(data, key, WeChatConstants.SignType.MD5);
    }

    /**
     * 生成带有 sign 的 XML 格式字符串
     *
     * @param data Map类型数据
     * @param key API密钥
     * @param signType 签名类型
     * @return 含有sign字段的XML
     */
    public static String generateSignedXml(final Map<String, String> data, String key, WeChatConstants.SignType signType) throws Exception {
        String sign = generateSignature(data, key, signType);
        data.put(WeChatConstants.FIELD_SIGN, sign);
        return mapToXml(data);
    }


    /**
     * 判断签名是否正确
     *
     * @param xmlStr XML格式数据
     * @param key API密钥
     * @return 签名是否正确
     * @throws Exception
     */
    public static boolean isSignatureValid(String xmlStr, String key) throws Exception {
        Map<String, String> data = xmlToMap(xmlStr);
        if (!data.containsKey(WeChatConstants.FIELD_SIGN) ) {
            return false;
        }
        String sign = data.get(WeChatConstants.FIELD_SIGN);
        return generateSignature(data, key).equals(sign);
    }

    /**
     * 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。
     *
     * @param data Map类型数据
     * @param key API密钥
     * @return 签名是否正确
     * @throws Exception
     */
    public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
        return isSignatureValid(data, key, WeChatConstants.SignType.MD5);
    }

    /**
     * 判断签名是否正确,必须包含sign字段,否则返回false。
     *
     * @param data Map类型数据
     * @param key API密钥
     * @param signType 签名方式
     * @return 签名是否正确
     * @throws Exception
     */
    public static boolean isSignatureValid(Map<String, String> data, String key, WeChatConstants.SignType signType) throws Exception {
        if (!data.containsKey(WeChatConstants.FIELD_SIGN) ) {
            return false;
        }
        String sign = data.get(WeChatConstants.FIELD_SIGN);
        return generateSignature(data, key, signType).equals(sign);
    }

    /**
     * 生成签名
     *
     * @param data 待签名数据
     * @param key API密钥
     * @return 签名
     */
    public static String generateSignature(final Map<String, String> data, String key) throws Exception {
        return generateSignature(data, key, WeChatConstants.SignType.MD5);
    }

    /**
     * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
     *
     * @param data 待签名数据
     * @param key API密钥
     * @param signType 签名方式
     * @return 签名
     */
    public static String generateSignature(final Map<String, String> data, String key, WeChatConstants.SignType signType) throws Exception {
        Set<String> keySet = data.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals(WeChatConstants.FIELD_SIGN)) {
                continue;
            }
            if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
                sb.append(k).append("=").append(data.get(k).trim()).append("&");
        }
        sb.append("key=").append(key);
        if (WeChatConstants.SignType.MD5.equals(signType)) {
//            return Md5Utils.hash(sb.toString());
            return MD5(sb.toString()).toUpperCase();
        }
        else if (WeChatConstants.SignType.HMACSHA256.equals(signType)) {
            return HMACSHA256(sb.toString(), key);
        }
        else {
            throw new Exception(String.format("Invalid sign_type: %s", signType));
        }
    }


    /**
     * 获取随机字符串 Nonce Str
     *
     * @return String 随机字符串
     */
    public static String generateNonceStr() {
        char[] nonceChars = new char[32];
        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
        }
        return new String(nonceChars);
    }


    /**
     * 生成 MD5
     *
     * @param data 待处理数据
     * @return MD5结果
     */
    public static String MD5(String data) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] array = md.digest(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    /**
     * 生成 HMACSHA256
     * @param data 待处理数据
     * @param key 密钥
     * @return 加密结果
     * @throws Exception
     */
    public static String HMACSHA256(String data, String key) throws Exception {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    /**
     * 获取当前时间戳,单位秒
     * @return
     */
    public static long getCurrentTimestamp() {
        return System.currentTimeMillis()/1000;
    }

}
4、WXPayXmlUtil
package com.ruoyi.garden.controller.wxpay.dto;

import org.w3c.dom.Document;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

public final class WXPayXmlUtil {
    public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
        documentBuilderFactory.setXIncludeAware(false);
        documentBuilderFactory.setExpandEntityReferences(false);

        return documentBuilderFactory.newDocumentBuilder();
    }

    public static Document newDocument() throws ParserConfigurationException {
        return newDocumentBuilder().newDocument();
    }
}
5、MyX509TrustManager
package com.ruoyi.garden.controller.wxpay.dto;

import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * 对于https请求,我们需要一个证书信任管理器,这个管理器类需要自己定义,但需要实现X509TrustManager接口
 * 证书信任管理器(用于https请求)
 * 这个证书管理器的作用就是让它信任我们指定的证书,上面的代码意味着信任所有证书,不管是否权威机构颁发。
 *
 * @author jiangyin
 */
public class MyX509TrustManager implements X509TrustManager {

    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    }

    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    }

    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
}
6、BasicResponse

import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;


/**
 * 功能描述:基础交互对象
*/
public class BasicResponse<T> implements Serializable{


		/**  描述   (@author: yangxg) */

	private static final long serialVersionUID = 1L;

	private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

	private boolean success = true;

	private T object;

	private String message = "";

	private String code = "200";

	private String remark = "";

	private String time;//服务器返回时间

	private String addition = "";//附加信息

	//系统状态码
	public static final String STATUS_CODE_FAIL_300 = "300";//token失效
	public static final String STATUS_CODE_FAIL_301 = "301";//未上传sys_amdin_id
	public static final String STATUS_CODE_FAIL_400 = "400";//错误请求,如语法错误
	public static final String STATUS_CODE_FAIL_401 = "401";//未登录
	public static final String STATUS_CODE_FAIL_404 = "404";//没有发现文件、查询或URl
	public static final String STATUS_CODE_FAIL_500 = "500";//服务器产生内部错误
	public static final String STATUS_CODE_FAIL_501 = "501";//服务器不支持请求的函数

	public static final String SUCCESS = "success";

	public static final String MESSAGE = "message";

	public static final String REMARK = "remark";

	public static final String ADDITION = "addition";

	public static final String TIME = "time";

	public static final String CODE = "code";

	@SuppressWarnings("unchecked")
	public BasicResponse() {
		this.success = true;
		this.object = (T)new HashMap<String,Object>();
		this.time = sdf.format(new Date());
	}

	public boolean isSuccess() {
		return success;
	}

	public void setSuccess(boolean success) {
		this.success = success;
	}

	public T getObject() {
		return object;
	}

	public BasicResponse setObject(T object) {
		this.object = object;
		return this;
	}

	public String getMessage() {
		return message;
	}

	public BasicResponse setMessage(String message) {
		this.message = message;
		return this;
	}

	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}

	public void setDefultSuccesInfo() {
		this.message = "OPT SUCCESS!";
		this.code = "200";
		this.success = true;
	}
	public BasicResponse setDefultFailInfo() {
		return this.setDefultFailInfo(StringUtils.EMPTY);
	}
	public BasicResponse setDefultFailInfo(String msg) {
		this.message = StringUtils.isBlank(msg)?"OPT FAILED!" : msg;
		this.code = BasicResponse.STATUS_CODE_FAIL_400;
		this.success = false;
		return this;
	}
	public void setFailInfo501() {
		this.message = "user not exist!";
		this.code = BasicResponse.STATUS_CODE_FAIL_501;
		this.success = false;
	}
	public void SETUseNotlogin() {
		this.message = "user not login!";
		this.code = BasicResponse.STATUS_CODE_FAIL_401;
		this.success = false;
	}

	public String getRemark() {
		return remark;
	}

	public void setRemark(String remark) {
		this.remark = remark;
	}

	@SuppressWarnings("unchecked")
	public void setDefultObject() {
		this.object = (T) new HashMap<String,Object>();
	}

	public String getAddition() {
		return addition;
	}

	public void setAddition(String addition) {
		this.addition = addition;
	}

	@Override
	public String toString() {
		return "BasicResponse [success=" + success + ", object=" + object
				+ ", message=" + message + ", code=" + code + ", remark="
				+ remark + ", time=" + time + ", addition=" + addition + "]";
	}

	public static String getNowTime() {
		String time = sdf.format(new Date());
		return time;
	}

	public String getTime() {
		return time;
	}

	public void setTime(String time) {
		this.time = time;
	}

	public static BasicResponse  buildNew(){
		return new BasicResponse();
	}
}
7、CommonText

/**
 * 功能描述:系统提示 使用方法: create by voayc 
 */
public class CommonText {

	public static final String DATA_NOT_EXIST = "数据不存在";

	/** 微信公众号*/
	public static final String WECHAT_CONFIG_DOES_NOT_EXIST = "未配置公司微信公众号的相关信息";
	public static final String WECHAT_PAY_CONFIG_DOES_NOT_EXIST = "未配置公司微信支付的相关信息";
	public static final String OPENID_DOES_NOT_EXIST = "请上传微信用户openid";
	public static final String WECHAT_TRADE_TYPE_DOES_NOT_EXIST = "请上传微信支付交易类型";
	public static final String OPENID_ALREADY_BIND = "该微信号已被其他账号绑定!";
	public static final String ADMIN_OPENID_DOES_NOT_EXIST = "请上传管理员openid";
	public static final String OPENID_DOES_NOT_FIND = "未获取到对应微信用户openid";
	public static final String WECHAT_CODE_DOES_NOT_EXIST = "请上传微信用户code";
	public static final String WECHAT_USER_TOKEN_DOES_ERR = "获取微信用户token失败";
	public static final String UNIFIEDORDER_ERR = "统一下单失败";
	public static final String UNIFIEDORDER_ERR_PREPAY_ID_NOT_FIND = "统一下单失败,未生成PREPAY_ID";

	/** 微信小程序*/
	public static final String MINI_OPENID_DOES_NOT_EXIST = "请上传小程序用户openid";
	public static final String WECHAT_MINI_CONFIG_DOES_NOT_EXIST = "未配置公司微信小程序的相关信息";
	public static final String WECHAT_MINI_PARAM_NOT_EXIST = "缺少所需小程序参数!";
	public static final String WECHAT_MINI_IV_NOT_EXIST = "请上传iv参数";
	public static final String WECHAT_MINI_SESSION_KEY_DOES_NOT_EXIST = "请上传session_key参数";
	public static final String WECHAT_MINI_ENCRYPTED_DATA_DOES_NOT_EXIST = "请上传encrypted_data参数";
	public static final String WECHAT_MINI_ENCRYPTED_DATA_PARSE_ERR = "encrypted_data解析失败";
	public static final String WECHAT_MINI_ENCRYPTED_DATA_NOT_HAVE_PHONE_NUM = "encrypted_data中未解析出电话号码";

	/** 支付相关 */
	public static final String ORDER_PAY_TYPE_NOT_EXIST = "请上传支付方式!";

}
8、UUIDUtils

import java.util.UUID;

/**
 * 功能描述:uuid工具类
 * 使用方法:
*/
public class UUIDUtils {

	/**
	 * 功能描述:获取uuid不带"-"
	 * 使用方法:
	 * 使用约束:
	 * 逻辑:
	 * @return
	 * create voayc 2017年9月4日 下午6:34:44
	*/
	public static String getUUID36() {
		return UUID.randomUUID().toString().replace("-", "");
	}

	/**
	 * 功能描述:获取原始uuid
	 * 使用方法:
	 * 使用约束:
	 * 逻辑:
	 * @return
	 * create voayc 2017年9月4日 下午6:34:34
	*/
	public static String getUUID36OLD() {
		return UUID.randomUUID().toString();
	}

}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值