package com.ruoyi.pay.type.weixin.service.impl;


import cn.hutool.core.util.RandomUtil;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayUtil;
import com.ruoyi.pay.type.weixin.config.MyWXPayConfig;
import com.ruoyi.pay.type.weixin.param.WxPayReturnData;
import com.ruoyi.pay.type.weixin.param.WxPaySendData;
import com.ruoyi.pay.type.weixin.param.WxRefundData;
import com.ruoyi.pay.type.weixin.service.WXPayService;
import com.ruoyi.pay.utils.HttpRequestUtil;
import com.ruoyi.pay.utils.PaymentApi;
import com.ruoyi.pay.utils.PaymentKit;
import com.ruoyi.response.ResponseData;
import com.ruoyi.utils.pwd.MD5Util;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;

/**
 * @author liuyao
 * Created by Mr.Loe on 2019-01-08
 * 类说明：支付宝工具类
 */
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class WXPayServiceImpl implements WXPayService {


    /**
     * 用户
     */
    @Value("${wxpay.appid}")
    private String wxAppid;

    @Value("${wxpay.mchid}")
    private String mchid;

    @Value("${wxpay.paykey}")
    private String paykey;

    @Value("${wxpay.app-secret}")
    private String userAppSecret;


    @Value("${app.name}")
    private String appName;

    @Value("${wxrefundfile.path}")
    private String refundFile;

    @Autowired
    private HttpServletRequest request;

    /**
     * 用户使用微信支付
     *
     * @param orderNo
     * @param amount
     * @param returnUrl
     * @return
     */
    @Override
    public ResponseData wxPayUserPay(String orderNo, BigDecimal amount, String returnUrl,Long orderId) {
        if (amount.compareTo(new BigDecimal(0)) != 1) {
            return ResponseData.error("支付金额不可小于0");
        }
        WxPaySendData wxPaySendData = new WxPaySendData();
        //普通用户
        wxPaySendData.setAppid(wxAppid); // 微信开放平台核通过的应用APPID
        wxPaySendData.setAttach(appName); // 附加数据，在查询API和支付通知中原样返回，该字段主要用于商户携带订单的自定义数据
        //普通用户
        wxPaySendData.setMch_id(mchid); // 微信支付分配的商户号

        wxPaySendData.setBody(appName + "-订单号：" + orderNo); //商品或支付单简要描述
        wxPaySendData.setNonce_str(RandomUtil.randomNumbers(20)); // 随机字符串，不长于32位
        wxPaySendData.setNotify_url(returnUrl);// 回调地址
        wxPaySendData.setTotal_fee(amount.multiply(new BigDecimal(100)).intValue());
        wxPaySendData.setSpbill_create_ip(request.getRemoteAddr());
        wxPaySendData.setOut_trade_no(orderNo); // 商户订单号
        wxPaySendData.setTrade_type("APP"); // 支付类型
        String returnXml = unifiedOrder(wxPaySendData, paykey);

        WxPayReturnData reData;
        XStream xs1 = new XStream(new DomDriver());
        xs1.alias("xml", WxPayReturnData.class);
        reData = (WxPayReturnData) xs1.fromXML(returnXml);
        System.out.println("===========reData.getReturn_code()=======：" + reData.getReturn_code());
        if ("SUCCESS".equals(reData.getReturn_code()) && "SUCCESS".equals(reData.getResult_code())) {
            // 时间戳
            String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
            String _package = "prepay_id=" + reData.getPrepay_id();
            SortedMap<Object, Object> signMap = new TreeMap<Object, Object>();
            signMap.put("appid", reData.getAppid());
            signMap.put("timestamp", timeStamp);
            signMap.put("noncestr", reData.getNonce_str());
            signMap.put("package", _package);
            signMap.put("signtype", "MD5");
            String paySign = createSign(signMap, paykey);
            wxPaySendData.setSign(paySign);
            SortedMap<Object, Object> iosSignMap = new TreeMap<Object, Object>();
            iosSignMap.put("appid", wxPaySendData.getAppid());
            iosSignMap.put("partnerid", wxPaySendData.getMch_id());
            iosSignMap.put("prepayid", reData.getPrepay_id());
            iosSignMap.put("package", "Sign=WXPay");
            iosSignMap.put("noncestr", wxPaySendData.getNonce_str());
            iosSignMap.put("timestamp", timeStamp);
            // IOS端需要的SIGN
            String iosSign = createSign(iosSignMap, paykey);
            System.out.println("---------------签名222------------------" + iosSign);
            Map<String, Object> map2 = new HashMap<String, Object>();
            map2.put("timestamp", timeStamp);
            map2.put("prepayid", reData.getPrepay_id());
            map2.put("sign", iosSign);
            map2.put("noncestr", wxPaySendData.getNonce_str());
            map2.put("package", "Sign=WXPay");
            map2.put("partnerid", wxPaySendData.getMch_id());
            map2.put("appid", wxPaySendData.getAppid());
            map2.put("orderId",orderId);
            System.out.println("===================nonceStr===============" + wxPaySendData.getNonce_str());
            return ResponseData.success(map2);
        }
        return ResponseData.error("支付失败");
    }


    @Override
    public ResponseData wxH5Pay(String orderInfoId, BigDecimal price, String notify,Long orderId) {
        //微信支付：申请支付连接
        WXPay wxPay = new WXPay(new MyWXPayConfig(wxAppid, mchid, paykey));


        //在data集合中存储生成微信二维码的重要参数
        HashMap<String, String> data = new HashMap<>();

        data.put("body", "支付"); //支付说明
        data.put("out_trade_no", orderInfoId); //使用当前用户订单的编号作为当前支付交易的交易号
        data.put("fee_type", "CNY"); //支付模式为人民币
        data.put("total_fee", price.multiply(new BigDecimal("100")).intValue() + ""); //设置支付金额为1分
        data.put("trade_type", "MWEB"); //交易的类型
        //需要注意的是这是需要自己配置内网穿透地址
        data.put("notify_url", notify); //设置支付完成时回调方法的接口
        data.put("spbill_create_ip",getIP(request));

        //用于储存返回给前端必要数据的map集合（按需添加但是一般订单价格，订单编号，支付二维码编号都是必须的）
        HashMap<String, String> map = new HashMap<>();
        //储存订单编号
        map.put("number", orderInfoId);
        //储存订单价格
        map.put("price", price.multiply(new BigDecimal("100")).intValue() + "");

        try {
            //通过data集合中的重要参数请求微信服务器，然后微信服务器将参数封装到resp中
            Map<String, String> resp = wxPay.unifiedOrder(data);
            //通过微信服务器返回的数据得到微信支付二维码，然后通过map集合返回给前端
            map.put("resp", resp.get("mweb_url"));
            map.put("orderId", orderId.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
        //封装返回给前端的信息
        return ResponseData.success(map);
    }


    @Override
    public ResponseData wxAppletPay(String orderNum, BigDecimal price, String openId, String notify,Long orderId) {

        Map<String, String> reqParams = new HashMap<>();
        //微信分配的小程序ID
        reqParams.put("appid", wxAppid);
        //微信支付分配的商户号
        reqParams.put("mch_id", mchid);
        //随机字符串
        reqParams.put("nonce_str", RandomUtil.randomNumbers(10));
        //签名类型
        reqParams.put("sign_type", "MD5");
        //充值订单 商品描述
        reqParams.put("body", "油桃绘本订单支付");
        //商户订单号
        reqParams.put("out_trade_no", orderNum);
        log.info("业务订单号是 reqParams.get(out_trade_no)=" + reqParams.get("out_trade_no"));
        //订单总金额，单位为分
        reqParams.put("total_fee", String.valueOf(price.multiply(new BigDecimal(100)).intValue()));
        //终端IP
        reqParams.put("spbill_create_ip", "118.24.78.87");
        //通知地址
        reqParams.put("notify_url", notify);
        log.info(reqParams.get("notify_url"));
        //交易类型
        reqParams.put("trade_type", "JSAPI");
        //用户标识
        reqParams.put("openid", openId);
        //签名
        String sign = null;
        try {
            sign = WXPayUtil.generateSignature(reqParams, paykey);

        } catch (Exception e) {
            e.printStackTrace();
        }
        reqParams.put("sign", sign);

        /*
            调用支付定义下单API,返回预付单信息 prepay_id
         */
        String xmlResult = PaymentApi.pushOrder(reqParams);
        log.info(xmlResult);
        Map<String, String> result = PaymentKit.xmlToMap(xmlResult);
        //预付单信息
        String prepay_id = result.get("prepay_id");

        /*
            小程序调起支付数据签名
         */
        Map<String, String> packageParams = new HashMap<>();
        packageParams.put("appId", wxAppid);
        packageParams.put("timeStamp", System.currentTimeMillis() / 1000 + "");
        packageParams.put("nonceStr", System.currentTimeMillis() + "");
        packageParams.put("package", "prepay_id=" + prepay_id);
        packageParams.put("signType", "MD5");
        String packageSign = null;
        try {
            packageSign = WXPayUtil.generateSignature(packageParams, paykey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        packageParams.put("paySign", packageSign);
        packageParams.put("orderId", orderId.toString());
        return ResponseData.success(packageParams);
    }



    /***
     * 获取客户端ip地址
     * @param request
     */
    public  String getIP(final HttpServletRequest request) {
        String ipStr = request.getHeader("x-forwarded-for");
        if (StringUtils.isBlank(ipStr) || "unknown".equalsIgnoreCase(ipStr)) {
            ipStr = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ipStr) || "unknown".equalsIgnoreCase(ipStr)) {
            ipStr = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ipStr) || "unknown".equalsIgnoreCase(ipStr)) {
            ipStr = request.getRemoteAddr();
        }

        // 多个路由时，取第一个非unknown的ip
        final String[] arr = ipStr.split(",");
        for (final String str : arr) {
            if (!"unknown".equalsIgnoreCase(str)) {
                ipStr = str;
                break;
            }
        }
        //目的是将localhost访问对应的ip 0:0:0:0:0:0:0:1 转成 127.0.0.1。
        return "0:0:0:0:0:0:0:1".equals(ipStr) ? "127.0.0.1" : ipStr;
    }


    /**
     * 功能描述:微信支付第一步——统一下单
     *
     * @param data
     * @param key
     * @return
     * @author yanzy  2016-1-18 下午10:01:20
     */
    private String unifiedOrder(WxPaySendData data, String key) {
        System.out.println("=========================data:" + data);
        System.out.println("=========================key:" + key);
        //统一下单支付
        String returnXml = null;
        try {
            //生成sign签名
            SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
            parameters.put("appid", data.getAppid());
            parameters.put("attach", data.getAttach());
            parameters.put("body", data.getBody());
//          parameters.put("key", data.getKey());
            parameters.put("mch_id", data.getMch_id());
            parameters.put("nonce_str", data.getNonce_str());
            parameters.put("notify_url", data.getNotify_url());
            parameters.put("openid", data.getOpenid());
            parameters.put("out_trade_no", data.getOut_trade_no());
            parameters.put("spbill_create_ip", data.getSpbill_create_ip());
            parameters.put("total_fee", data.getTotal_fee());
            parameters.put("trade_type", data.getTrade_type());
//            parameters.put("device_info", data.getDevice_info());
            //  data.setSign(WeiXinUtil.createSign(characterEncoding,parameters));
            data.setSign(createSign(parameters, key));

            XStream xs = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
            xs.alias("xml", WxPaySendData.class);
            String xml = xs.toXML(data);
            returnXml = HttpRequestUtil.sendPostXml("https://api.mch.weixin.qq.com/pay/unifiedorder", xml);

            System.out.println("=============================返回结果:" + returnXml);
        } catch (Exception e) {
        }
        return returnXml;
    }

    private String createSign(SortedMap<Object, Object> parameters, String key) {
        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);
        String sign = MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();
        return sign;
    }

    /**
     * @param price
     * @return
     * @Description TD: 2微信
     * @Return cn.hutool.json.JSONObject
     * @Author sangsang
     * @Date 2020/2/15 17:51
     */
    @Override
    public ResponseData wxPayMethodApplet(String orderNum, BigDecimal price, String openId, String notify) {

        Map<String, String> reqParams = new HashMap<>();
        //微信分配的小程序ID
        reqParams.put("appid", wxAppid);
        //微信支付分配的商户号
        reqParams.put("mch_id", mchid);
        //随机字符串
        reqParams.put("nonce_str", RandomUtil.randomNumbers(10));
        //签名类型
        reqParams.put("sign_type", "MD5");
        //充值订单 商品描述
        reqParams.put("body", "订单支付");
        //商户订单号
        reqParams.put("out_trade_no", orderNum);
        log.info("业务订单号是 reqParams.get(out_trade_no)=" + reqParams.get("out_trade_no"));
        //订单总金额，单位为分
        reqParams.put("total_fee", String.valueOf(price.multiply(new BigDecimal(100)).intValue()));
        //终端IP
        reqParams.put("spbill_create_ip", "118.24.78.87");
        //通知地址
        reqParams.put("notify_url", notify);
        log.info(reqParams.get("notify_url"));
        //交易类型
        reqParams.put("trade_type", "JSAPI");
        //用户标识
        reqParams.put("openid", openId);
        //签名
        String sign = null;
        try {
            sign = WXPayUtil.generateSignature(reqParams, paykey);

        } catch (Exception e) {
            e.printStackTrace();
        }
        reqParams.put("sign", sign);

        /*
            调用支付定义下单API,返回预付单信息 prepay_id
         */
        String xmlResult = PaymentApi.pushOrder(reqParams);
        log.info(xmlResult);
        Map<String, String> result = PaymentKit.xmlToMap(xmlResult);
        //预付单信息
        String prepay_id = result.get("prepay_id");

        /*
            小程序调起支付数据签名
         */
        Map<String, String> packageParams = new HashMap<>();
        packageParams.put("appId", wxAppid);
        packageParams.put("timeStamp", System.currentTimeMillis() / 1000 + "");
        packageParams.put("nonceStr", System.currentTimeMillis() + "");
        packageParams.put("package", "prepay_id=" + prepay_id);
        packageParams.put("signType", "MD5");
        String packageSign = null;
        try {
            packageSign = WXPayUtil.generateSignature(packageParams, paykey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        packageParams.put("paySign", packageSign);
        return ResponseData.success(packageParams);
    }



    /**
     * 微信退款
     *
     * @return
     */
    @Override
    public ResponseData wxRefund(String orderNo, BigDecimal payPrice, BigDecimal refundPrice) {
        WxRefundData wxPaySendData;
        try {
            wxPaySendData = new WxRefundData(wxAppid,
                    mchid, UUID.randomUUID().toString().replace("-", ""),
                    orderNo, UUID.randomUUID().toString().replace("-", ""),
                    payPrice.multiply(new BigDecimal(100)).intValue(),
                    refundPrice.multiply(new BigDecimal(100)).intValue(), mchid);
            WxRefundData refund = refund(wxPaySendData, paykey);
            if ("FAIL".equals(refund.getResult_code())) {
                return ResponseData.error(refund.getErr_code_des());
            }
            log.info("微信退款成功");
        } catch (Exception e) {
            log.info("微信退款失败");
            e.printStackTrace();
        }
        return ResponseData.success(null);

    }

    /**
     * 功能描述:微信退款
     *
     * @param data
     * @param key
     * @return
     * @author yanzy  2016-10-25 下午01:52:19
     */
    private WxRefundData refund(WxRefundData data, String key) {
        String returnXml = null;
        WxRefundData reData = null;
        try {
            //生成sign签名
            SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
            parameters.put("appid", data.getAppid());
            parameters.put("mch_id", data.getMch_id());
            parameters.put("nonce_str", data.getNonce_str());
            parameters.put("out_trade_no", data.getOut_trade_no());
            parameters.put("out_refund_no", data.getOut_refund_no());
            parameters.put("total_fee", data.getTotal_fee());
            parameters.put("refund_fee", data.getRefund_fee());
            parameters.put("op_user_id", data.getOp_user_id());
            log.info("SIGN:" + createSign(parameters, key));
            data.setSign(createSign(parameters, key));
            XStream xs = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
            xs.alias("xml", WxRefundData.class);
            String xml = xs.toXML(data);
            log.info("微信退款xml为:\n" + xml);
            //防止小程序和微信用不一样的参数 此处先判断
            returnXml = HttpRequestUtil.sendCertPostXml("https://api.mch.weixin.qq.com/secapi/pay/refund", data.getMch_id(), xml, refundFile);
            log.info("返回结果:" + returnXml);

            XStream xs1 = new XStream(new DomDriver());
            xs1.alias("xml", WxRefundData.class);
            reData = (WxRefundData) xs1.fromXML(returnXml);

            log.info("==========解析结果==============" + reData.getResult_code());
            return reData;

        } catch (Exception e) {
            e.printStackTrace();
        }
        reData = new WxRefundData();
        reData.setResult_code("FAIL");
        return reData;
    }
}
