package com.jeequan.jeepay.mch.ctrl.pos;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jeequan.jeepay.JeepayClient;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrderCount;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.DBApplicationConfig;
import com.jeequan.jeepay.core.model.OriginalRes;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.core.utils.JeepayKit;
import com.jeequan.jeepay.core.utils.SeqKit;
import com.jeequan.jeepay.db.entity.*;
import com.jeequan.jeepay.exception.JeepayException;
import com.jeequan.jeepay.mch.ctrl.CommonCtrl;
import com.jeequan.jeepay.mch.ctrl.paytest.PaytestController;
import com.jeequan.jeepay.model.DeviceInfo;
import com.jeequan.jeepay.model.PayOrderCreateResModel;
import com.jeequan.jeepay.model.RefundOrderCreateReqModel;
import com.jeequan.jeepay.model.RefundOrderCreateResModel;
import com.jeequan.jeepay.request.PayOrderCreateRequest;
import com.jeequan.jeepay.request.RefundOrderCreateRequest;
import com.jeequan.jeepay.response.PayOrderCreateResponse;
import com.jeequan.jeepay.response.RefundOrderCreateResponse;
import com.jeequan.jeepay.service.impl.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import static com.jeequan.jeepay.mch.ctrl.pos.PosSignUtil.sign;

/**
 * 扫码POS 接口管理类
 *
 * @author zx
 * @date 2022/3/4 9:43
 */
@Slf4j
@RestController
@RequestMapping("/api/pos")
public class PosController extends CommonCtrl {

    @Autowired private DeviceProvideConfigService deviceProvideConfigService;
    @Autowired private MchStoreDeviceService mchStoreDeviceService;
    @Autowired private MchInfoService mchInfoService;
    @Autowired private MchStoreService mchStoreService;
    @Autowired private MchAppService mchAppService;
    @Autowired private PayOrderService payOrderService;
    @Autowired private RefundOrderService refundOrderService;
    @Autowired private PayWayService payWayService;

    /** 设备初始化 **/
    @PostMapping("/deviceInit/{version}")
    public OriginalRes init(@PathVariable("version") String version) {

        JSONObject reqParams = getReqParamJSON();
        log.info("扫码POS初始化请求：{}", reqParams.toJSONString());

        // 验证通用请求字段、签名
        Object object = checkParamsWithSign(reqParams, false);
        if (object instanceof String) {
            return OriginalRes.ok(buildResFail((String) object));
        }

        JSONObject deviceJSON = (JSONObject) object;
        MchInfo mchInfo = deviceJSON.getObject("mchInfo", MchInfo.class);
        MchStore mchStore = deviceJSON.getObject("mchStore", MchStore.class);
        if (mchInfo == null) {
            log.error("系统异常，请联系管理员。设备绑定的商户不存在");
            return OriginalRes.ok(buildResFail("系统异常，请联系管理员"));
        }

        JSONObject result = new JSONObject();
        result.put("merchantName", mchStore.getStoreName());
        result.put("merchantId", mchInfo.getMchNo());

        return OriginalRes.ok(buildResSuccess(result,reqParams.getString("key")));
    }

    /** 设备支付 **/
    @PostMapping("/pay/{version}")
    public OriginalRes pay(@PathVariable("version") String version) {

        JSONObject reqParams = getReqParamJSON();
        log.info("扫码POS支付请求：{}", reqParams.toJSONString());

        // 验证通用请求字段、签名
        Object object = checkParamsWithSign(reqParams, true, "pay");
        if (object instanceof String) {
            return OriginalRes.ok(buildResFail((String) object));
        }

        JSONObject deviceJSON = (JSONObject) object;

        try {
            PayOrderCreateResModel resModel = doPay(reqParams.getJSONObject("data"), deviceJSON);

            JSONObject result = new JSONObject();
            result.put("cxOrderNum", resModel.getPayOrderId());
            result.put("payAmount", reqParams.getJSONObject("data").getString("payAmount"));
            result.put("receiveAmount", reqParams.getJSONObject("data").getString("payAmount"));
            result.put("payableAmount", reqParams.getJSONObject("data").getString("payAmount"));
            if (resModel.getOrderState() == 2) {  // 支付成功
                result.put("payStatus", 10);
            } else if (resModel.getOrderState() == 0) {
                result.put("payStatus", 0);
            } else if (resModel.getOrderState() == 1) {
                result.put("payStatus", 5);
            }

            return OriginalRes.ok(buildResSuccess(result,reqParams.getString("key")));
        }catch (BizException e) {
            log.error("扫码POS下单异常：", e);
            return OriginalRes.ok(buildResFail(e.getMessage()));
        }
    }

