package com.jeequan.jeepay.pay.ctrl.payorder;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.URLUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.jeequan.jeepay.components.mq.model.PayOrderReissueMQ;
import com.jeequan.jeepay.components.mq.model.PushWxMpMsgMQ;
import com.jeequan.jeepay.components.mq.vender.IMQSender;
import com.jeequan.jeepay.core.annotate.kits.ChannelMchIdKit;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.constants.ChannelMchIdTypeEnum;
import com.jeequan.jeepay.core.entity.MchApp;
import com.jeequan.jeepay.core.entity.MchInfo;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.exception.ChannelException;
import com.jeequan.jeepay.core.interfaces.paychannel.IPaymentService;
import com.jeequan.jeepay.core.interfaces.wxmp.IWxLiteUrlLinkService;
import com.jeequan.jeepay.core.model.ApiRes;
import com.jeequan.jeepay.core.model.DBApplicationConfig;
import com.jeequan.jeepay.core.model.QRCodeParams;
import com.jeequan.jeepay.core.model.applyment.PaywayFee;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.mchconfig.MchDivisionConfig;
import com.jeequan.jeepay.core.model.oauth2.AlipayOauth2Params;
import com.jeequan.jeepay.core.model.oauth2.Oauth2Params;
import com.jeequan.jeepay.core.model.oauth2.WxpayOauth2Params;
import com.jeequan.jeepay.core.model.params.IsvParams;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.QueryPayOrderRS;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRS;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.QrCashierOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.QrCashierOrderRS;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.WebCashierOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.WebCashierOrderRS;
import com.jeequan.jeepay.core.utils.JeepayKit;
import com.jeequan.jeepay.core.utils.SeqKit;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.core.utils.StringKit;
import com.jeequan.jeepay.db.entity.*;
import com.jeequan.jeepay.pay.ctrl.ApiController;
import com.jeequan.jeepay.pay.service.PayOrderProcessService;
import com.jeequan.jeepay.pay.util.ApiResKit;
import com.jeequan.jeepay.service.impl.*;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.beans.factory.annotation.Autowired;

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

/*
* 创建支付订单抽象类
*
* @author terrfly
*
* @date 2021/6/8 17:26
*/
@Slf4j
public abstract class AbstractPayOrderController extends ApiController {

    @Autowired private IpLocationService ipLocationService;
    @Autowired private PayOrderService payOrderService;
    @Autowired private ConfigContextQueryService configContextQueryService;
    @Autowired private PayOrderProcessService payOrderProcessService;
    @Autowired private SysConfigService sysConfigService;
    @Autowired private MchStoreService mchStoreService;
    @Autowired private PayWayService payWayService;
    @Autowired private IMQSender mqSender;
    @Autowired private MchQrcodeCardService mchQrcodeCardService;
    @Autowired private RateConfigService rateConfigService;
    @Autowired private MchConfigService mchConfigService;
    @Autowired private DeviceProvideConfigService deviceProvideConfigService;
    @Autowired private MchStoreDeviceService mchStoreDeviceService;
    @Autowired private IWxLiteUrlLinkService wxLiteUrlLinkService;
    @Autowired private MchDivisionReceiverService mchDivisionReceiverService;
    @Autowired private RedPacketRuleService redPacketRuleService;
    @Autowired private RouteService routeService;
    @Autowired private RouteManageService routeManageService;
    @Autowired private DivisionTaskService divisionTaskService;

    /** 统一下单 (新建订单模式) **/
    protected ApiRes unifiedOrder(String wayCode, UnifiedOrderRQ bizRQ){
        return unifiedOrder(wayCode, bizRQ, null,null);
    }

