最近工作中遇到的需求需要使用微信支付,这里把代码做一下记录。
以下代码主要实现了 微信支付,二维码支付,微信退款,微信转账(企业付款)
其中退款和转账需要用到证书,官方有详细的指引,这里就不赘述了。
详情请查看微信支付官方文档


支付参数类

package bean;

public class SysPayParams{
    private String appId;

    private String mchId;

    private String apiKey;

    private String sslCertPath;

    private String certPwd;

    private String notifyUrl;

    private String refundNotifyUrl;

    private String rechargeNotifyUrl;

    public String getAppId() {
        return appId;
    }

    public void setAppId(String appId) {
        this.appId = appId;
    }

    public String getMchId() {
        return mchId;
    }

    public void setMchId(String mchId) {
        this.mchId = mchId;
    }

    public String getApiKey() {
        return apiKey;
    }

    public void setApiKey(String apiKey) {
        this.apiKey = apiKey;
    }

    public String getNotifyUrl() {
        return notifyUrl;
    }

    public void setNotifyUrl(String notifyUrl) {
        this.notifyUrl = notifyUrl;
    }

    public String getSslCertPath() {
        return sslCertPath;
    }

    public void setSslCertPath(String sslCertPath) {
        this.sslCertPath = sslCertPath;
    }

    public String getRefundNotifyUrl() {
        return refundNotifyUrl;
    }

    public void setRefundNotifyUrl(String refundNotifyUrl) {
        this.refundNotifyUrl = refundNotifyUrl;
    }

    public String getCertPwd() {
        return certPwd;
    }

    public void setCertPwd(String certPwd) {
        this.certPwd = certPwd;
    }

    public String getRechargeNotifyUrl() {
        return rechargeNotifyUrl;
    }

    public void setRechargeNotifyUrl(String rechargeNotifyUrl) {
        this.rechargeNotifyUrl = rechargeNotifyUrl;
    }
}

http请求工具类