    /** 支付查单 **/
    @PostMapping("/orderDetail/{version}")
    public OriginalRes query(@PathVariable("version") String version) {

        JSONObject reqParams = getReqParamJSON();
        log.info("扫码POS查单请求：{}", reqParams.toJSONString());

        if (StringUtils.isBlank(reqParams.getString("data"))) {
            return OriginalRes.ok(buildResFail("订单号不能为空"));
        }

        // 验证通用请求字段、签名
        Object object = checkParamsWithSign(reqParams, true, "orderDetail");
        if (object instanceof String) {
            return OriginalRes.ok(buildResFail((String) object));
        }

        JSONObject deviceJSON = (JSONObject) object;
        MchInfo mchInfo = deviceJSON.getObject("mchInfo", MchInfo.class);
        String provider = deviceJSON.getString("provider");

        // 查询订单号，退款前查单为交易凭证商户单号platformMchOrderNo，其他为payOrderId
        String cxOrderNum;
        if (provider.equals(CS.DEVICE_PROVIDER.PS)) {
            String dataStr = reqParams.getString("data").replace("\\", "");
            cxOrderNum = JSONObject.parseObject(dataStr).getString("deviceOrderNum");;
        }else {
            cxOrderNum = reqParams.getJSONObject("data").getString("cxOrderNum");
        }
        if (StringUtils.isBlank(cxOrderNum)) {
            return OriginalRes.ok(buildResFail("订单号不能为空"));
        }

        PayOrder payOrder;

        // queryEntryType 可为空，订单查询类型：
        // 默认 payOrder：使用支付订单查询，一般指设备支付接口返回的cxOrderNum字段
        // mchOrder：使用商户订单号查询，一般指设备支付接口的deviceOrderNum字段
        // beforeRefund：扫码退款或输入订单号退款，一般指交易凭证上的商户单号
        String queryEntryType = reqParams.getJSONObject("data").getString("queryEntryType");

        if (StringUtils.isNotBlank(queryEntryType) && queryEntryType.equals("beforeRefund")) {
            // 订单信息
            List<PayOrder> payOrderList = payOrderService.list(PayOrder.gw().eq(PayOrder::getPlatformMchOrderNo, cxOrderNum));
            if (CollUtil.isEmpty(payOrderList)) {
                return OriginalRes.ok(buildResFail("订单不存在"));
            }
            if (payOrderList.size() > 1) {
                return OriginalRes.ok(buildResFail("当前订单不支持扫码退款，可前往订单列表查询并完成退款"));
            }
            payOrder = payOrderList.get(0);

        }else if (StringUtils.isNotBlank(queryEntryType) && queryEntryType.equals("mchOrder")) {

            // 订单信息
            payOrder = payOrderService.queryMchOrder(mchInfo.getMchNo(), null, cxOrderNum);
            if (payOrder == null) {
                return OriginalRes.ok(buildResFail("订单不存在"));
            }

        }else {
            payOrder = payOrderService.queryMchOrder(mchInfo.getMchNo(), cxOrderNum, null);
            if (payOrder == null) {
                return OriginalRes.ok(buildResFail("订单不存在"));
            }
        }

        JSONObject result = new JSONObject();
        result.put("cxOrderNum", payOrder.getPayOrderId());
        result.put("payAmount", AmountUtil.convertCent2Dollar(payOrder.getAmount()));
        result.put("merchantReceiveAmount", AmountUtil.convertCent2Dollar(payOrder.getAmount()));
        result.put("payableAmount", AmountUtil.convertCent2Dollar(payOrder.getAmount()));
        result.put("createdAt", payOrder.getCreatedAt());
        if (payOrder.getState() == 2) {  // 支付成功
            result.put("payStatus", 10);
        } else if (payOrder.getState() == 0) {
            result.put("payStatus", 0);
        } else if (payOrder.getState() == 1) {
            result.put("payStatus", 5);
        } else if (payOrder.getState() == 3) {
            result.put("payStatus", 15);
        }

        PayWay payWay = payWayService.getById(payOrder.getWayCode());
        if (payWay.getWayType().equals(CS.PAY_WAY_CODE_TYPE.ALIPAY)) {  // 支付成功
            result.put("payType", 1);
        } else if (payWay.getWayType().equals(CS.PAY_WAY_CODE_TYPE.WECHAT)) {
            result.put("payType", 2);
        } else if (payWay.getWayType().equals(CS.PAY_WAY_CODE_TYPE.YSFPAY)) {
            result.put("payType", 3);
        } else {
            result.put("payType", 4);
        }

        return OriginalRes.ok(buildResSuccess(result,reqParams.getString("key")));
    }

