package com.iformall.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.iformall.common.ErrorCode;
import com.iformall.common.IdWorker;
import com.iformall.common.Result;
import com.iformall.common.ResultData;
import com.iformall.domain.po.WxAppinfo;
import com.iformall.domain.po.WxBillSettle;
import com.iformall.domain.po.WxPayAccountBill;
import com.iformall.domain.po.WxPayBill;
import com.iformall.domain.vo.WxBillAll;
import com.iformall.enums.*;
import com.iformall.exception.MallinkException;
import com.iformall.mapper.*;
import com.iformall.pay.*;
import com.iformall.service.WxBillAllService;
import com.iformall.service.WxBillSettleService;
import com.iformall.service.WxPayBillService;
import com.iformall.utils.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.text.ParseException;
import java.util.*;

/**
 * @author gongbiao
 */
@Service
public class WxPayBillServiceImpl implements WxPayBillService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    WxAppinfoMapper wxAppinfoMapper;

    @Autowired
    WxPayAccountBillMapper wxPayAccountBillMapper;

    @Autowired
    WxPayBillMapper wxPayBillMapper;

    @Autowired
    WxBillAllMapper wxBillAllMapper;

    @Autowired
    WxBillAllService wxBillAllService;

    @Autowired
    WxMerchantBUserMapper wxMerchantBUserMapper;

    @Autowired
    private WxBillSettleService wxBillSettleService;

    JSONObject errorMap = JSON.parseObject("{" +
            "\"NOAUTH\":{\"detail\":\"商户无此接口权限\",\"reason\":\"商户未开通此接口权限\",\"resolution\":\"请商户前往申请此接口权限\"}," +
            "\"NOTENOUGH\":{\"detail\":\"余额不足\",\"reason\":\"用户帐号余额不足\",\"resolution\":\"用户帐号余额不足，请用户充值或更换支付卡后再支付\"}," +
            "\"ORDERPAID\":{\"detail\":\"商户订单已支付\",\"reason\":\"商户订单已支付，无需重复操作\",\"resolution\":\"商户订单已支付，无需更多操作\"}," +
            "\"ORDERCLOSED\":{\"detail\":\"订单已关闭\",\"reason\":\"当前订单已关闭，无法支付\",\"resolution\":\"当前订单已关闭，请重新下单\"}," +
            "\"SYSTEMERROR\":{\"detail\":\"系统错误\t\",\"reason\":\"系统超时\",\"resolution\":\"系统异常，请用相同参数重新调用\"}," +
            "\"APPID_NOT_EXIST\":{\"detail\":\"APPID不存在\",\"reason\":\"参数中缺少APPID\",\"resolution\":\"请检查APPID是否正确\"}," +
            "\"MCHID_NOT_EXIST\":{\"detail\":\"MCHID不存在\",\"reason\":\"参数中缺少MCHID\",\"resolution\":\"请检查MCHID是否正确\"}," +
            "\"APPID_MCHID_NOT_MATCH\":{\"detail\":\"appid和mch_id不匹配\",\"reason\":\"appid和mch_id不匹配\",\"resolution\":\"请确认appid和mch_id是否匹配\"}," +
            "\"LACK_PARAMS\":{\"detail\":\"缺少参数\t\",\"reason\":\"缺少必要的请求参数\",\"resolution\":\"请检查参数是否齐全\"}," +
            "\"OUT_TRADE_NO_USED\":{\"detail\":\"商户订单号重复\",\"reason\":\"同一笔交易不能多次提交\",\"resolution\":\"请核实商户订单号是否重复提交\"}," +
            "\"SIGNERROR\":{\"detail\":\"签名错误\",\"reason\":\"参数签名结果不正确\",\"resolution\":\"请检查签名参数和方法是否都符合签名算法要求\"}," +
            "\"XML_FORMAT_ERROR\":{\"detail\":\"XML格式错误\t\",\"reason\":\"XML格式错误\",\"resolution\":\"请检查XML参数格式是否正确\"}," +
            "\"REQUIRE_POST_METHOD\":{\"detail\":\"请使用post方法\",\"reason\":\"未使用post传递参数\",\"resolution\":\"请检查请求参数是否通过post方法提交\"}," +
            "\"POST_DATA_EMPTY\":{\"detail\":\"post数据为空\",\"reason\":\"post数据不能为空\",\"resolution\":\"请检查post数据是否为空\"}," +
            "\"NOT_UTF8\":{\"detail\":\"编码格式错误\",\"reason\":\"未使用指定编码格式\",\"resolution\":\"请使用UTF-8编码格式\"}}");

    JSONObject errorMapQuery = JSON.parseObject("{" +
            "\"ORDERNOTEXIST\":{\"detail\":\"此交易订单号不存在\",\"reason\":\"查询系统中不存在此交易订单号\",\"resolution\":\"该API只能查提交支付交易返回成功的订单，请商户检查需要查询的订单号是否正确\"},\n" +
            "\"SYSTEMERROR\":{\"detail\":\"系统错误\t\",\"reason\":\"后台系统返回错误\",\"resolution\":\"系统异常，请再调用发起查询\"}}");

    JSONObject errorMapClose = JSON.parseObject("{" +
            "\"ORDERPAID\":{\"detail\":\"订单已支付\",\"reason\":\"订单已支付，不能发起关单\",\"resolution\":\"订单已支付，不能发起关单，请当作已支付的正常交易\"}," +
            "\"SYSTEMERROR\":{\"detail\":\"系统错误\",\"reason\":\"系统错误\",\"resolution\":\"系统异常，请重新调用该API\"}," +
            "\"ORDERCLOSED\":{\"detail\":\"订单已关闭\",\"reason\":\"订单已关闭，无法重复关闭\",\"resolution\":\"订单已关闭，无需继续调用\"}," +
            "\"SIGNERROR\":{\"detail\":\"签名错误\",\"reason\":\"参数签名结果不正确\",\"resolution\":\"请检查签名参数和方法是否都符合签名算法要求\"}," +
            "\"REQUIRE_POST_METHOD\":{\"detail\":\"请使用post方法\",\"reason\":\"未使用post传递参数\",\"resolution\":\"请检查请求参数是否通过post方法提交\"}," +
            "\"XML_FORMAT_ERROR\":{\"detail\":\"XML格式错误\t\",\"reason\":\"XML格式错误\",\"reason\":\"请检查XML参数格式是否正确\"}}");

    @Override
    public ResultData createPayBill(WxAppinfo appInfo, WxPayBill record, EnumPayWay payWay) {
        final IdWorker idworker = IdWorker.get();

        EnumPayShare isShare = EnumPayShare.NO;
        try {
            //查询账单是否存在
            WxBillAll wxBillAll = new WxBillAll();
            wxBillAll.setTenantId(appInfo.getTenantId());
            wxBillAll.setId(record.getBillId());
            List<Map<String, Object>> bills = wxBillAllMapper.listData(wxBillAll);
            if (bills.size()==0) {
                logger.error("pay bill, bill not allow, repaymentReq: " + JSONObject.toJSONString(record) + ", payWay: " + payWay.toString());
                throw new MallinkException(ErrorCode.BILL_ROUTINE_IS_NOT_FOUND);
            }
            //获取账单数据
            Map<String, Object> bill = bills.get(0);
            Long owe = (Long) bill.get("owe");
            Integer billTypeValue = ((Long) bill.get("billTypeValue")).intValue();

            if(EnumBillType.SETTLE.getCode().equals(billTypeValue)){
                WxBillSettle wxBillSettle = wxBillSettleService.getById(record.getBillId(), EnumFilterSettle.YES.getCode());
                owe = wxBillSettle.getBalance();
                bill.put("owe",owe);
                logger.info("wxBillSettle.balance:{}",owe);
            }
            if (owe.longValue() <= 0) {
                logger.info("账单欠缴为0");
                return new ResultData(ErrorCode.BILL_OWE_ZERO);
            }
            //获取支付账户信息
            WxPayAccountBill payAccount = wxPayAccountBillMapper.selectById(appInfo.getPayBillId());
            Date currentDate = new Date();
            String payBillNo;
            WxPayBill billParam= new WxPayBill();
            billParam.setBillId(record.getBillId());
            List<WxPayBill> list = wxPayBillMapper.findList(billParam);
            //数据库不存在已有订单，新建订单
            if (list.size() <= 0) {
                // 创建支付订单
                Long id = idworker.nextId();
                payBillNo = id+DateUtils.getSystemTime("yyMMddHHmmss");
                record.setId(id);
                record.setTenantId(appInfo.getTenantId());
                record.setbUserId(record.getbUserId());
                record.setCreateTime(currentDate);
                record.setUpdateTime(currentDate);
                record.setPayTimeStart(currentDate);
                long endtime = currentDate.getTime() + 120 * 60 * 1000;
                record.setPayTimeEnd(new Date(endtime));
                // 支付单号
                record.setPayBillNo(payBillNo);
                record.setPayAmount((Long) bill.get("owe"));
                record.setPayVendor(EnumPayWay.PAY_WAY_WEAPP.getCode());
                record.setPayBillStatus(EnumPayStatus.PAY_STATUS_WAIT.getCode());
                record.setShare(isShare.getCode());

                int sqlRow = wxPayBillMapper.insert(record);
                if (sqlRow != 1) {
                    logger.error("pay bill insert error: " + JSONObject.toJSONString(record));
                    throw new MallinkException(ErrorCode.DB_FAIL);
                }
                logger.info("创建支付订单:"+record.toString());
            } else {
                //数据库存在订单信息,更新支付中状态，返回之前的预支付信息
                WxPayBill wxPayBill = list.get(0);
                wxPayBill.setShare(isShare.getCode());
                //更新订单状态为支付中
                wxPayBill.setPayBillStatus(EnumPayStatus.PAY_STATUS_WAIT.getCode());
                wxPayBill.setUpdateTime(currentDate);
                int sqlRow = wxPayBillMapper.updateById(wxPayBill);
                if (sqlRow != 1) {
                    logger.error("pay bill update error: " + wxPayBill.toString());
                    throw new MallinkException(ErrorCode.DB_FAIL);
                }
                //数据库中openId与再次传来的openId不相同那么关闭此订单再次生成新的订单
                if(!wxPayBill.getOpenId().equals(record.getOpenId())){
                    String openId = record.getOpenId();
                    //关闭订单
                    logger.info("openId不同时关闭之前的订单:"+record.toString());
                    ResultData resultData = payBillClose(appInfo, wxPayBill);
                    if(resultData.code!=Result.SUCCESS){
                        return new ResultData(ErrorCode.ORDER_IS_FAIL);
                    }
                    record = wxPayBillMapper.selectById(wxPayBill.getId());
                    record.setOpenId(openId);
                    //创建新的订单号
                    payBillNo = record.getId()+DateUtils.getSystemTime("yyMMddHHmmss");
                    record.setPayTimeStart(currentDate);
                    long endtime = currentDate.getTime() + 120 * 60 * 1000;
                    record.setPayTimeEnd(new Date(endtime));
                    record.setPayBillNo(payBillNo);
                    record.setPayAmount((Long) bill.get("owe"));
                    record.setUpdateTime(currentDate);
                    sqlRow = wxPayBillMapper.updateById(record);
                    if (sqlRow != 1) {
                        logger.error("pay bill update error: " + JSONObject.toJSONString(record));
                        throw new MallinkException(ErrorCode.DB_FAIL);
                    }
                    logger.info("新的订单数据:"+JSONObject.toJSONString(record));
                }else{
                    logger.info("数据库存在订单信息,更新支付中状态，返回之前的预支付信息:"+JSONObject.toJSONString(wxPayBill));
                    if (!owe.equals(wxPayBill.getPayAmount())) {
                        logger.info("欠缴费用与之前不同发起新的订单:" + wxPayBill.toString());
                        ResultData resultData = payBillClose(appInfo, wxPayBill);
                        if (resultData.code != Result.SUCCESS) {
                            return new ResultData(ErrorCode.ORDER_IS_FAIL);
                        }
                        record = wxPayBillMapper.selectById(wxPayBill.getId());
                        //创建新的订单号
                        payBillNo = record.getId() + DateUtils.getSystemTime("yyMMddHHmmss");
                        record.setPayTimeStart(currentDate);
                        long endtime = currentDate.getTime() + 120 * 60 * 1000;
                        record.setPayTimeEnd(new Date(endtime));
                        record.setPayBillNo(payBillNo);
                        record.setPayAmount((Long) bill.get("owe"));
                        record.setUpdateTime(currentDate);
                        sqlRow = wxPayBillMapper.updateById(record);
                        if (sqlRow != 1) {
                            logger.error("pay bill update error: " + JSONObject.toJSONString(record));
                            throw new MallinkException(ErrorCode.DB_FAIL);
                        }
                        logger.info("新的订单数据:" + JSONObject.toJSONString(record));
                    } else if (wxPayBill.getPayTimeEnd().after(new Date())) {
                        //订单在120分钟之内没有失效，延用之前预支付订单号
                        String noncestr = Utility.generate32UUID();
                        String timestamp = String.valueOf(Utility.getCurrentTimeStamp());
                        Map<String, String> sighMap = MapUtil.getOrderMap();
                        sighMap.put("appId", appInfo.getAppId());
                        sighMap.put("timeStamp", timestamp);
                        sighMap.put("nonceStr", noncestr);
                        sighMap.put("package", "prepay_id=" + wxPayBill.getPrepayId());
                        sighMap.put("signType", "HMAC-SHA256");
                        String signAgent = WxPayment.createSignHMAC(sighMap, payAccount.getApiKey());
                        Map<String,String> returnMap=new HashMap<>();
                        returnMap.put("timeStamp", timestamp);
                        returnMap.put("nonceStr", noncestr);
                        returnMap.put("package", "prepay_id=" + wxPayBill.getPrepayId());
                        returnMap.put("paySign", signAgent);
                        returnMap.put("signType", "HMAC-SHA256");
                        logger.info("订单在120分钟之内没有失效，延用之前预支付订单号:"+JSONObject.toJSONString(wxPayBill));
                        return new ResultData(Result.SUCCESS, "创建支付订单成功", returnMap);
                    } else {
                        //超过时间关闭订单
                        logger.info("超过时间关闭订单:" + wxPayBill.toString());
                        ResultData resultData = payBillClose(appInfo, wxPayBill);
                        if(resultData.code!=Result.SUCCESS){
                            return new ResultData(ErrorCode.ORDER_IS_FAIL);
                        }
                        record = wxPayBillMapper.selectById(wxPayBill.getId());
                        //创建新的订单号
                        payBillNo = record.getId()+DateUtils.getSystemTime("yyMMddHHmmss");
                        record.setPayTimeStart(currentDate);
                        long endtime = currentDate.getTime() + 120 * 60 * 1000;
                        record.setPayTimeEnd(new Date(endtime));
                        record.setPayBillNo(payBillNo);
                        record.setPayAmount((Long) bill.get("owe"));
                        record.setUpdateTime(currentDate);
                        sqlRow = wxPayBillMapper.updateById(record);
                        if (sqlRow != 1) {
                            logger.error("pay bill update error: " + JSONObject.toJSONString(record));
                            throw new MallinkException(ErrorCode.DB_FAIL);
                        }
                        logger.info("新的订单数据:"+JSONObject.toJSONString(record));
                    }

                }

            }

            // 统一下单 // 服务商模式
            String noncestr = Utility.generate32UUID();
            WxPayOrderSP wxPayBillSP = new WxPayOrderSP();
            wxPayBillSP.setSub_openid(record.getOpenId());
            wxPayBillSP.setAppid(appInfo.getParentAppId());
            wxPayBillSP.setMch_id(payAccount.getMchId());
            wxPayBillSP.setSub_appid(appInfo.getAppId());
            wxPayBillSP.setSub_mch_id(payAccount.getSubMchId());
            wxPayBillSP.setNonce_str(noncestr);
            wxPayBillSP.setBody(bill.get("billType").toString());
            wxPayBillSP.setOut_trade_no(record.getPayBillNo());
            wxPayBillSP.setTotal_fee(bill.get("owe").toString());
            // 终端IP
            wxPayBillSP.setSpbill_create_ip(record.getIp());
            wxPayBillSP.setNotify_url(payAccount.getPayNotifyUrl());
            // 终端类型
            wxPayBillSP.setTrade_type(WxPay.TradeType.JSAPI.name());
            // 订单ID
            wxPayBillSP.setProduct_id(String.valueOf(bill.get("id").toString()));
            wxPayBillSP.setTime_start(Utility.getDataFormatStringYYYYMMDDHHmmss(currentDate));
            wxPayBillSP.setTime_expire(Utility.getDataFormatStringYYYYMMDDHHmmss(record.getPayTimeEnd()));
            wxPayBillSP.setSign_type("HMAC-SHA256");
            wxPayBillSP.setProfit_sharing(null);
            if (isShare == EnumPayShare.YES) {
                wxPayBillSP.setProfit_sharing("Y");
            }
            Map<String, String> payBillMap = BeanUtils.toStringMap(wxPayBillSP);
            wxPayBillSP.setSign(WxPayment.createSignHMAC(payBillMap, payAccount.getApiKey()));
            String response = WxPay.pushOrder(BeanUtils.toStringMap(wxPayBillSP));
            logger.info("pay bill, wechat pushBill, " + wxPayBillSP.toString() + ", response: " + response);
            Map<String, String> returnMap = WxPayment.xmlToMap(response);
            returnMap.put("payBillId", record.getId().toString());
            String result_code = returnMap.get("result_code");
            if ("SUCCESS".equals(result_code)) {
                String prepay_id = returnMap.get("prepay_id");
                // update payBill with prepay_id
                record.setPrepayId(prepay_id);
                record.setUpdateTime(new Date());
                try {
                    wxPayBillMapper.updateById(record);
                } catch (Exception e) {
                    logger.error("pay bill update error: " + record.toString());
                    throw new MallinkException(ErrorCode.DB_FAIL);
                }
                String timestamp = String.valueOf(Utility.getCurrentTimeStamp());
                Map<String, String> sighMap = MapUtil.getOrderMap();
                sighMap.put("appId", appInfo.getAppId());
                sighMap.put("timeStamp", timestamp);
                sighMap.put("nonceStr", noncestr);
                sighMap.put("package", "prepay_id=" + prepay_id);
                sighMap.put("signType", "HMAC-SHA256");
                String signAgent = WxPayment.createSignHMAC(sighMap, payAccount.getApiKey());
                returnMap.put("timeStamp", timestamp);
                returnMap.put("nonceStr", noncestr);
                returnMap.put("package", "prepay_id=" + prepay_id);
                returnMap.put("paySign", signAgent);
                returnMap.put("signType", "HMAC-SHA256");
                logger.info("back to UI: " + returnMap.toString());
                return new ResultData(Result.SUCCESS, "创建支付订单成功", returnMap);
            }
            return updatePayBill(record, returnMap, result_code);
        } catch (RuntimeException e) {
            throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
        } catch (Exception e) {
            throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
        }
    }

    private ResultData updatePayBill(WxPayBill record, Map<String, String> returnMap, String result_code) {
        String errMsg = "";
        JSONObject errObj = errorMap.getJSONObject(result_code);
        if (errObj != null) {
            errMsg = errObj.toJSONString();
            record.setFailReason(errMsg);
        } else {
            errMsg = returnMap.get("return_msg");
            record.setFailReason(errMsg);
        }
        record.setUpdateTime(new Date());
        try {
            wxPayBillMapper.updateById(record);
        } catch (Exception e) {
            logger.error("pay bill update error: " + record.toString());
            throw new MallinkException(ErrorCode.DB_FAIL);
        }
        return new ResultData(ErrorCode.PAY_ORDER_ERROR.getCode(), errMsg, returnMap);
    }


    private String wechatPayBillQuery(WxAppinfo appInfo, WxPayBill record) {
        // get payAccount
        WxPayAccountBill payAccount = wxPayAccountBillMapper.selectById(appInfo.getPayBillId());
        if (payAccount.getType().equals(EnumPayMode.MCH.getCode())) {
            // 普通商户号模式
            WxPayOrderQ payBillQ = new WxPayOrderQ();
            String noncestr = Utility.generate32UUID();
            payBillQ.setAppid(appInfo.getAppId());
            payBillQ.setMch_id(payAccount.getMchId());
            payBillQ.setNonce_str(noncestr);
            payBillQ.setOut_trade_no(record.getPayBillNo());

            try {
                Map map = BeanUtils.toStringMap(payBillQ);
                payBillQ.setSign(WxPayment.createSign(map, payAccount.getApiKey()));
                map = BeanUtils.toStringMap(payBillQ);
                String response = WxPay.orderQuery(map);
                return response;
            } catch (RuntimeException e) {
                throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
            } catch (Exception e) {
                throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
            }
        } else {
            // 服务商模式
            WxPayOrderSQ payBillSQ = new WxPayOrderSQ();
            String noncestr = Utility.generate32UUID();
            payBillSQ.setAppid(appInfo.getParentAppId());
            payBillSQ.setSub_appid(appInfo.getAppId());
            payBillSQ.setMch_id(payAccount.getMchId());
            payBillSQ.setSub_mch_id(payAccount.getSubMchId());
            payBillSQ.setNonce_str(noncestr);
            payBillSQ.setOut_trade_no(record.getPayBillNo());
            payBillSQ.setSign_type("HMAC-SHA256");

            try {
                Map map = BeanUtils.toStringMap(payBillSQ);
                payBillSQ.setSign(WxPayment.createSignHMAC(map, payAccount.getApiKey()));
                map = BeanUtils.toStringMap(payBillSQ);

                String response = WxPay.orderQuery(map);
                return response;
            } catch (RuntimeException e) {
                throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
            } catch (Exception e) {
                throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
            }
        }
    }

    /**
     * 微信订单查询
     */
    @Override
    public ResultData payBillQuery(WxAppinfo appInfo, WxPayBill record) {
        try {
            if (StringUtils.isBlank(record.getPayBillNo())) {
                record.setPayBillNo(record.getPayBillNo());
            }

            String response = wechatPayBillQuery(appInfo, record);

            logger.info("pay bill query, " + record.toString() + ", response: " + response);
            Map<String, String> returnMap = WxPayment.xmlToMap(response);
            String result_code = returnMap.get("result_code");
            if ("SUCCESS".equals(result_code)) {
                String trade_state = returnMap.get("trade_state");
                //SUCCESS—支付成功
                //REFUND—转入退款
                //NOTPAY—未支付
                //CLOSED—已关闭
                //REVOKED—已撤销（刷卡支付）
                //USERPAYING--用户支付中
                //PAYERROR--支付失败(其他原因，如银行返回失败)
                if ("SUCCESS".equals(trade_state)) {
                    record.setPayBillStatus(EnumPayStatus.PAY_STATUS_SUCCESS.getCode());
                    handlePayBillStatusUpdate(record);
                } else if ("USERPAYING".equals(trade_state)) {
                    record.setPayBillStatus(EnumPayStatus.PAY_STATUS_WAIT.getCode());
                    handlePayBillStatusUpdate(record);
                } else {
                    record.setPayBillStatus(EnumPayStatus.PAY_STATUS_FAIL.getCode());
                    handlePayBillStatusUpdate(record);
                }
                return new ResultData(Result.SUCCESS, "订单查询成功", returnMap);
            } else {
                return updatePayBill(record, returnMap, result_code);
            }
        } catch (MallinkException e) {
            throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
        } catch (RuntimeException e) {
            throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
        } catch (Exception e) {
            throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
        }
    }

    private String wechatPayBillClose(WxAppinfo appInfo, WxPayBill record) {
        // get payAccount
        WxPayAccountBill payAccount = wxPayAccountBillMapper.selectById(appInfo.getPayBillId());
        if (payAccount.getType().equals(EnumPayMode.MCH.getCode())) {
            // 普通商户号模式
            WxPayOrderQ payBillC = new WxPayOrderQ();
            String noncestr = Utility.generate32UUID();
            payBillC.setAppid(appInfo.getAppId());
            payBillC.setMch_id(payAccount.getMchId());
            payBillC.setNonce_str(noncestr);
            payBillC.setOut_trade_no(record.getPayBillNo());
            return closeOrderNormal(payAccount, payBillC);
        } else {
            // 服务商模式
            WxPayOrderSQ payBillSC = new WxPayOrderSQ();
            String noncestr = Utility.generate32UUID();
            payBillSC.setAppid(appInfo.getParentAppId());
            payBillSC.setSub_appid(appInfo.getAppId());
            payBillSC.setMch_id(payAccount.getMchId());
            payBillSC.setSub_mch_id(payAccount.getSubMchId());
            payBillSC.setNonce_str(noncestr);
            payBillSC.setOut_trade_no(record.getPayBillNo());
            payBillSC.setSign_type("HMAC-SHA256");
            return closeOrderServer(payAccount, payBillSC);
        }
    }

    private String closeOrderServer(WxPayAccountBill payAccount, WxPayOrderSQ payBillSC) {
        try {
            Map map = BeanUtils.toStringMap(payBillSC);
            payBillSC.setSign(WxPayment.createSignHMAC(map, payAccount.getApiKey()));
            map = BeanUtils.toStringMap(payBillSC);
            String response = WxPay.closeOrder(map);
            return response;
        } catch (RuntimeException e) {
            throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
        } catch (Exception e) {
            throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
        }
    }

    private String closeOrderNormal(WxPayAccountBill payAccount, WxPayOrderQ payBillC) {
        try {
            Map map = BeanUtils.toStringMap(payBillC);
            payBillC.setSign(WxPayment.createSign(map, payAccount.getApiKey()));
            map = BeanUtils.toStringMap(payBillC);
            String response = WxPay.closeOrder(map);
            return response;
        } catch (RuntimeException e) {
            throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
        } catch (Exception e) {
            throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
        }
    }

    /**
     * 微信关闭支付订单
     */
    @Override
    public ResultData payBillClose(WxAppinfo appInfo, WxPayBill record) {

        try {
            String response = wechatPayBillClose(appInfo, record);

            logger.info("pay bill close, " + record.toString() + ", response: " + response);
            Map<String, String> returnMap = WxPayment.xmlToMap(response);
            String result_code = returnMap.get("result_code");
            if ("SUCCESS".equals(result_code)) {
                try {
                    record.setPayBillStatus(EnumPayStatus.PAY_STATUS_CANCEL.getCode());
                    wxPayBillMapper.updateById(record);
                } catch (Exception e) {
                    logger.error("pay bill update error: " + record.toString());
                    throw new MallinkException(ErrorCode.DB_FAIL);
                }
                return new ResultData(Result.SUCCESS, "订单关闭成功", returnMap);
            } else {
                String errMsg = "";
                JSONObject errObj = errorMapClose.getJSONObject(result_code);
                if (errObj != null) {
                    errMsg = errObj.toJSONString();
                } else {
                    errMsg = returnMap.get("return_msg");
                }
                record.setFailReason(errMsg);
                record.setUpdateTime(new Date());
                try {
                    wxPayBillMapper.updateById(record);
                } catch (Exception e) {
                    logger.error("pay bill update error: " + record.toString());
                    throw new MallinkException(ErrorCode.DB_FAIL);
                }
                return new ResultData(ErrorCode.PAY_ORDER_ERROR.getCode(), errMsg, returnMap);
            }
        } catch (RuntimeException e) {
            throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
        } catch (Exception e) {
            throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
        }
    }


    /**
     * 提供微信支付回调调用
     *
     * @param paramMap 异步通知参数
     * @param payWay   支付方式
     * @return
     */
    @Override
    public String notify(Map<String, String> paramMap, EnumPayWay payWay) {
        // how to get wechatAppId, wechatMchId, partnerKey
        logger.info("-------------");
        logger.info("微信回调返回值：" + JSONObject.toJSONString(paramMap));
        logger.info("-------------");
        String appId = paramMap.get("appid");
        String subAppId = paramMap.get("sub_appid");
        String mchId = paramMap.get("mch_id");
        String subMchId = paramMap.get("sub_mch_id");
        WxAppinfo appinfo;
        boolean isNormal = true;
        if (StringUtils.isBlank(subAppId) && StringUtils.isBlank(subMchId)) {
            // 普通商户号
            appinfo = wxAppinfoMapper.findByAppId(appId);
            if (appinfo == null) {
                throw new MallinkException(ErrorCode.APP_ID_NOT_FOUND);
            }
            isNormal = true;
        } else {
            // 服务号 现在用hmac-sha256
            appinfo = wxAppinfoMapper.findByAppId(subAppId);
            if (appinfo == null) {
                throw new MallinkException(ErrorCode.APP_ID_NOT_FOUND);
            }
            isNormal = false;
        }

        WxPayAccountBill payAccount = wxPayAccountBillMapper.selectById(appinfo.getPayBillId());
        if (payAccount == null) {
            throw new MallinkException(ErrorCode.MCH_INFO_NOT_FOUND);
        }
        String partnerKey = payAccount.getApiKey();
        try {
            if (payWay == EnumPayWay.PAY_WAY_WEAPP) {
                boolean signVerified = false;
                if (isNormal) {
                    // 普通商户号支付
                    signVerified = WxPayment.verifyNotify(paramMap, partnerKey);
                    if (!signVerified) {
                        logger.warn("notify bill, wxpay checksign error, paramMap: " + paramMap.toString() + ",  payWay:" + payWay.toString());
                        throw new MallinkException(ErrorCode.PAY_ORDER_NOTIFY_CHECK_SIGN_ERROR);
                    }
                } else {
                    // 服务号 现在用hmac-sha256
                    signVerified = WxPayment.verifyNotifyHMAC(paramMap, partnerKey);
                    if (!signVerified) {
                        logger.warn("notify bill, wxpay checksign error, paramMap: " + paramMap.toString() + ",  payWay:" + payWay.toString());
                        throw new MallinkException(ErrorCode.PAY_ORDER_NOTIFY_CHECK_SIGN_ERROR);
                    }
                }

                if (!"SUCCESS".equals(paramMap.get("return_code"))) {
                    logger.warn("notify bill, wxpay status not success, paramMap: " + paramMap.toString() + ",  payWay:" + payWay.toString());
                    SortedMap resultMap = new TreeMap();
                    resultMap.put("return_code", "FAIL");
                    resultMap.put("return_msg", "订单状态码非SUCCESS");
                    return XmlUtil.getRequestXml(resultMap);
                }

                String payBillNo = paramMap.get("out_trade_no");
                String timEndStr = paramMap.get("time_end");
                Long payBillId = Long.valueOf(payBillNo.substring(0,payBillNo.length()-12));
                WxPayBill payBill = wxPayBillMapper.selectById(payBillId);
                if (payBill == null) {
                    logger.warn("notify bill, wxpay check pay bill not exists, paramMap: " + paramMap.toString() + ",  payWay:" + payWay.toString());
                    SortedMap resultMap = new TreeMap();
                    resultMap.put("return_code", "FAIL");
                    resultMap.put("return_msg", "订单不存在");
                    return XmlUtil.getRequestXml(resultMap);
                }
                // 验证支付金额
                if (!paramMap.get("total_fee").equals(payBill.getPayAmount().toString())) {
                    logger.warn("notify bill, wxpay check total_fee is invalid, paramMap: " + paramMap.toString() + ",  payWay:" + payWay.toString());
                    SortedMap resultMap = new TreeMap();
                    resultMap.put("return_code", "FAIL");
                    resultMap.put("return_msg", "订单总金额不一致");
                    return XmlUtil.getRequestXml(resultMap);
                }

                Date timeEnd = null;

                try {
                    timeEnd = Utility.getDateFromString(timEndStr);
                } catch (ParseException e) {
                    logger.error("解析timeEnd失败");
                    timeEnd = new Date();
                }
                payBill.setPayTimeEnd(timeEnd);

                // 处理支付成功
                handleBillPaySuccess(payBill, paramMap.get("transaction_id"));
                logger.info("notify bill, wxpay checksign success, paramMap:{}, paramMap: " + paramMap.toString() + ",  payWay:" + payWay.toString());
                SortedMap resultMap = new TreeMap();
                resultMap.put("return_code", "SUCCESS");
                resultMap.put("return_msg", "OK");
                return XmlUtil.getRequestXml(resultMap);
            }
        } catch (RuntimeException e) {
            logger.warn("notify bill, checksign error, paramMap: " + paramMap.toString() + ",  payWay:" + payWay.toString() + ", e:" + e.getMessage());
            throw new MallinkException(ErrorCode.PAY_ORDER_ERROR);
        }

        SortedMap resultMap = new TreeMap();
        resultMap.put("return_code", "FAIL");
        resultMap.put("return_msg", "FAILED");
        return XmlUtil.getRequestXml(resultMap);
    }


    @Override
    public WxPayBill getById(Long id) {
        return wxPayBillMapper.selectById(id);
    }

    @Override
    public void updatePayBillStatus(WxPayBill payBill) {
        try {
            logger.info("更新支付订单");
            wxPayBillMapper.updateById(payBill);
        }catch (MallinkException e){
            logger.info("更新支付订单失败");
            throw new MallinkException(ErrorCode.ORDER_UPDATE_ERR);
        }
    }

    @Override
    public WxPayBill queryPayBill(WxPayBill payBill) {
        List<WxPayBill> select = wxPayBillMapper.selectList(new QueryWrapper(payBill));
        if(select.size()>0){
            return select.get(0);
        }
        return null;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class})
    public void handleBillPaySuccess(WxPayBill record, String transactionId) {
        Date currentDate = new Date();
        EnumPayStatus payStatus = EnumPayStatus.getEnum(record.getPayBillStatus());
        // 判断支付状态
        if (payStatus == EnumPayStatus.PAY_STATUS_SUCCESS) {
            // 已经是成功状态，只更新transactionId
            try {
                record.setUpdateTime(currentDate);
                record.setTransactionId(transactionId);
                wxPayBillMapper.updateById(record);
                WxBillAll wxBillAll = new WxBillAll();
                wxBillAll.setId(record.getBillId());
                List<Map<String, Object>> bills = wxBillAllMapper.listData(wxBillAll);
                if (bills.size() > 0) {
                    Map<String, Object> bill = bills.get(0);
                    bill.put("userId", record.getbUserId());
                    wxBillAllService.updateBill(bill);
                }
            } catch (Exception e) {
                logger.error(e.getMessage());
                throw new MallinkException(ErrorCode.DB_FAIL);
            }
            return;
        }
        WxBillAll wxBillAll = new WxBillAll();
        wxBillAll.setId(record.getBillId());
        wxBillAll.setTenantId(record.getTenantId());
        List<Map<String, Object>> bills = wxBillAllMapper.listData(wxBillAll);
        if (bills.size()==0) {
            logger.error("pay success handle, bill " + record.getBillId() + " not found , payBillNo : " + record.getPayBillNo());
            throw new MallinkException(ErrorCode.BILL_ROUTINE_IS_NOT_FOUND);
        }
        Map<String, Object> bill;
        bill = bills.get(0);
        bill.put("userId", record.getbUserId());
        // 修改支付订单状态
        WxPayBill updateBill = new WxPayBill();
        updateBill.setId(record.getId());
        updateBill.setBillId(record.getBillId());
        updateBill.setUpdateTime(currentDate);
        updateBill.setPayBillStatus(EnumPayStatus.PAY_STATUS_SUCCESS.getCode());
        updateBill.setTransactionId(transactionId);
        try {
            wxPayBillMapper.updateById(updateBill);
        } catch (Exception e) {
            logger.error("支付订单数据库更新失败: " + e.getMessage());
            throw new MallinkException(ErrorCode.DB_FAIL.getCode(), "支付订单数据库更新失败: " + e.getMessage());
        }

        //修改账单状态
        try {
            wxBillAllService.updateBill(bill);
        } catch (Exception e) {
            logger.error(e.getMessage());
            throw new MallinkException(ErrorCode.BILL_UPDATE_FAILED);
        }

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class})
    public void handlePayBillStatusUpdate(WxPayBill record) {
        // 判断支付状态
        if (record.getPayBillStatus().equals(EnumPayStatus.PAY_STATUS_WAIT.getCode())) {
            logger.error("pay handle, payBill " + record.getPayBillNo() +
                    "is complete, billId : " + record.getBillId());
            return;
        }
        WxBillAll wxBillAll = new WxBillAll();
        wxBillAll.setId(record.getBillId());
        List<Map<String, Object>> bills = wxBillAllMapper.listData(wxBillAll);
        if (bills.size()==0) {
            logger.error("pay handle, bill " + record.getBillId() + " not found , payBillNo : " + record.getPayBillNo());
            throw new MallinkException(ErrorCode.ORDER_IS_NOT_FIND);
        }
        Map<String, Object> bill = bills.get(0);
        if (record.getId() > 0) {
            // 1. get appinfo
            WxAppinfo appInfo = null;
            WxAppinfo appinfoQ = new WxAppinfo();
            appinfoQ.setTenantId(bill.get("tenantId").toString());
            appinfoQ.setType(EnumAppType.B.getCode());
            List<WxAppinfo> appList = wxAppinfoMapper.selectList(new QueryWrapper(appinfoQ));
            if (appList.size() > 0) {
                appInfo = appList.get(0);
            }

            // 检查支付订单状态
            WxPayBill payBillQ = new WxPayBill();
            payBillQ.setId(record.getId());
            payBillQ.setTenantId(bill.get("tenantId").toString());
            payBillQ.setBillId(record.getBillId());
            WxPayBill updateBill;
            try {
                updateBill = wxPayBillMapper.selectOne(new QueryWrapper(payBillQ));
            } catch (Exception e) {
                logger.error(e.getMessage());
                throw new MallinkException(ErrorCode.PAY_ORDER_NOT_FOUND);
            }

            if (record.getPayBillStatus().equals(EnumPayStatus.PAY_STATUS_FAIL.getCode())) {
                // 支付订单取消
                if (updateBill.getPayAmount() > 0) {
                    payBillQ = new WxPayBill();
                    payBillQ.setTenantId(bill.get("tenantId").toString());
                    payBillQ.setBillId(record.getBillId());
                    List<WxPayBill> payBills = wxPayBillMapper.selectList(new QueryWrapper(payBillQ));
                    for (WxPayBill payBill : payBills) {
                        if(payBill.getPayTimeEnd().before(new Date())){
                            payBillClose(appInfo, payBill);
                        }
                    }
                }
            } else {
                // 支付订单成功？
                if (updateBill.getPayBillStatus().equals(EnumPayStatus.PAY_STATUS_SUCCESS.getCode())) {
                    if (!StringUtils.isBlank(updateBill.getTransactionId())) {
                        // 回调已设置成功
                        // 继续修改订单状态
                    } else {
                        // 回调异常
                        throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), "transactionId未获取");
                    }
                } else if (updateBill.getPayBillStatus().equals(EnumPayStatus.PAY_STATUS_WAIT.getCode())) {
                    // 回调未返回，主动检查支付订单状态
                    try {
                        record.setPayBillNo(String.valueOf(record.getId()));
                        String response = wechatPayBillQuery(appInfo, record);
                        logger.info("pay bill query, " + record.toString() + ", response: " + response);
                        Map<String, String> returnMap = WxPayment.xmlToMap(response);
                        String result_code = returnMap.get("result_code");
                        if ("SUCCESS".equals(result_code)) {
                            String trade_state = returnMap.get("trade_state");
                            //SUCCESS—支付成功
                            //REFUND—转入退款
                            //NOTPAY—未支付
                            //CLOSED—已关闭
                            //REVOKED—已撤销（刷卡支付）
                            //USERPAYING--用户支付中
                            //PAYERROR--支付失败(其他原因，如银行返回失败)
                            if ("SUCCESS".equals(trade_state)) {
                                // 查询支付订单 -- 已支付
                                // 继续更改状态
                                wxBillAllService.updateBill(bill);
                            } else {
                                // 支付订单未成功，返回异常
                                throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), "支付订单未成功");
                            }
                        }
                    } catch (MallinkException e) {
                        throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
                    } catch (RuntimeException e) {
                        throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
                    } catch (Exception e) {
                        throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
                    }
                }
            }
        }

    }


}
