package com.slodon.b2b2c.controller.seller.seller;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.URLUtil;
import com.alibaba.fastjson.JSON;
import com.slodon.b2b2c.core.constant.StoreConst;
import com.slodon.b2b2c.core.constant.StoreOpenAccountConst;
import com.slodon.b2b2c.core.controller.BaseController;
import com.slodon.b2b2c.core.exception.MallException;
import com.slodon.b2b2c.core.response.JsonResult;
import com.slodon.b2b2c.core.response.SldResponse;
import com.slodon.b2b2c.core.util.AssertUtil;
import com.slodon.b2b2c.core.util.FileUrlUtil;
import com.slodon.b2b2c.core.util.UserUtil;
import com.slodon.b2b2c.model.seller.StoreCertificateModel;
import com.slodon.b2b2c.model.seller.StoreOpenAccountModel;
import com.slodon.b2b2c.model.seller.VendorModel;
import com.slodon.b2b2c.seller.dto.SellerBindCardDTO;
import com.slodon.b2b2c.seller.dto.SellerOpenAccountDTO;
import com.slodon.b2b2c.seller.example.StoreCertificateExample;
import com.slodon.b2b2c.seller.pojo.StoreCertificate;
import com.slodon.b2b2c.seller.pojo.StoreOpenAccount;
import com.slodon.b2b2c.seller.pojo.Vendor;
import com.slodon.b2b2c.starter.entity.cpcn.*;
import com.slodon.b2b2c.starter.util.ZjPayUtil;
import com.slodon.b2b2c.vo.seller.StoreOpenAccountVO;
import com.slodon.b2b2c.vo.seller.TxSellerSignVO;
import cpcn.institution.tools.util.StringUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import payment.api.notice.Notice4618Request;
import payment.api.notice.Notice7709Request;
import payment.api.notice.NoticeRequest;
import payment.api.notice.NoticeResponse;
import payment.api.tx.enrollment.Tx7703Response;
import payment.api.tx.foundationaccount.Tx4601Response;
import payment.api.tx.foundationaccount.Tx4611Response;
import payment.api.tx.foundationaccount.Tx4613Response;
import payment.api.tx.payroll.Tx4600Response;
import payment.api.util.GUIDGenerator;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;

@Api(tags = "seller-商户开户接口,1-绑卡，2-绑卡确认，3-开户")
@Slf4j
@RestController
@RequestMapping("v3/seller/seller/openAccount")
public class SellerOpenAccountController extends BaseController {

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private StoreOpenAccountModel storeOpenAccountModel;
    @Resource
    private VendorModel vendorModel;
    @Resource
    private StoreCertificateModel storeCertificateModel;

    @ApiOperation("获取开户信息")
    @GetMapping("openAccountInfo")
    public JsonResult<StoreOpenAccountVO> openAccountInfo(HttpServletRequest request) {
        Vendor vendor = UserUtil.getUser(request, Vendor.class);
        StoreOpenAccountVO vo;
        StoreOpenAccount storeOpenAccount = storeOpenAccountModel.getStoreOpenAccountByStoreId(vendor.getStoreId());
        if (storeOpenAccount == null){
            //没有绑卡或者开户记录
            vo = new StoreOpenAccountVO();
            vo.setState(StoreOpenAccountConst.STATE_0);
            //查询入驻信息
            StoreCertificateExample storeCertificateExample = new StoreCertificateExample();
            storeCertificateExample.setVendorId(vendorModel.getStoreMainVendorId(vendor));
            List<StoreCertificate> storeCertificateList = storeCertificateModel.getStoreCertificateList(storeCertificateExample, null);
            AssertUtil.notEmpty(storeCertificateList,"店铺未入驻");
            StoreCertificate storeCertificate = storeCertificateList.get(0);
            if (storeCertificate.getEnterType().equals(StoreConst.APPLY_TYPE_PERSON)) {
                //个人
                vo.setUserType("11");
                vo.setBankAccountName(storeCertificate.getContactName());
                vo.setBankPhoneNumber(storeCertificate.getContactPhone());
            }else {
                //企业
                vo.setUserType("12");
                vo.setBankAccountName(storeCertificate.getCompanyName());
            }

        }else {
            vo = new StoreOpenAccountVO(storeOpenAccount);
        }

        return SldResponse.success(vo);
    }