    /** 退款 **/
    @PostMapping("/refund/{version}")
    public OriginalRes refund(@PathVariable("version") String version) {

        JSONObject reqParams = getReqParamJSON();
        log.info("扫码POS退款请求：{}", reqParams.toJSONString());

        // 验证通用请求字段、签名
        Object object = checkParamsWithSign(reqParams, true);
        if (object instanceof String) {
            return OriginalRes.ok(buildResFail((String) object));
        }

        JSONObject deviceJSON = (JSONObject) object;

        try {
            // 调起退款
            RefundOrderCreateResModel resModel = doRefund(reqParams.getJSONObject("data"), deviceJSON);

            JSONObject result = new JSONObject();
            result.put("cxOrderNum", reqParams.getJSONObject("data").getString("cxOrderNum"));
            result.put("refundOrderNum", resModel.getRefundOrderId());
            result.put("refundAmount", AmountUtil.convertCent2Dollar(resModel.getRefundAmount()));
            result.put("refundStatus", resModel.getState());
            result.put("payAmount", resModel.getPayAmount());

            return OriginalRes.ok(buildResSuccess(result,reqParams.getString("key")));
        }catch (BizException e) {
            log.error("扫码POS退款异常：", e);
            return OriginalRes.ok(buildResFail(e.getMessage()));
        }
    }

    /** 退款订单查单 **/
    @PostMapping("/queryRefund/{version}")
    public OriginalRes queryRefund(@PathVariable("version") String version) {

        JSONObject reqParams = getReqParamJSON();
        log.info("扫码POS退款查单请求：{}", reqParams.toJSONString());

        // 验证通用请求字段、签名
        Object object = checkParamsWithSign(reqParams, true);
        if (object instanceof String) {
            return OriginalRes.ok(buildResFail((String) object));
        }

        String refundOrderNum = reqParams.getJSONObject("data").getString("refundOrderNum");
        if (StringUtils.isBlank(refundOrderNum)) {
            return OriginalRes.ok(buildResFail("退款单号不能为空"));
        }

        RefundOrder refundOrder = refundOrderService.getById(refundOrderNum);

        JSONObject result = new JSONObject();
        result.put("refundOrderNum", refundOrderNum);
        result.put("refundAmount", AmountUtil.convertCent2Dollar(refundOrder.getRefundAmount()));
        result.put("refundStatus", refundOrder.getState());
        result.put("createdAt", refundOrder.getCreatedAt());

        return OriginalRes.ok(buildResSuccess(result,reqParams.getString("key")));
    }

    /** 汇总查询 **/
    @PostMapping("/statictics/{version}")
    public OriginalRes statictics(@PathVariable("version") String version) {

        JSONObject reqParams = getReqParamJSON();
        log.info("扫码POS汇总查询请求：{}", reqParams.toJSONString());

        // 验证通用请求字段、签名
        Object object = checkParamsWithSign(reqParams, true);
        if (object instanceof String) {
            return OriginalRes.ok(buildResFail((String) object));
        }

        JSONObject deviceJSON = (JSONObject) object;
        MchInfo mchInfo = deviceJSON.getObject("mchInfo", MchInfo.class);

        JSONObject dataJSON = reqParams.getJSONObject("data");
        if (StringUtils.isBlank(dataJSON.getString("queryDate"))) {
            return OriginalRes.ok(buildResFail("queryDate不能为空"));
        }

        try {
            // 时间范围
            Date[] dateRange = getQueryDate(dataJSON.getString("queryDate"));
            String createStart = null;
            String createEnd = null;
            if (dateRange != null) {
                createStart = DateUtil.formatDateTime(dateRange[0]);
                createEnd = DateUtil.formatDateTime(dateRange[1]);
            }

            // 成功交易统计
            PayOrderCount payCountMap = payOrderService.payCount(mchInfo.getMchNo(), null, null, null, PayOrder.STATE_SUCCESS,
                    null, createStart, createEnd, null, null);
            // 退款交易统计
            PayOrderCount refundCountMap = payOrderService.payCount(mchInfo.getMchNo(), null, null, null, null,
                    PayOrder.REFUND_STATE_NONE, createStart, createEnd, null, null);

            JSONObject result = new JSONObject();
            result.put("totalPayCount", payCountMap.getTotalNum());
            result.put("totalPayAmount", payCountMap.getTotalAmt());
            result.put("totalRefundCount", refundCountMap.getTotalNum());
            result.put("totalRefundAmount", refundCountMap.getTotalRefundAmt());

            return OriginalRes.ok(buildResSuccess(result,reqParams.getString("key")));

        } catch (BizException e) {
            log.error("pos机汇总查询异常：", e);
            return OriginalRes.ok(buildResFail(e.getMessage()));
        }
    }