    /** 统一下单 **/
    protected ApiRes unifiedOrder(String wayCode, UnifiedOrderRQ bizRQ, PayOrder payOrder,RouteManage routeManage){

        // 响应数据
        UnifiedOrderRS bizRS = null;

        //是否新订单模式 [  一般接口都为新订单模式，  由于QR_CASHIER支付方式，需要先 在DB插入一个新订单， 导致此处需要特殊判断下。 如果已存在则直接更新，否则为插入。  ]
        boolean isNewOrder = payOrder == null;
        String oriPayCode = null;

        try {

            if(payOrder != null){ //当订单存在时，封装公共参数。

                if(payOrder.getState() != PayOrder.STATE_INIT && payOrder.getState() != PayOrder.STATE_ING){
                    throw new BizException("订单状态异常");
                }
                oriPayCode = payOrder.getWayCode();
                payOrder.setWayCode(wayCode); // 需要将订单更新 支付方式
                PayWay payWay = payWayService.getById(wayCode);
                payOrder.setWayCodeType(payWay == null ? CS.PAY_WAY_CODE_TYPE.OTHER :payWay.getWayType());

                payOrder.setChannelUser(bizRQ.getChannelUserId()); //更新渠道用户信息
                bizRQ.setMchNo(payOrder.getMchNo());
                bizRQ.setAppId(payOrder.getAppId());
                bizRQ.setMchOrderNo(payOrder.getMchOrderNo());
                bizRQ.setWayCode(wayCode);
                bizRQ.setAmount(payOrder.getAmount());
                bizRQ.setCurrency(payOrder.getCurrency());
                bizRQ.setClientIp(payOrder.getClientIp());
                bizRQ.setSubject(payOrder.getSubject());
                bizRQ.setNotifyUrl(payOrder.getNotifyUrl());
                bizRQ.setReturnUrl(payOrder.getReturnUrl());
                bizRQ.setChannelExtra(payOrder.getChannelExtra());
                bizRQ.setExtParam(payOrder.getExtParam());
                bizRQ.setDivisionMode(payOrder.getDivisionMode());
                bizRQ.setStoreId(payOrder.getStoreId());
            }

            String mchNo = bizRQ.getMchNo();
            String appId = bizRQ.getAppId();
            // 只有新订单模式，进行校验
            if(isNewOrder && payOrderService.count(PayOrder.gw().eq(PayOrder::getMchNo, mchNo).eq(PayOrder::getMchOrderNo, bizRQ.getMchOrderNo())) > 0){
                throw new BizException("商户订单["+bizRQ.getMchOrderNo()+"]已存在");
            }

            if(StringUtils.isNotEmpty(bizRQ.getNotifyUrl()) && !StringKit.isAvailableUrl(bizRQ.getNotifyUrl())){
                throw new BizException("异步通知地址协议仅支持http:// 或 https:// !");
            }
            if(StringUtils.isNotEmpty(bizRQ.getReturnUrl()) && !StringKit.isAvailableUrl(bizRQ.getReturnUrl())){
                throw new BizException("同步通知地址协议仅支持http:// 或 https:// !");
            }
            // TODO 新版直接取商户号 获取门店数据
            MchStore mchStore = null;
            if(bizRQ.getStoreId() != null){
                mchStore = mchStoreService.getById(bizRQ.getStoreId());
            }else{
                mchStore = mchStoreService.queryDefaultStore(bizRQ.getMchNo());
            }
            if(mchStore == null || StringUtils.isEmpty(mchStore.getMchApplyId())){
                throw new BizException("门店信息异常或门店关联商户信息异常");
            }
            if(StringUtils.isBlank(bizRQ.getSubject())){
                bizRQ.setSubject(mchStore.getStoreName());
            }
            if(StringUtils.isBlank(bizRQ.getBody())){
                bizRQ.setBody(mchStore.getStoreName());
            }
            //获取支付参数 (缓存数据) 和 商户信息
            MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(mchNo, appId,mchStore.getMchApplyId());
            if(mchAppConfigContext == null){
                throw new BizException("获取商户应用信息失败");
            }
            PaywayFee d0PaywayFee = null;
            if(CS.SETTLEMENT_TYPE.D0.equals(mchAppConfigContext.getMchApplyment().getSettlementType())){
                d0PaywayFee = rateConfigService.queryCreateOrderPaywayFee(mchAppConfigContext.getMchApplyment().getApplyId(), mchAppConfigContext.getMchApplyment().getIsvNo(), mchAppConfigContext.getMchApplyment().getAutoConfigMchAppId(), mchAppConfigContext.getMchApplyment().getIfCode(), CS.PAY_WAY_CODE.D0);
                if(d0PaywayFee == null){
                    throw new BizException("当前商户为D0商户，D0费率未配置生效");
                }
            }
            if(bizRQ.getQrcId() != null){
                MchQrcodeCard mchQrcodeCard = mchQrcodeCardService.getById(bizRQ.getQrcId());
                if(mchQrcodeCard == null){
                    throw new BizException("商户码牌不存在！");
                }
                if(mchQrcodeCard.getBindState() == CS.NO || !bizRQ.getMchNo().equals(mchQrcodeCard.getMchNo())){
                    throw new BizException("码牌所属商户不匹配！");
                }
            }

            //收银台支付并且只有新订单需要走这里，  收银台二次下单的wayCode应该为实际支付方式。
            if(isNewOrder && CS.PAY_WAY_CODE.QR_CASHIER.equals(wayCode)){
                return qrCashierPayWay(bizRQ, mchAppConfigContext, null,routeManage);
            }

            //web统一收银台支付并且只有新订单需要走这里
            if(isNewOrder && CS.PAY_WAY_CODE.WEB_CASHIER.equals(wayCode)){
                return webCashierPayWay(bizRQ, mchAppConfigContext,routeManage);
            }

            // 根据支付方式， 查询出 该商户 可用的支付接口
//            MchPayPassage mchPayPassage = mchPayPassageService.findMchPayPassage(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), wayCode);
//            if(mchPayPassage == null){
//                throw new BizException("商户应用不支持该支付方式");
//            }

            //获取支付接口
            IPaymentService paymentService = checkMchWayCodeAndGetService(mchAppConfigContext, mchAppConfigContext.getMchApplyment().getIfCode());

            // 支付接口克隆改造2323-05-15，支持服务商对同一通道配置多个支付接口
            String cloneIfCode = mchAppConfigContext.getMchApplyment().getIfCode();

            // 校验终端设备
            checkDeviceParams(bizRQ, cloneIfCode);

            // 查询渠道服务商号和渠道子商户号
            String[] channelMchNoArr = getChannelMchNo(mchAppConfigContext);

            // 查询费率
            PaywayFee paywayFee = rateConfigService.queryCreateOrderPaywayFee(mchAppConfigContext.getMchApplyment().getApplyId(), mchAppConfigContext.getMchApplyment().getIsvNo(), mchAppConfigContext.getMchApplyment().getAutoConfigMchAppId(), mchAppConfigContext.getMchApplyment().getIfCode(), wayCode);
            if(paywayFee == null){
                throw new BizException("商户费率未配置");
            }

            //生成订单
            if(isNewOrder){
                payOrder = genPayOrder(bizRQ, mchAppConfigContext, paywayFee, channelMchNoArr,d0PaywayFee,routeManage);
            }else{
                payOrder.setIfCode(cloneIfCode);

                // 查询支付方式的费率，并 在更新ing时更新费率信息
                MutablePair<String, Long> feeAndSnapshot = paywayFee.calFeeAndSnapshot(payOrder.getFindAmt());
                payOrder.setMchFeeRate(feeAndSnapshot.left);
                //TODO 银盛/直付通特殊性 保底收 0.01
                if(CS.IF_CODE.YSPAY.equals(payOrder.getIfCode()) || CS.IF_CODE.ZFTPAY.equals(payOrder.getIfCode())){
                    feeAndSnapshot.right = feeAndSnapshot.right == BigDecimal.ZERO.longValue() ? BigDecimal.ONE.longValue() : feeAndSnapshot.right;
                }
                payOrder.setMchFeeAmount(feeAndSnapshot.right); //商户手续费,单位分
                payOrder.setMchOrderFeeAmount(feeAndSnapshot.right); //商户手续费,单位分
                // 服务商机构号或子商户号
                payOrder.setChannelIsvNo(channelMchNoArr[0]);
                payOrder.setChannelMchNo(channelMchNoArr[1]);

                if(d0PaywayFee != null && CS.SETTLEMENT_TYPE.D0.equals(mchAppConfigContext.getMchApplyment().getSettlementType())){
                    MutablePair<String, Long> feeD0 = d0PaywayFee.calFeeAndSnapshot(payOrder.getFindAmt());
                    payOrder.setCashRate(d0PaywayFee.getFeeRate().multiply(BigDecimal.valueOf(100)));
                    Long cashFee = feeD0.right == 0 ? BigDecimal.ONE.longValue() : feeD0.right;
                    payOrder.setCashFee(cashFee); //商户手续费,单位分
                    //拉卡拉D0 除了支付宝或者微信以外银联交易都是D1 或者小于10元都是D1
                    if(CS.IF_CODE.LKLSPAY.equals(payOrder.getIfCode()) && (!CS.PAY_WAY_CODE_TYPE.WECHAT.equals(payOrder.getWayCodeType()) && !CS.PAY_WAY_CODE_TYPE.ALIPAY.equals(payOrder.getWayCodeType())) || payOrder.getFindAmt().longValue() < 10){
                        payOrder.setCashFee(0L);
                        payOrder.setCashRate(BigDecimal.ZERO);
                    }
                }

                // 自动分账模式判断 A : 与 B对应
                // 自动分账模式： 判断是否有 分账用户。  若没有分账用户：  则分账模式为关闭。 ( 解决  斗拱接口，没有分账用户无法调用完结导致资金冻结的问题 )
                // 注意： 此处为： 聚合码下单后， 第二次调用下单接口的场景。
                if(payOrder.getDivisionMode() != null && payOrder.getDivisionMode() == PayOrder.DIVISION_MODE_AUTO && cloneIfCode != null){
                    // 无 自动分账组用户
                    if(mchDivisionReceiverService.getBaseMapper().countByAutoDivisionFlag(payOrder.getMchNo(), payOrder.getAppId(), payOrder.getIfCode()) <= 0){
                        payOrder.setDivisionMode(PayOrder.DIVISION_MODE_FORBID);
                        bizRQ.setDivisionMode(PayOrder.DIVISION_MODE_FORBID); // 修改 req对象。
                    }
                }
            }
            if(!bizRQ.getIsRoute()){
                //预先校验
                String errMsg = paymentService.preCheck(bizRQ, payOrder);
                if(StringUtils.isNotEmpty(errMsg)){
                    throw new BizException(errMsg);
                }
                ipLocationService.preCheck(payOrder.getClientIp(),mchAppConfigContext.getMchApplyment());
            }
//            ipLocationService.preCheck(payOrder.getClientIp(),mchAppConfigContext.getMchApplyment());
//            String newPayOrderId = paymentService.customPayOrderId(bizRQ, payOrder, mchAppConfigContext);

            if(isNewOrder){
                //订单入库 订单状态： 生成状态  此时没有和任何上游渠道产生交互。
                payOrderService.save(payOrder);
            }
//            else{
//
//                // TODO 系统生成的二维码聚合支付方式（QR_CASHIER）模式，商户不应该通过payOrderId来查单， 此时订单号有可能更改！
//                if(StringUtils.isNotBlank(newPayOrderId)){ // 自定义订单号, 更新原订单号
//                    payOrderService.update(new LambdaUpdateWrapper<PayOrder>().set(PayOrder::getPayOrderId, newPayOrderId).eq(PayOrder::getPayOrderId, payOrder.getPayOrderId()));
//                    payOrder.setPayOrderId(newPayOrderId);
//                }
//            }
            //如果是路由
            if(bizRQ.getIsRoute() && isNewOrder){
                bizRS = new UnifiedOrderRS();
                if(routeManage.getIsSend()){
                    mqSender.send(PushWxMpMsgMQ.build(payOrder.getPayOrderId(),PushWxMpMsgMQ.ROUTE_NOTICE));
                }
                routeManageService.upRouteData(routeManage,payOrder);
                return packageApiResByPayOrder(bizRQ, bizRS, payOrder);
            }
            if(!isNewOrder && StringUtils.isNotEmpty(payOrder.getPayData())){
                if(payOrder.getWayCode().equals(oriPayCode)){
                    bizRS = new UnifiedOrderRS();
                    bizRS.setPayOrderId(payOrder.getPayOrderId());
                    bizRS.setPayData(payOrder.getPayData());
                    bizRS.setChannelRetMsg(ChannelRetMsg.waiting());
                    bizRS.setMchOrderNo(payOrder.getMchOrderNo());
                    bizRS.setOrderState(payOrder.getState());
                }else{
                    throw new BizException("当前订单号已被使用，请重新下单");
                }
            }else{
                //调起上游支付接口
                bizRS = (UnifiedOrderRS) paymentService.pay(bizRQ, payOrder, mchAppConfigContext);
            }
            //处理上游返回数据
            this.processChannelMsg(bizRS,bizRS.getChannelRetMsg(), payOrder);
            return packageApiResByPayOrder(bizRQ, bizRS, payOrder);
        } catch (BizException e) {
            return ApiRes.customFail(e.getMessage());

        } catch (ChannelException e) {

            //处理上游返回数据
            this.processChannelMsg(null,e.getChannelRetMsg(), payOrder);

            if(e.getChannelRetMsg().getChannelState() == ChannelRetMsg.ChannelState.SYS_ERROR ){
                return ApiRes.customFail(e.getMessage());
            }

            return this.packageApiResByPayOrder(bizRQ, bizRS, payOrder);


        } catch (Exception e) {
            log.error("系统异常：{}", e);
            return ApiRes.customFail("系统异常");
        }
    }