package util;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
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.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
* @Description  http请求工具类
*/
public class HttpClientUtil {
    private static Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);
	
	private static RequestConfig RC = RequestConfig.custom()
            .setSocketTimeout(60000)  
            .setConnectTimeout(60000)  
            .setConnectionRequestTimeout(60000)  
            .build();
	
	/** 
     * 发送Post请求 
     * @param httpPost 
     * @return 
     */  
    private static String sendHttpPost(HttpPost httpPost, String charset) {
    	if(charset == null) {
			charset = "UTF-8";
		}
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        String responseContent = null;  
        try {  
            // 创建默认的httpClient实例.  
            httpClient = HttpClients.createDefault();
            httpPost.setConfig(RC);  
            // 执行请求  
            response = httpClient.execute(httpPost);  
            entity = response.getEntity();  
            responseContent = EntityUtils.toString(entity, charset);
        } catch (Exception e) {  
            e.printStackTrace();  
        } finally {  
            try {  
                // 关闭连接,释放资源  
                if (response != null) {  
                    response.close();  
                }  
                if (httpClient != null) {  
                    httpClient.close();  
                }  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
        return responseContent;  
    }  
	
    /** 
     * 请求String[]参数
     * @return 请求结果 str
     */  
	public static String createPostRequest(String url, String[] names, String[] values, String charset){
		if(charset == null) {
			charset = "UTF-8";
		}
		HttpPost httpPost = new HttpPost(url);//创建httpPost
		if(names != null && values != null){
			// 创建参数队列
			List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
			for(int i=0; i < names.length; i++){
				nameValuePairs.add(new BasicNameValuePair(names[i], values[i]));
			}
			try {  
				httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, charset));
			} catch (Exception e) {  
				e.printStackTrace();  
			}  
		}
        return sendHttpPost(httpPost, charset);
    }
	
	/** 
     * Map<String, String> params
     * @return 请求结果 str
     */
	public static String createPostRequest(String url, Map<String, String> params, String charset){
		if(charset == null)
			charset = "UTF-8";
		
		HttpPost httpPost = new HttpPost(url);//创建httpPost
		if(params != null && !params.isEmpty()){
			// 创建参数队列
			List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
			for(String name : params.keySet()){
				nameValuePairs.add(new BasicNameValuePair(name, params.get(name)));
			}
			try {  
				httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, charset));
			} catch (Exception e) {  
				e.printStackTrace();  
			}  
		}
        return sendHttpPost(httpPost, charset);
    }
	
	/** 
     * json
     * @return 请求结果 str
     */
	public static String createPostRequestJSON(String url,String json, String charset){
		if(charset == null)
			charset = "UTF-8";
		HttpPost httpPost = new HttpPost(url);
		httpPost.addHeader("Content-type","application/json; charset=" + charset);  
		httpPost.setHeader("Accept", "application/json");  
		try {  
//			System.out.println("json:"+json);
			httpPost.setEntity(new StringEntity(json, Charset.forName(charset)));
        } catch (Exception e) {  
            e.printStackTrace();  
        }
		return sendHttpPost(httpPost, charset);
    }
	
	/** 
     * xml
     * @return 请求结果 str
     */
	public static String createPostRequestXML(String url, String xml, String charset){
		if(charset == null)
			charset = "UTF-8";
		HttpPost httpPost = new HttpPost(url);
		httpPost.addHeader("Content-type","application/xml; charset=" + charset);
		httpPost.setHeader("Accept", "application/xml"); 
		try {  
			httpPost.setEntity(new StringEntity(xml, Charset.forName(charset)));
        } catch (Exception e) {  
            e.printStackTrace();  
        }
		return sendHttpPost(httpPost, charset);
	}

    public static String createSslPostRequest(String url, String data, String sslcertPath, String pwd) throws Exception {
        /**
         * 注意PKCS12证书 是从微信商户平台-》账户设置-》 API安全中下载的
         */
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        /**
         *此处要改
         *wxconfig.SSLCERT_PATH : 指向你的证书的绝对路径,带着证书去访问
         */

        FileInputStream instream = new FileInputStream(new File(sslcertPath));//P12文件目录
        try {
            /**
             * 下载证书时的密码、默认密码是你的MCHID mch_id
             * */
            keyStore.load(instream, pwd.toCharArray());//这里写密码
        } finally {
            instream.close();
        }

        // Trust own CA and all self-signed certs
        /**
         * 下载证书时的密码、默认密码是你的MCHID mch_id
         * */
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, pwd.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();
        }
    }
	
	 /**
     * 向指定 URL 发送POST方法的请求
     * 
     * @param url
     *            发送请求的 URL
     * @param param
     *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @param charset         
     *             发送和接收的格式       
     * @return 所代表远程资源的响应结果
     */
    public static String createPostRequestStr(String url, String param,String charset) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        String line;
        StringBuffer sb=new StringBuffer();
        try {
            URL realUrl = new URL(url);
            //如果是https请求,忽略SSL证书
            if("https".equalsIgnoreCase(realUrl.getProtocol())){
            	   //SslUtils.ignoreSsl();
            }
            // 打开和URL之间的连接 
            URLConnection conn = realUrl.openConnection();
            // 设置通用的请求属性 设置请求格式
            conn.setRequestProperty("contentType", charset);  
            conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
            //设置超时时间
            conn.setConnectTimeout( 60000);
            conn.setReadTimeout( 60000);
            // 发送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(),charset));
            while ((line = in.readLine()) != null) {
                sb.append(line);
            }
            result=sb.toString();
        } catch (Exception e) {
            logger.error("发送 POST请求出现异常!"+e);
            //e.printStackTrace();
        }
        //使用finally块来关闭输出流、输入流
        finally{
            try{
                if(out!=null){
                    out.close();
                }
                if(in!=null){
                    in.close();
                }
            }
            catch(IOException ex){
                ex.printStackTrace();
            }
        }
        return result;
    }
	
	private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5',  
            '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};  

	/** 
	* Takes the raw bytes from the digest and formats them correct. 
	* 
	* @param bytes the raw bytes from the digest. 
	* @return the formatted bytes. 
	*/  
	private static String getFormattedText(byte[] bytes) {  
	int len = bytes.length;  
	StringBuilder buf = new StringBuilder(len * 2);  
	// 把密文转换成十六进制的字符串形式  
	for (int j = 0; j < len; j++) {  
		buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);  
		buf.append(HEX_DIGITS[bytes[j] & 0x0f]);  
	}  
	return buf.toString();  
	}  
	
	public static String encode(String str) {  
		if (str == null) {  
		return null;  
	}  
	try {  
		MessageDigest messageDigest = MessageDigest.getInstance("SHA1");  
		messageDigest.update(str.getBytes());  
		return getFormattedText(messageDigest.digest());  
		} catch (Exception e) {  
		throw new RuntimeException(e);  
		}  
	} 
	
	/**
     * 发送get请求
     * @param url    路径
     * @return
     */
    @SuppressWarnings({ "deprecation", "resource" })
	public static String httpGet(String url){
		String strResult = null;
        //get请求返回结果
        try {
            DefaultHttpClient client = new DefaultHttpClient();
            //发送get请求
            HttpGet request = new HttpGet(url);
            HttpResponse response = client.execute(request);
 
            /**请求发送成功,并得到响应**/
            if (response.getStatusLine().getStatusCode() == 200) {
                /**读取服务器返回过来的json字符串数据**/
                strResult = EntityUtils.toString(response.getEntity());
                /**把json字符串转换成json对象**/
                url = URLDecoder.decode(url, "UTF-8");
            } else {
                logger.error("get请求提交失败:" + url);
            }
        } catch (IOException e) {
        	logger.error("get请求提交失败:" + url);
        }
        return strResult;
    }
	
}