    /** 交易查询 **/
    @PostMapping("/payList/{version}")
    public OriginalRes payList(@PathVariable("version") String version) {

        JSONObject reqParams = getReqParamJSON();
        log.info("扫码POS交易查询请求：{}", reqParams.toJSONString());

        // 验证通用请求字段、签名
        Object object = checkParamsWithSign(reqParams, true);
        if (object instanceof String) {
            return OriginalRes.ok(buildResFail((String) object));
        }

        JSONObject deviceJSON = (JSONObject) object;
        MchAppEntity mchAppEntity = deviceJSON.getObject("mchApp", MchAppEntity.class);

        JSONObject dataJSON = reqParams.getJSONObject("data");
        if (dataJSON.getInteger("pageNumber") == null) {
            return OriginalRes.ok(buildResFail("pageNumber不能为空"));
        }
        if (dataJSON.getInteger("pageSize") == null) {
            return OriginalRes.ok(buildResFail("pageSize不能为空"));
        }

        try {
            Date[] dateRange = getQueryDate(dataJSON.getString("queryDate"));

            LambdaQueryWrapper<PayOrder> gw = PayOrder.gw();
            gw.orderByDesc(PayOrder::getCreatedAt);
            gw.eq(PayOrder::getMchNo, mchAppEntity.getMchNo());
            gw.in(PayOrder::getState, Arrays.asList(PayOrder.STATE_SUCCESS, PayOrder.STATE_REFUND));

            gw.select(PayOrder::getPayOrderId, PayOrder::getState, PayOrder::getWayCodeType, PayOrder::getAmount, PayOrder::getRefundAmount, PayOrder::getCreatedAt);

            // 时间范围条件
            if (dateRange != null && dateRange[0] != null) {
                gw.ge(PayOrder::getCreatedAt, dateRange[0]);
            }
            if (dateRange != null && dateRange[1] != null) {
                gw.le(PayOrder::getCreatedAt, dateRange[1]);
            }

            // 分页
            IPage page = new Page(dataJSON.getInteger("pageNumber"), dataJSON.getInteger("pageSize"));

            IPage iPage = payOrderService.page(page, gw);

            JSONObject result = new JSONObject();
            result.put("records", iPage.getRecords());  //记录明细
            result.put("total", iPage.getTotal());  //总条数
            result.put("current", iPage.getCurrent());  //当前页码
            result.put("hasNext", iPage.getPages() > iPage.getCurrent() );  //是否有下一页

            return OriginalRes.ok(buildResSuccess(result,reqParams.getString("key")));

        } catch (BizException e) {
            log.error("pos机异常：", e);
            return OriginalRes.ok(buildResFail(e.getMessage()));
        }
    }