    /**
     * 构建订单
     * @param rq
     * @param mchAppConfigContext
     * @param paywayFee
     * @param channelMchNoArr
     * @param d0PaywayFee
     * @return
     */
    public PayOrder genPayOrder(UnifiedOrderRQ rq, MchAppConfigContext mchAppConfigContext, PaywayFee paywayFee, String[] channelMchNoArr,PaywayFee d0PaywayFee,RouteManage routeManage){
        MchInfo mchInfo = mchAppConfigContext.getMchInfo();
        MchApp mchApp = mchAppConfigContext.getMchApp();
        PayOrder payOrder = new PayOrder();
        payOrder.setPayOrderId(SeqKit.genPayOrderId()); //生成订单ID
        payOrder.setMchNo(mchInfo.getMchNo()); //商户号
        payOrder.setEpUserId(mchInfo.getCreatedUid()); // 商户拓展员
        payOrder.setIsvNo(mchInfo.getIsvNo()); //服务商号
        payOrder.setMchName(mchAppConfigContext.getMchApplyment().getMchShortName()); //商户名称（简称）
        payOrder.setMchType(mchInfo.getType()); //商户类型
        payOrder.setMchOrderNo(rq.getMchOrderNo()); //商户订单号
        payOrder.setAppId(mchApp.getAppId()); //商户应用appId
        payOrder.setIfCode(mchAppConfigContext.getMchApplyment().getIfCode()); //接口代码
        payOrder.setWayCode(rq.getWayCode()); //支付方式
        payOrder.setAgentNo(mchInfo.getAgentNo());  // 服务商ID
        payOrder.setTopAgentNo(mchInfo.getTopAgentNo());    // 上级服务商ID
        payOrder.setSettleType(mchAppConfigContext.getMchApplyment().getSettlementType());    //结算类型
        PayWay payWay = payWayService.getById(rq.getWayCode());
        payOrder.setWayCodeType(payWay == null ? CS.PAY_WAY_CODE_TYPE.OTHER :payWay.getWayType());

        payOrder.setAmount(rq.getAmount()); //订单金额
        payOrder.setFindAmt(rq.getAmount());
        MchStore mchStore = null;
        if(rq.getStoreId() != null){
            mchStore = mchStoreService.getById(rq.getStoreId()); // 查询参数门店
        }else{
            mchStore = mchStoreService.queryDefaultStore(rq.getMchNo());  //查询默认门店
        }

//        if(mchStore == null || !mchStore.getMchNo().equals(rq.getMchNo())){
//            throw new BizException("门店不存在");
//        }
        payOrder.setStoreId(mchStore.getStoreId());         // 门店ID
        payOrder.setStoreName(mchStore.getStoreName());     // 门店名称
        payOrder.setLng(mchStore.getLng());                 // 经度
        payOrder.setLat(mchStore.getLat());                 // 纬度
        payOrder.setAddress(mchStore.getAddress());         // 详细地址
        payOrder.setStoreUserId(rq.getStoreUserId());     // 门店收银员ID
        payOrder.setMchExtNo(mchStore.getMchApplyId());
        if (StringUtils.isNotBlank(rq.getDeviceInfo())) {
            UnifiedOrderRQ.DeviceInfo deviceInfo = JSON.parseObject(rq.getDeviceInfo(), UnifiedOrderRQ.DeviceInfo.class);
            payOrder.setDeviceType(deviceInfo.getDeviceType()); // 设备类型
            payOrder.setDeviceNo(deviceInfo.getDeviceNo()); // 设备号
            payOrder.setDeviceProvider(deviceInfo.getProvider()); // 设备厂商
        }
        if (rq.getQrcId() != null) {
            payOrder.setQrcId(rq.getQrcId()); // 码牌ID
            payOrder.setDeviceType(PayOrder.DEVICE_TYPE_QR_CODE); // 码牌类型
            payOrder.setDeviceNo(String.valueOf(rq.getQrcId()));
            if(StringUtils.isEmpty(mchStore.getMchApplyId())){
                throw new BizException("门店关联的商户信息异常");
            }
            payOrder.setMchExtNo(mchStore.getMchApplyId());
        }
        //判断门店是否开通了营销规则
        boolean isRule = redPacketRuleService.isCheckStoreRedRule(payOrder.getMchNo(), payOrder.getStoreId());
        if(paywayFee != null && !isRule){
            MutablePair<String, Long> feeAndSnapshot = paywayFee.calFeeAndSnapshot(payOrder.getFindAmt());
            payOrder.setMchFeeRate(feeAndSnapshot.left);
            //TODO 银盛特殊性 保底收 0.01
            if(CS.IF_CODE.YSPAY.equals(payOrder.getIfCode())){
                feeAndSnapshot.right = feeAndSnapshot.right == BigDecimal.ZERO.longValue() ? BigDecimal.ONE.longValue() : BigDecimal.ZERO.longValue();
            }
            payOrder.setMchFeeAmount(feeAndSnapshot.right); //商户手续费,单位分
            payOrder.setMchOrderFeeAmount(feeAndSnapshot.right); //商户手续费,单位分
        }else{
            payOrder.setMchFeeRate(""); //预下单模式， 按照0计算入库， 后续进行更新
            payOrder.setMchFeeAmount(0L); //商户手续费,单位分
            payOrder.setMchOrderFeeAmount(0L); //商户手续费,单位分
        }
        if(d0PaywayFee != null && CS.SETTLEMENT_TYPE.D0.equals(mchAppConfigContext.getMchApplyment().getSettlementType())){
            MutablePair<String, Long> feeAndSnapshot = d0PaywayFee.calFeeAndSnapshot(payOrder.getFindAmt());
            payOrder.setCashRate(d0PaywayFee.getFeeRate().multiply(BigDecimal.valueOf(100)));
            Long cashFee = feeAndSnapshot.right;
            if(cashFee == 0){
                cashFee = BigDecimal.ONE.longValue();
            }
            payOrder.setCashFee(cashFee); //商户手续费,单位分
            //拉卡拉D0 除了支付宝或者微信以外银联交易都是D1 或者小于10元都是D1
            if(CS.IF_CODE.LKLSPAY.equals(payOrder.getIfCode()) && (!CS.PAY_WAY_CODE_TYPE.WECHAT.equals(payOrder.getWayCodeType()) && !CS.PAY_WAY_CODE_TYPE.ALIPAY.equals(payOrder.getWayCodeType())) || payOrder.getFindAmt().longValue() < 10){
                payOrder.setCashFee(0L);
                payOrder.setCashRate(BigDecimal.ZERO);
            }
        }

        payOrder.setCurrency(rq.getCurrency()); //币种
        payOrder.setState(PayOrder.STATE_INIT); //订单状态, 默认订单生成状态
        payOrder.setClientIp(StringUtils.defaultIfEmpty(rq.getClientIp(), getClientIp())); //客户端IP
        payOrder.setSubject(rq.getSubject()); //商品标题
        payOrder.setBody(rq.getBody()); //商品描述信息
        payOrder.setChannelBizData(rq.getChannelBizData()); // 渠道业务参数

//      payOrder.setChannelExtra(rq.getChannelExtra()); //特殊渠道发起的附件额外参数,  是否应该删除该字段了？？ 比如authCode不应该记录， 只是在传输阶段存在的吧？  之前的为了在payOrder对象需要传参。

        payOrder.setChannelUser(rq.getChannelUserId()); //渠道用户标志
        payOrder.setExtParam(rq.getExtParam()); //商户扩展参数
        payOrder.setNotifyUrl(rq.getNotifyUrl()); //异步通知地址
        payOrder.setReturnUrl(rq.getReturnUrl()); //页面跳转地址
        payOrder.setSellerRemark(rq.getSellerRemark()); // 卖家地址
        payOrder.setBuyerRemark(rq.getBuyerRemark()); // 买家地址

        // 服务商机构号或子商户号
        if (channelMchNoArr != null) {
            payOrder.setChannelIsvNo(channelMchNoArr[0]);
            payOrder.setChannelMchNo(channelMchNoArr[1]);
        }

        if(rq.getIsMarketRed() != null && rq.getIsMarketRed()){
            payOrder.setDivisionMode(PayOrder.MAKET_RED_PACKET_MANUAL);
        }else{
            Byte defalutDivisionMode = ObjectUtils.defaultIfNull(rq.getDivisionMode(), PayOrder.DIVISION_MODE_FORBID);
            if(rq.getDivisionMode() == null){
                //如果检测出自动分账的任务，则进行增加订单自动分账的标识
                DivisionTask autoTask = divisionTaskService.getAutoTask(payOrder.getIfCode());
                if(autoTask != null){
                    defalutDivisionMode = PayOrder.DIVISION_MODE_AUTO;
                }
            }
            // 分账模式
            payOrder.setDivisionMode(defalutDivisionMode);

            // 订单不是自动分账模式(如果自动分账， 那么就无需查询DB了)
//            if(payOrder.getDivisionMode() != PayOrder.DIVISION_MODE_AUTO){
//
//                // && 当前商户配置模式为： 全局自动分账 && 金额满足
//                MchDivisionConfig mchDivisionConfig = mchConfigService.queryMchDivisionConfigByDefault(payOrder.getMchNo());
//                if(mchDivisionConfig.getOverrideAutoFlag() == CS.YES && payOrder.getAmount() >= mchDivisionConfig.getAutoDivisionRules().getAmountLimit()){
//                    payOrder.setDivisionMode(PayOrder.DIVISION_MODE_AUTO);
//                }
//            }

            // 自动分账模式判断 B : 与 A对应
            // 自动分账模式： 判断是否有 分账用户。  若没有分账用户：  则分账模式为关闭。 ( 解决  斗拱接口，没有分账用户无法调用完结导致资金冻结的问题 )
            // 注意： 聚合码此时没有ifCode,  判断不出来是否包含分账接收方。
            // 此处判断的为：   直接下单的场景， 比如 微信二维码、 支付宝二维码、 被扫等场景。 TODO 这里需要处理 先不操作
//            if(payOrder.getDivisionMode() != null && payOrder.getDivisionMode() == PayOrder.DIVISION_MODE_AUTO && mchAppConfigContext.getMchApplyment().getIfCode() != null){
//                // 无 自动分账组用户
//                if(mchDivisionReceiverService.getBaseMapper().countByAutoDivisionFlag(payOrder.getMchNo(), payOrder.getAppId(), payOrder.getIfCode()) <= 0){
//                    payOrder.setDivisionMode(PayOrder.DIVISION_MODE_FORBID);
//                }
//            }
        }

        Date nowDate = new Date();

        //订单过期时间 单位： 秒
        if(rq.getExpiredTime() != null){
            payOrder.setExpiredTime(DateUtil.offsetSecond(nowDate, rq.getExpiredTime()));
        }else{
            payOrder.setExpiredTime(DateUtil.offsetHour(nowDate, 2)); //订单过期时间 默认两个小时
        }

        // 会员信息
        if (StringUtils.isNotBlank(rq.getMbrId())) {
            payOrder.setMbrId(rq.getMbrId());
        }
        if (StringUtils.isNotBlank(rq.getMbrTel())) {
            payOrder.setMbrTel(rq.getMbrTel());
        }

        payOrder.setCreatedAt(nowDate); //订单创建时间

        // 设置优惠
        JSONObject discount = rq.getDiscount();
        if(discount != null){
            Long discountAmt = discount.getLong("discountAmt") == null ? BigDecimal.ZERO.longValue() : discount.getLong("discountAmt");
            payOrder.setFindAmt(payOrder.getAmount() - discountAmt);
            payOrder.setDiscountAmt(discountAmt);
            payOrder.setDiscountScale(discount.getBigDecimal("discountScale"));
        }
        if(routeManage != null){
            payOrder.setRouteId(routeManage.getRouteId());
        }
        return payOrder;
    }


