package com.ruoyi.web.controller.utils;

import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.ruoyi.common.utils.sign.Md5Utils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
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.message.BasicHeader;
import org.apache.http.protocol.HTTP;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.util.PropertiesUtil;
import org.jdom2.JDOMException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.security.KeyStore;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * WeixinpayService
 *
 * @author
 * @version 1.0.0
 * @since 2017年02月24日 09:36
 **/
@Service
public class XiaochengxuPayService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    private static final Gson GSON = new GsonBuilder().create();

    private static final String URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";;
    //我们这边测试的小程序ID
    private static final String APP_ID = "wx898f44c913f1de6f";// AppID(小程序ID)
    private static final String MCH_ID = "1656665623";// 商户号
    private static final String TRADE_TYPE = "JSAPI";
    private static final String SIGN_TYPE = "MD5";
    private static final String CHARSET = "UTF-8";
    private static final String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";
    // private static final String TRANSFER_URL = "https://api.mch.weixin.qq.com/v3/transfer/batches";
    private static final String TRANSFER_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";

    private static String Key = "L9Rjnlbaa0c68ca550nv578KJen97ye0";// APIv2密钥
    private static String APIv3 = "588yUnlb0aopun89512nv982626TUHBN";// APIv3密钥

    //退款所需
    private  static int socketTimeout = 10000;// 连接超时时间，默认10秒
    private  static int connectTimeout = 30000;// 传输超时时间，默认30秒
    private  static RequestConfig requestConfig;// 请求器的配置
    private  static CloseableHttpClient httpClient;// HTTP请求器


    /**
     * 微信支付签名算法sign
     * @param characterEncoding
     * @param parameters
     * @return
     */
    public static String createSign(String characterEncoding,SortedMap<String,Object> parameters){
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序（升序）
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            Object v = entry.getValue();
            if(null != v && !"".equals(v)
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + Key);
        System.out.println("拼接信息:"+sb);
        String sign = Md5Utils.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
        return sign;
    }


    /**
     * @param map
     * @return
     */
    public Map<String,Object> signature(SortedMap map) {
        logger.error("签名前信息");
        logger.error(map.toString());
        String sign =  createSign(CHARSET,map);
        logger.error("签名信息");
        logger.error(sign);
        String xml ="<xml>\n" +
                "   <appid>"+map.get("appid")+"</appid>\n" +
                "   <attach>"+map.get("attach")+"</attach>" +
                "   <body>"+map.get("body")+"</body>" +
                "   <mch_id>"+map.get("mch_id")+"</mch_id>" +
                "   <nonce_str>"+map.get("nonce_str")+"</nonce_str>" +
                "   <notify_url>"+map.get("notify_url")+"</notify_url>" +
                "   <openid>"+map.get("openid")+"</openid>" +
                "   <out_trade_no>"+map.get("out_trade_no")+"</out_trade_no>" +
                "   <spbill_create_ip>"+map.get("spbill_create_ip")+"</spbill_create_ip>" +
                "   <time_start>"+map.get("time_start")+"</time_start>" +
                "   <total_fee>"+map.get("total_fee")+"</total_fee>" +
                "   <trade_type>"+map.get("trade_type")+"</trade_type>" +
                "   <sign>"+sign+"</sign>" +
                "</xml>";
        logger.error("------XML-------");
        logger.error(xml);
        String result = PayCommonUtil.httpsRequest(
                URL, "POST", xml);
        logger.error("统一下单结果");
        logger.error(result);
        Map<String, String> resultMap = null;
        try {
            resultMap = PayCommonUtil.doXMLParse(result);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        SortedMap<String, Object> relMap = new TreeMap<String, Object>();
        relMap.put("appId", APP_ID);
        String noncestr =map.get("nonce_str").toString();
        relMap.put("nonceStr", noncestr);
        relMap.put("package", "prepay_id="+resultMap.get("prepay_id"));
        relMap.put("timeStamp", System.currentTimeMillis()/1000);
        relMap.put("signType", "MD5");
        String sign2 = createSign(CHARSET, relMap);
        relMap.put("paySign",sign2);
        return relMap ;
    }


    public Map<String,Object> getOrderSignature(SortedMap map) {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
        map.put("nonce_str", Md5Utils.getRandomStringByLength(32));
        map.put("time_start",df.format(new Date()));
        // map.put("notify_url","https://server.yzgkj888.com/prod-api/api/pay/xcxPayNotify");
        map.put("notify_url","https://yizhiguo.xnszz.com/prod-api/api/pay/xcxPayNotify");
        //map.put("notify_url","http://bbeuhx.natappfree.cc/api/pay/xcxPayNotify");
        map.put("appid", APP_ID); //应用ID  必
        map.put("mch_id", MCH_ID); //商户号  必
        //map.put("sign_type", "MD5"); //签名类型默认MD5
        map.put("trade_type", TRADE_TYPE); //交易类型  APP 必
        //signMap.put("time_expire", order.getTotalAmount()); //交易结束时间    大于5分钟
        return this.signature(map);
    }

    //微信XCX退款
    public static String wxXcxRefund(SortedMap map) {
        map.put("appid", APP_ID);
        map.put("mch_id", MCH_ID);
        map.put("sign_type", "MD5");
        // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        String preStr = PayCommonUtil.createLinkString(map);
        //MD5运算生成签名，这里是第一次签名，用于调用统一下单接口
        String mySign = PayCommonUtil.sign(preStr, Key, "utf-8").toUpperCase();
        map.put("sign", mySign);
        //拼接统一下单接口使用的xml数据，要将上一步生成的签名一起拼接进去  支付结果通知的xml格式数据
        String xmlStr = postData(REFUND_URL, PayCommonUtil.GetMapToXML(map));
        System.out.println(xmlStr);
        Map notifyMap = null;
        try {
            notifyMap = PayCommonUtil.doXMLParse(xmlStr);
        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if ("SUCCESS".equals(notifyMap.get("return_code"))) {
            if("SUCCESS".equals(notifyMap.get("result_code"))) {
                //退款成功的操作
//                OrderOrderDO orderOrderDO = new OrderOrderDO();
//                orderOrderDO.setId(orderId);
//                orderOrderDO.setStatus(6);
//                orderOrderService.backOrder(orderOrderDO);
//                return R.ok("退款成功");
                return "";
            }else{
                System.out.println("退款失败:原因"+notifyMap.get("err_code_des"));
                return "退款失败:原因--"+notifyMap.get("err_code_des");
            }
        }else{
            System.out.println("退款失败:原因"+notifyMap.get("return_msg"));
            return "退款失败:原因--"+notifyMap.get("return_msg");
        }
    }

    /**
     * 企业付款到零钱
     *
     * @param tradeNo：唯一订单号
     * @param openId：用户openId
     * @param realityMoney：金额
     * @return
     */
    public static String wxXcxEnterprisePaymentToChange(String tradeNo, String openId, BigDecimal realityMoney) throws JDOMException, IOException {

        //请求参数
        Map<String, String> paramMap = new TreeMap<>();

        paramMap.put("mch_appid",APP_ID);//申请商户号的appid或商户号绑定的appid
        paramMap.put("mchid",MCH_ID);//微信支付分配的商户号
        paramMap.put("nonce_str",Md5Utils.getRandomStringByLength(32));//随机字符串，不长于32位
        paramMap.put("partner_trade_no",tradeNo);//商户订单号，需保持唯一性(只能是字母或者数字，不能包含有其它字符)
        paramMap.put("openid",openId);//openid是微信用户
        paramMap.put("check_name","NO_CHECK");//NO_CHECK：不校验真实姓名,FORCE_CHECK：强校验真实姓名
        paramMap.put("amount",String.valueOf(realityMoney.multiply(new BigDecimal(100)).intValue()));//付款金额，单位为分
        paramMap.put("desc","提现");//付款备注，必填。注意：备注中的敏感词会被转成字符*

        System.out.println(paramMap);

        String preStr = PayCommonUtil.createLinkString2(paramMap); // 把Map所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        String mySign = PayCommonUtil.sign(preStr, Key, "utf-8").toUpperCase();
        paramMap.put("sign", mySign);

        //拼接统一下单接口使用的xml数据，要将上一步生成的签名一起拼接进去
        String xmlStr = postData(TRANSFER_URL, PayCommonUtil.GetMapToXML(paramMap)); //支付结果通知的xml格式数据
        System.out.println(xmlStr);

        Map notifyMap = null;
        try {
            notifyMap = PayCommonUtil.doXMLParse(xmlStr);
        } catch (JDOMException | IOException e) {
            e.printStackTrace();
        }
        if ("SUCCESS".equals(notifyMap.get("return_code"))) {
            if("SUCCESS".equals(notifyMap.get("result_code"))) {
                return "";
            }else{
                System.out.println("提现失败:原因"+notifyMap.get("err_code_des"));
                return "提现失败:原因--"+notifyMap.get("err_code_des");
            }
        }else{
            System.out.println("提现失败:原因"+notifyMap.get("return_msg"));
            return "提现失败:原因--"+notifyMap.get("return_msg");
        }
    }

    /**
     * 加载证书
     *
     */
    private static void initCert() throws Exception {
        // 证书密码，默认为商户ID
        String key = MCH_ID;
        // 商户证书的路径, 读取本机存放的PKCS12证书文件
        InputStream instream = PropertiesUtil.class.getResourceAsStream("/apiclient_cert.p12");
        // 指定读取证书格式为PKCS12
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        try {
            // 指定PKCS12的密码(商户ID)
            keyStore.load(instream, key.toCharArray());
        } finally {
            instream.close();
        }
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build();
        // 指定TLS版本
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        // 设置httpclient的SSLSocketFactory
        httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
    }


    /**
     * 通过Https往API post xml数据
     * @param url  API地址
     * @param xmlObj   要提交的XML数据对象
     * @return
     */
    public static String postData(String url, String xmlObj) {
        // 加载证书
        try {
            initCert();
        } catch (Exception e) {
            e.printStackTrace();
        }
        String result = null;
        HttpPost httpPost = new HttpPost(url);
        // 得指明使用UTF-8编码，否则到API服务器XML的中文不能被成功识别
        StringEntity postEntity = new StringEntity(xmlObj, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.setEntity(postEntity);
        // 根据默认超时限制初始化requestConfig
        requestConfig = RequestConfig.custom()
                .setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectTimeout)
                .build();
        // 设置请求器的配置
        httpPost.setConfig(requestConfig);
        try {
            HttpResponse response = null;
            try {
                response = httpClient.execute(httpPost);
            }  catch (IOException e) {
                e.printStackTrace();
            }
            HttpEntity entity = response.getEntity();
            try {
                result = EntityUtils.toString(entity, "UTF-8");
            }  catch (IOException e) {
                e.printStackTrace();
            }
        } finally {
            httpPost.abort();
        }
        return result;
    }
}