    // 校验数据 + 验签
    private Object checkParamsWithSign(JSONObject reqParams, boolean isCheckMchNo, String apiType) {
        String appId = reqParams.getString("appId");
        if (StringUtils.isBlank(appId)) {
            return "appId不能为空";
        }
        String deviceId = reqParams.getString("deviceId");
        if (StringUtils.isBlank(deviceId)) {
            return "deviceId不能为空";
        }
//        String merchantId = reqParams.getString("merchantId");
        // 只有初始化接口不校验此参数
//        if (isCheckMchNo && StringUtils.isBlank(merchantId)) {
//            return "merchantId不能为空";
//        }
        String requestTime = reqParams.getString("requestTime");
        if (StringUtils.isBlank(requestTime)) {
            return "requestTime不能为空";
        }
        String sign = reqParams.getString("sign");
        if (StringUtils.isBlank(sign)) {
            return "签名错误";
        }

        DeviceProvideConfig deviceProvideConfig = deviceProvideConfigService.getOne(DeviceProvideConfig.gw().eq(DeviceProvideConfig::getAppId, appId));
        if (deviceProvideConfig == null) {
            return "appId不正确！";
        }

        // 验签
        JSONObject configJSON = JSONObject.parseObject(deviceProvideConfig.getProviderParams());
        String provider = deviceProvideConfig.getProvider();
        // 为了兼容品生的扫码pos，针对品生科技的支付和查单接口，验签时需要特殊处理
        if(CS.DEVICE_PROVIDER.PS.equalsIgnoreCase(provider)) {
            if("pay".equals(apiType)) {
                // 支付接口,如果有productNum字段则去掉
                reqParams.remove("productNum");
            }else if("orderDetail".equals(apiType)) {
                // 查询支付接口, data数据需要加 \" {\"deviceOrderNum\":\"P1514055777637289986\"}
                JSONObject dataObj = reqParams.getJSONObject("data");
                String deviceOrderNum = dataObj.getString("deviceOrderNum");
                if(StringUtils.isNotBlank(deviceOrderNum)) {
                    reqParams.put("data", "{\\\"deviceOrderNum\\\":\\\""+deviceOrderNum+"\\\"}");
                }
            }
        }

        if (!PosSignUtil.verify(reqParams, configJSON.getString("appSecret"))) {
            return "签名错误";
        }

        // 扫码POS设备信息
        MchStoreDevice mchStoreDevice = mchStoreDeviceService.getOne(MchStoreDevice.gw()
                .eq(MchStoreDevice::getProvider, deviceProvideConfig.getProvider())
                .eq(MchStoreDevice::getDeviceType, MchStoreDevice.DEVICE_TYPE_POS)
                .eq(MchStoreDevice::getDeviceNo, deviceId)
        );
        if (mchStoreDevice == null) {
            return "该设备没有入库";
        }else if(mchStoreDevice.getState() != CS.YES) {
            return "该设备已停用";
        }else if (mchStoreDevice.getBindState() != CS.YES) {
            return "该设备没有绑定商户";
        }
        String mchNo = mchStoreDevice.getMchNo();
        MchInfo mchInfo = mchInfoService.getById(mchNo);
        if (mchInfo == null) {
            return "设备绑定商户不存在";
        }else if(mchInfo.getState() != CS.YES) {
            return "当前商户已停用";
        }

        String mchAppId = mchStoreDevice.getAppId();
        MchAppEntity mchAppEntity = mchAppService.getById(mchAppId);
        if (mchAppEntity == null) {
            return "设备绑定应用不存在";
        }else if(mchAppEntity.getState() != CS.YES) {
            return "设备绑定应用已停用";
        }

        String storeId = mchStoreDevice.getStoreId();
        MchStore mchStore = mchStoreService.getById(storeId);
        if (mchStore == null) {
            return "设备绑定门店不存在";
        }

        JSONObject deviceParamsJSON = new JSONObject();
        deviceParamsJSON.put("mchInfo", mchInfo);
        deviceParamsJSON.put("mchStore", mchStore);
        deviceParamsJSON.put("mchApp", mchAppEntity);
        deviceParamsJSON.put("provider", provider);
        deviceParamsJSON.put("deviceNo", mchStoreDevice.getDeviceNo());
        reqParams.put("key",configJSON.getString("appSecret"));
        return deviceParamsJSON;
    }

    // 校验数据 + 验签
    private Object checkParamsWithSign(JSONObject reqParams, boolean isCheckMchNo) {
        return checkParamsWithSign(reqParams, isCheckMchNo, "");
    }

    // 获取日期搜索条件
    private Date[] getQueryDate(String queryDateStr) {

        if (StringUtils.isBlank(queryDateStr)) {
            return null;
        }

        String[] dateArray = queryDateStr.split("-");
        if(dateArray.length == 2){ // 月份

            queryDateStr += "-01";
            Date queryDate = DateUtil.parseDate(queryDateStr);

            return new Date[]{ DateUtil.beginOfMonth(queryDate), DateUtil.endOfMonth(queryDate) };

        } else if(dateArray.length == 3){ // 日期

            Date queryDate = DateUtil.parseDate(queryDateStr);
            return new Date[]{ DateUtil.beginOfDay(queryDate), DateUtil.endOfDay(queryDate) };

        } else { // 格式错误
            throw new BizException("查询时间参数有误");
        }
    }