xml工具类

package util;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.dom4j.*;

import java.util.List;

public class XmlTool {
	/**
     * String 转 org.dom4j.Document
     * @param xml
     * @return
     * @throws DocumentException
     */
    public static Document strToDocument(String xml){
        try {
        	//加上xml标签是为了获取最外层的标签,如果不需要可以去掉
			return DocumentHelper.parseText("<xml>"+xml+"</xml>");
		} catch (DocumentException e) {
			return null;
		}
    }
 
    /**
     * org.dom4j.Document 转  com.alibaba.fastjson.JSONObject
     * @param xml
     * @return
     * @throws DocumentException
     */
    public static JSONObject documentToJSONObject(String xml){
		return elementToJSONObject(strToDocument(xml).getRootElement());
    }
 
    /**
     * org.dom4j.Element 转  com.alibaba.fastjson.JSONObject
     * @param node
     * @return
     */
    public static JSONObject elementToJSONObject(Element node) {
        JSONObject result = new JSONObject();
        // 当前节点的名称、文本内容和属性
        List<Attribute> listAttr = node.attributes();// 当前节点的所有属性的list
        for (Attribute attr : listAttr) {// 遍历当前节点的所有属性
            result.put(attr.getName(), attr.getValue());
        }
        // 递归遍历当前节点所有的子节点
        List<Element> listElement = node.elements();// 所有一级子节点的list
        if (!listElement.isEmpty()) {
            for (Element e : listElement) {// 遍历所有一级子节点
                if (e.attributes().isEmpty() && e.elements().isEmpty()) // 判断一级节点是否有属性和子节点
                    result.put(e.getName(), e.getTextTrim());// 沒有则将当前节点作为上级节点的属性对待
                else {
                    if (!result.containsKey(e.getName())) // 判断父节点是否存在该一级节点名称的属性
                        result.put(e.getName(), new JSONArray());// 没有则创建
                    ((JSONArray) result.get(e.getName())).add(elementToJSONObject(e));// 将该一级节点放入该节点名称的属性对应的值中
                }
            }
        }
        return result;
    }
}

支付方法类

package util;

import bean.SysPayParams;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.collections.MapUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.util.*;

/**
 * @ClassName util.WxPayUtil
 * @Description 微信支付工具类
 * @Author leo
 * @Date 2019/8/7 14:29
 **/
