ChenJiaHe
2021-04-19 6efc676c6599cee0f82989fbda0e6bbdd71b9bfe
新增企业付款接口(优化)
1个文件已删除
3个文件已添加
1009 ■■■■ 已修改文件
hx-common.iml 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/hx/corp/entity/CorpPayRequest.java 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/hx/corp/entity/CorpPayResponse.java 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/hx/corp/util/WxCorpPayUtil.java 610 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
hx-common.iml
File was deleted
src/main/java/com/hx/corp/entity/CorpPayRequest.java
New file
@@ -0,0 +1,135 @@
package com.hx.corp.entity;
import java.util.UUID;
/**企业付款请求参数实体
 *
 */
public class CorpPayRequest {
    /*(Y)商户账号appid,申请商户号的appid或商户号绑定的appid*/
    private String mch_appid;
    /*(Y)商户号*/
    private String mchid;
    /*(N)设备号*/
    private String device_info;
    /*(Y)随机字符串*/
    private String nonce_str = UUID.randomUUID().toString().substring(0, 30);
    /*(Y)随签名*/
    private String sign;
    /*(Y)商户订单号*/
    private String partner_trade_no;
    /*(Y)用户openid*/
    private String openid;
    /*(Y)校验用户姓名选项,NO_CHECK:不校验真实姓名 ;FORCE_CHECK:强校验真实姓名*/
    private String check_name = "NO_CHECK";
    /*(O)收款用户姓名,如果check_name设置为FORCE_CHECK,则必填用户真实姓名*/
    private String re_user_name;
    /*(Y)金额*/
    private int amount;
    /*(Y)企业付款备注*/
    private String desc;
    /*(N)Ip地址,该IP同在商户平台设置的IP白名单中的IP没有关联,该IP可传用户端或者服务端的IP*/
    private String spbill_create_ip = "8.8.8.8";
    public CorpPayRequest() {
    }
    ////////////////////////////////////////////////////////
    public String getMch_appid() {
        return mch_appid;
    }
    public void setMch_appid(String mch_appid) {
        this.mch_appid = mch_appid;
    }
    public String getMchid() {
        return mchid;
    }
    public void setMchid(String mchid) {
        this.mchid = mchid;
    }
    public String getDevice_info() {
        return device_info;
    }
    public void setDevice_info(String device_info) {
        this.device_info = device_info;
    }
    public String getSign() {
        return sign;
    }
    public void setSign(String sign) {
        this.sign = sign;
    }
    public String getPartner_trade_no() {
        return partner_trade_no;
    }
    public void setPartner_trade_no(String partner_trade_no) {
        this.partner_trade_no = partner_trade_no;
    }
    public String getOpenid() {
        return openid;
    }
    public void setOpenid(String openid) {
        this.openid = openid;
    }
    public String getCheck_name() {
        return check_name;
    }
    public void setCheck_name(String check_name) {
        this.check_name = check_name;
    }
    public String getRe_user_name() {
        return re_user_name;
    }
    public void setRe_user_name(String re_user_name) {
        this.re_user_name = re_user_name;
    }
    public int getAmount() {
        return amount;
    }
    public void setAmount(int amount) {
        this.amount = amount;
    }
    public String getDesc() {
        return desc;
    }
    public void setDesc(String desc) {
        this.desc = desc;
    }
    public String getSpbill_create_ip() {
        return spbill_create_ip;
    }
    public void setSpbill_create_ip(String spbill_create_ip) {
        this.spbill_create_ip = spbill_create_ip;
    }
    public String getNonce_str() {
        return nonce_str;
    }
    public void setNonce_str(String nonce_str) {
        this.nonce_str = nonce_str;
    }
}
src/main/java/com/hx/corp/entity/CorpPayResponse.java
New file
@@ -0,0 +1,132 @@
package com.hx.corp.entity;
/**企业付款返回参数实体
 *
 */