    private JSONObject buildResSuccess(JSONObject result,String key) {
        JSONObject resJSON = new JSONObject();
        resJSON.put("code", "00000");
        resJSON.put("message", "success");
        resJSON.put("result", result.toString());

        log.info("扫码POS响应结果：" + resJSON.toJSONString());
        String sign = sign(resJSON, key);
        resJSON.put("sign",sign);
        return resJSON;
    }

    private JSONObject buildResFail(String message) {

        JSONObject resJSON = new JSONObject();
        resJSON.put("code", "99999");
        resJSON.put("message", message);

        log.info("扫码POS响应结果：" + resJSON.toJSONString());

        return resJSON;
    }

    // 支付
    private PayOrderCreateResModel doPay(JSONObject reqParams, JSONObject deviceJSON) {

        // 条码不能为空
        if (StringUtils.isBlank(reqParams.getString("qrCode"))) {
            throw new BizException("qrCode不能为空");
        }

        // 支付金额不能为空
        if (StringUtils.isBlank(reqParams.getString("payAmount"))) {
            throw new BizException("payAmount不能为空");
        }

        // 下单参数
        String amount = AmountUtil.convertDollar2Cent(reqParams.getString("payAmount"));
        String wayCode = JeepayKit.getPayWayCodeByBarCode(reqParams.getString("qrCode"));
        String authCode = reqParams.getString("qrCode"); // 付款码

        MchAppEntity mchAppEntity = deviceJSON.getObject("mchApp", MchAppEntity.class);
        MchStore mchStore = deviceJSON.getObject("mchStore", MchStore.class);

        PayOrderCreateRequest request = new PayOrderCreateRequest();
        PaytestController.PayOrderCreateReqModelAddStoreId model = new PaytestController.PayOrderCreateReqModelAddStoreId();
        model.setPas(SysConfigService.PLATFORM_API_SECRET); // 通信秘钥
        request.setBizModel(model);

        model.setMchNo(mchAppEntity.getMchNo()); // 商户号
        model.setAppId(mchAppEntity.getAppId());
        model.setStoreId(mchStore.getStoreId());
        model.setMchOrderNo(StringUtils.defaultString(reqParams.getString("deviceOrderNum"), SeqKit.genMhoOrderId()));
        model.setWayCode(wayCode);
        model.setAmount(Long.valueOf(amount));
        model.setCurrency("CNY");
        model.setClientIp(getClientIp());
        model.setSubject( "扫码终端收银");
        model.setBody( "扫码终端收银");

        // model.setDeviceNo(deviceJSON.getString("deviceNo"));
        // model.setDeviceType(PayOrder.DEVICE_TYPE_SCAN_POS);
        DeviceInfo deviceInfo = new DeviceInfo();
        deviceInfo.setDeviceType(PayOrder.DEVICE_TYPE_SCAN_POS);
        deviceInfo.setDeviceNo(deviceJSON.getString("deviceNo"));
        deviceInfo.setProvider(deviceJSON.getString("provider"));
        model.setDeviceInfo(deviceInfo);

        DBApplicationConfig dbApplicationConfig = sysConfigService.getDBApplicationConfig();

        model.setNotifyUrl(dbApplicationConfig.getMchSiteUrl() + "/api/anon/paytestNotify/payOrder"); //回调地址

        //设置扩展参数
        JSONObject extParams = new JSONObject();
        extParams.put("authCode", authCode.trim());
        model.setChannelExtra(extParams.toString());
        JeepayClient jeepayClient = new JeepayClient(dbApplicationConfig.getPaySiteUrl(), mchAppEntity.getAppSecret());

        try {
            PayOrderCreateResponse response = jeepayClient.execute(request);
            // 请求失败
            if(response.getCode() != 0){
                throw new BizException(StringUtils.isNotBlank(response.getMsg()) ? response.getMsg(): "请求失败");
            }

            // 支付失败
            PayOrderCreateResModel resModel = response.get();
            if (!response.isSuccess(mchAppEntity.getAppSecret())) {
                throw new BizException(resModel.getErrMsg());
            }

            return response.get();
        } catch (JeepayException e) {
            throw new BizException(e.getMessage());
        }
    }