public class WxPayUtil {
    private static Logger logger = LoggerFactory.getLogger(WxPayUtil.class);
    private static final String PAY_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    private static final String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";
    private static final String TRANS_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
    private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS7Padding";
    private static final String ALGORITHM = "AES";
    /**
     * 微信支付
     * @param payParams
     * @param outOrderId
     * @param totalPayment
     * @param openId
     * @param ip
     * @param body
     * @return
     */
    public Map wxPay(SysPayParams payParams, String outOrderId, BigDecimal totalPayment, String openId, String ip, String body) throws Exception {
        JSONObject jobj = new JSONObject();
        jobj.put("appid",payParams.getAppId());//小程序id
        jobj.put("mch_id",payParams.getMchId());//商户号
        jobj.put("device_info","WEB");//设备号,非必填
        jobj.put("nonce_str", buildUUID());//随机字符串
        //jobj.put("sign_type","MD5");//签名方式
        jobj.put("body",body);//商品描述,规范
        //jobj.put("detail","");//非必填,规范
        //jobj.put("attach","");//非必填,原样返回
        jobj.put("out_trade_no",outOrderId);//外部订单号
        //计算支付金额,单位分 (总价-钱包付款部分)*100
        jobj.put("total_fee",String.valueOf((totalPayment.multiply(BigDecimal.valueOf(100))).intValue()));//金额,单位分
        jobj.put("spbill_create_ip",ip);//调用支付api的机器ip
        //jobj.put("time_start",DateUtil.formatDateToString(orderInfo.getCreateTime(),DateUtil.yyyyMMddHHmmss2));//非必填,开始时间
        //jobj.put("time_expire","yyyyMMddHHmmss");//非必填,失效时间
        //jobj.put("goods_tag","WXG");//非必填,优惠标记,规则
        jobj.put("notify_url",payParams.getNotifyUrl());//回调地址
        jobj.put("trade_type","JSAPI");
        //jobj.put("product_id","");//非必填,商品id
        //jobj.put("limit_pay","no_credit");//非必填,限制用户不能使用信用卡
        jobj.put("openid",openId);//用户唯一标识
        //jobj.put("receipt","");//开发票
        //jobj.put("scene_info","");//场景信息上报
        jobj.put("sign",paramsSign(jobj,payParams.getApiKey()));//签名

        String xml =jsonToXmlstr(jobj,new StringBuffer());
        xml = "<xml>"+xml+"</xml>";
        String charset = "utf-8";
        String result = HttpClientUtil.createPostRequestXML(PAY_URL,xml,charset);
        JSONObject json = XmlTool.documentToJSONObject(result);
        JSONObject data = (JSONObject)json.getJSONArray("xml").get(0);
        if ("SUCCESS".equals(MapUtils.getString(data,"return_code"))){
            //组装返回参数
            String prepayId = MapUtils.getString(data,"prepay_id");
            String timeStamp = String.valueOf(System.currentTimeMillis()/1000);
            JSONObject map = new JSONObject();
            map.put("appId",payParams.getAppId());
            map.put("timeStamp",timeStamp);
            map.put("nonceStr",buildUUID());
            map.put("package","prepay_id="+prepayId);
            map.put("signType","MD5");
            String paySign = paramsSign(map,payParams.getApiKey());
            map.put("paySign",paySign);
            return map;
        }else {
             System.out.println("下单失败"+result);
            throw new Exception("下单失败");
        }
    }

    /**
     * 发起退款
     * @param payParams
     * @param outOrderId
     * @param backOrderId
     * @param totalAmount
     * @param backAmount
     * @param reason
     * @return
     */
    public Map refund(SysPayParams payParams,String outOrderId,String backOrderId,BigDecimal totalAmount,BigDecimal backAmount,String reason) throws Exception {
        JSONObject jobj = new JSONObject();
        jobj.put("appid",payParams.getAppId());//小程序id
        jobj.put("mch_id",payParams.getMchId());//商户号
        jobj.put("nonce_str", buildUUID());//随机字符串
        jobj.put("out_trade_no", outOrderId);//外部订单号
        jobj.put("out_refund_no", backOrderId);//商户退款单号
        jobj.put("total_fee",String.valueOf(totalAmount.multiply(new BigDecimal(100)).intValue()));//订单金额
        jobj.put("refund_fee",String.valueOf(backAmount.multiply(new BigDecimal(100)).intValue()));//退款金额
        jobj.put("refund_desc",reason);//退款原因
        jobj.put("notify_url",payParams.getRefundNotifyUrl());
        jobj.put("sign",paramsSign(jobj,payParams.getApiKey()));//签名
        String xml =jsonToXmlstr(jobj,new StringBuffer());
        xml = "<xml>"+xml+"</xml>";
        String result = null;
        try {
            result = HttpClientUtil.createSslPostRequest(REFUND_URL,xml,payParams.getSslCertPath(),payParams.getCertPwd());
        } catch (Exception e) {
             System.out.println("订单:"+outOrderId+"退款异常,错误信息:"+e.getMessage());
        }
        JSONObject json = XmlTool.documentToJSONObject(result);
        JSONObject data = (JSONObject)json.getJSONArray("xml").get(0);
        if ("SUCCESS".equals(MapUtils.getString(data,"return_code"))){
            Map<String,Object> map = new HashMap<>();
            map.put("wxBackId",MapUtils.getString(data,"refund_id"));
            map.put("wxBackTime",new Date());
            return map;
        }else {
             System.out.println("退款失败"+result);
            throw new Exception("退款失败");
        }
    }