public class CorpPayResponse {
    /*(Y)业务结果,SUCCESS/FAIL,注意:当状态为FAIL时,存在业务结果未明确的情况。如果状态为FAIL,请务必关注错误代码(err_code字段),通过查询接口确认此次付款的结果*/
    private String result_code = "FAIL";
    /*(Y)错误代码,成功:SUCCESS*/
    private String err_code;
    /*(Y)错误代码描述*/
    private String err_code_des;
    /*(Y)商户appid*/
    private String mch_appid;
    /*(Y)商户号*/
    private String mchid;
    /*(N)设备号*/
    private String device_info;
    /*(Y)随机字符串*/
    private String nonce_str;
    //以下字段在return_code 和result_code都为SUCCESS的时候有返回
    /*(O)商户订单号*/
    private String partner_trade_no;
    /*(O)商户订单号*/
    private String payment_no;
    /*(O)付款成功时间*/
    private String payment_time;
    public CorpPayResponse() {
    }
    ////////////////////////////////////////////////////////
    public String getMch_appid() {
        return mch_appid;
    }
    public void setMch_appid(String mch_appid) {
        this.mch_appid = mch_appid;
    }
    public String getMchid() {
        return mchid;
    }
    public void setMchid(String mchid) {
        this.mchid = mchid;
    }
    public String getDevice_info() {
        return device_info;
    }
    public void setDevice_info(String device_info) {
        this.device_info = device_info;
    }
    public String getResult_code() {
        return result_code;
    }
    public void setResult_code(String result_code) {
        this.result_code = result_code;
    }
    public String getErr_code() {
        return err_code;
    }
    public void setErr_code(String err_code) {
        this.err_code = err_code;
    }
    public String getErr_code_des() {
        return err_code_des;
    }
    public void setErr_code_des(String err_code_des) {
        this.err_code_des = err_code_des;
    }
    public String getNonce_str() {
        return nonce_str;
    }
    public void setNonce_str(String nonce_str) {
        this.nonce_str = nonce_str;
    }
    public String getPartner_trade_no() {
        return partner_trade_no;
    }
    public void setPartner_trade_no(String partner_trade_no) {
        this.partner_trade_no = partner_trade_no;
    }
    public String getPayment_no() {
        return payment_no;
    }
    public void setPayment_no(String payment_no) {
        this.payment_no = payment_no;
    }
    public String getPayment_time() {
        return payment_time;
    }
    public void setPayment_time(String payment_time) {
        this.payment_time = payment_time;
    }
    @Override
    public String toString() {
        return "CorpPayResponse{" +
                "result_code='" + result_code + '\'' +
                ", err_code='" + err_code + '\'' +
                ", err_code_des='" + err_code_des + '\'' +
                ", mch_appid='" + mch_appid + '\'' +
                ", mchid='" + mchid + '\'' +
                ", device_info='" + device_info + '\'' +
                ", nonce_str='" + nonce_str + '\'' +
                ", partner_trade_no='" + partner_trade_no + '\'' +
                ", payment_no='" + payment_no + '\'' +
                ", payment_time='" + payment_time + '\'' +
                '}';
    }
}
src/main/java/com/hx/corp/util/WxCorpPayUtil.java
New file
@@ -0,0 +1,610 @@
package com.hx.corp.util;
import com.alibaba.fastjson.JSON;
import com.hx.corp.entity.CorpPayRequest;
import com.hx.corp.entity.CorpPayResponse;
import com.hx.exception.ServiceException;
import com.hx.mp.util.*;
import com.hx.util.SimpleTool;
import com.hx.util.StringUtils;
import net.sf.json.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.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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.util.*;
/** 企业微信支付/退款
 * @author ChenJiaHe
 */
