package com.sptg.pay.biz.controller.wxpay;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.enums.TradeType;
import com.ijpay.core.kit.*;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.WxPayApiConfig;
import com.ijpay.wxpay.WxPayApiConfigKit;
import com.ijpay.wxpay.model.*;
import com.sptg.common.core.beans.ResultBean;
import com.sptg.common.core.constant.OperationConstant;
import com.sptg.estate.api.dto.ApplyPayRefundDto;
import com.sptg.estate.api.entity.ApplyPayRefund;
import com.sptg.estate.api.entity.FaceChargeProject;
import com.sptg.estate.api.entity.User;
import com.sptg.estate.api.feigns.ApplyPayRefundFeign;
import com.sptg.estate.api.feigns.FaceChargeProjectFeign;
import com.sptg.estate.api.feigns.UserFeign;
import com.sptg.pay.api.dto.PayCertificateDto;
import com.sptg.pay.api.dto.PayChannelConfigDto;
import com.sptg.pay.api.dto.UserOrderDto;
import com.sptg.pay.api.entity.H5SceneInfo;
import com.sptg.pay.api.entity.PayCertificate;
import com.sptg.pay.api.entity.PayChannelConfig;
import com.sptg.pay.api.entity.UserOrder;
import com.sptg.pay.api.feigns.PayCertificateFeign;
import com.sptg.pay.api.feigns.UserOrderFeign;
import com.sptg.pay.api.feigns.WxPayFeign;
import com.sptg.pay.api.vendor.wxPay.data.WxPayBean;
import com.sptg.pay.api.vo.AjaxResult;
import com.sptg.pay.biz.service.PayCertificateServiceInterface;
import com.sptg.pay.biz.service.PayChannelConfigServiceInterface;
import com.sptg.pay.biz.service.UserOrderServiceInterface;
import com.sptg.pay.biz.utils.StringUtils;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.scheduling.annotation.Async;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.UnknownHostException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>IJPay 让支付触手可及，封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。</p>
 *
 * <p>不依赖任何第三方 mvc 框架，仅仅作为工具使用简单快速完成支付模块的开发，可轻松嵌入到任何系统里。 </p>
 *
 * <p>IJPay 交流群: 723992875</p>
 *
 * <p>Node.js 版: https://gitee.com/javen205/TNWX</p>
 *
 * <p>微信支付 Demo</p>
 *
 * @author Javen
 */
@RestController
//@RequestMapping("/wxPay")
@RequiredArgsConstructor
public class WxPayController extends AbstractWxPayApiController implements WxPayFeign {
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    private final static String SUCCESS = "SUCCESS";

    /**
     * 渠道代码：固定为 wxpay
     */
    private static final String WXPAY_CHANNEL_CODE = "wxPay";

    private WxPayBean wxPayBean = new WxPayBean();

    private final UserOrderFeign userOrderFeign;
    private final ApplyPayRefundFeign applyPayRefundFeign;
    private final PayCertificateFeign payCertificateFeign;
    private final FaceChargeProjectFeign faceChargeProjectFeign;
    private final UserFeign userFeign;

    @Autowired
    private PayChannelConfigServiceInterface payChannelConfigServiceInterface;
    @Autowired
    private UserOrderServiceInterface userOrderServiceInterface;
    @Autowired
    private PayCertificateServiceInterface payCertificateServiceInterface;

    private String notifyUrl;
    private String refundNotifyUrl;
    private static final String USER_PAYING = "USERPAYING";


    @Override
    public WxPayApiConfig getApiConfig() {
        WxPayApiConfig apiConfig;

        try {
            apiConfig = WxPayApiConfigKit.getApiConfig(wxPayBean.getAppId());
        } catch (Exception e) {
            apiConfig = WxPayApiConfig.builder()
                    .appId(wxPayBean.getAppId())
                    .mchId(wxPayBean.getMchId())
                    .partnerKey(wxPayBean.getPartnerKey())
                    .certPath(wxPayBean.getCertPath())
                    .domain(wxPayBean.getDomain())
                    .build();
        }
        notifyUrl = apiConfig.getDomain().concat("/wxPay/payNotify");
        refundNotifyUrl = apiConfig.getDomain().concat("/wxPay/refundNotify");
        return apiConfig;
    }

    @RequestMapping("")
    @ResponseBody
    public String index() {
        log.warn("欢迎使用 IJPay 中的微信支付 -By Javen  <br/><br>  交流群：723992875");
        log.warn(wxPayBean.toString());
        return ("欢迎使用 IJPay 中的微信支付 -By Javen  <br/><br>  交流群：723992875");
    }

    @GetMapping("/wxPay/test")
    @ResponseBody
    public WxPayBean test() {
        return wxPayBean;
    }

    @GetMapping("/wxPay/getKey")
    @ResponseBody
    public String getKey() {
        return WxPayApi.getSignKey(wxPayBean.getMchId(), wxPayBean.getPartnerKey(), SignType.MD5);
    }