    /**
     * 转账
     * @param payParams
     * @param transferId
     * @param openId
     * @param realName
     * @param amount
     * @param ip
     * @param remark
     * @return
     */
    public Map transfer(SysPayParams payParams,String transferId,String openId,String realName,BigDecimal amount,String ip,String remark) throws Exception {
        JSONObject jobj = new JSONObject();
        jobj.put("mch_appid",payParams.getAppId());//小程序id
        jobj.put("mchid",payParams.getMchId());//商户号
        jobj.put("nonce_str", buildUUID());//随机字符串
        jobj.put("partner_trade_no", transferId);//商户订单号
        jobj.put("openid", openId);//用户openid
        jobj.put("check_name","NO_CHECK");//姓名校验
        //jobj.put("re_user_name",realName);//真实姓名,若需要校验则必填
        jobj.put("amount",String.valueOf(amount.multiply(new BigDecimal(100)).intValue()));//金额
        jobj.put("desc",remark);
        jobj.put("spbill_create_ip",ip);
        jobj.put("sign",paramsSign(jobj,payParams.getApiKey()));//签名
        String xml =jsonToXmlstr(jobj,new StringBuffer());
        xml = "<xml>"+xml+"</xml>";
        String result = null;
        try {
            result = HttpClientUtil.createSslPostRequest(TRANS_URL,xml,payParams.getSslCertPath(),payParams.getCertPwd());
        } catch (Exception e) {
             System.out.println("转账订单:"+transferId+"发生异常,错误信息:"+e.getMessage());
        }
        JSONObject json = XmlTool.documentToJSONObject(result);
        JSONObject data = (JSONObject)json.getJSONArray("xml").get(0);
        if ("SUCCESS".equals(MapUtils.getString(data,"return_code")) && "SUCCESS".equals(MapUtils.getString(data,"result_code"))){
            Map<String,Object> map = new HashMap<>();
            map.put("payOrderId",MapUtils.getString(data,"payment_no"));
            map.put("payTime",MapUtils.getString(data,"payment_time"));
            return map;
        }else {
             System.out.println("转账失败"+result);
            throw new Exception("转账失败");
        }
    }

    /**
     * 微信支付Native(二维码) 跟wxpay支付方法同一接口,场景不同
     * @param payParams
     * @param logId
     * @param totalPayment
     * @param ip
     * @param body
     * @return
     */
    public String recharge(SysPayParams payParams,String logId, BigDecimal totalPayment, String ip, String body) throws Exception {
        JSONObject jobj = new JSONObject();
        jobj.put("appid",payParams.getAppId());//小程序id
        jobj.put("mch_id",payParams.getMchId());//商户号
        jobj.put("device_info","WEB");//设备号,非必填
        jobj.put("nonce_str", buildUUID());//随机字符串
        //jobj.put("sign_type","MD5");//签名方式
        jobj.put("body",body);//商品描述,规范
        //jobj.put("detail","");//非必填,规范
        //jobj.put("attach","");//非必填,原样返回
        jobj.put("out_trade_no",logId);//外部订单号
        //计算支付金额,单位分 (总价-钱包付款部分)*100
        jobj.put("total_fee",String.valueOf((totalPayment.multiply(BigDecimal.valueOf(100))).intValue()));//金额,单位分
        jobj.put("spbill_create_ip",ip);//调用支付api的机器ip
        jobj.put("notify_url",payParams.getRechargeNotifyUrl());//回调地址
        jobj.put("trade_type","NATIVE");
        jobj.put("product_id",logId);//必填,商品id
        //jobj.put("limit_pay","no_credit");//非必填,限制用户不能使用信用卡
        //jobj.put("receipt","");//开发票
        //jobj.put("scene_info","");//场景信息上报
        jobj.put("sign",paramsSign(jobj,payParams.getApiKey()));//签名

        String xml =jsonToXmlstr(jobj,new StringBuffer());
        xml = "<xml>"+xml+"</xml>";
        String charset = "utf-8";
        String result = HttpClientUtil.createPostRequestXML(PAY_URL,xml,charset);
        JSONObject json = XmlTool.documentToJSONObject(result);
        JSONObject data = (JSONObject)json.getJSONArray("xml").get(0);
        if ("SUCCESS".equals(MapUtils.getString(data,"return_code"))){
            //组装返回参数
            String codeUrl = MapUtils.getString(data,"code_url");
            return codeUrl;
        }else {
            System.out.println("失败"+result);
            throw new Exception("下单失败");
        }
    }