    // 退款
    private RefundOrderCreateResModel doRefund(JSONObject data, JSONObject deviceJSON) {

        if (data == null) {
            throw new BizException("data不能为空");
        }
        // 订单号不能为空
//        if (StringUtils.isBlank(data.getString("cxOrderNum"))) {
//            throw new BizException("cxOrderNum不能为空");
//        }
//        // 退款密码
//        if (StringUtils.isBlank(data.getString("refundPwd"))) {
//            throw new BizException("refundPwd不能为空");
//        }

        // 退款权限
        MchInfo mchInfo = deviceJSON.getObject("mchInfo", MchInfo.class);
        if (!mchInfo.getRefundMode().contains(MchInfo.REFUND_MODEL_PLAT)) {
            throw new BizException("无退款权限");
        }
//        //验证当前密码是否正确
//        if(StringUtils.isEmpty(mchInfo.getSipw())){
//            throw new BizException("当前未设置支付密码，请进入平台[系统管理-系统配置-安全管理]设置支付密码");
//        }
//
//        if(!new BCryptPasswordEncoder().matches(data.getString("refundPwd"), mchInfo.getSipw())){
//            throw new BizException("支付密码验证失败！");
//        }

        // 订单信息
        String cxOrderNum = data.getString("cxOrderNum");
        //支付宝账单上的商户订单号
        String cxMchOrderNo = data.getString("cxMchOrderNo");
        BigDecimal refundAmt = data.getBigDecimal("refundAmt");
        PayOrder payOrder = null;
        if(StringUtils.isNotEmpty(cxOrderNum)){
            payOrder = payOrderService.getById(cxOrderNum);
        }else if(StringUtils.isNotEmpty(cxMchOrderNo)){
            payOrder = payOrderService.getPlatformMchOrderNo(cxMchOrderNo);
        }else{
            throw new BizException("cxOrderNum或cxMchOrderNo不能为空");
        }
        if (payOrder == null || payOrder.getState() != PayOrder.STATE_SUCCESS || !payOrder.getMchNo().equals(mchInfo.getMchNo())) {
            throw new BizException("订单信息错误");
        }
        long useRefundAmt = refundAmt == null ? BigDecimal.ZERO.longValue() + payOrder.getRefundAmount() : refundAmt.multiply(BigDecimal.valueOf(100)).longValue() + payOrder.getRefundAmount() ;
        if(useRefundAmt > payOrder.getAmount()){
            throw new BizException("退款金额超出订单剩余可退金额");
        }

        MchAppEntity mchAppEntity = deviceJSON.getObject("mchApp", MchAppEntity.class);

        RefundOrderCreateRequest request = new RefundOrderCreateRequest();
        RefundOrderCreateReqModel model = new RefundOrderCreateReqModel();
        model.setPas(SysConfigService.PLATFORM_API_SECRET); // 通信秘钥
        request.setBizModel(model);

        model.setMchNo(mchAppEntity.getMchNo());     // 商户号
        model.setAppId(mchAppEntity.getAppId()); // 商户应用
        model.setPayOrderId(payOrder.getPayOrderId());
        model.setMchRefundNo(SeqKit.genMhoOrderId());
        model.setRefundAmount(refundAmt != null ? refundAmt.multiply(BigDecimal.valueOf(100)).longValue() : payOrder.getAmount() - useRefundAmt); // 退款金额
        model.setRefundReason("扫码终端退款");
        model.setCurrency("CNY");

        JeepayClient jeepayClient = new JeepayClient(sysConfigService.getDBApplicationConfig().getPaySiteUrl(), mchAppEntity.getAppSecret());

        try {
            RefundOrderCreateResponse response = jeepayClient.execute(request);

            // 请求失败
            if(response.getCode() != 0){
                throw new BizException(StringUtils.isNotBlank(response.getMsg()) ? response.getMsg(): "请求失败");
            }

            // 退款失败
            RefundOrderCreateResModel resModel = response.get();
            if (!response.isSuccess(mchAppEntity.getAppSecret())) {
                throw new BizException(resModel.getErrMsg());
            }

            return response.get();
        } catch (JeepayException e) {
            throw new BizException(e.getMessage());
        }
    }