    @ApiOperation("绑定银行卡，data=>bindingTxSn")
    @PostMapping("bindCard")
    public JsonResult<Object> bindCard(HttpServletRequest request, SellerBindCardDTO sellerBindCardDTO) throws Exception {
        Vendor vendor = UserUtil.getUser(request, Vendor.class);

        /*
         * 1.开户表保存绑卡信息
         * 2.向中金提交打款申请
         * 3.更新开户表中的绑卡流水号
         */
        //查询绑卡进度
        StoreOpenAccount storeOpenAccount = storeOpenAccountModel.getStoreOpenAccountByStoreId(vendor.getStoreId());
        if (storeOpenAccount == null){
            //1.开户表保存绑卡信息
            storeOpenAccount = new StoreOpenAccount();
            storeOpenAccount.setStoreId(vendor.getStoreId());

            storeOpenAccount.setState(StoreOpenAccountConst.STATE_0);
            storeOpenAccount.setBankAccountName(sellerBindCardDTO.getBankAccountName());
            storeOpenAccount.setBankAccountNumber(sellerBindCardDTO.getBankAccountNumber());
            storeOpenAccount.setBankId(sellerBindCardDTO.getBankId());
            storeOpenAccount.setCredentialNumber(sellerBindCardDTO.getCredentialNumber());
            storeOpenAccount.setBankPhoneNumber(sellerBindCardDTO.getBankPhoneNumber());
            storeOpenAccount.setBranchName(sellerBindCardDTO.getBranchName());
            storeOpenAccount.setProvince(sellerBindCardDTO.getProvince());
            storeOpenAccount.setCity(sellerBindCardDTO.getCity());

            //查询入驻信息
            StoreCertificateExample storeCertificateExample = new StoreCertificateExample();
            storeCertificateExample.setVendorId(vendorModel.getStoreMainVendorId(vendor));
            List<StoreCertificate> storeCertificateList = storeCertificateModel.getStoreCertificateList(storeCertificateExample, null);
            AssertUtil.notEmpty(storeCertificateList,"店铺未入驻");
            StoreCertificate storeCertificate = storeCertificateList.get(0);
            //入驻信息写入开户表
            if (storeCertificate.getEnterType().equals(StoreConst.APPLY_TYPE_PERSON)){
                //个人
                storeOpenAccount.setUserType("11");
                storeOpenAccount.setPUserName(storeCertificate.getContactName());
                storeOpenAccount.setPPhoneNumber(storeCertificate.getContactPhone());
                storeOpenAccount.setPIndCardUp(storeCertificate.getPersonCardUp());
                storeOpenAccount.setPIndCardDown(storeCertificate.getPersonCardDown());
                storeOpenAccount.setPIndAddress(storeCertificate.getCompanyAddress());
            }else {
                //企业
                storeOpenAccount.setUserType("12");
                storeOpenAccount.setECorporationName(storeCertificate.getCompanyName());
                storeOpenAccount.setEProvince(storeCertificate.getCompanyProvinceCode());
                storeOpenAccount.setECity(storeCertificate.getCompanyCityCode());
                storeOpenAccount.setEDistrict(storeCertificate.getCompanyAreaCode());
                storeOpenAccount.setECorAddress(storeCertificate.getCompanyAddress());
                storeOpenAccount.setEBusinessLicenseImage(storeCertificate.getBusinessLicenseImage());
                storeOpenAccount.setELegalCardUp(storeCertificate.getPersonCardUp());
                storeOpenAccount.setELegalCardDown(storeCertificate.getPersonCardDown());
            }

            storeOpenAccountModel.saveStoreOpenAccount(storeOpenAccount);
        }
        if (!StringUtils.isEmpty(storeOpenAccount.getBindCardSn())){
            //已经保存过绑卡流水号，说明已打款，不能重复打款
            return SldResponse.fail("已打款,请输入打款金额");
        }

        //2.向中金提交打款申请
        TxBindCardRequest txBindCardRequest = new TxBindCardRequest();
        BeanUtils.copyProperties(sellerBindCardDTO,txBindCardRequest);
        txBindCardRequest.setInstitutionId(stringRedisTemplate.opsForValue().get("zj_pay_institution_id"));
        txBindCardRequest.setBindingTxSn(GUIDGenerator.genGUID());
        txBindCardRequest.setUserId(vendor.getStoreId().toString());
        txBindCardRequest.setOperationFlag("10");
        txBindCardRequest.setBindingWay("11".equals(sellerBindCardDTO.getBankAccountType()) ? "10" : "20");//个人账号短信验证，企业账户打款验证
        Tx4611Response txResponse = ZjPayUtil.bindCard(txBindCardRequest);
        log.debug("绑卡响应==================================================================");
        log.debug(JSON.toJSONString(txResponse));
        log.debug("==================================================================");
        if (!"2000".equals(txResponse.getCode())){
            //请求失败
            log.error(txBindCardRequest.getBindingTxSn() + "绑卡请求失败：" + txResponse.getMessage());
            throw new MallException(txResponse.getMessage());
        }
        if ("40".equals(txResponse.getStatus())){
            //请求失败
            log.error(txBindCardRequest.getBindingTxSn() + "绑卡请求失败：" + txResponse.getResponseMessage());
            throw new MallException(txResponse.getResponseMessage());
        }

        //3.更新开户表中的绑卡流水号
        StoreOpenAccount update = new StoreOpenAccount();
        update.setOpenAccountId(storeOpenAccount.getOpenAccountId());
        update.setBindCardSn(txResponse.getBindingTxSN());
        storeOpenAccountModel.updateStoreOpenAccount(update);
        return SldResponse.success((Object) txResponse.getBindingTxSN());
    }