    public String buildUUID(){
        return UUID.randomUUID().toString().replace("-","");
    }
    //签名
    private String paramsSign(JSONObject jobj,String key){
        TreeMap<String,String> map = new TreeMap<String, String>();
        //参数名ASCII码从小到大排序,转treeMap排序
        map.putAll((Map)jobj);
        StringBuffer signStr = new StringBuffer();
        for (Map.Entry entry:map.entrySet()) {
            if (entry.getValue()!=null && !"".equals(entry.getValue().toString())) {
                signStr.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
        }
        signStr.append("key="+key);
        //md5加密转大写
        String sign = computeUTF(signStr.toString()).toUpperCase();
        return sign;
    }

    /**
     * 验签
     * @param jobj
     * @return
     */
    public boolean checkSign(JSONObject jobj,String apiKey){
        String paramSign = MapUtils.getString(jobj,"sign");
        jobj.remove("sign");
        String sign = paramsSign(jobj,apiKey);
        return sign.equals(paramSign);
    }

    /**
     *
     * @param str
     * @return
     */
    public static String computeUTF(String str) {
        MessageDigest messageDigest = null;

        try {
            messageDigest = MessageDigest.getInstance("MD5");
            messageDigest.reset();
            messageDigest.update(str.getBytes("utf-8"));
        } catch (NoSuchAlgorithmException var5) {
            System.out.println("NoSuchAlgorithmException caught!");
            System.exit(-1);
        } catch (UnsupportedEncodingException var6) {
            var6.printStackTrace();
        }

        byte[] byteArray = messageDigest.digest();
        StringBuffer md5StrBuff = new StringBuffer();

        for(int i = 0; i < byteArray.length; ++i) {
            if (Integer.toHexString(255 & byteArray[i]).length() == 1) {
                md5StrBuff.append("0").append(Integer.toHexString(255 & byteArray[i]));
            } else {
                md5StrBuff.append(Integer.toHexString(255 & byteArray[i]));
            }
        }

        return md5StrBuff.toString();
    }

    /**
     * json转xml
     * @param jObj
     * @param buffer
     * @return
     */
    public String jsonToXmlstr(JSONObject jObj,StringBuffer buffer){
        Set<Map.Entry<String, Object>> se = jObj.entrySet();
        for(Iterator<Map.Entry<String, Object>> it = se.iterator(); it.hasNext(); )
        {
            Map.Entry<String, Object> en = it.next();
            if(en.getValue().getClass().getName().equals("com.alibaba.fastjson.JSONObject")){
                buffer.append("<"+en.getKey()+">");
                JSONObject jo = jObj.getJSONObject(en.getKey());
                jsonToXmlstr(jo,buffer);
                buffer.append("</"+en.getKey()+">");
            }else if(en.getValue().getClass().getName().equals("com.alibaba.fastjson.JSONArray")){
                JSONArray jarray = jObj.getJSONArray(en.getKey());
                for (int i = 0; i < jarray.size(); i++) {
                    buffer.append("<"+en.getKey()+">");
                    JSONObject jsonobject =  jarray.getJSONObject(i);
                    jsonToXmlstr(jsonobject,buffer);
                    buffer.append("</"+en.getKey()+">");
                }
            }else if(en.getValue().getClass().getName().equals("java.lang.String")){
                buffer.append("<"+en.getKey()+">"+en.getValue());
                buffer.append("</"+en.getKey()+">");
            }
        }
        return buffer.toString();
    }

    /**
     * 退款回调的req_info解密
     * @param reqInfo
     * @param apiKey
     * @return
     * @throws Exception
     */
    public String decryptReqInfo(String reqInfo,String apiKey) throws Exception {
        byte[] decodeBase64Data = Base64.getDecoder().decode(reqInfo);
        Security.addProvider(new BouncyCastleProvider());
        Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING, "BC");
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(computeUTF(apiKey).toLowerCase().getBytes(), ALGORITHM));
        return new String(cipher.doFinal(decodeBase64Data));
    }

}