    /**
     * 微信APP支付
     */
//    @RequestMapping(value = "/appPay", method = {RequestMethod.POST, RequestMethod.GET})
    @PostMapping(value = "/wxPay/appPay")
    public AjaxResult appPay(@RequestBody Map<String, Object> map) {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        UserOrderDto userOrderDto = BeanUtil.mapToBean(map, UserOrderDto.class, false);
        /** 订单状态校验 */
        UserOrder userOrder = userOrderServiceInterface.findUserOrderByCondition(userOrderDto);
        Assert.notNull(userOrder, "订单不存在！");
        Assert.isTrue(!userOrder.getPayStatus().equals(OperationConstant.TWO), "订单已支付，请勿重复操作");

        /**
         * 查询本地是否已有支付凭证（且仍在有效期），如果已有则直接返回
         */
        PayCertificateDto payCertificateDto = new PayCertificateDto();
        payCertificateDto.setOrderNo(userOrder.getBizOrderNo());
        payCertificateDto.setChannelCode(WXPAY_CHANNEL_CODE);
        PayCertificate localCertificate = payCertificateServiceInterface.findPayCertificateByCondition(payCertificateDto);
        if (null != localCertificate
                && (null == localCertificate.getExpireTime() || DateTime.now().isBefore(localCertificate.getExpireTime()))) {
            return new AjaxResult().success(localCertificate.getCertificate());
        }


        String ip = IpKit.getRealIp(request);
        if (StringUtils.isBlank(ip)) {
            ip = "127.0.0.1";
        }

        PayChannelConfigDto payChannelConfigDto = new PayChannelConfigDto();
        payChannelConfigDto.setCode(WXPAY_CHANNEL_CODE);
        PayChannelConfig payChannelConfig = payChannelConfigServiceInterface.findPayChannelConfigByCondition(payChannelConfigDto);
//        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

        Map<String, String> params = UnifiedOrderModel
                .builder()
                .appid(payChannelConfig.getAppId())
                .mch_id(payChannelConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .body(userOrder.getDescription()) //这个如果为null值，将会调不起微信支付
                .attach(userOrder.getOrderType())
                .out_trade_no(userOrder.getBizOrderNo())
                .total_fee(userOrder.getActualPayAmount().multiply(new BigDecimal(100)).intValue() + "")
                .spbill_create_ip(ip)
                .notify_url(payChannelConfig.getNotifyUrl())
                .trade_type(TradeType.APP.getTradeType())
                .build()
                .createSign(payChannelConfig.getWxKey(), SignType.HMACSHA256);

        String xml = WxPayKit.toXml(params);
        log.warn(xml);
        String xmlResult = WxPayApi.pushOrder(false, params);

        log.warn(xmlResult);
        Map<String, String> result = WxPayKit.xmlToMap(xmlResult);

        String returnCode = result.get("return_code");
        String returnMsg = result.get("return_msg");

        if (!WxPayKit.codeIsOk(returnCode)) {
            if (!WxPayKit.codeIsOk(returnCode)) {
                return new AjaxResult().addError(returnMsg);
            }
        }
        String resultCode = result.get("result_code");
        if (!WxPayKit.codeIsOk(resultCode)) {
            return new AjaxResult().addError(returnMsg);
        }
        // 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
        String prepayId = result.get("prepay_id");

        Map<String, String> packageParams = WxPayKit.appPrepayIdCreateSign(payChannelConfig.getAppId(), payChannelConfig.getMchId(), prepayId,
                payChannelConfig.getWxKey(), SignType.HMACSHA256);

        String jsonStr = JSON.toJSONString(packageParams);
        log.warn("返回apk的参数:" + jsonStr);
        PayCertificateDto dto = new PayCertificateDto();
        dto.setOrderNo(userOrder.getBizOrderNo());
        dto.setChannelCode(WXPAY_CHANNEL_CODE);
        PayCertificate queryPayCertificate = payCertificateServiceInterface.findPayCertificateByCondition(dto);
        if (null != queryPayCertificate) {
            /**
             * 有则更新
             */
            queryPayCertificate.setOrderNo(userOrder.getBizOrderNo());
            queryPayCertificate.setChannelCode(WXPAY_CHANNEL_CODE);
            queryPayCertificate.setCertificate(jsonStr);
            queryPayCertificate.setExpireTime(null);
            queryPayCertificate.setUpdateDate(new Date());
            payCertificateServiceInterface.updatePayCertificate(queryPayCertificate);
        } else {
            //保存微信支付的支付凭证到本地
            PayCertificateDto newPayCertificateDto = new PayCertificateDto();
            newPayCertificateDto.setOrderId(userOrder.getId());
            newPayCertificateDto.setOrderNo(userOrder.getBizOrderNo());
            newPayCertificateDto.setPayStatus(OperationConstant.ZERO);
            newPayCertificateDto.setUserId(userOrder.getUserId());
            newPayCertificateDto.setStatus(0);
            newPayCertificateDto.setRefund(OperationConstant.ZERO);
            Date today = new Date();
            newPayCertificateDto.setCreateDate(today);
            newPayCertificateDto.setUpdateDate(today);
            newPayCertificateDto.setChannelCode(WXPAY_CHANNEL_CODE);
            newPayCertificateDto.setExpireTime(null);
            newPayCertificateDto.setApplyRefund(OperationConstant.ZERO); //设置为未发起退款申请
            newPayCertificateDto.setCertificate(jsonStr);
            payCertificateServiceInterface.addPayCertificate(newPayCertificateDto);
        }
        return new AjaxResult().success(jsonStr);
    }

    /**
     * 查询订单
     */
    @PostMapping(value = "/wxPay/orderQuery")
    public AjaxResult orderQuery(@RequestBody UserOrder userOrder) {
        Assert.notNull(userOrder.getBizOrderNo(), "订单号不存在");
        Map<String, String> params = new HashMap<>();
        PayChannelConfigDto payChannelConfigDto = new PayChannelConfigDto();
        payChannelConfigDto.setCode(WXPAY_CHANNEL_CODE);
        PayChannelConfig payChannelConfig = payChannelConfigServiceInterface.findPayChannelConfigByCondition(payChannelConfigDto);
        params.put("appid", payChannelConfig.getAppId());
        params.put("mch_id", payChannelConfig.getMchId());
        if (userOrder.getTransactionId() != null) {
            params.put("transaction_id", userOrder.getTransactionId());
        } else {
            params.put("out_trade_no", userOrder.getBizOrderNo());
        }
        params.put("nonce_str", WxPayKit.generateStr());
        String sign = WxPayKit.createSign(params, payChannelConfig.getWxKey(), SignType.HMACSHA256);
        params.put("sign", sign);
        String xml = WxPayKit.toXml(params);
        log.warn(xml);
        String xmlResult = WxPayApi.orderQuery(false, params);

        Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
        String returnCode = result.get("return_code");
        String returnMsg = result.get("return_msg");
        if (WxPayKit.verifyNotify(result, payChannelConfig.getWxKey(), SignType.HMACSHA256)) {
            if (!WxPayKit.codeIsOk(returnCode)) {
                if (!WxPayKit.codeIsOk(returnCode)) {
                    return new AjaxResult().addError(returnMsg);
                }
            }
            String resultCode = result.get("result_code");
            if (!WxPayKit.codeIsOk(resultCode)) {
                return new AjaxResult().addError(returnMsg);
            }

            String trade_state = result.get("trade_state");
            if (WxPayKit.codeIsOk(trade_state)) {
                if (userOrder.getPayStatus() != OperationConstant.TWO) {
                    userOrder.setPayStatus(OperationConstant.TWO);
                    userOrderServiceInterface.updateUserOrder(userOrder);
                }
            }
        }
        String jsonStr = JSON.toJSONString(result);
        return new AjaxResult().success(jsonStr);
    }

    /**
     * 微信H5 支付
     * 注意：必须再web页面中发起支付且域名已添加到开发配置中
     */
    @RequestMapping(value = "/wxPay/wapPay", method = {RequestMethod.POST, RequestMethod.GET})
    public void wapPay(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String ip = IpKit.getRealIp(request);
        if (StringUtils.isBlank(ip)) {
            ip = "127.0.0.1";
        }

        H5SceneInfo sceneInfo = new H5SceneInfo();

        H5SceneInfo.H5 h5_info = new H5SceneInfo.H5();
        h5_info.setType("Wap");
        //此域名必须在商户平台--"产品中心"--"开发配置"中添加
        h5_info.setWap_url("https://gitee.com/javen205/IJPay");
        h5_info.setWap_name("IJPay VIP 充值");
        sceneInfo.setH5Info(h5_info);

        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

        Map<String, String> params = UnifiedOrderModel
                .builder()
                .appid(wxPayApiConfig.getAppId())
                .mch_id(wxPayApiConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .body("IJPay 让支付触手可及-H5支付")
                .attach("Node.js 版:https://gitee.com/javen205/TNWX")
                .out_trade_no(WxPayKit.generateStr())
                .total_fee("1000")
                .spbill_create_ip(ip)
                .notify_url(notifyUrl)
                .trade_type(TradeType.MWEB.getTradeType())
                .scene_info(JSON.toJSONString(sceneInfo))
                .build()
                .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);

        String xmlResult = WxPayApi.pushOrder(false, params);
        log.warn(xmlResult);

        Map<String, String> result = WxPayKit.xmlToMap(xmlResult);

        String return_code = result.get("return_code");
        String return_msg = result.get("return_msg");
        if (!WxPayKit.codeIsOk(return_code)) {
            throw new RuntimeException(return_msg);
        }
        String result_code = result.get("result_code");
        if (!WxPayKit.codeIsOk(result_code)) {
            throw new RuntimeException(return_msg);
        }
        // 以下字段在return_code 和result_code都为SUCCESS的时候有返回

        String prepayId = result.get("prepay_id");
        String webUrl = result.get("mweb_url");

        log.warn("prepay_id:" + prepayId + " mweb_url:" + webUrl);
        response.sendRedirect(webUrl);
    }

    /**
     * 公众号支付
     */
    @RequestMapping(value = "/wxPay/webPay", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public AjaxResult webPay(HttpServletRequest request, @RequestParam("total_fee") String totalFee) {
        // openId，采用 网页授权获取 access_token API：SnsAccessTokenApi获取
        String openId = (String) request.getSession().getAttribute("openId");
        if (openId == null) {
            openId = "11111111";
        }

        if (StrUtil.isEmpty(openId)) {
            return new AjaxResult().addError("openId is null");
        }
        if (StrUtil.isEmpty(totalFee)) {
            return new AjaxResult().addError("请输入数字金额");
        }
        String ip = IpKit.getRealIp(request);
        if (StrUtil.isEmpty(ip)) {
            ip = "127.0.0.1";
        }

        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

        Map<String, String> params = UnifiedOrderModel
                .builder()
                .appid(wxPayApiConfig.getAppId())
                .mch_id(wxPayApiConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .body("IJPay 让支付触手可及-公众号支付")
                .attach("Node.js 版:https://gitee.com/javen205/TNWX")
                .out_trade_no(WxPayKit.generateStr())
                .total_fee("1000")
                .spbill_create_ip(ip)
                .notify_url(notifyUrl)
                .trade_type(TradeType.JSAPI.getTradeType())
                .openid(openId)
                .build()
                .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);

        String xmlResult = WxPayApi.pushOrder(false, params);
        log.warn(xmlResult);

        Map<String, String> resultMap = WxPayKit.xmlToMap(xmlResult);
        String returnCode = resultMap.get("return_code");
        String returnMsg = resultMap.get("return_msg");
        if (!WxPayKit.codeIsOk(returnCode)) {
            return new AjaxResult().addError(returnMsg);
        }
        String resultCode = resultMap.get("result_code");
        if (!WxPayKit.codeIsOk(resultCode)) {
            return new AjaxResult().addError(returnMsg);
        }

        // 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回

        String prepayId = resultMap.get("prepay_id");

        Map<String, String> packageParams = WxPayKit.prepayIdCreateSign(prepayId, wxPayApiConfig.getAppId(),
                wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);

        String jsonStr = JSON.toJSONString(packageParams);
        return new AjaxResult().success(jsonStr);
    }

    /**
     * 扫码模式一
     */
    @RequestMapping(value = "/wxPay/scanCode1", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public AjaxResult scanCode1(HttpServletRequest request, HttpServletResponse response,
                                @RequestParam("productId") String productId) {
        try {
            if (StringUtils.isBlank(productId)) {
                return new AjaxResult().addError("productId is null");
            }
            WxPayApiConfig config = WxPayApiConfigKit.getWxPayApiConfig();
            //获取扫码支付（模式一）url
            String qrCodeUrl = WxPayKit.bizPayUrl(config.getPartnerKey(), config.getAppId(), config.getMchId(), productId);
            log.warn(qrCodeUrl);
            //生成二维码保存的路径
            String name = "payQRCode1.png";
            log.warn(ResourceUtils.getURL("classpath:").getPath());
            boolean encode = QrCodeKit.encode(qrCodeUrl, BarcodeFormat.QR_CODE, 3, ErrorCorrectionLevel.H,
                    "png", 200, 200,
                    ResourceUtils.getURL("classpath:").getPath().concat("static").concat(File.separator).concat(name));
            if (encode) {
                //在页面上显示
                return new AjaxResult().success(name);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return new AjaxResult().addError("系统异常：" + e.getMessage());
        }
        return null;
    }

    /**
     * 扫码支付模式一回调
     */
    @RequestMapping(value = "/wxPay/scanCodeNotify", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public String scanCodeNotify(HttpServletRequest request, HttpServletResponse response) {
        try {
            String result = HttpKit.readData(request);
            log.warn("scanCodeNotify>>>" + result);
            /**
             * 获取返回的信息内容中各个参数的值
             */
            Map<String, String> map = WxPayKit.xmlToMap(result);
            for (String key : map.keySet()) {
                log.warn("key= " + key + " and value= " + map.get(key));
            }

            String appId = map.get("appid");
            String openId = map.get("openid");
            String mchId = map.get("mch_id");
            String isSubscribe = map.get("is_subscribe");
            String nonceStr = map.get("nonce_str");
            String productId = map.get("product_id");
            String sign = map.get("sign");

            Map<String, String> packageParams = new HashMap<String, String>(6);
            packageParams.put("appid", appId);
            packageParams.put("openid", openId);
            packageParams.put("mch_id", mchId);
            packageParams.put("is_subscribe", isSubscribe);
            packageParams.put("nonce_str", nonceStr);
            packageParams.put("product_id", productId);

            WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

            String packageSign = WxPayKit.createSign(packageParams, wxPayApiConfig.getPartnerKey(), SignType.MD5);

            String ip = IpKit.getRealIp(request);
            if (StringUtils.isBlank(ip)) {
                ip = "127.0.0.1";
            }
            Map<String, String> params = UnifiedOrderModel
                    .builder()
                    .appid(wxPayApiConfig.getAppId())
                    .mch_id(wxPayApiConfig.getMchId())
                    .nonce_str(WxPayKit.generateStr())
                    .body("IJPay 让支付触手可及-扫码支付模式一")
                    .attach("Node.js 版:https://gitee.com/javen205/TNWX")
                    .out_trade_no(WxPayKit.generateStr())
                    .total_fee("1")
                    .spbill_create_ip(ip)
                    .notify_url(notifyUrl)
                    .trade_type(TradeType.NATIVE.getTradeType())
                    .openid(openId)
                    .build()
                    .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);
            String xmlResult = WxPayApi.pushOrder(false, params);
            log.warn("统一下单:" + xmlResult);
            /**
             * 发送信息给微信服务器
             */
            Map<String, String> payResult = WxPayKit.xmlToMap(xmlResult);
            String returnCode = payResult.get("return_code");
            String resultCode = payResult.get("result_code");
            if (WxPayKit.codeIsOk(returnCode) && WxPayKit.codeIsOk(resultCode)) {
                // 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
                String prepayId = payResult.get("prepay_id");

                Map<String, String> prepayParams = new HashMap<String, String>(10);
                prepayParams.put("return_code", "SUCCESS");
                prepayParams.put("appid", appId);
                prepayParams.put("mch_id", mchId);
                prepayParams.put("nonce_str", System.currentTimeMillis() + "");
                prepayParams.put("prepay_id", prepayId);
                String prepaySign;
                if (sign.equals(packageSign)) {
                    prepayParams.put("result_code", "SUCCESS");
                } else {
                    prepayParams.put("result_code", "FAIL");
                    //result_code为FAIL时，添加该键值对，value值是微信告诉客户的信息
                    prepayParams.put("err_code_des", "订单失效");
                }
                prepaySign = WxPayKit.createSign(prepayParams, wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);
                prepayParams.put("sign", prepaySign);
                String xml = WxPayKit.toXml(prepayParams);
                log.error(xml);
                return xml;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 扫码支付模式二
     */
    @RequestMapping(value = "/wxPay/scanCode2", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public AjaxResult scanCode2(HttpServletRequest request, HttpServletResponse response,
                                @RequestParam("total_fee") String totalFee) {

        if (StringUtils.isBlank(totalFee)) {
            return new AjaxResult().addError("支付金额不能为空");
        }

        String ip = IpKit.getRealIp(request);
        if (StringUtils.isBlank(ip)) {
            ip = "127.0.0.1";
        }
        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

        Map<String, String> params = UnifiedOrderModel
                .builder()
                .appid(wxPayApiConfig.getAppId())
                .mch_id(wxPayApiConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .body("IJPay 让支付触手可及-扫码支付模式二")
                .attach("Node.js 版:https://gitee.com/javen205/TNWXX")
                .out_trade_no(WxPayKit.generateStr())
                .total_fee("1")
                .spbill_create_ip(ip)
                .notify_url(notifyUrl)
                .trade_type(TradeType.NATIVE.getTradeType())
                .build()
                .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);

        String xmlResult = WxPayApi.pushOrder(false, params);
        log.warn("统一下单:" + xmlResult);

        Map<String, String> result = WxPayKit.xmlToMap(xmlResult);

        String returnCode = result.get("return_code");
        String returnMsg = result.get("return_msg");
        System.out.println(returnMsg);
        if (!WxPayKit.codeIsOk(returnCode)) {
            return new AjaxResult().addError("error:" + returnMsg);
        }
        String resultCode = result.get("result_code");
        if (!WxPayKit.codeIsOk(resultCode)) {
            return new AjaxResult().addError("error:" + returnMsg);
        }
        //生成预付订单success

        String qrCodeUrl = result.get("code_url");
        String name = "payQRCode2.png";

        boolean encode = QrCodeKit.encode(qrCodeUrl, BarcodeFormat.QR_CODE, 3, ErrorCorrectionLevel.H, "png", 200, 200,
                request.getSession().getServletContext().getRealPath("/") + File.separator + name);
        if (encode) {
            //在页面上显示
            return new AjaxResult().success(name);
        }
        return null;
    }

    /**
     * 刷卡支付
     */
    @RequestMapping(value = "/wxPay/micropay", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public AjaxResult microPay(HttpServletRequest request, HttpServletResponse response) {
        String authCode = request.getParameter("auth_code");
        String totalFee = request.getParameter("total_fee");
        if (StringUtils.isBlank(totalFee)) {
            return new AjaxResult().addError("支付金额不能为空");
        }
        if (StringUtils.isBlank(authCode)) {
            return new AjaxResult().addError("auth_code参数错误");
        }
        String ip = IpKit.getRealIp(request);
        if (StringUtils.isBlank(ip)) {
            ip = "127.0.0.1";
        }
        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

        Map<String, String> params = MicroPayModel.builder()
                .appid(wxPayApiConfig.getAppId())
                .mch_id(wxPayApiConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .body("IJPay 让支付触手可及-刷卡支付")
                .attach("Node.js 版:https://gitee.com/javen205/TNWXX")
                .out_trade_no(WxPayKit.generateStr())
                .total_fee("1")
                .spbill_create_ip(ip)
                .auth_code(authCode)
                .build()
                .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);

        String xmlResult = WxPayApi.microPay(false, params);
        //同步返回结果
        log.warn("xmlResult:" + xmlResult);
        Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
        String returnCode = result.get("return_code");
        String returnMsg = result.get("return_msg");
        if (!WxPayKit.codeIsOk(returnCode)) {
            //通讯失败
            String errCode = result.get("err_code");
            if (StringUtils.isNotBlank(errCode)) {
                //用户支付中，需要输入密码
                if (USER_PAYING.equals(errCode)) {
                    //等待5秒后调用【查询订单API】
                }
            }
            log.warn("提交刷卡支付失败>>" + xmlResult);
            return new AjaxResult().addError(returnMsg);
        }

        String resultCode = result.get("result_code");
        if (!WxPayKit.codeIsOk(resultCode)) {
            log.warn("支付失败>>" + xmlResult);
            String errCodeDes = result.get("err_code_des");
            return new AjaxResult().addError(errCodeDes);
        }
        //支付成功
        return new AjaxResult().success(xmlResult);
    }

    /**
     * 微信小程序支付
     */
    @RequestMapping(value = "/wxPay/miniAppPay", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public AjaxResult miniAppPay(HttpServletRequest request) {
        //需要通过授权来获取openId
        String openId = (String) request.getSession().getAttribute("openId");

        String ip = IpKit.getRealIp(request);
        if (StringUtils.isBlank(ip)) {
            ip = "127.0.0.1";
        }

        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

        Map<String, String> params = UnifiedOrderModel
                .builder()
                .appid(wxPayApiConfig.getAppId())
                .mch_id(wxPayApiConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .body("IJPay 让支付触手可及-小程序支付")
                .attach("Node.js 版:https://gitee.com/javen205/TNWXX")
                .out_trade_no(WxPayKit.generateStr())
                .total_fee("1000")
                .spbill_create_ip(ip)
                .notify_url(notifyUrl)
                .trade_type(TradeType.JSAPI.getTradeType())
                .openid(openId)
                .build()
                .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);

        String xmlResult = WxPayApi.pushOrder(false, params);

        log.warn(xmlResult);
        Map<String, String> result = WxPayKit.xmlToMap(xmlResult);

        String returnCode = result.get("return_code");
        String returnMsg = result.get("return_msg");
        if (!WxPayKit.codeIsOk(returnCode)) {
            return new AjaxResult().addError(returnMsg);
        }
        String resultCode = result.get("result_code");
        if (!WxPayKit.codeIsOk(resultCode)) {
            return new AjaxResult().addError(returnMsg);
        }
        // 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
        String prepayId = result.get("prepay_id");
        Map<String, String> packageParams = WxPayKit.miniAppPrepayIdCreateSign(wxPayApiConfig.getAppId(), prepayId,
                wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);
        String jsonStr = JSON.toJSONString(packageParams);
        log.warn("小程序支付的参数:" + jsonStr);
        return new AjaxResult().success(jsonStr);
    }

    /**
     * 企业付款到零钱
     */
    @RequestMapping(value = "/wxPay/transfer", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public String transfer(HttpServletRequest request, @RequestParam("openId") String openId) {

        String ip = IpKit.getRealIp(request);
        if (StringUtils.isBlank(ip)) {
            ip = "127.0.0.1";
        }

        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

        Map<String, String> params = TransferModel.builder()
                .mch_appid(wxPayApiConfig.getAppId())
                .mchid(wxPayApiConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .partner_trade_no(WxPayKit.generateStr())
                .openid(openId)
                .check_name("NO_CHECK")
                .amount("100")
                .desc("IJPay 让支付触手可及-企业付款")
                .spbill_create_ip(ip)
                .build()
                .createSign(wxPayApiConfig.getPartnerKey(), SignType.MD5, false);

        // 提现
        String transfers = WxPayApi.transfers(params, wxPayApiConfig.getCertPath(), wxPayApiConfig.getMchId());
        log.warn("提现结果:" + transfers);
        Map<String, String> map = WxPayKit.xmlToMap(transfers);
        String returnCode = map.get("return_code");
        String resultCode = map.get("result_code");
        if (WxPayKit.codeIsOk(returnCode) && WxPayKit.codeIsOk(resultCode)) {
            // 提现成功
        } else {
            // 提现失败
        }
        return transfers;
    }

    /**
     * 查询企业付款到零钱
     */
    @RequestMapping(value = "/wxPay/transferInfo", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public String transferInfo(@RequestParam("partner_trade_no") String partnerTradeNo) {
        try {
            WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

            Map<String, String> params = GetTransferInfoModel.builder()
                    .nonce_str(WxPayKit.generateStr())
                    .partner_trade_no(partnerTradeNo)
                    .mch_id(wxPayApiConfig.getMchId())
                    .appid(wxPayApiConfig.getAppId())
                    .build()
                    .createSign(wxPayApiConfig.getPartnerKey(), SignType.MD5, false);

            return WxPayApi.getTransferInfo(params, wxPayApiConfig.getCertPath(), wxPayApiConfig.getMchId());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取RSA加密公钥
     */
    @RequestMapping(value = "/wxPay/getPublicKey", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public String getPublicKey() {
        try {
            WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

            Map<String, String> params = new HashMap<String, String>(4);
            params.put("mch_id", wxPayApiConfig.getMchId());
            params.put("nonce_str", String.valueOf(System.currentTimeMillis()));
            params.put("sign_type", "MD5");
            String createSign = WxPayKit.createSign(params, wxPayApiConfig.getPartnerKey(), SignType.MD5);
            params.put("sign", createSign);
            return WxPayApi.getPublicKey(params, wxPayApiConfig.getCertPath(), wxPayApiConfig.getMchId());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 企业付款到银行卡
     */
    @RequestMapping(value = "/wxPay/payBank", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public String payBank() {
        try {
            WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

            //通过WxPayApi.getPublicKey接口获取RSA加密公钥
            //假设获取到的RSA加密公钥为PUBLIC_KEY(PKCS#8格式)
            final String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6Bl76IwSvBTiibZ+CNRUA6BfahMshZ0WJpHD1GpmvcQjeN6Yrv6c9eIl6gB4nU3isN7bn+LmoVTpH1gHViaV2YyG/zXj4z4h7r+V+ezesMqqorEg38BCNUHNmhnw4/C0I4gBAQ4x0SJOGnfKGZKR9yzvbkJtvEn732JcEZCbdTZmaxkwlenXvM+mStcJaxBCB/h5xJ5VOF5nDbTPzLphIpzddr3zx/Jxjna9QB1v/YSKYXn+iuwruNUXGCvvxBWaBGKrjOdRTRy9adWOgNmtuYDQJ2YOfG8PtPe06ELKjmr2CfaAGrKKUroyaGvy3qxAV0PlT+UQ4ADSXWt/zl0o5wIDAQAB";

            Map<String, String> params = new HashMap<String, String>(10);
            params.put("mch_id", wxPayApiConfig.getMchId());
            params.put("partner_trade_no", System.currentTimeMillis() + "");
            params.put("nonce_str", System.currentTimeMillis() + "");
            //收款方银行卡号
            params.put("enc_bank_no", RsaKit.encryptByPublicKeyByWx("银行卡号", PUBLIC_KEY));
            //收款方用户名
            params.put("enc_true_name", RsaKit.encryptByPublicKeyByWx("银行卡持有人姓名", PUBLIC_KEY));
            //收款方开户行
            params.put("bank_code", "1001");
            params.put("amount", "1");
            params.put("desc", "IJPay 让支付触手可及-付款到银行卡");
            params.put("sign", WxPayKit.createSign(params, wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256));
            return WxPayApi.payBank(params, wxPayApiConfig.getCertPath(), wxPayApiConfig.getMchId());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 查询企业付款到银行
     */
    @RequestMapping(value = "/wxPay/queryBank", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public String queryBank(@RequestParam("partner_trade_no") String partnerTradeNo) {
        try {
            WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

            Map<String, String> params = new HashMap<String, String>(4);
            params.put("mch_id", wxPayApiConfig.getMchId());
            params.put("partner_trade_no", partnerTradeNo);
            params.put("nonce_str", System.currentTimeMillis() + "");
            params.put("sign", WxPayKit.createSign(params, wxPayApiConfig.getPartnerKey(), SignType.MD5));
            return WxPayApi.queryBank(params, wxPayApiConfig.getCertPath(), wxPayApiConfig.getMchId());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 微信退款，第一步提交退款申请
     */
    @PostMapping(value = "/wxPay/refund")
//    @Async
    public Map<String, Object> refund(@RequestBody Map<String, String> map) {
//       // PayCertificate payCertificate = BeanUtil.mapToBean(map, PayCertificate.class, false);
        PayCertificate payCertificate = payCertificateServiceInterface.findPayCertificateById(Long.valueOf(map.get("payCertificateId")));
        /** 订单校验 */
        Assert.notNull(payCertificate, "支付凭证不存在！不能退款");
        Assert.isTrue(OperationConstant.ONE != payCertificate.getRefund(), "支付凭证已退款，不能重复退款");
        Assert.isTrue(OperationConstant.TWO == payCertificate.getPayStatus(), "支付未完成，不能退款");

        UserOrder userOrder = userOrderServiceInterface.findUserOrderById(payCertificate.getOrderId());
        String outTradeNo = userOrder.getBizOrderNo();
        String transactionId = userOrder.getTransactionId();
        if (StringUtils.isBlank(outTradeNo) && StringUtils.isBlank(transactionId)) {
            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("return_code", "FAIL");
            resultMap.put("return_msg", "订单号不存在");
            return resultMap;
        }
        ApplyPayRefundDto applyPayRefundDto = new ApplyPayRefundDto();
        applyPayRefundDto.setPayCertificateId(payCertificate.getId());
        applyPayRefundDto.setPayType(WXPAY_CHANNEL_CODE);
        applyPayRefundDto.setCheckStatus(OperationConstant.ONE);
//        applyPayRefundDto.setRefundStatus("WAIT");
        ApplyPayRefund applyPayRefund = applyPayRefundFeign.findApplyPayRefundByCondition(applyPayRefundDto);
        Assert.notNull(applyPayRefund, "支付退款申请记录不存在，无法向微信申请退款");

        PayChannelConfigDto payChannelConfigDto = new PayChannelConfigDto();
        payChannelConfigDto.setCode(WXPAY_CHANNEL_CODE);
        PayChannelConfig payChannelConfig = payChannelConfigServiceInterface.findPayChannelConfigByCondition(payChannelConfigDto);

        Map<String, String> params = RefundModel.builder()
                .appid(payChannelConfig.getWxAppId())
                .mch_id(payChannelConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .transaction_id(transactionId)
                .out_trade_no(outTradeNo)
                .out_refund_no(applyPayRefund.getRefundNo())
                .total_fee(payCertificate.getPayment().multiply(new BigDecimal(100)).intValue() + "")
                .refund_fee(userOrder.getActualPayAmount().multiply(new BigDecimal(100)).intValue() + "")
                .notify_url(payChannelConfig.getRefundNotifyUrl())
                .build()
                .createSign(payChannelConfig.getWxKey(), SignType.MD5);

//        String certPath = ClassUtils.getDefaultClassLoader().getResource("").getPath() + "weixin_qawsxedc_cert/apiclient_cert.p12";
//        String certPath = "sptg-pay/sptg-pay-biz/src/main/resources/weixin_qawsxedc_cert/apiclient_cert.p12";
        ApplicationHome home = new ApplicationHome(getClass());
        File jar = home.getSource();
        String certPath = jar.getParentFile() + "/weixin_qawsxedc_cert/apiclient_cert.p12";
        String xmlMsg = WxPayApi.orderRefund(false, params, certPath, payChannelConfig.getMchId());
        Map<String, String> mapMsg = WxPayKit.xmlToMap(xmlMsg);
        Map<String, Object> resultMap = new HashMap<>();
        String jsonStr = JSON.toJSONString(mapMsg);
//        ApplyPayRefundDto payRefundDto = new ApplyPayRefundDto();
//        payRefundDto.setRefundNo(applyPayRefund.getRefundNo());
//        ApplyPayRefund payRefund = applyPayRefundFeign.findApplyPayRefundByCondition(payRefundDto);
//        payRefund.setThirdRefundApply(JSON.toJSONString(jsonStr));
//        applyPayRefundFeign.updateApplyPayRefundByRefundNo(payRefund);
        log.warn("wx_refund_info-------" + jsonStr);
        resultMap.put("return_code", mapMsg.get("return_code"));
        resultMap.put("return_msg", mapMsg.get("return_msg"));
        resultMap.put("third_refund_apply", JSON.toJSONString(jsonStr));
        return resultMap;
    }

    /**
     * 微信退款查询
     */
    @RequestMapping(value = "/wxPay/refundQuery", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public String refundQuery(@RequestParam("transactionId") String transactionId,
                              @RequestParam("out_trade_no") String outTradeNo,
                              @RequestParam("out_refund_no") String outRefundNo,
                              @RequestParam("refund_id") String refundId) {

        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

        Map<String, String> params = RefundQueryModel.builder()
                .appid(wxPayApiConfig.getAppId())
                .mch_id(wxPayApiConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .transaction_id(transactionId)
                .out_trade_no(outTradeNo)
                .out_refund_no(outRefundNo)
                .refund_id(refundId)
                .build()
                .createSign(wxPayApiConfig.getPartnerKey(), SignType.MD5);

        return WxPayApi.orderRefundQuery(false, params);
    }

    /**
     * 退款通知
     */
    @PostMapping(value = "/wxPay/refundNotify")
    @ResponseBody
    public String refundNotify(HttpServletRequest request) {
        String xmlMsg = HttpKit.readData(request);
        log.warn("退款通知=" + xmlMsg);
        /**
         * 返回的应答数据
         */
        Map<String, String> xml = new HashMap<String, String>(2);
        try {
            Map<String, String> param = WxPayKit.xmlToMap(xmlMsg);
            String returnCode = param.get("return_code");
            PayChannelConfigDto payChannelConfigDto = new PayChannelConfigDto();
            payChannelConfigDto.setCode(WXPAY_CHANNEL_CODE);
            PayChannelConfig payChannelConfig = this.payChannelConfigServiceInterface.findPayChannelConfigByCondition(payChannelConfigDto);
            if (WxPayKit.codeIsOk(returnCode)) {
                String reqInfo = param.get("req_info");
                String key = payChannelConfig.getWxKey();
                String decryptData = WxPayKit.decryptData(reqInfo, key);
                log.warn("退款通知解密后的数据=" + decryptData);
                Map<String, String> params = WxPayKit.xmlToMap(decryptData);
                String orderNo = params.get("out_trade_no");
                String refundRecvAccout = params.get("refund_recv_accout");
                String thridRefundId = params.get("refund_id");
                String refundFee = params.get("refund_fee");
                Date today = new Date();
                // 注意重复通知的情况，同一订单号可能收到多次通知，请注意一定先判断订单状态
                UserOrderDto userOrderDto = new UserOrderDto();
                userOrderDto.setBizOrderNo(orderNo);
                UserOrder order = userOrderServiceInterface.findUserOrderByCondition(userOrderDto);
                ApplyPayRefundDto dto = new ApplyPayRefundDto();
                dto.setOrderId(order.getId());
                ApplyPayRefund payRefund = applyPayRefundFeign.findApplyPayRefundByCondition(dto);
                String refundStatus = payRefund.getRefundStatus();
                if (SUCCESS.equals(refundStatus)) {
                    xml.put("return_code", "SUCCESS");
                    xml.put("return_msg", "OK");
                    return WxPayKit.toXml(xml);
                } else {
                    // 更新pay_refund_apply表数据
                    ApplyPayRefundDto applyPayRefundDto = new ApplyPayRefundDto();
                    applyPayRefundDto.setRefundNo(payRefund.getRefundNo());
                    ApplyPayRefund applyPayRefund = applyPayRefundFeign.findApplyPayRefundByCondition(applyPayRefundDto);
                    applyPayRefund.setRefundStatus(SUCCESS);
                    applyPayRefund.setRefundRecvAccout(refundRecvAccout);
                    applyPayRefund.setThridRefundId(thridRefundId);
                    applyPayRefund.setThirdRefundNotify(JSON.toJSONString(decryptData));
                    applyPayRefund.setUpdateDate(today);
                    Boolean updateApplyPayRefundBoolean = applyPayRefundFeign.updateApplyPayRefundByRefundNo(applyPayRefund);
                    if (!updateApplyPayRefundBoolean) {
                        log.warn("修改退款申请表pay_refund_apply失败");
                        xml.put("return_code", "FAIL");
                        xml.put("return_msg", "修改退款申请表pay_refund_apply失败");
                        return WxPayKit.toXml(xml);
                    }
                    //更新pay_certificate表数据
                    PayCertificate payCertificate = new PayCertificate();
                    payCertificate.setId(applyPayRefund.getPayCertificateId());
                    payCertificate.setRefund(OperationConstant.ONE);
                    ResultBean<Boolean> updatePayCertificateBoolean = payCertificateFeign.updatePayCertificate(payCertificate);
                    if (!updatePayCertificateBoolean.getMsg().equals("success")) {
                        log.warn("修改支付凭证表pay_certificate失败");
                        xml.put("return_code", "FAIL");
                        xml.put("return_msg", "修改支付凭证表pay_certificate失败");
                        return WxPayKit.toXml(xml);
                    }

                    //更新user_order表数据
                    UserOrder userOrder = new UserOrder();
                    userOrder.setId(applyPayRefund.getOrderId());
                    userOrder.setRefund(OperationConstant.ONE);
                    userOrder.setRefundFee(new BigDecimal(refundFee).divide(new BigDecimal(100)));
                    userOrder.setUpdateDate(today);
                    userOrder.setRefundDate(today);
                    ResultBean<Boolean> updateUserOrderBoolean = userOrderServiceInterface.updateUserOrder(userOrder);
                    if (!updateUserOrderBoolean.getMsg().equals("success")) {
                        log.warn("修改订单表user_order失败");
                        xml.put("return_code", "FAIL");
                        xml.put("return_msg", "修改订单表user_order失败");
                        return WxPayKit.toXml(xml);
                    }
                    //更新user表，减去人脸套餐时间
                    User user = this.userFeign.findUserById(userOrder.getUserId());
                    FaceChargeProject faceChargeProject = this.faceChargeProjectFeign.getFaceChargeProjectById(userOrder.getGoodsId());
                    Date faceEndDate = user.getFaceEndDate();
                    Calendar calendar = Calendar.getInstance();
                    calendar.setTime(faceEndDate);
                    calendar.set(Calendar.DATE,calendar.get(Calendar.DATE)-faceChargeProject.getDays());
                    user.setFaceEndDate(calendar.getTime());
                    if (!this.userFeign.updateEntity(user)){
                        log.warn("修改住户表user失败");
                        xml.put("return_code", "FAIL");
                        xml.put("return_msg", "修改住户表use失败");
                        return WxPayKit.toXml(xml);
                    }
                    // 发送通知等
                    xml.put("return_code", "SUCCESS");
                    xml.put("return_msg", "OK");
                    return WxPayKit.toXml(xml);
                }
            } else {
                xml.put("return_code", "FAIL");
                xml.put("return_msg", "返回信息失败");
                return WxPayKit.toXml(xml);
            }
        } catch (Exception e) {
            // 发送通知等
            xml.put("return_code", "FAIL");
            xml.put("return_msg", e.getMessage());
            log.warn("退款回调异常：{}",e.getMessage());
            return WxPayKit.toXml(xml);
        }
    }

    @RequestMapping(value = "/wxPay/sendRedPack", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public String sendRedPack(HttpServletRequest request, @RequestParam("openId") String openId) {
        try {
            String ip = IpKit.getRealIp(request);
            if (StringUtils.isBlank(ip)) {
                ip = "127.0.0.1";
            }

            WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

            Map<String, String> params = SendRedPackModel.builder()
                    .nonce_str(WxPayKit.generateStr())
                    .mch_billno(WxPayKit.generateStr())
                    .mch_id(wxPayApiConfig.getMchId())
                    .wxappid(wxPayApiConfig.getAppId())
                    .send_name("IJPay 红包测试")
                    .re_openid(openId)
                    .total_amount("1000")
                    .total_num("1")
                    .wishing("感谢您使用 IJPay")
                    .client_ip(ip)
                    .act_name("感恩回馈活动")
                    .remark("点 start 送红包，快来抢!")
                    .build()
                    .createSign(wxPayApiConfig.getPartnerKey(), SignType.MD5);
            String result = WxPayApi.sendRedPack(params, wxPayApiConfig.getCertPath(), wxPayApiConfig.getMchId());
            System.out.println("发送红包结果:" + result);
            Map<String, String> map = WxPayKit.xmlToMap(result);
            return JSON.toJSONString(map);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 异步通知
     */
    @RequestMapping(value = "/wxPay/payNotify", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public String payNotify(HttpServletRequest request) {
        String xmlMsg = HttpKit.readData(request);
        log.warn("支付通知=" + xmlMsg);
        /**
         * 返回的应答数据
         */
        Map<String, String> xml = new HashMap<>(2);
        String returnStr = null;
        try {
            Map<String, String> params = WxPayKit.xmlToMap(xmlMsg);

            String returnCode = params.get("return_code");
            String out_trade_no = params.get("out_trade_no");
            String cash_fee = params.get("cash_fee");

            // 注意重复通知的情况，同一订单号可能收到多次通知，请注意一定先判断订单状态
            UserOrderDto userOrderDto = new UserOrderDto();
            userOrderDto.setBizOrderNo(out_trade_no);
            UserOrder userOrder = userOrderServiceInterface.findUserOrderByCondition(userOrderDto);
            if (userOrder.getPayStatus() == OperationConstant.TWO) {
                xml.put("return_code", "SUCCESS");
                xml.put("return_msg", "OK");
                returnStr = WxPayKit.toXml(xml);
                return returnStr;
            } else {
                // 注意此处签名方式需与统一下单的签名类型一致
                PayChannelConfigDto payChannelConfigDto = new PayChannelConfigDto();
                payChannelConfigDto.setCode(WXPAY_CHANNEL_CODE);
                PayChannelConfig payChannelConfig = this.payChannelConfigServiceInterface.findPayChannelConfigByCondition(payChannelConfigDto);
                Date today = new Date();
                if (WxPayKit.verifyNotify(params, payChannelConfig.getWxKey(), SignType.HMACSHA256)) {
                    if (WxPayKit.codeIsOk(returnCode)) {

                        /**
                         * 更新订单状态 支付状态 & 支付时间
                         */
                        if (!userOrder.getPayStatus().equals(OperationConstant.TWO)) {
                            userOrder.setPayStatus(OperationConstant.TWO);
                            userOrder.setTransactionId(params.get("transaction_id"));
                            userOrder.setPayTime(today);
                            userOrder.setPayType(WXPAY_CHANNEL_CODE);
                            userOrderServiceInterface.updateUserOrder(userOrder);
                            userOrderFeign.payFinish(userOrder);
                        }

                        /**
                         * 更新支付凭证的 支付状态 & 支付时间
                         */
                        PayCertificateDto payCertificateDto = new PayCertificateDto();
                        payCertificateDto.setChannelCode(WXPAY_CHANNEL_CODE);
                        payCertificateDto.setOrderNo(out_trade_no);
                        PayCertificate queryPayCertificate = payCertificateServiceInterface.findPayCertificateByCondition(payCertificateDto);
                        if (null != queryPayCertificate) {
                            queryPayCertificate.setPayStatus(OperationConstant.TWO);
                            queryPayCertificate.setPayTime(today);
                            queryPayCertificate.setPayment(new BigDecimal(cash_fee).divide(new BigDecimal(100)));
                            queryPayCertificate.setNotify(JSON.toJSONString(params));
                            this.payCertificateServiceInterface.updatePayCertificate(queryPayCertificate);
                        }

                        // 发送通知等
                        xml.put("return_code", "SUCCESS");
                        xml.put("return_msg", "OK");
                        return WxPayKit.toXml(xml);
                    } else {
                        xml.put("return_code", "FAIL");
                        xml.put("return_msg", "结果返回失败");
                        return WxPayKit.toXml(xml);
                    }
                } else {
                    xml.put("return_code", "FAIL");
                    xml.put("return_msg", "验证通知失败");
                    return WxPayKit.toXml(xml);
                }
            }
        } catch (Exception e) {
            // 发送通知等
            xml.put("return_code", "FAIL");
            xml.put("return_msg", e.getMessage());
            return WxPayKit.toXml(xml);
        }
    }
}