    /**
     * 校验： 商户的支付方式是否可用
     * 返回： 支付接口
     * **/
    private IPaymentService checkMchWayCodeAndGetService(MchAppConfigContext mchAppConfigContext, String ifCode){

        IPaymentService paymentService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(ifCode) + "PaymentService", IPaymentService.class);
        if(paymentService == null){
            throw new BizException("无此支付通道接口");
        }
        if(mchAppConfigContext.getMchType() == MchInfo.TYPE_NORMAL){ //普通商户

            if(configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), ifCode) == null){
                throw new BizException("商户应用参数未配置");
            }
        }else if(mchAppConfigContext.getMchType() == MchInfo.TYPE_ISVSUB){ //特约商户

//            if(configContextQueryService.queryIsvsubMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), ifCode) == null){
//                throw new BizException("特约商户参数未配置");
//            }

            if (configContextQueryService.queryIsvParams(mchAppConfigContext.getMchApplyment().getIsvNo(), ifCode) == null) {
                throw new BizException("服务商参数未配置");
            }
        }

        return paymentService;

    }


    /** 处理返回的渠道信息，并更新订单状态
     *  payOrder将对部分信息进行 赋值操作。
     * **/
    protected void processChannelMsg(UnifiedOrderRS urs,ChannelRetMsg channelRetMsg, PayOrder payOrder){

        //对象为空 || 上游返回状态为空， 则无需操作
        if(channelRetMsg == null || channelRetMsg.getChannelState() == null){
            return ;
        }

        String payOrderId = payOrder.getPayOrderId();

        //明确成功
        if(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS == channelRetMsg.getChannelState()) {

            this.updateInitOrderStateThrowException(PayOrder.STATE_SUCCESS, payOrder, urs);

            //订单支付成功，其他业务逻辑
            payOrderProcessService.confirmSuccess(payOrder, null);

        //明确失败
        }else if(ChannelRetMsg.ChannelState.CONFIRM_FAIL == channelRetMsg.getChannelState()) {

            this.updateInitOrderStateThrowException(PayOrder.STATE_FAIL, payOrder, urs);

        // 上游处理中 || 未知 || 上游接口返回异常  订单为支付中状态
        }else if( ChannelRetMsg.ChannelState.WAITING == channelRetMsg.getChannelState() ||
                  ChannelRetMsg.ChannelState.UNKNOWN == channelRetMsg.getChannelState() ||
                  ChannelRetMsg.ChannelState.API_RET_ERROR == channelRetMsg.getChannelState()

        ){
            this.updateInitOrderStateThrowException(PayOrder.STATE_ING, payOrder, urs);

        // 系统异常：  订单不再处理。  为： 生成状态
        }else if( ChannelRetMsg.ChannelState.SYS_ERROR == channelRetMsg.getChannelState()){

        }else{

            throw new BizException("ChannelState 返回异常！");
        }

        //判断是否需要轮询查单
        if(channelRetMsg.isNeedQuery()){
            mqSender.send(PayOrderReissueMQ.build(payOrderId, 1), 5);
        }

    }


    /** 更新订单状态 --》 订单生成--》 其他状态  (向外抛出异常) **/
    private void updateInitOrderStateThrowException(byte orderState, PayOrder payOrder, UnifiedOrderRS urs){
        if(payOrder.getState() == PayOrder.STATE_ING){
            return ;
        }
        payOrder.setState(orderState);
        boolean isSuccess = payOrderService.updateInit2Ing(payOrder.getPayOrderId(), payOrder);
        if(!isSuccess){
            throw new BizException("更新订单异常!");
        }

        isSuccess = payOrderService.updateIng2SuccessOrFail(payOrder.getPayOrderId(), payOrder.getState(),urs.getChannelRetMsg());
        if(!isSuccess){
            throw new BizException("更新订单异常!");
        }
    }


    /** 统一封装订单数据  **/
    private ApiRes packageApiResByPayOrder(UnifiedOrderRQ bizRQ, UnifiedOrderRS bizRS, PayOrder payOrder){

        // 返回接口数据
        bizRS.setPayOrderId(payOrder.getPayOrderId());
        bizRS.setOrderState(payOrder.getState());
        bizRS.setMchOrderNo(payOrder.getMchOrderNo());
        bizRS.setStoreName(payOrder.getStoreName());
        if(bizRQ.getIsRoute()){
            String tk = sysConfigService.genQrToken(QRCodeParams.TYPE_PAY_ORDER, payOrder.getPayOrderId(), null);
            bizRS.setPayData(tk);
            Route route = routeService.preCheck(payOrder.getRouteId());
            JSONObject extData = new JSONObject();
            extData.put("payMethod",route.getPayMethod());
            if(payOrder.getWayCode().contains("WX")){
                WxpayOauth2Params oauth2Params = (WxpayOauth2Params)configContextQueryService.queryIsvOauth2Params(payOrder.getIsvNo(), CS.IF_CODE.WXPAY);
                extData.put("appid",oauth2Params.getAppId());
                if(Route.LITE == route.getPayMethod()){
                    extData.put("liteAppid",oauth2Params.getLiteAppId());
                }
            }else{
                AlipayOauth2Params oauth2Params = (AlipayOauth2Params)configContextQueryService.queryIsvOauth2Params(payOrder.getIsvNo(), CS.IF_CODE.ALIPAY);
                extData.put("appid",oauth2Params.getAppId());
                if(Route.LITE == route.getPayMethod()){
                    AlipayOauth2Params liteParams = oauth2Params.getLiteParams();
                    extData.put("liteAppid",liteParams.getAppId());
                }
            }
            bizRS.setExtData(extData);
        }
        if(payOrder.getState() == PayOrder.STATE_FAIL){
            bizRS.setErrCode(bizRS.getChannelRetMsg() != null ? bizRS.getChannelRetMsg().getChannelErrCode() : null);
            bizRS.setErrMsg(bizRS.getChannelRetMsg() != null ? bizRS.getChannelRetMsg().getChannelErrMsg() : null);
        }

        // 支付成功， 返回订单数据给接口调用者
        if(payOrder.getState() == PayOrder.STATE_SUCCESS){

            QueryPayOrderRS queryPayOrderRS = QueryPayOrderRS.buildByPayOrder(payOrderService.getById(payOrder.getPayOrderId()));

            JSONObject jsonObject = (JSONObject) JSON.toJSON(queryPayOrderRS);

            // 查询扩展参数
            MchConfig mchConfig = mchConfigService.getByMchNoAndConfigKey(payOrder.getMchNo(), "payOrderNotifyExtParams");
            List<String> extParams = new ArrayList<>();
            if(mchConfig != null && StringUtils.isNotBlank(mchConfig.getConfigVal())){
                extParams = JSON.parseArray(mchConfig.getConfigVal(), String.class);
            }
            for (String key : QueryPayOrderRS.EXT_PRAMS) {
                if(!extParams.contains(key)){
                    jsonObject.remove(key);
                }
            }
            bizRS.setPayOrderInfo(jsonObject.toJSONString());
        }

        return ApiResKit.okWithSign(bizRS, configContextQueryService.queryMchApp(bizRQ.getMchNo(), bizRQ.getAppId()), bizRQ.getSignType());
    }



    /**  聚合码支付方式
     *
     * dbPayOrderId : null,  表示需要新增save订单，  否则无需save直接查库（ web收银台 H5跳小程序方式 ）
     *
     * */
    public ApiRes qrCashierPayWay(UnifiedOrderRQ bizRQ,MchAppConfigContext mchAppConfigContext, String dbPayOrderId,RouteManage routeManage) {

        QrCashierOrderRQ qrCashierOrderRQ = (QrCashierOrderRQ)bizRQ;
        if(StringUtils.isNotEmpty(qrCashierOrderRQ.getEntryPageType())
                && !QRCodeParams.ENTRY_PAGE_H5.equals(qrCashierOrderRQ.getEntryPageType())
                && !QRCodeParams.ENTRY_PAGE_LITE.equals(qrCashierOrderRQ.getEntryPageType())){
            throw new BizException("聚合码入口参数有误！ ");
        }

        // 判断商户是否拥有 可用通道
//        int passageCount = mchPayPassageService.count(MchPayPassage.gw().eq(MchPayPassage::getAppId, mchApp.getAppId()).eq(MchPayPassage::getState, CS.YES));
//        if(passageCount <= 0){
//            throw new BizException("当前商户未开通任何支付通道！");
//        }

        String payOrderId = dbPayOrderId;
        PayOrder payOrder = null;

        if(StringUtils.isEmpty(dbPayOrderId)){
            //生成订单
            String[] channelMchNoArr = getChannelMchNo(mchAppConfigContext);
            payOrder = genPayOrder(bizRQ, mchAppConfigContext, null, channelMchNoArr,null,routeManage);
            payOrderId = payOrder.getPayOrderId();

            //订单入库 订单状态： 生成状态  此时没有和任何上游渠道产生交互。
            payOrderService.save(payOrder);

        }else{

            payOrder = payOrderService.getById(payOrderId);
        }

        QrCashierOrderRS qrCashierOrderRS = new QrCashierOrderRS();

        DBApplicationConfig dbApplicationConfig = sysConfigService.getDBApplicationConfig();

        String payUrl = dbApplicationConfig.genUniJsapiPayUrl(QRCodeParams.TYPE_PAY_ORDER, StringUtils.defaultIfEmpty(qrCashierOrderRQ.getEntryPageType(), QRCodeParams.ENTRY_PAGE_H5), payOrderId, payOrder.getIsvNo());
        if(CS.PAY_DATA_TYPE.CODE_IMG_URL.equals(qrCashierOrderRQ.getPayDataType())){ //二维码图片地址
            qrCashierOrderRS.setCodeImgUrl(dbApplicationConfig.genScanImgUrl(payUrl));
        }else{ //默认都为跳转地址方式
            // TODO Dingzhiwei 临时解决(待手机端支持后删除) 为了兼容之前通过payDataType支持app打开小程序支付功能
            if(StringUtils.isEmpty(qrCashierOrderRQ.getEntryLiteType()) &&
                    (CS.PAY_DATA_TYPE.ALI_APP.equals(qrCashierOrderRQ.getPayDataType()) || CS.PAY_DATA_TYPE.WX_APP.equals(qrCashierOrderRQ.getPayDataType()))){
                qrCashierOrderRQ.setEntryLiteType(qrCashierOrderRQ.getPayDataType());
            }

            if(StringUtils.isNotEmpty(qrCashierOrderRQ.getEntryLiteType())){
                if(QRCodeParams.ENTRY_LITE_WXAPP.equals(qrCashierOrderRQ.getEntryLiteType())){
                    JSONObject liteObject = new JSONObject();
                    WxpayOauth2Params wxpayOauth2Params = null;

                    if(mchAppConfigContext.isIsvsubMch()){ // 特约商户

                        // 先获取 特约商户的 小程序 配置信息
                        wxpayOauth2Params = (WxpayOauth2Params) configContextQueryService.queryNormalOauth2Params(mchAppConfigContext.getMchApplyment().getIsvNo(), payOrder.getAppId(), CS.IF_CODE.WXPAY);

                        // 商户未配置 || 使用服务商的小程序时 ：  查询服务商的配置信息
                        if(wxpayOauth2Params == null || wxpayOauth2Params.getIsUseSubmchAccount() == null ||  wxpayOauth2Params.getIsUseSubmchAccount() == CS.NO){

                            // 查询服务商的配置，  判断取哪个 配置条目。

                            // 查询商户 配置 的oauth2配置 ( 支付参数配置 )    selectOauth2InfoId: 配置的oauth2参数
                            String ifCodeByMchPassage = configContextQueryService.queryIfCodeByPageType(mchAppConfigContext, QRCodeParams.PAGE_TYPE_WECHAT_LITE);
                            String selectOauth2InfoId = configContextQueryService.queryAgentOrIsvSelectOauth2InfoId(mchAppConfigContext.getMchApplyment(), ifCodeByMchPassage);
                            wxpayOauth2Params = (WxpayOauth2Params) configContextQueryService.
                                    queryIsvOauth2ParamsByAutoSelectOauth2InfoId(mchAppConfigContext.getMchApplyment().getIsvNo(), CS.IF_CODE.WXPAY, selectOauth2InfoId);

                        }

                    }else{
                        wxpayOauth2Params = (WxpayOauth2Params) configContextQueryService.queryNormalOauth2Params(payOrder.getIsvNo(), payOrder.getAppId(), CS.IF_CODE.WXPAY);
                    }
                    if(wxpayOauth2Params != null){
                        liteObject.put("appid", wxpayOauth2Params.getLiteAppId()); // 小程序的appId
                        liteObject.put("ghid", wxpayOauth2Params.getLiteGhid());  // 小程序原始ID
                        liteObject.put("path", wxpayOauth2Params.getLitePagePath());   // 小程序打开页面
                        liteObject.put("env", wxpayOauth2Params.getLiteEnv());           // 小程序版本
                        liteObject.put("qrUrl", payUrl);            // 聚合码支付URL
                    }
                    payUrl = liteObject.toJSONString();
                }
                //静态托管H5
                if(QRCodeParams.ENTRY_LITE_WX_TG_H5.equals(qrCashierOrderRQ.getEntryLiteType())){
                    JSONObject liteObject = new JSONObject();
                    WxpayOauth2Params wxpayOauth2Params = null;

                    if(mchAppConfigContext.isIsvsubMch()){ // 特约商户

                        // 先获取 特约商户的 小程序 配置信息
                        wxpayOauth2Params = (WxpayOauth2Params) configContextQueryService.queryNormalOauth2Params(payOrder.getIsvNo(), payOrder.getAppId(), CS.IF_CODE.WXPAY);

                        // 商户未配置 || 使用服务商的小程序时 ：  查询服务商的配置信息
                        if(wxpayOauth2Params == null || wxpayOauth2Params.getIsUseSubmchAccount() == null ||  wxpayOauth2Params.getIsUseSubmchAccount() == CS.NO){

                            // 查询服务商的配置，  判断取哪个 配置条目。

                            // 查询商户 配置 的oauth2配置 ( 支付参数配置 )    selectOauth2InfoId: 配置的oauth2参数
                            String ifCodeByMchPassage = configContextQueryService.queryIfCodeByPageType(mchAppConfigContext, QRCodeParams.PAGE_TYPE_WECHAT_LITE);
                            String selectOauth2InfoId = configContextQueryService.queryAgentOrIsvSelectOauth2InfoId(mchAppConfigContext.getMchApplyment(), ifCodeByMchPassage);
                            wxpayOauth2Params = (WxpayOauth2Params) configContextQueryService.
                                    queryIsvOauth2ParamsByAutoSelectOauth2InfoId(mchAppConfigContext.getMchApplyment().getIsvNo(), CS.IF_CODE.WXPAY, selectOauth2InfoId);

                        }

                    }else{
                        wxpayOauth2Params = (WxpayOauth2Params) configContextQueryService.queryNormalOauth2Params(payOrder.getIsvNo(), payOrder.getAppId(), CS.IF_CODE.WXPAY);
                    }
                    if(wxpayOauth2Params != null){
                        payUrl = WxpayOauth2Params.WX_TG_PRIFEX + dbApplicationConfig.genQrToken(QRCodeParams.TYPE_PAY_ORDER,payOrderId,null);
                        liteObject.put("appid", wxpayOauth2Params.getLiteAppId()); // 小程序的appId
                        liteObject.put("ghid", wxpayOauth2Params.getLiteGhid());  // 小程序原始ID
                        liteObject.put("path", wxpayOauth2Params.getLitePagePath());   // 小程序打开页面
                        liteObject.put("env", wxpayOauth2Params.getLiteEnv());           // 小程序版本
                        liteObject.put("payUrl", payUrl);
                    }
                    payUrl = liteObject.toJSONString();
                }else if(QRCodeParams.ENTRY_LITE_ALIAPP.equals(qrCashierOrderRQ.getEntryLiteType())) {
                    JSONObject liteObject = new JSONObject();
                    AlipayOauth2Params alipayOauth2Params = null;

                    if(mchAppConfigContext.isIsvsubMch()){ // 特约商户

                        // 先获取 特约商户的 小程序 配置信息
                        alipayOauth2Params = (AlipayOauth2Params) configContextQueryService.queryNormalOauth2Params(payOrder.getIsvNo(), payOrder.getAppId(), CS.IF_CODE.ALIPAY);

                        // 商户未配置 || 使用服务商的小程序时 ：  查询服务商的配置信息
                        if(alipayOauth2Params == null || alipayOauth2Params.getIsUseSubmchAccount() == null ||  alipayOauth2Params.getIsUseSubmchAccount() == CS.NO){

                            // 查询服务商的配置，  判断取哪个 配置条目。

                            // 查询商户 配置 的oauth2配置 ( 支付参数配置 )    selectOauth2InfoId: 配置的oauth2参数
                            String ifCodeByMchPassage = configContextQueryService.queryIfCodeByPageType(mchAppConfigContext, QRCodeParams.PAGE_TYPE_ALIPAY_LITE);
                            String selectOauth2InfoId = configContextQueryService.queryAgentOrIsvSelectOauth2InfoId(mchAppConfigContext.getMchApplyment(), ifCodeByMchPassage);
                            alipayOauth2Params = (AlipayOauth2Params) configContextQueryService.
                                    queryIsvOauth2ParamsByAutoSelectOauth2InfoId(mchAppConfigContext.getMchApplyment().getIsvNo(), CS.IF_CODE.ALIPAY, selectOauth2InfoId);

                        }

                    }else{
                        alipayOauth2Params = (AlipayOauth2Params) configContextQueryService.queryNormalOauth2Params(payOrder.getIsvNo(), payOrder.getAppId(), CS.IF_CODE.ALIPAY);
                    }
                    if(alipayOauth2Params != null && alipayOauth2Params.getLiteParams() != null ){
                        liteObject.put("appId", alipayOauth2Params.getLiteParams().getAppId());// 小程appId
                        liteObject.put("path", alipayOauth2Params.getLiteParams().getPagePath());   // 小程序路径
                        liteObject.put("qrUrl", payUrl);            // 聚合码支付URL
                    }

                    payUrl = liteObject.toJSONString();
                }else if(QRCodeParams.ENTRY_LITE_WXH5.equals(qrCashierOrderRQ.getEntryLiteType())) {
                    // 调用微信的接口得到转入小程序的url
                    payUrl = wxLiteUrlLinkService.generateUrlLink(mchAppConfigContext, "q=" + URLUtil.encodeAll(payUrl), payOrder.getExpiredTime().getTime());

                }else if(QRCodeParams.ENTRY_LITE_ALIH5.equals(qrCashierOrderRQ.getEntryLiteType())) {
                    AlipayOauth2Params alipayOauth2Params = null;

                    if(mchAppConfigContext.isIsvsubMch()){ // 特约商户

                        // 先获取 特约商户的 小程序 配置信息
                        alipayOauth2Params = (AlipayOauth2Params) configContextQueryService.queryNormalOauth2Params(payOrder.getIsvNo(), payOrder.getAppId(), CS.IF_CODE.ALIPAY);

                        // 商户未配置 || 使用服务商的小程序时 ：  查询服务商的配置信息
                        if(alipayOauth2Params == null || alipayOauth2Params.getIsUseSubmchAccount() == null ||  alipayOauth2Params.getIsUseSubmchAccount() == CS.NO){

                            // 查询服务商的配置，  判断取哪个 配置条目。

                            // 查询商户 配置 的oauth2配置 ( 支付参数配置 )    selectOauth2InfoId: 配置的oauth2参数
                            String ifCodeByMchPassage = configContextQueryService.queryIfCodeByPageType(mchAppConfigContext, QRCodeParams.PAGE_TYPE_ALIPAY_LITE);
                            String selectOauth2InfoId = configContextQueryService.queryAgentOrIsvSelectOauth2InfoId(mchAppConfigContext.getMchApplyment(), ifCodeByMchPassage);
                            alipayOauth2Params = (AlipayOauth2Params) configContextQueryService.
                                    queryIsvOauth2ParamsByAutoSelectOauth2InfoId(mchAppConfigContext.getMchApplyment().getIsvNo(), CS.IF_CODE.ALIPAY, selectOauth2InfoId);

                        }

                    }else{
                        alipayOauth2Params = (AlipayOauth2Params) configContextQueryService.queryNormalOauth2Params(payOrder.getIsvNo(), payOrder.getAppId(), CS.IF_CODE.ALIPAY);
                    }
                    if(alipayOauth2Params == null || alipayOauth2Params.getLiteParams() == null ){
                        throw new BizException("请配置支付宝小程序oauth2参数");
                    }

                    String alipayStartAppUrl = String.format("alipays://platformapi/startapp?appId=%s", alipayOauth2Params.getLiteParams().getAppId());
                    if (StringUtils.isNotBlank(alipayOauth2Params.getLiteParams().getPagePath())) {
                        alipayStartAppUrl = alipayStartAppUrl + "&page=" + alipayOauth2Params.getLiteParams().getPagePath();
                    }
                    if (StringUtils.isNotBlank(payUrl)) {
                        alipayStartAppUrl = alipayStartAppUrl + "&query=qrCode=" + payUrl;
                    }

                    payUrl = alipayStartAppUrl;

                }else if(QRCodeParams.ENTRY_LITE_ALIJSAPIH5.equals(qrCashierOrderRQ.getEntryLiteType())) { // 支付宝 H5 --> 生活号服务窗。
//                    String alipayStartAppUrl = "alipays://platformapi/startapp?appId=20000067"; // 20000067 固定格式。
//                    if (StringUtils.isNotBlank(payUrl)) {
//                        alipayStartAppUrl = alipayStartAppUrl + "&url=" + URLUtil.encodeAll(payUrl);
//                    }
                    //TODO 更改
                    String alipayStartAppUrl = "https://ds.alipay.com/?scheme=";
                    String scheme = "alipays://platformapi/startapp?appId=10000007&qrcode=";
                    if (StringUtils.isNotBlank(payUrl)) {
                        alipayStartAppUrl = alipayStartAppUrl + URLUtil.encodeAll(scheme + URLUtil.encodeAll(payUrl));
                    }
                    payUrl = alipayStartAppUrl;
                }
            }

            qrCashierOrderRS.setPayUrl(payUrl);
        }

        return packageApiResByPayOrder(bizRQ, qrCashierOrderRS, payOrder);

    }


    /**  统一收银台  */
    private ApiRes webCashierPayWay(UnifiedOrderRQ bizRQ, MchAppConfigContext mchAppConfigContext,RouteManage routeManage){

        WebCashierOrderRQ webCashierOrderRQ = (WebCashierOrderRQ)bizRQ;

        // 判断商户是否拥有 可用通道
//        int passageCount = mchPayPassageService.count(MchPayPassage.gw().eq(MchPayPassage::getAppId, mchApp.getAppId()).eq(MchPayPassage::getState, CS.YES));
//        if(passageCount <= 0){
//            throw new BizException("当前商户未开通任何支付通道！");
//        }

        //生成订单
        PayOrder payOrder = genPayOrder(bizRQ, mchAppConfigContext, null, null, null,routeManage);
        String payOrderId = payOrder.getPayOrderId();
        //订单入库 订单状态： 生成状态  此时没有和任何上游渠道产生交互。
        payOrderService.save(payOrder);

        WebCashierOrderRS webCashierOrderRS = new WebCashierOrderRS();

        DBApplicationConfig dbApplicationConfig = sysConfigService.getDBApplicationConfig();

        String payUrl = dbApplicationConfig.genWebCashierPayUrl(payOrderId);

        webCashierOrderRS.setPayUrl(payUrl);

        return packageApiResByPayOrder(bizRQ, webCashierOrderRS, payOrder);

    }

    /**  检查设备类型和设备号传值必填性  */
    private void checkDeviceParams(UnifiedOrderRQ bizRQ, String ifCode) {
        if (StringUtils.isBlank(bizRQ.getDeviceInfo())) {
            return;
        }

        UnifiedOrderRQ.DeviceInfo deviceInfo = JSON.parseObject(bizRQ.getDeviceInfo(), UnifiedOrderRQ.DeviceInfo.class);
        if (deviceInfo == null) {
            return;
        }

        String deviceType = deviceInfo.getDeviceType();
        String deviceNo = deviceInfo.getDeviceNo();
        String provider = deviceInfo.getProvider();

        if (StringUtils.isBlank(deviceType)) {
            throw new BizException("设备类型必填");
        }

        if (!PayOrder.DEVICE_TYPE_MAP.containsKey(deviceType)) {
            throw new BizException("请传入正确的设备类型");
        }

        if (StringUtils.isBlank(deviceNo)) {
            throw new BizException("设备号必填");
        }

        // 智能POS
        if (StringUtils.equalsAny(deviceInfo.getDeviceType(), PayOrder.DEVICE_TYPE_AUTO_POS)) {
            MchStoreDevice mchStoreDevice = mchStoreDeviceService.getByUniqueKey(ifCode, MchStoreDevice.DEVICE_TYPE_AUTO_POS, deviceInfo.getDeviceNo());
            if (mchStoreDevice == null) {
                throw new BizException("智能POS设备不存在！");
            }
            if(mchStoreDevice.getBindState() == CS.NO || !bizRQ.getMchNo().equals(mchStoreDevice.getMchNo())){
                throw new BizException("智能POS设备所属商户不匹配！");
            }
            deviceInfo.setProvider(ifCode);
        }


        // 设备类型为扫码POS、收银插件
        else if (StringUtils.equalsAny(deviceType, PayOrder.DEVICE_TYPE_SCAN_POS, PayOrder.DEVICE_TYPE_CASH_PLUGIN, PayOrder.DEVICE_TYPE_PRINTER)) {
            if (StringUtils.isBlank(provider)) {
                throw new BizException("设备厂商必填");
            }

            MchStoreDevice mchStoreDevice = mchStoreDeviceService.getByUniqueKey(provider, (Byte) MchStoreDevice.covertDeviceType(deviceType), deviceNo);
            if (mchStoreDevice == null) {
                throw new BizException("终端设备不存在！");
            }
            if(mchStoreDevice.getBindState() == CS.NO || !bizRQ.getMchNo().equals(mchStoreDevice.getMchNo())){
                throw new BizException("终端设备所属商户不匹配！");
            }

            DeviceProvideConfig provideConfig = deviceProvideConfigService.getById(mchStoreDevice.getConfigId());
            if (provideConfig == null || provideConfig.getState() == CS.NO || !provider.equals(provideConfig.getProvider())) {
                throw new BizException("终端设备厂商错误！");
            }
        }
    }

    /**  获取服务商机构号和子商户号  */
    private String[] getChannelMchNo(MchAppConfigContext mchAppConfigContext) {
        final String[] channelMchNoArr = { null, null };
        if (mchAppConfigContext.isIsvsubMch()) {
            IsvParams isvParams = configContextQueryService.queryIsvParams(mchAppConfigContext.getMchApplyment().getIsvNo(), mchAppConfigContext.getMchApplyment().getIfCode());
            channelMchNoArr[0] = ChannelMchIdKit.getChannelMchNo(isvParams, ChannelMchIdTypeEnum.ISV_NO);

//            IsvsubMchParams isvsubMchParams = configContextQueryService.queryIsvsubMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), ifCode);
            channelMchNoArr[1] = mchAppConfigContext.getMchApplyment().getChannelMchNo();

        }else {
//            NormalMchParams normalMchParams = configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), ifCode);
//            channelMchNoArr[1] = ChannelMchIdKit.getChannelMchNo(normalMchParams, ChannelMchIdTypeEnum.MCH_NO);
            channelMchNoArr[1] = mchAppConfigContext.getMchApplyment().getChannelMchNo();
        }
        return channelMchNoArr;
    }

}