回调处理

package util;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.collections.MapUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * @ClassName util.CallbackController
 * @Description 回调处理
 * @Author leo
 * @Date 2019-08-08 15:02
 **/
public class CallbackController {
    WxPayUtil wxPayUtil= new WxPayUtil();

    public Object rechargeCallBack(HttpServletRequest request) throws IOException {
        InputStream inStream = request.getInputStream();
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        String resultxml = new String(outSteam.toByteArray(), "utf-8");
        JSONObject json = XmlTool.documentToJSONObject(resultxml);
        outSteam.close();
        inStream.close();
        JSONObject params = (JSONObject) json.getJSONArray("xml").get(0);
        String apiKey = "";
        if (wxPayUtil.checkSign(params,apiKey)) {
            //todo 处理业务

            //返回参数
            JSONObject returnParam = new JSONObject();
            returnParam.put("return_code", "SUCCESS");
            returnParam.put("return_msg", "OK");
            return "<xml>" + wxPayUtil.jsonToXmlstr(returnParam, new StringBuffer()) + "</xml>";
        } else {
            System.out.println("验签失败");
            return "fasle";
        }
    }

    public Object refundCallBack(HttpServletRequest request) throws IOException {
        InputStream inStream = request.getInputStream();
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        String resultxml = new String(outSteam.toByteArray(), "utf-8");
        JSONObject json = XmlTool.documentToJSONObject(resultxml);
        outSteam.close();
        inStream.close();
        JSONObject params = (JSONObject) json.getJSONArray("xml").get(0);
        String apiKey = "";
        String reqInfo = params.getString("req_info");
        String decodeXml;
        try {
            decodeXml = wxPayUtil.decryptReqInfo(reqInfo,apiKey);
        } catch (Exception e) {
            System.out.println("解密失败"+reqInfo);
            return "false";
        }
        JSONObject data = XmlTool.documentToJSONObject(decodeXml);
        JSONObject resultJson = (JSONObject) data.getJSONArray("root").get(0);
        String flag = MapUtils.getString(resultJson,"refund_status");
        if ("SUCCESS".equals(flag)) {
            //todo 处理业务

            //返回参数
            JSONObject returnParam = new JSONObject();
            returnParam.put("return_code", "SUCCESS");
            returnParam.put("return_msg", "OK");
            return "<xml>" + wxPayUtil.jsonToXmlstr(returnParam, new StringBuffer()) + "</xml>";
        }
        return false;
    }
}

测试方法

import bean.SysPayParams;
import util.WxPayUtil;

import java.math.BigDecimal;

/**
 * @ClassName DemoTest
 * @Description 测试类
 * @Author leo
 * @Date 2019-08-09 11:04
 **/
public class DemoTest {
    public static void main(String[] args) throws Exception {
        SysPayParams sysPayParams = new SysPayParams();
        sysPayParams.setApiKey("");
        sysPayParams.setAppId("");
        sysPayParams.setMchId("");
        sysPayParams.setNotifyUrl("");
        sysPayParams.setRefundNotifyUrl("");
        sysPayParams.setRechargeNotifyUrl("");
        sysPayParams.setSslCertPath("");//证书地址
        sysPayParams.setCertPwd("");//证书密码,默认是商户号
        String outOrderId = "";
        String openId = "";
        String ip = "";
        String body = "测试-充值";
        BigDecimal amount = new BigDecimal("0.01");
        WxPayUtil wxPayUtil = new WxPayUtil();
        //二维码支付 返回二维码链接
        String result = wxPayUtil.recharge(sysPayParams,outOrderId,amount,ip,body);
        //微信支付 返回包装过的map方便前端直接调用
        //Map result = wxPayUtil.wxPay(sysPayParams,outOrderId,amount,openId,ip,body);
        String backOrderId = "";
        //退款
        //wxPayUtil.refund(sysPayParams,outOrderId,backOrderId,amount,amount,body);
        //转账,实时接口
        //wxPayUtil.transfer(sysPayParams,outOrderId,openId,null,amount,ip,body);
        System.out.println(result);
    }
}