    @ApiOperation("确认绑卡")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "bindingTxSn", value = "绑卡流水号", required = true),
            @ApiImplicitParam(name = "verifyWay", value = "验证方式：10-短信验证，20-小额打款验证", required = true),
            @ApiImplicitParam(name = "code", value = "短信验证码/打款金额（单位分）", required = true),
    })
    @PostMapping("confirmBindCard")
    public JsonResult<Object> confirmBindCard(HttpServletRequest request,
                                              @RequestParam(value = "verifyWay") String verifyWay,
                                              @RequestParam(value = "code") String code) throws Exception {
        Vendor vendor = UserUtil.getUser(request, Vendor.class);
        //查询绑卡进度
        StoreOpenAccount storeOpenAccount = storeOpenAccountModel.getStoreOpenAccountByStoreId(vendor.getStoreId());
        AssertUtil.notNull(storeOpenAccount,"请提交打款申请");
        TxBindCardConfirmRequest txBindCardConfirmRequest = new TxBindCardConfirmRequest();
        txBindCardConfirmRequest.setInstitutionId(stringRedisTemplate.opsForValue().get("zj_pay_institution_id"));
        txBindCardConfirmRequest.setBindingTxSn(storeOpenAccount.getBindCardSn());
        txBindCardConfirmRequest.setVerifyWay(verifyWay);
        if ("10".equals(verifyWay)) {
            txBindCardConfirmRequest.setSmsCode(code);
        }else if ("20".equals(verifyWay)) {
            txBindCardConfirmRequest.setAmount(code);
        }else {
            return SldResponse.fail("验证方式有误");
        }

        Tx4613Response txResponse = ZjPayUtil.confirmBindCard(txBindCardConfirmRequest);
        log.debug("确认绑卡响应==================================================================");
        log.debug(JSON.toJSONString(txResponse));
        log.debug("==================================================================");
        if (!"2000".equals(txResponse.getCode())){
            //请求失败
            log.error(txBindCardConfirmRequest.getBindingTxSn() + "绑卡确认请求失败：" + txResponse.getMessage());
            return SldResponse.fail(txResponse.getMessage());
        }
        if (!"30".equals(txResponse.getStatus())){
            //确认失败
            log.error(txBindCardConfirmRequest.getBindingTxSn() + "绑卡确认请求失败：" + txResponse.getResponseMessage());
            return SldResponse.fail(txResponse.getResponseMessage());
        }

        //更新状态
        StoreOpenAccount update = new StoreOpenAccount();
        update.setOpenAccountId(storeOpenAccount.getOpenAccountId());
        update.setState(StoreOpenAccountConst.STATE_10);
        storeOpenAccountModel.updateStoreOpenAccount(update);

        return SldResponse.success("绑卡成功");
    }

    @ApiOperation("开户")
    @PostMapping("openAccount")
    public JsonResult<Object> openAccount(HttpServletRequest request,  SellerOpenAccountDTO sellerOpenAccountDTO) throws Exception {
        Vendor vendor = UserUtil.getUser(request, Vendor.class);

        //todo 参数校验

        //查询绑卡进度
        StoreOpenAccount storeOpenAccount = storeOpenAccountModel.getStoreOpenAccountByStoreId(vendor.getStoreId());
        if (storeOpenAccount == null){
            return SldResponse.fail("请绑定银行卡后开户");
        }

        AssertUtil.isTrue(storeOpenAccount.getState().equals(StoreOpenAccountConst.STATE_20),"开户已完成");
        AssertUtil.isTrue(storeOpenAccount.getState().equals(StoreOpenAccountConst.STATE_22),"开户请求处理中，请等待");

        //获取影印件上传流水号
        String imageCollectionSn = this.getImageCollectionSn(storeOpenAccount,vendor,sellerOpenAccountDTO);

        //中金开户
        Tx4601Response tx4601Response = this.doOpenAccount(sellerOpenAccountDTO,storeOpenAccount,imageCollectionSn);

        String status = tx4601Response.getStatus();
        String returnMsg = "";

        //更新开户信息
        StoreOpenAccount update = new StoreOpenAccount();
        update.setOpenAccountId(storeOpenAccount.getOpenAccountId());
        update.setOpenAccountSn(tx4601Response.getTxSN());
        update.setPUserName(sellerOpenAccountDTO.getpUserName());
        update.setPPhoneNumber(sellerOpenAccountDTO.getpPhoneNumber());
        update.setPCredentialNumber(sellerOpenAccountDTO.getpCredentialNumber());
        update.setPIssDate(sellerOpenAccountDTO.getpIssDate());
        update.setPExpiryDate(sellerOpenAccountDTO.getpExpiryDate());
        update.setPIndAddress(sellerOpenAccountDTO.getpIndAddress());
        update.setPIndEmail(sellerOpenAccountDTO.getpIndEmail());
        update.setPIndCardUp(sellerOpenAccountDTO.getpIndCardUp());
        update.setPIndCardDown(sellerOpenAccountDTO.getpIndCardDown());
        update.setECorporationName(sellerOpenAccountDTO.geteCorporationName());
        update.setECategoryType(sellerOpenAccountDTO.geteCategoryType());
        update.setEUnifiedSocialCreditCode(sellerOpenAccountDTO.geteUnifiedSocialCreditCode());
        update.setECorEmail(sellerOpenAccountDTO.geteCorEmail());
        update.setECorAddress(sellerOpenAccountDTO.geteCorAddress());
        update.setEProvince(sellerOpenAccountDTO.geteProvince());
        update.setECity(sellerOpenAccountDTO.geteCity());
        update.setEDistrict(sellerOpenAccountDTO.geteDistrict());
        update.setEScale(sellerOpenAccountDTO.geteScale());
        update.setEBasicAcctNo(sellerOpenAccountDTO.geteBasicAcctNo());
        update.setEApprovalNo(sellerOpenAccountDTO.geteApprovalNo());
        update.setEAllLicenceIssDate(sellerOpenAccountDTO.geteAllLicenceIssDate());
        update.setEAllLicenceExpiryDate(sellerOpenAccountDTO.geteAllLicenceExpiryDate());
        update.setELegalPersonName(sellerOpenAccountDTO.geteLegalPersonName());
        update.setELegalCredentialNumber(sellerOpenAccountDTO.geteLegalCredentialNumber());
        update.setELegalPersonIssDate(sellerOpenAccountDTO.geteLegalPersonIssDate());
        update.setELegalPersonExpiryDate(sellerOpenAccountDTO.geteLegalPersonExpiryDate());
        update.setELegalPersonContactNumber(sellerOpenAccountDTO.geteLegalPersonContactNumber());
        update.setEBusinessLicenseImage(sellerOpenAccountDTO.geteBusinessLicenseImage());
        update.setEOpenAccountCertificate(sellerOpenAccountDTO.geteOpenAccountCertificate());
        update.setELegalCardUp(sellerOpenAccountDTO.geteLegalCardUp());
        update.setELegalCardDown(sellerOpenAccountDTO.geteLegalCardDown());
        switch (status){
            case "10"://受理成功
            case "20"://处理中
                update.setState(StoreOpenAccountConst.STATE_22);
                update.setFailReason("");
                returnMsg = "开户请求处理中";
                break;
            case "30":
                //成功
                update.setState(StoreOpenAccountConst.STATE_20);
                update.setFailReason("");
                returnMsg = "开户成功";
                break;
            case "40":
                update.setState(StoreOpenAccountConst.STATE_21);
                update.setFailReason(tx4601Response.getResponseMessage());
                returnMsg = "开户失败：" + tx4601Response.getResponseMessage();
                //失败
                break;
            default:
                break;
        }
        storeOpenAccountModel.updateStoreOpenAccount(update);
        return SldResponse.success(returnMsg);
    }

    /**
     * 向中金发送开户请求
     * @param sellerOpenAccountDTO 开户信息
     * @param storeOpenAccount 商户开户进度
     * @param imageCollectionSn 影印件上传流水号
     * @return 开户响应
     */
    private Tx4601Response doOpenAccount(SellerOpenAccountDTO sellerOpenAccountDTO, StoreOpenAccount storeOpenAccount, String imageCollectionSn) throws Exception {
        TxOpenAccountRequest txOpenAccountRequest = new TxOpenAccountRequest();


        txOpenAccountRequest.setInstitutionId(stringRedisTemplate.opsForValue().get("zj_pay_institution_id"));
        txOpenAccountRequest.setTxSn(GUIDGenerator.genGUID());
        txOpenAccountRequest.setUserId(storeOpenAccount.getStoreId().toString());
        txOpenAccountRequest.setUserType(storeOpenAccount.getUserType());
        txOpenAccountRequest.setImageCollectionTxSN(imageCollectionSn);

        if (storeOpenAccount.getUserType().equals("11")){
            //个人
            TxOpenAccountRequest.PersonalInfo personalInfo = new TxOpenAccountRequest.PersonalInfo();
            personalInfo.setPhoneNumber(sellerOpenAccountDTO.getpPhoneNumber());
            personalInfo.setUserName(sellerOpenAccountDTO.getpUserName());
            personalInfo.setCredentialNumber(sellerOpenAccountDTO.getpCredentialNumber());
            personalInfo.setIssDate(sellerOpenAccountDTO.getpIssDate());
            personalInfo.setExpiryDate(sellerOpenAccountDTO.getpExpiryDate());
            personalInfo.setIndAddress(sellerOpenAccountDTO.getpIndAddress());
            personalInfo.setIndEmail(sellerOpenAccountDTO.getpIndEmail());
            txOpenAccountRequest.setPersonalInfo(personalInfo);
        }else {
            //企业
            TxOpenAccountRequest.EnterpriseInfo enterpriseInfo = new TxOpenAccountRequest.EnterpriseInfo();
            enterpriseInfo.setCorporationName(sellerOpenAccountDTO.geteCorporationName());
            enterpriseInfo.setCorporationShort(sellerOpenAccountDTO.geteCorporationName());
            enterpriseInfo.setCategoryType(sellerOpenAccountDTO.geteCategoryType());
            enterpriseInfo.setCorEmail(sellerOpenAccountDTO.geteCorEmail());
            enterpriseInfo.setCorAddress(sellerOpenAccountDTO.geteCorAddress());
            enterpriseInfo.setProvince(sellerOpenAccountDTO.geteProvince().substring(0,2));
            enterpriseInfo.setCity(sellerOpenAccountDTO.geteCity());
            enterpriseInfo.setDistrict(sellerOpenAccountDTO.geteDistrict());
            enterpriseInfo.setScale(sellerOpenAccountDTO.geteScale());
            enterpriseInfo.setBasicAcctNo(sellerOpenAccountDTO.geteBasicAcctNo());
            enterpriseInfo.setApprovalNo(sellerOpenAccountDTO.geteApprovalNo());
            enterpriseInfo.setUnifiedSocialCreditCode(sellerOpenAccountDTO.geteUnifiedSocialCreditCode());
            enterpriseInfo.setAllLicenceIssDate(sellerOpenAccountDTO.geteAllLicenceIssDate());
            enterpriseInfo.setAllLicenceExpiryDate(sellerOpenAccountDTO.geteAllLicenceExpiryDate());
            enterpriseInfo.setLegalPersonName(sellerOpenAccountDTO.geteLegalPersonName());
            enterpriseInfo.setLegalCredentialNumber(sellerOpenAccountDTO.geteLegalCredentialNumber());
            enterpriseInfo.setLegalPersonIssDate(sellerOpenAccountDTO.geteLegalPersonIssDate());
            enterpriseInfo.setLegalPersonExpiryDate(sellerOpenAccountDTO.geteLegalPersonExpiryDate());
            enterpriseInfo.setLegalPersonContactNumber(sellerOpenAccountDTO.geteLegalPersonContactNumber());
            txOpenAccountRequest.setEnterpriseInfo(enterpriseInfo);
        }

        Tx4601Response tx4601Response = ZjPayUtil.openAccount(txOpenAccountRequest);
        log.debug("开户响应==================================================================");
        log.debug(JSON.toJSONString(tx4601Response));
        log.debug("==================================================================");
        if (!"2000".equals(tx4601Response.getCode())){
            //请求失败
            log.error(txOpenAccountRequest.getTxSn() + "开户请求失败：" + tx4601Response.getMessage());
            throw new MallException(tx4601Response.getMessage());
        }

        return tx4601Response;
    }

    /**
     * 获取影印件上传流水号
     *
     * @param storeOpenAccount 商户开户进度
     * @param vendor 管理员
     * @param sellerOpenAccountDTO 开户信息
     * @return 影印件上传流水号
     */
    private String getImageCollectionSn(StoreOpenAccount storeOpenAccount, Vendor vendor, SellerOpenAccountDTO sellerOpenAccountDTO) throws Exception {

        if (!StringUtils.isEmpty(storeOpenAccount.getImageCollectionSn())){
            //已经上传过资料
            return storeOpenAccount.getImageCollectionSn();
        }

        //上传影印资料
        TxSellerQualificationRequest txSellerQualificationRequest = new TxSellerQualificationRequest();
        txSellerQualificationRequest.setInstitutionId(stringRedisTemplate.opsForValue().get("zj_pay_institution_id"));
        txSellerQualificationRequest.setTxSn(GUIDGenerator.genGUID());
        txSellerQualificationRequest.setUserId(vendor.getStoreId().toString());

        if (storeOpenAccount.getUserType().equals("11")){
            //个人,上传身份证正反面
            String cardUp = FileUrlUtil.getFileUrl(sellerOpenAccountDTO.getpIndCardUp(), null);
            AssertUtil.notNull(cardUp,"请补全身份证正面信息");
            String cardUpBase64 = Base64.encode(URLUtil.getStream(URLUtil.url(cardUp)));
            String cardDown = FileUrlUtil.getFileUrl(sellerOpenAccountDTO.getpIndCardDown(), null);
            AssertUtil.notNull(cardDown,"请补全身份证背面信息");
            String cardDownBase64 = Base64.encode(URLUtil.getStream(URLUtil.url(cardDown)));
            txSellerQualificationRequest.getImageList().add(new TxSellerQualificationRequest.QualificationImage(GUIDGenerator.genGUID(),"10",cardUpBase64));
            txSellerQualificationRequest.getImageList().add(new TxSellerQualificationRequest.QualificationImage(GUIDGenerator.genGUID(),"11",cardDownBase64));
        }else {
            //企业,上传身份证正反面、营业执照和基本开户证明
            String cardUp = FileUrlUtil.getFileUrl(sellerOpenAccountDTO.geteLegalCardUp(), null);
            AssertUtil.notNull(cardUp,"请补全身份证正面信息");
            String cardUpBase64 = Base64.encode(URLUtil.getStream(URLUtil.url(cardUp)));
            String cardDown = FileUrlUtil.getFileUrl(sellerOpenAccountDTO.geteLegalCardDown(), null);
            AssertUtil.notNull(cardDown,"请补全身份证背面信息");
            String cardDownBase64 = Base64.encode(URLUtil.getStream(URLUtil.url(cardDown)));
            txSellerQualificationRequest.getImageList().add(new TxSellerQualificationRequest.QualificationImage(GUIDGenerator.genGUID(),"10",cardUpBase64));
            txSellerQualificationRequest.getImageList().add(new TxSellerQualificationRequest.QualificationImage(GUIDGenerator.genGUID(),"11",cardDownBase64));

            String businessLicenseImage = FileUrlUtil.getFileUrl(sellerOpenAccountDTO.geteBusinessLicenseImage(), null);
            AssertUtil.notNull(businessLicenseImage,"请补全营业执照信息");
            String businessLicenseImageBase64 = Base64.encode(URLUtil.getStream(URLUtil.url(businessLicenseImage)));
            String openAccountCertificate = FileUrlUtil.getFileUrl(sellerOpenAccountDTO.geteOpenAccountCertificate(), null);
            AssertUtil.notNull(openAccountCertificate,"请补全基础开户证明信息");
            String openAccountCertificateBase64 = Base64.encode(URLUtil.getStream(URLUtil.url(openAccountCertificate)));
            txSellerQualificationRequest.getImageList().add(new TxSellerQualificationRequest.QualificationImage(GUIDGenerator.genGUID(),"20",businessLicenseImageBase64));
            txSellerQualificationRequest.getImageList().add(new TxSellerQualificationRequest.QualificationImage(GUIDGenerator.genGUID(),"21",openAccountCertificateBase64));
        }

        //上传中金
        Tx4600Response tx4600Response = ZjPayUtil.uploadSellerQualification(txSellerQualificationRequest);
        log.debug("影印资料上传响应==================================================================");
        log.debug(JSON.toJSONString(tx4600Response));
        log.debug("==================================================================");
        if (!"2000".equals(tx4600Response.getCode())){
            //请求失败
            log.error(txSellerQualificationRequest.getTxSn() + "上传影印资料失败：" + tx4600Response.getMessage());
            throw new MallException(tx4600Response.getMessage());
        }

        //更新开户进度
        StoreOpenAccount update = new StoreOpenAccount();
        update.setOpenAccountId(storeOpenAccount.getOpenAccountId());
        update.setImageCollectionSn(tx4600Response.getTxSN());
        storeOpenAccountModel.updateStoreOpenAccount(update);
        return tx4600Response.getTxSN();
    }

    @ApiOperation("签约，状态为20开户完成或31签约处理中，可以签约")
    @PostMapping("sign")
    public JsonResult<TxSellerSignVO> sign(HttpServletRequest request) throws Exception {
        Vendor vendor = UserUtil.getUser(request, Vendor.class);
        //查询开户信息
        StoreOpenAccount storeOpenAccount = storeOpenAccountModel.getStoreOpenAccountByStoreId(vendor.getStoreId());
        AssertUtil.notNull(storeOpenAccount,"请先开户");
        AssertUtil.isTrue(storeOpenAccount.getState().equals(StoreOpenAccountConst.STATE_30),"已完成签约");
        AssertUtil.isTrue(storeOpenAccount.getState().equals(StoreOpenAccountConst.STATE_31),"签约处理中");
        AssertUtil.isTrue(!storeOpenAccount.getState().equals(StoreOpenAccountConst.STATE_20),"开户未完成");

        //签约申请
        SellerSignRequest sellerSignRequest = new SellerSignRequest();
        sellerSignRequest.setInstitutionId(stringRedisTemplate.opsForValue().get("zj_pay_institution_id"));
        sellerSignRequest.setApplyNo(vendor.getStoreId() + DateUtil.format(new Date(),"yyyyMMddHHmmss"));
        sellerSignRequest.setUserId(vendor.getStoreId().toString());
        sellerSignRequest.setProtocolNumber(stringRedisTemplate.opsForValue().get("zj_protocol_number"));
        Tx7703Response tx7703Response = ZjPayUtil.sellerSign(sellerSignRequest);
        log.debug("签约申请响应==================================================================");
        log.debug(JSON.toJSONString(tx7703Response));
        log.debug("==================================================================");
        if (!"2000".equals(tx7703Response.getCode())){
            //请求失败
            log.error(sellerSignRequest.getApplyNo() + "签约申请失败：" + tx7703Response.getMessage());
            throw new MallException(tx7703Response.getMessage());
        }
        String status = tx7703Response.getStatus();
        if (!"10".equals(status)){
            //受理失败
            throw new MallException("受理失败");
        }

        //更新状态，签约处理中
        StoreOpenAccount update = new StoreOpenAccount();
        update.setOpenAccountId(storeOpenAccount.getOpenAccountId());
        update.setState(StoreOpenAccountConst.STATE_31);
        storeOpenAccountModel.updateStoreOpenAccount(update);

        return SldResponse.success(new TxSellerSignVO(tx7703Response.getPhoneNumber(),tx7703Response.getApplyNo(),ZjPayUtil.ZJ_SIGN_URL));
    }

    /**
     * 中金开户、绑卡回调
     *
     */
    @PostMapping("callback")
    public void openAccountCallback(HttpServletRequest request, HttpServletResponse response) {
        log.debug("=========================================");
        log.debug("中金开户回调开始");
        log.debug(JSON.toJSONString(request.getParameterMap()));
        log.debug("=========================================");

        try {
            // 1 获得参数message和signature
            String message = request.getParameter("message");
            String signature = request.getParameter("signature");
            String isDgEnv=request.getParameter("isDgEnv");
            String digitalEnvelope=request.getParameter("digitalEnvelope");
            String signAlgorithm=request.getParameter("signAlgorithm");
            String signSN=request.getParameter("signSN");
            String encryptSN=request.getParameter("encryptSN");

            // 2 生成交易结果对象
            NoticeRequest noticeRequest;
            if("YES".equals(isDgEnv)) {
                //数字信封解密验签
                noticeRequest = new NoticeRequest(message, signature,digitalEnvelope,signSN,encryptSN,signAlgorithm);
            }else {
                //非数字信封解密验签
                if(StringUtil.isNotEmpty(signSN)) {
                    message = message +","+signSN+","+signAlgorithm;
                }
                noticeRequest = new NoticeRequest(message, signature);
            }

            Notice4618Request nr = new Notice4618Request(noticeRequest.getDocument());
            log.debug("=========================================");
            log.debug(JSON.toJSONString(nr));
            log.debug("=========================================");

            /*
            回调类型:
            4601=开户
            4611=绑卡
            4600=影印件审核结果通知
             */
            String sourceTxCode = nr.getSourceTxCode();
            if ("4601".equals(sourceTxCode)){
                //开户回调
                String storeId = nr.getUserID();//用户id
                //查询开户信息
                StoreOpenAccount storeOpenAccount = storeOpenAccountModel.getStoreOpenAccountByStoreId(Long.valueOf(storeId));
                //安全校验
                AssertUtil.notNull(storeOpenAccount,"该商户提交未开户请求");
                AssertUtil.isTrue(!nr.getSourceTxSN().equals(storeOpenAccount.getOpenAccountSn()),"开户流水号不匹配");
                if (storeOpenAccount.getState().equals(StoreOpenAccountConst.STATE_20)){
                    //已经开户成功，无需再处理
                    PrintWriter out = response.getWriter();
                    this.response(out);
                    out.flush();
                    out.close();
                }
                String status = nr.getStatus();//开户结果，30成功，40失败
                StoreOpenAccount update = new StoreOpenAccount();
                update.setOpenAccountId(storeOpenAccount.getOpenAccountId());
                switch (status){
                    case "30":
                        update.setState(StoreOpenAccountConst.STATE_20);
                        update.setFailReason("");
                        break;
                    case "40":
                        update.setState(StoreOpenAccountConst.STATE_21);
                        update.setFailReason(nr.getResponseMessage());
                        break;
                    default:
                        throw new MallException("回调状态有误");
                }
                storeOpenAccountModel.updateStoreOpenAccount(update);
            }else {
                //其他回调暂不处理
                PrintWriter out = response.getWriter();
                this.response(out);
                out.flush();
                out.close();
            }

            PrintWriter out = response.getWriter();
            this.response(out);
            out.flush();
            out.close();
        }catch (Exception e){
            log.error("中金支付回调异常：" , e);
        }
    }

    /**
     * 中金开户、绑卡回调
     *
     */
    @PostMapping("callbackSign")
    public void signCallback(HttpServletRequest request, HttpServletResponse response) {
        log.debug("=========================================");
        log.debug("中金签约回调开始");
        log.debug(JSON.toJSONString(request.getParameterMap()));
        log.debug("=========================================");

        try {
            // 1 获得参数message和signature
            String message = request.getParameter("message");
            String signature = request.getParameter("signature");
            String isDgEnv=request.getParameter("isDgEnv");
            String digitalEnvelope=request.getParameter("digitalEnvelope");
            String signAlgorithm=request.getParameter("signAlgorithm");
            String signSN=request.getParameter("signSN");
            String encryptSN=request.getParameter("encryptSN");

            // 2 生成交易结果对象
            NoticeRequest noticeRequest;
            if("YES".equals(isDgEnv)) {
                //数字信封解密验签
                noticeRequest = new NoticeRequest(message, signature,digitalEnvelope,signSN,encryptSN,signAlgorithm);
            }else {
                //非数字信封解密验签
                if(StringUtil.isNotEmpty(signSN)) {
                    message = message +","+signSN+","+signAlgorithm;
                }
                noticeRequest = new NoticeRequest(message, signature);
            }

            Notice7709Request nr = new Notice7709Request(noticeRequest.getDocument());
            log.debug("=========================================");
            log.debug(JSON.toJSONString(nr));
            log.debug("=========================================");
            String applyNo = nr.getApplyNo();
            Long storeId = Long.valueOf(applyNo.substring(0, applyNo.length() - 14));
            //查询开户
            StoreOpenAccount storeOpenAccount = storeOpenAccountModel.getStoreOpenAccountByStoreId(storeId);
            if (storeOpenAccount != null &&
                    (storeOpenAccount.getState().equals(StoreOpenAccountConst.STATE_20) || storeOpenAccount.getState().equals(StoreOpenAccountConst.STATE_31))){
                //开户成功或签约处理中才修改状态
                StoreOpenAccount update = new StoreOpenAccount();
                update.setOpenAccountId(storeOpenAccount.getOpenAccountId());
                update.setState(StoreOpenAccountConst.STATE_30);
                storeOpenAccountModel.updateStoreOpenAccount(update);
            }

            PrintWriter out = response.getWriter();
            this.response(out);
            out.flush();
            out.close();
        }catch (Exception e){
            log.error("中金支付回调异常：" , e);
        }
    }

    /**
     * 给支付平台发送回调成功信息，发送后支付平台不再进行回调
     * @param out 响应
     */
    private void response(PrintWriter out){
        String xmlString = new NoticeResponse().getMessage();
        String base64String = new String(cpcn.institution.tools.util.Base64.encode(xmlString.getBytes(StandardCharsets.UTF_8)));
        out.print(base64String);
    }

}