    public static void main(String[] args) {
        String appId = "5c93bb17952842e1afde1ad5ff461b18";
        String key = "2fbgmc9otniikpc0mgmuav7s25lfpr5m9d0z8aqy0ylu86sex7w7cj11nqn6p3buxvkoh5eyxygbrbny158spnunx0d1ub75ii5lr72ipx7jhew67fh8gmq08tylwj54";

        // initTest(appId, key); // 设备初始化
        // payTest(appId, key); // 设备支付
        // payQueryTest(appId, key); // 订单查询
        // refundTest(appId, key); // 退款
        // refundQueryTest(appId, key); // 退款查单
        statisticsTest(appId, key); // 汇总查询
        // orderQueryTest(appId, key); // 交易查询（查询成功或退款）
    }

    private static void initTest(String appId, String key) {
        JSONObject reqJSON = getCommonJSON(appId);
        reqJSON.put("sign", sign(reqJSON, key));
        log.info("请求参数：{}", reqJSON.toJSONString());
        PosSignUtil.verify(reqJSON, key);
    }

    private static void payTest(String appId, String key) {
        JSONObject reqJSON = getCommonJSON(appId);
        JSONObject data = new JSONObject();
        data.put("qrCode", "286120303995188105");
        data.put("payType", 1);
        data.put("payAmount", "0.01");
        data.put("deviceOrderNum", "A999999");
        reqJSON.put("data", data.toJSONString());
        reqJSON.put("sign", sign(reqJSON, key));
        log.info("请求参数：{}", reqJSON.toJSONString());
        PosSignUtil.verify(reqJSON, key);
    }

    private static void payQueryTest(String appId, String key) {

        JSONObject reqJSON = getCommonJSON(appId);
        JSONObject data = new JSONObject();
        data.put("cxOrderNum", "P1514893239079043074");
        reqJSON.put("data", data.toJSONString());
        reqJSON.put("sign", sign(reqJSON, key));
        log.info("请求参数：{}", reqJSON.toJSONString());
        PosSignUtil.verify(reqJSON, key);
    }

    private static void refundTest(String appId, String key) {
        JSONObject reqJSON = getCommonJSON(appId);
        JSONObject data = new JSONObject();
        data.put("cxOrderNum", "P1514894810143686657");
        data.put("refundPwd", "123456");
        reqJSON.put("data", data.toJSONString());
        reqJSON.put("sign", sign(reqJSON, key));
        log.info("请求参数：{}", reqJSON.toJSONString());
        PosSignUtil.verify(reqJSON, key);
    }

    private static void refundQueryTest(String appId, String key) {
        JSONObject reqJSON = getCommonJSON(appId);
        JSONObject data = new JSONObject();
        data.put("refundOrderNum", "R1514905832518746113");
        reqJSON.put("data", data.toJSONString());
        reqJSON.put("sign", sign(reqJSON, key));
        log.info("请求参数：{}", reqJSON.toJSONString());
        PosSignUtil.verify(reqJSON, key);
    }

    private static void statisticsTest(String appId, String key) {
        JSONObject reqJSON = getCommonJSON(appId);
        JSONObject data = new JSONObject();
        data.put("queryDate", "2022-04-11");
        reqJSON.put("data", data.toJSONString());
        reqJSON.put("sign", sign(reqJSON, key));
        log.info("请求参数：{}", reqJSON.toJSONString());
        PosSignUtil.verify(reqJSON, key);
    }

    private static void orderQueryTest(String appId, String key) {
        JSONObject reqJSON = getCommonJSON(appId);
        JSONObject data = new JSONObject();
        data.put("pageNumber", "1");
        data.put("pageSize", "10");
        data.put("queryDate", "2022-04");
        reqJSON.put("data", data.toJSONString());
        reqJSON.put("sign", sign(reqJSON, key));
        log.info("请求参数：{}", reqJSON.toJSONString());
        PosSignUtil.verify(reqJSON, key);
    }

    public static JSONObject getCommonJSON(String appId) {
        JSONObject reqJSON = new JSONObject();
        reqJSON.put("appId", appId);
        reqJSON.put("deviceId", "JEEPAY666");
        // reqJSON.put("requestTime", "1649816066");
        reqJSON.put("requestTime", DateUtil.currentSeconds());
        reqJSON.put("merchantId", "M1648869277");
        reqJSON.put("productNum", "123456");
        return reqJSON;
    }


}