public class WxCorpPayUtil {
    //log4j日志
    private static Logger logger = LoggerFactory.getLogger(WxCorpPayUtil.class.getName());
    // 退款接口连接
    private static final String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";
     /**查询订单链接*/
    @SuppressWarnings("unused")
    private static final String QUERY_URL = "https://api.mch.weixin.qq.com/pay/orderquery";
    /**同意下单链接*/
    private static final String FIRST_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    // 企业付款
    private static final String CORP_PAY_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
    /** 企业付款
     * @param corpPayRequest 请求对象
     * @param mchKey 商户号秘钥
     * @param certPath 支付证书
     * @return CorpPayResponse
     * @throws Exception
     */
    public static CorpPayResponse qdCorpPay(CorpPayRequest corpPayRequest, String mchKey, String certPath)
            throws Exception {
        CorpPayResponse corpPayResponse = new CorpPayResponse();
        SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
        parameters.put("mch_appid", corpPayRequest.getMch_appid());
        parameters.put("mchid", corpPayRequest.getMchid());
        parameters.put("partner_trade_no", corpPayRequest.getPartner_trade_no());
        //parameters.put("nonce_str", UUID.randomUUID().toString().substring(0, 30));
        parameters.put("nonce_str", corpPayRequest.getNonce_str());
        parameters.put("openid", corpPayRequest.getOpenid());
        parameters.put("check_name", corpPayRequest.getCheck_name());
        parameters.put("amount", corpPayRequest.getAmount());
        parameters.put("spbill_create_ip", corpPayRequest.getSpbill_create_ip());
        parameters.put("desc", corpPayRequest.getDesc());
        String sign = WXSignUtils.createSign("UTF-8", parameters, mchKey);
        parameters.put("sign", sign);
        String xmlInfo = HttpXmlUtils.transferXml(parameters);
        try {
            CloseableHttpResponse response = HttpUtil.Post(CORP_PAY_URL, xmlInfo, true, certPath, corpPayRequest.getMchid());
            String transfersXml = EntityUtils.toString(response.getEntity(), "utf-8");
            // System.out.println("渠道端企业付款:" + transfersXml);
            Map<String, String> transferMap = HttpXmlUtils.parseRefundXml(transfersXml);
            // 将 Map 转换为 实体类
            corpPayResponse = JSON.parseObject(JSON.toJSONString(transferMap),CorpPayResponse.class);
        } catch (Exception e) {
            logger.error("企业付款接口报错",e);
        }
        return corpPayResponse;
    }
    /**统一支付
     * @param request 方法获取
     * @param appId  小程序号
     * @param partner  商户号
     * @param key  秘钥
     * @param notifyUrl  回调链接
     * @param out_trade_no  订单号
     * @param body 商品描述
     * @param total_fee 支付金额
     * @param openid 用户openId
     * @param attach 附带数据包
     * @param notifyUrl 回调通知地址
     * @param trade_type 交易类型
     * @return JSON  status = "SUC"为成功
     */
    public static JSONObject unifiedPay(HttpServletRequest request,String appId,String partner,String key,String notifyUrl,String out_trade_no, String body, String total_fee, String openid,
            String attach,String trade_type) throws Exception {
        if (!SimpleTool.checkNotNull(notifyUrl)) {
            throw new ServiceException("支付功能故障!");
        }
        // 创建查询请求对象
        RequestHandler reqHandler = new RequestHandler(null, null);
        // 通信对象
        TenpayHttpClient httpClient = new TenpayHttpClient();
        // 应答对象
        ClientResponseHandler resHandler = new ClientResponseHandler();
        // -----------------------------
        // 设置请求参数
        // -----------------------------
        // reqHandler.init();
        reqHandler.setKey(key);
        reqHandler.setGateUrl(FIRST_ORDER_URL);// 请求URL
        // -----------------------------
        // 设置接口参数(sign后台自动生成)
        // -----------------------------
        reqHandler.setParameter("appid", appId); // 公众号/小程序
        reqHandler.setParameter("mch_id", partner); // 商户号
        reqHandler.setParameter("nonce_str", SimpleTool.getUUIDName().substring(0, 30));// 随机乱码
        reqHandler.setParameter("body", body);// 商品描述
        reqHandler.setParameter("out_trade_no", out_trade_no);// 商户订单号
        reqHandler.setParameter("total_fee", total_fee);// 总金额
        reqHandler.setParameter("spbill_create_ip", "8.8.8.8");// 终端IP
        reqHandler.setParameter("notify_url",notifyUrl);// 通知地址
        reqHandler.setParameter("trade_type", trade_type);// 交易类型
                                                          // JSAPI,NATIVE,APP
        reqHandler.setParameter("openid", openid);// openId
        reqHandler.setParameter("attach", attach);// 附带数据包
        // -----------------------------
        // 设置通信参数
        // -----------------------------
        // 设置请求返回的等待时间
        httpClient.setTimeOut(5);
        // 设置ca证书
        // httpClient.setCaInfo(new File(CA_PATH));
        // 设置个人(商户)证书
        // httpClient.setCertInfo(new File(CERT_PATH), CERT_PWD);
        // 设置发送类型POST
        httpClient.setMethod("POST");
        // 设置请求内容(生成sign)
        String requestUrl = reqHandler.getRequestURL();// 组拼https://www.baidu.com?a=x&b=xx
        httpClient.setReqContent(requestUrl);// https://www.baidu.com?a=x&b=xx
        String rescontent = "null";
        httpClient.setRequestHandler(reqHandler);// 把处理对象,像是参数各种东西都设置进去方便获取(quan)
        // 返回出去的对象(状态,错误原因,该操作相关信息(参数,返回值))
        JSONObject returnObj = new JSONObject();
        // 后台调用
        if (httpClient.call()) {
            System.out.println("统一下单,成功cll了::");
            // 设置结果参数
            rescontent = httpClient.getResContent();
            System.out.println("统一下单返回结果:" + rescontent);
            resHandler.setContent(rescontent);// 解析xml
            resHandler.setKey(key);
            // 获取返回参数
            String return_code = resHandler.getParameter("return_code");
            String return_msg = resHandler.getParameter("return_msg");
            // 判断签名及结果
            if (resHandler.isTenpaySign() && "SUCCESS".equals(return_code)) {
                String prepay_id = resHandler.getParameter("prepay_id");// 预支付交易会话标识
                String code_url = resHandler.getParameter("code_url");// 二维码链接
                String result_code = resHandler.getParameter("result_code");// 业务结果
                String appid = resHandler.getParameter("appid");// 公众账号ID
                String mch_id = resHandler.getParameter("mch_id");// 商户号
                String nonce_str = resHandler.getParameter("nonce_str");// 随机码
                String sign = resHandler.getParameter("sign");// 签名
                if (result_code.equals("SUCCESS")) {
                    returnObj.put("status", "suc");
                    returnObj.put("sign", sign);
                    returnObj.put("nonce_str", nonce_str);
                    returnObj.put("mch_id", mch_id);
                    returnObj.put("appid", appid);
                    returnObj.put("prepay_id", prepay_id);
                    returnObj.put("code_url", code_url);
                    returnObj.put("out_trade_no", out_trade_no);
                } else {
                    String errMsg = "[ERROR]result_code:" + resHandler.getParameter("result_code") + " err_code:"
                            + resHandler.getParameter("err_code") + "err_code_des:"
                            + resHandler.getParameter("err_code_des");
                    // 错误时,返回结果未签名,记录retcode、retmsg看失败详情。
                    returnObj.put("status", "ERROR-C");
                    returnObj.put("errMsg", errMsg);
                }
            } else {
                String errMsg = "return_code:" + return_code + "err_code:" + resHandler.getParameter("err_code")
                        + " return_msg:" + return_msg;
                // 错误时,返回结果未签名,记录retcode、retmsg看失败详情。
                returnObj.put("status", "ERROR-B");
                returnObj.put("errMsg", errMsg);
            }
        } else {
            // 有可能因为网络原因,请求已经处理,但未收到应答。
            returnObj.put("status", "ERROR-A");
            returnObj.put("errMsg", httpClient.getResponseCode() + ":" + httpClient.getErrInfo());
        }
        // 获取debug信息,建议把请求、应答内容、debug信息,通信返回码写入日志,方便定位问题
        String detail = "http res:" + httpClient.getResponseCode() + "," + httpClient.getErrInfo() + ";" + "req url:"
                + requestUrl + ";" + ";" + "req debug:" + reqHandler.getDebugInfo() + ";" + "res content:" + rescontent
                + ";" + "res debug:" + resHandler.getDebugInfo() + ";";
        returnObj.put("detail", detail);
        return returnObj;
    }
    /**统一支付(分账)
     * @param request 方法获取
     * @param appId  小程序号
     * @param partner  商户号
     * @param key  秘钥
     * @param notifyUrl  回调链接
     * @param out_trade_no  订单号
     * @param body 商品描述
     * @param total_fee 支付金额
     * @param openid 用户openId
     * @param attach 附带数据包
     * @param notifyUrl 回调通知地址
     * @param trade_type 交易类型
     * @param profit_sharing 是否分账:N不,Y是
     * @return JSON  status = "SUC"为成功
     */
    public static JSONObject unifiedPay(HttpServletRequest request,String appId,String partner,String key,String notifyUrl,String out_trade_no, String body, String total_fee, String openid,
                                        String attach,String trade_type,String profit_sharing) throws Exception {
        if (!SimpleTool.checkNotNull(notifyUrl)) {
            throw new ServiceException("支付功能故障!");
        }
        // 创建查询请求对象
        RequestHandler reqHandler = new RequestHandler(null, null);
        // 通信对象
        TenpayHttpClient httpClient = new TenpayHttpClient();
        // 应答对象
        ClientResponseHandler resHandler = new ClientResponseHandler();
        // -----------------------------
        // 设置请求参数
        // -----------------------------
        // reqHandler.init();
        reqHandler.setKey(key);
        reqHandler.setGateUrl(FIRST_ORDER_URL);// 请求URL
        // -----------------------------
        // 设置接口参数(sign后台自动生成)
        // -----------------------------
        reqHandler.setParameter("appid", appId); // 公众号/小程序
        reqHandler.setParameter("mch_id", partner); // 商户号
        reqHandler.setParameter("nonce_str", SimpleTool.getUUIDName().substring(0, 30));// 随机乱码
        reqHandler.setParameter("body", body);// 商品描述
        reqHandler.setParameter("out_trade_no", out_trade_no);// 商户订单号
        reqHandler.setParameter("total_fee", total_fee);// 总金额
        reqHandler.setParameter("spbill_create_ip", "8.8.8.8");// 终端IP
        reqHandler.setParameter("notify_url",notifyUrl);// 通知地址
        reqHandler.setParameter("trade_type", trade_type);// 交易类型
        //JSAPI,NATIVE,APP
        reqHandler.setParameter("openid", openid);// openId
        reqHandler.setParameter("attach", attach);// 附带数据包
        reqHandler.setParameter("profit_sharing", profit_sharing);// 附带数据包
        // -----------------------------
        // 设置通信参数
        // -----------------------------
        // 设置请求返回的等待时间
        httpClient.setTimeOut(5);
        // 设置ca证书
        // httpClient.setCaInfo(new File(CA_PATH));
        // 设置个人(商户)证书
        // httpClient.setCertInfo(new File(CERT_PATH), CERT_PWD);
        // 设置发送类型POST
        httpClient.setMethod("POST");
        // 设置请求内容(生成sign)
        String requestUrl = reqHandler.getRequestURL();// 组拼https://www.baidu.com?a=x&b=xx
        httpClient.setReqContent(requestUrl);// https://www.baidu.com?a=x&b=xx
        String rescontent = "null";
        httpClient.setRequestHandler(reqHandler);// 把处理对象,像是参数各种东西都设置进去方便获取(quan)
        // 返回出去的对象(状态,错误原因,该操作相关信息(参数,返回值))
        JSONObject returnObj = new JSONObject();
        // 后台调用
        if (httpClient.call()) {
            System.out.println("统一下单,成功cll了::");
            // 设置结果参数
            rescontent = httpClient.getResContent();
            System.out.println("统一下单返回结果:" + rescontent);
            resHandler.setContent(rescontent);// 解析xml
            resHandler.setKey(key);
            // 获取返回参数
            String return_code = resHandler.getParameter("return_code");
            String return_msg = resHandler.getParameter("return_msg");
            // 判断签名及结果
            if (resHandler.isTenpaySign() && "SUCCESS".equals(return_code)) {
                String prepay_id = resHandler.getParameter("prepay_id");// 预支付交易会话标识
                String code_url = resHandler.getParameter("code_url");// 二维码链接
                String result_code = resHandler.getParameter("result_code");// 业务结果
                String appid = resHandler.getParameter("appid");// 公众账号ID
                String mch_id = resHandler.getParameter("mch_id");// 商户号
                String nonce_str = resHandler.getParameter("nonce_str");// 随机码
                String sign = resHandler.getParameter("sign");// 签名
                if (result_code.equals("SUCCESS")) {
                    returnObj.put("status", "suc");
                    returnObj.put("sign", sign);
                    returnObj.put("nonce_str", nonce_str);
                    returnObj.put("mch_id", mch_id);
                    returnObj.put("appid", appid);
                    returnObj.put("prepay_id", prepay_id);
                    returnObj.put("code_url", code_url);
                    returnObj.put("out_trade_no", out_trade_no);
                } else {
                    String errMsg = "[ERROR]result_code:" + resHandler.getParameter("result_code") + " err_code:"
                            + resHandler.getParameter("err_code") + "err_code_des:"
                            + resHandler.getParameter("err_code_des");
                    // 错误时,返回结果未签名,记录retcode、retmsg看失败详情。
                    returnObj.put("status", "ERROR-C");
                    returnObj.put("errMsg", errMsg);
                }
            } else {
                String errMsg = "return_code:" + return_code + "err_code:" + resHandler.getParameter("err_code")
                        + " return_msg:" + return_msg;
                // 错误时,返回结果未签名,记录retcode、retmsg看失败详情。
                returnObj.put("status", "ERROR-B");
                returnObj.put("errMsg", errMsg);
            }
        } else {
            // 有可能因为网络原因,请求已经处理,但未收到应答。
            returnObj.put("status", "ERROR-A");
            returnObj.put("errMsg", httpClient.getResponseCode() + ":" + httpClient.getErrInfo());
        }
        // 获取debug信息,建议把请求、应答内容、debug信息,通信返回码写入日志,方便定位问题
        String detail = "http res:" + httpClient.getResponseCode() + "," + httpClient.getErrInfo() + ";" + "req url:"
                + requestUrl + ";" + ";" + "req debug:" + reqHandler.getDebugInfo() + ";" + "res content:" + rescontent
                + ";" + "res debug:" + resHandler.getDebugInfo() + ";";
        returnObj.put("detail", detail);
        return returnObj;
    }
    /**处理信息
     */
    public static JSONObject paymentData(JSONObject payObj,String key){
        JSONObject wxObj = new JSONObject();
        /**统一下单*/
        String payStatus = payObj.getString("status");
        if (payStatus.equals("suc")) {
            // JSONObject payObj = po.getJSONObject("inf");
            String appId = payObj.getString("appid");
            String nonceStr = payObj.getString("nonce_str");
            String prepay_id = payObj.getString("prepay_id");
            // JSAPI调用支付返回的数据
            String timeStamp = SimpleTool.getTenTime(new Date()).toString();
            String signType = "MD5";
            String packagef = "prepay_id=" + prepay_id;
            RequestHandler reqHandler = new RequestHandler(null, null);
            reqHandler.setParameter("appId", appId);
            reqHandler.setParameter("nonceStr", nonceStr);
            reqHandler.setParameter("timeStamp", timeStamp);
            reqHandler.setParameter("package", packagef);
            reqHandler.setParameter("signType", signType);
            reqHandler.setKey(key);
            String paySign = reqHandler.createSign();// 生成签名
            wxObj.put("orderNo", payObj.getString("out_trade_no"));
            wxObj.put("paySign", paySign);
            wxObj.put("appId", appId);
            wxObj.put("nonceStr", nonceStr);
            wxObj.put("package", packagef);
            wxObj.put("timeStamp", timeStamp);
        } else {
            throw new RuntimeException(payObj.toString());
        }
        return wxObj;
    }
    /**
     * 退款
     * @param appId 小程序/公众号 appId
     * @param partner 商户号
     * @param key 商户号秘钥
     * @param certPath 个人商户证书
     * @param out_trade_no 商户订单号
     * @param transaction_id 财付通订单号(微信订单号)
     * @param out_refund_no 商户退单号
     * @param total_fee 订单总额(单位:分)
     * @param refund_fee 退款金额(单位:分)
     * @return JSON status="SUCCESS"(成功) (状态,错误原因,该操作相关信息(参数,返回值))
     */
    public static JSONObject refund(String appId,String partner,String key,String certPath,String out_trade_no, String transaction_id, String out_refund_no, String total_fee,
            String refund_fee) {
         try{
             KeyStore keyStore = KeyStore.getInstance("PKCS12");
             FileInputStream instream = new FileInputStream(new File(certPath));
             try {
                 keyStore.load(instream,partner.toCharArray());
             }finally {
                 instream.close();
             }
             // Trust own CA and all self-signed certs
             SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore,partner.toCharArray()).build();
             // Allow TLSv1 protocol only
             SSLConnectionSocketFactory sslsf;
            sslsf = new SSLConnectionSocketFactory(
                    sslcontext, new String[] { "TLSv1" }, null,
                    SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            CloseableHttpClient httpclient = HttpClients.custom()
                     .setSSLSocketFactory(sslsf).build();
             HttpPost httppost = new HttpPost(REFUND_URL);
             String xml = wxPayRefundData(appId, partner, key, out_trade_no, transaction_id, out_refund_no, total_fee, refund_fee);
             try {
                 StringEntity se = new StringEntity(xml);
                 httppost.setEntity(se);
                 CloseableHttpResponse responseEntry = httpclient.execute(httppost);
                 try {
                     HttpEntity entity = responseEntry.getEntity();
                     if (entity != null) {
                         SAXReader saxReader = new SAXReader();
                         Document document = saxReader.read(entity.getContent());
                         Element rootElt = document.getRootElement();
                         String returnCode = rootElt.elementText("return_code");
                         JSONObject result = new JSONObject();
                         if(returnCode.equals("SUCCESS")){
                             String resultCode = rootElt.elementText("result_code");
                             if(resultCode.equals("SUCCESS")) {
                                 result.put("weixinPayUrl", rootElt.elementText("code_url"));
                                 result.put("prepayId", rootElt.elementText("prepay_id"));
                                 result.put("msg", "success");
                                 String refund_id = rootElt.elementText("refund_id");//微信退款单号
                                 String r_out_refund_no = rootElt.elementText("out_refund_no");
                                 String errMsg = "商户号" + r_out_refund_no + "的退款流水号是:" + refund_id;
                                 result.put("status", "SUCCESS");
                                 result.put("errMsg", errMsg);
                                 result.put("refund_id", refund_id);
                             }else{
                                 String errMsg = "[ERROR]result_code:" + rootElt.elementText("result_code")+
                                         " err_code:" + rootElt.elementText("err_code");
                                 //错误时,返回结果未签名,记录retcode、retmsg看失败详情。
                                 result.put("errMsg", errMsg);
                                 result.put("status","false");
                                 result.put("msg",rootElt.elementText("err_code_des"));
                             }
                         }else{
                             String errMsg = "[ERROR]return_code:" + rootElt.elementText("return_code");
                             //错误时,返回结果未签名,记录retcode、retmsg看失败详情。
                             result.put("errMsg", errMsg);
                             result.put("status","false");
                             result.put("msg",rootElt.elementText("return_msg"));
                         }
                         return result;
                     }
                     EntityUtils.consume(entity);
                 }
                 finally {
                     responseEntry.close();
                 }
             }
             finally {
                 httpclient.close();
             }
             return null;
         }catch(Exception e){
             e.printStackTrace();
             JSONObject result = new JSONObject();
             result.put("status","error");
             result.put("msg",e.getMessage());
             return result;
         }
    }
    /** 封装参数数据
     * @param appId 小程序/公众号 appId
     * @param partner 商户号
     * @param key 商户号秘钥
     * @param out_trade_no 商户订单号
     * @param transaction_id 财付通订单号(微信订单号)
     * @param out_refund_no 商户退单号
     * @param total_fee 订单总额(单位:分)
     * @param refund_fee 退款金额(单位:分)
     * @return
     */
    public static String wxPayRefundData(String appId,String partner,String key,String out_trade_no, String transaction_id,String out_refund_no,String total_fee,String refund_fee) {
        StringBuffer xml = new StringBuffer();
        String data = null;
        try {
            String nonceStr = SimpleTool.getUUIDName().substring(0,30);
            xml.append("</xml>");
            SortedMap<String,String> parameters = new TreeMap<String,String>();
            parameters.put("appid",appId);
            parameters.put("mch_id",partner);
            parameters.put("nonce_str", nonceStr);
            if(!StringUtils.isEmpty(out_trade_no)) {
                parameters.put("out_trade_no", out_trade_no);
            }
            if(!StringUtils.isEmpty(transaction_id)) {
                parameters.put("transaction_id", transaction_id);
            }
            parameters.put("out_refund_no", out_refund_no);
            parameters.put("fee_type", "CNY");
            parameters.put("total_fee", total_fee);//总金额
            parameters.put("refund_fee", refund_fee);//退款金额
            parameters.put("op_user_id",partner);
            parameters.put("sign", createSign(parameters,key));
            data =SortedMaptoXml(parameters);
        } catch (Exception e) {
            System.err.println(e.getMessage());
            return null;
        }
        return data;
    }
    /**
     * 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
     */
    public static String createSign(SortedMap<String, String> packageParams, String AppKey) {
        StringBuffer sb;
        sb = new StringBuffer();
        Set es = packageParams.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + AppKey);
        String sign = MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();
        return sign;
    }
    /**
     * @Author: HONGLINCHEN
     * @Description:请求值转换为xml格式 SortedMap转xml
     * @param params
     * @Date: 2017-9-7 17:18
     */
    private static String SortedMaptoXml(SortedMap<String,String> params) {
        StringBuilder sb = new StringBuilder();
        Set es = params.entrySet();
        Iterator it = es.iterator();
        sb.append("<xml>\n");
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            Object v = entry.getValue();
            sb.append("<"+k+">");
            sb.append(v);
            sb.append("</"+k+">\n");
        }
        sb.append("</xml>");
        return sb.toString();
    }
}