package com.crm.pay.service.impl.pay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.crm.pay.dto.finance.withdraw.automatic.FinanceWithdrawOzowDto;
import com.crm.pay.entity.callback.ozow.OzowCallbackEntity;
import com.crm.pay.entity.callback.ozow.OzowPayoutCallbackEntity;
import com.crm.pay.entity.callback.ozow.OzowPayoutWebhookEntity;
import com.crm.pay.entity.property.OzowPropertyEntity;
import com.crm.pay.entity.property.OzowWithdrawPropertyEntity;
import com.crm.pay.service.CustomInfoService;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.FinanceWithdrawService;
import com.crm.pay.service.OzowService;
import com.crm.pay.service.impl.base.BasePayServiceImpl;
import com.crm.rely.backend.core.constant.Constants;
import com.crm.rely.backend.model.constant.PayConstants;
import com.crm.rely.backend.model.dto.finance.withdraw.FinanceWithdrawDto;
import com.crm.rely.backend.model.dto.finance.withdraw.ozow.FinanceWithdrawOzowBankDetailDto;
import com.crm.rely.backend.model.entity.finance.deposit.FinanceDepositAddEntity;
import com.crm.pay.entity.FinanceDepositCallbackUpdateEntity;
import com.crm.pay.entity.finance.withdraw.FinanceWithdrawApproveChannelEntity;
import com.crm.pay.entity.finance.withdraw.FinanceWithdrawApproveChannelSubmitEntity;
import com.crm.pay.entity.finance.withdraw.automatic.FinanceWithdrawInfoEntity;
import com.crm.pay.entity.finance.withdraw.automatic.FinanceWithdrawOzowEntity;
import com.crm.rely.backend.model.pojo.table.CustomInfoTable;
import com.crm.rely.backend.model.pojo.table.FinanceDepositTable;
import com.crm.rely.backend.model.pojo.table.SysRemitChannelTable;
import com.crm.rely.backend.model.pojo.table.SysRemittanceChannelTable;
import com.crm.rely.backend.core.exception.PayValidatedException;
import com.crm.rely.backend.core.exception.ServiceException;
import com.crm.rely.backend.util.AESUtil;
import com.crm.rely.backend.util.HttpUtil;
import com.crm.rely.backend.util.MD5Util;
import com.crm.rely.backend.util.ShaUtil;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Connection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.*;

/**
 * @program: cwg-my-base
 * @description:
 * @author: houn
 * @create: 2024-10-31 11:45
 */
@Slf4j
@Service
public class OzowServiceImpl extends BasePayServiceImpl implements OzowService {

    @Autowired
    private FinanceDepositService financeDepositService;
    @Autowired
    private CustomInfoService customInfoService;

    @Autowired
    private FinanceWithdrawService financeWithdrawService;

    /*@Autowired
    private FinanceAgentWithdrawService financeAgentWithdrawService;*/

    public static void main(String[] args) throws ServiceException {

        System.out.println(AESUtil.encrypt("",
                "bfa5559109f94c78af615bcf00d52060"));
////
//
        System.out.println(AESUtil.decrypt("",
                "bfa5559109f94c78af615bcf00d52060"));
//
//        System.out.println("key:" + getKey("PW202411191320324383136"));
//
//        System.out.println(getIvString("PW202411191320324383136", new BigDecimal(20).doubleValue(),
//                "PW202411191320324383136"));
        System.out.println(getAesEncrypt("", "f6457d3219bb2c8ecf64576cf9bd3219",
                "PW2411221056486249462000f6457d3219bb2c8ecf64576cf9bd3219"));

        System.out.println(getAesDecrypt("", "f6457d3219bb2c8ecf64576cf9bd3219",
                "PW2411221056486249462000f6457d3219bb2c8ecf64576cf9bd3219"));
    }

    private static String getKey(String key) throws ServiceException {
        if (Strings.isNullOrEmpty(key)) {
            throw ServiceException.exception(Constants.SYSTEM_ERROR);
        }
        key = MD5Util.getMD5(key);
        while (key.length() < 64) {
            key += key;
        }
        String keyString = key.substring(10, 15) + key.substring(20, 30) + key.substring(40, 57);
        log.info(String.format("key:%s", keyString));
        return keyString;
    }

    private static String getIvString(String merchantReference, double payoutAmount, String plaintextEncryptionKey) throws ServiceException {
        String ivString = merchantReference + (int) (payoutAmount * 100) + getKey(plaintextEncryptionKey);
        log.info(String.format("merchantReference:%s,ivString:%s", merchantReference, ivString));
        return ivString;
    }

    public static String getAesEncrypt(String content, String secureKey, String ivString) {
        return encryptAes(content, secureKey, ivString);
    }

    public static String encryptAes(String data, String key, String ivString) {
        try {
            byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);

            // Generate IV
            String iv = getSha512Hash(ivString.toLowerCase()).substring(0, 16);

            // Adjust key length to 32 bytes
            while (key.length() < 32) {
                key += key;
            }
            key = key.substring(0, 32);

            SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);

            byte[] encryptedBytes = cipher.doFinal(dataBytes);
            return Base64.getEncoder().encodeToString(encryptedBytes);
        } catch (Exception e) {
            throw new RuntimeException("Error while encrypting: " + e.getMessage(), e);
        }
    }

    public static String decryptAes(String encrypted, String key, String ivString) {
        try {
            encrypted = encrypted.replace(' ', '+');
            byte[] encryptedBytes = Base64.getDecoder().decode(encrypted);

            // Generate IV
            String iv = getSha512Hash(ivString.toLowerCase()).substring(0, 16);

            // Adjust key length to 32 bytes
            while (key.length() < 32) {
                key += key;
            }
            key = key.substring(0, 32);

            SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);

            byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
            return new String(decryptedBytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new RuntimeException("Error while decrypting: " + e.getMessage(), e);
        }
    }

    public static String getSha512Hash(String stringToHash) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-512");
            byte[] hashBytes = digest.digest(stringToHash.getBytes(StandardCharsets.UTF_8));

            StringBuilder sb = new StringBuilder();
            for (byte b : hashBytes) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (Exception e) {
            throw new RuntimeException("Error while hashing: " + e.getMessage(), e);
        }
    }

    public static String getAesDecrypt(String content, String secureKey, String ivString) {
        return decryptAes(content, secureKey, ivString);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String pay(FinanceDepositAddEntity balanceSaveEntity) throws Exception {

        //通道配置
        SysRemittanceChannelTable channel = getPayChannel(PayConstants.OZOW_PAY_TYPE);
        //通道配置实体
        OzowPropertyEntity payProperty = getPayProperty(channel, OzowPropertyEntity.class);

        balanceSaveEntity.setCurrency(channel.getCurrency());
        balanceSaveEntity.setTransformCurrency(channel.getTransformCurrency());

        FinanceDepositTable balanceTable = financeDepositService.save(balanceSaveEntity, channel);

        String url = getUrl(payProperty, balanceTable, balanceSaveEntity);
        balanceTable.setFormUrl(url);
        
        return url;
    }

    /**
     * 回调处理数据
     *
     * @param callbackEntity
     * @throws ServiceException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void callback(OzowCallbackEntity callbackEntity) throws Exception {
        log.info(String.format("callback orderNo:%s,callback data:%s", callbackEntity.getTransactionReference(),
                JSON.toJSONString(callbackEntity)));
        //获取配置
        OzowPropertyEntity payProperty = getPayProperty(PayConstants.OZOW_PAY_TYPE, false, OzowPropertyEntity.class);

        //验证传输数据
        boolean flag = validate(callbackEntity, payProperty);
        if (!flag) {
            throw new ServiceException(Constants.FAIL);
        }


        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(callbackEntity.getStatus(),
                        callbackEntity.getTransactionReference(), callbackEntity.getTransactionId(),
                        PayConstants.OZOW_PAY_SUCCESS_STATUS, callbackEntity);

        BigDecimal amount = new BigDecimal(callbackEntity.getAmount());

        balanceCallbackUpdateEntity.setTransformAmount(amount);
        /**
         * 更新数据
         */
        financeDepositService.updateCallback(balanceCallbackUpdateEntity);
    }

    /**
     * 获取支付url
     *
     * @param balanceTable
     * @return
     * @throws ServiceException
     */
    private String getUrl(OzowPropertyEntity propertyEntity, FinanceDepositTable balanceTable,
                          FinanceDepositAddEntity balanceSaveEntity) throws Exception {

        CustomInfoTable customInfoTable = customInfoService.getById(balanceTable.getCustomId());
        String orderNo = balanceTable.getSerial() + "_" + balanceTable.getLogin();

        Map<String, Object> params = new LinkedHashMap<>();
        params.put("SiteCode", propertyEntity.getSiteCode());
        params.put("CountryCode", balanceTable.getCountry());
        params.put("CurrencyCode", balanceTable.getTransformCurrency());
        params.put("Amount", String.format("%.2f", balanceTable.getTransformAmount().doubleValue()));
        params.put("TransactionReference", orderNo);
        params.put("BankReference", String.valueOf(balanceTable.getCId()));
        params.put("CancelUrl", propertyEntity.getCancelUrl());
        params.put("ErrorUrl", propertyEntity.getErrorUrl());
        params.put("SuccessUrl", propertyEntity.getSuccessUrl());
        params.put("NotifyUrl", propertyEntity.getNotifyUrl());
        params.put("IsTest", Boolean.valueOf(propertyEntity.getIsTest()));
        params.put("CustomerIdentifier", customInfoTable.getIdentity());

        String sign = getSign(propertyEntity, params);

        params.put("HashCheck", sign);

        Map<String, String> headers = new HashMap<>();
        headers.put("Accept", "application/json");
        headers.put("ApiKey", propertyEntity.getApiKey());
        headers.put("Content-Type", "application/json");


        Connection.Response response = HttpUtil.post(
                propertyEntity.getBaseUrl() + propertyEntity.getPaymentUrl(), headers, JSON.toJSONString(params));

        log.info(String.format("url:%s,hears:%s,json:%s,body:%s",
                propertyEntity.getBaseUrl() + propertyEntity.getPaymentUrl(), JSON.toJSONString(headers),
                JSON.toJSON(params), response.body()));
        JSONObject requestEntity = JSON.parseObject(response.body());

        if (requestEntity == null || !Strings.isNullOrEmpty(requestEntity.getString("errorMessage"))) {
            throw new PayValidatedException(requestEntity.getString("errorMessage"));
        }


        String url = String.valueOf(requestEntity.get("url"));
        log.info(String.format("orderNo:%s,url:%s", params.get("TransactionReference"), url));
        return url;
    }

    @Override
    public FinanceWithdrawOzowDto withdrawInfo(FinanceWithdrawInfoEntity entity) throws Exception {
        FinanceWithdrawDto withdrawDto = null;
        if (entity.getWithdrawInfoType() == null || entity.getWithdrawInfoType().equals(1)) {
            withdrawDto = financeWithdrawService.searchSingle(entity.getId());

        } else {
            //withdrawDto = financeAgentWithdrawService.searchSingle(entity.getId());
        }

        SysRemitChannelTable withdrawChannel = getWithdrawChannel(PayConstants.OZOW_PAY_TYPE_REMIT);
        OzowWithdrawPropertyEntity proxpayWithdrawProperty = getWithdrawPayProperty(withdrawChannel,
                OzowWithdrawPropertyEntity.class);

        FinanceWithdrawOzowDto dto = new FinanceWithdrawOzowDto();
        dto.setAmount(withdrawDto.getTransformAmount() == null ? null :
                withdrawDto.getTransformAmount().setScale(2, BigDecimal.ROUND_DOWN));

        List<FinanceWithdrawOzowBankDetailDto> detailDtos = getavailablebanks(proxpayWithdrawProperty);

        Map<String, String> bankMap = Maps.newHashMap();
        for (FinanceWithdrawOzowBankDetailDto detailDto : detailDtos) {
            bankMap.put(detailDto.getBankGroupName() + "-" + detailDto.getBankGroupId(),
                    detailDto.getBankGroupId());
        }
        dto.setBankingCodeSelect(bankMap);
        dto.setBankingCode(withdrawDto.getCustomBankCode());
        dto.setAccountNumber(withdrawDto.getBankCardNum());

        dto.setId(withdrawDto.getId());
        dto.setWithdrawUrl(withdrawChannel.getWithdrawUrl());
        dto.setSerial(withdrawDto.getSerial());
        return dto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String withdraw(FinanceWithdrawOzowEntity entity) throws Exception {


        FinanceWithdrawDto withdrawDto = null;
        if (entity.getWithdrawInfoType() == null || entity.getWithdrawInfoType().equals(1)) {
            withdrawDto = financeWithdrawService.searchSingle(entity.getId());

        } else {
            //withdrawDto = financeAgentWithdrawService.searchSingle(entity.getId());
        }
        FinanceWithdrawApproveChannelSubmitEntity financeWithdrawApproveChannelSubmitEntity =
                new FinanceWithdrawApproveChannelSubmitEntity();
        financeWithdrawApproveChannelSubmitEntity.setSubmitStatus(PayConstants.PASSED_STATUS);
        financeWithdrawApproveChannelSubmitEntity.setId(entity.getId());
        financeWithdrawApproveChannelSubmitEntity.setActualPayType(entity.getPayType());

        if (entity.getWithdrawInfoType() == null || entity.getWithdrawInfoType().equals(1)) {
            financeWithdrawService.approve(financeWithdrawApproveChannelSubmitEntity);

        } else {
            //financeAgentWithdrawService.approve(financeWithdrawApproveChannelSubmitEntity);
        }

        entity.setCId(withdrawDto.getCId());
        entity.setSerial(withdrawDto.getSerial());
        entity.setLogin(withdrawDto.getLogin());

        OzowWithdrawPropertyEntity proxpayWithdrawProperty = getWithdrawProperty(PayConstants.OZOW_PAY_TYPE_REMIT,
                OzowWithdrawPropertyEntity.class);

        payout(entity, proxpayWithdrawProperty);


        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void withdrawCallback(Integer withdrawType, OzowPayoutCallbackEntity callbackEntity) throws Exception {
        log.info(String.format("withdrawCallback orderNo:%s,callback data:%s",
                callbackEntity.getMerchantReference(), JSON.toJSONString(callbackEntity)));

        if (Strings.isNullOrEmpty(callbackEntity.getPayoutId())) {
            throw ServiceException.exception(Constants.PARAMETER_ERROR);
        }

        FinanceWithdrawApproveChannelEntity financeWithdrawApproveChannelEntity =
                new FinanceWithdrawApproveChannelEntity();
        JSONObject jsonObject = queryWithdrawOrderInfo(callbackEntity);
        if (jsonObject == null || jsonObject.size() <= 0 || !queryValidateWithdrawOrderInfo(jsonObject)) {
            if (jsonObject.getJSONObject("payoutStatus").getInteger("status").equals(2) || jsonObject.getJSONObject(
                    "payoutStatus").getInteger("status").equals(3) || jsonObject.getJSONObject(
                    "payoutStatus").getInteger("status").equals(6) || jsonObject.getJSONObject(
                    "payoutStatus").getInteger("status").equals(1)) {
                return;
            }
            financeWithdrawApproveChannelEntity.setCallbackStatus(2);
        } else if (queryValidateWithdrawOrderInfo(jsonObject)) {

            boolean flag = validate(callbackEntity);
            if (!flag || !queryValidateWithdrawOrderInfo(jsonObject)) {
                throw new ServiceException(Constants.FAIL);
            }
            financeWithdrawApproveChannelEntity.setCallbackStatus(1);
        }

        financeWithdrawApproveChannelEntity.setSerial(callbackEntity.getMerchantReference());
        financeWithdrawApproveChannelEntity.setChannelSerial(callbackEntity.getPayoutId());
        financeWithdrawApproveChannelEntity.setNote(callbackEntity.getPayoutStatus().getErrorMessage());

        /**
         * 更新数据
         */


        if (withdrawType == null || withdrawType.equals(1)) {
            financeWithdrawService.approve(financeWithdrawApproveChannelEntity);
        } else {
            //financeAgentWithdrawService.channel(financeWithdrawApproveChannelEntity);
        }
    }

    @Override
    public String webhook(OzowPayoutWebhookEntity callbackEntity) throws Exception {
        log.info("webhook:" + JSON.toJSONString(callbackEntity));
        OzowWithdrawPropertyEntity proxpayWithdrawProperty = getWithdrawProperty(PayConstants.OZOW_PAY_TYPE_REMIT,
                OzowWithdrawPropertyEntity.class);
        String signString = String.format("%s%s%s%s%s%s%s%s%s%s%s", callbackEntity.getPayoutId(),
                callbackEntity.getSiteCode(),
                String.format("%.0f", callbackEntity.getAmount().doubleValue() * 100)
                , callbackEntity.getMerchantReference(),
                callbackEntity.getCustomerBankReference(), callbackEntity.getIsRtc(), callbackEntity.getNotifyUrl(),
                callbackEntity.getBankingDetails().getBankGroupId(),
                callbackEntity.getBankingDetails().getAccountNumber(),
                callbackEntity.getBankingDetails().getBranchCode(), proxpayWithdrawProperty.getApiKey());
        String sign = ShaUtil.sha512(signString.toLowerCase()).toLowerCase();

        log.info(String.format("orderNo:%s,signString:%s,sign:%s", callbackEntity.getMerchantReference(), signString,
                sign));

        if (sign.equals(callbackEntity.getHashCheck())) {
            return getKey(callbackEntity.getMerchantReference());
        }
        return null;
    }

    private void payout(FinanceWithdrawOzowEntity entity, OzowWithdrawPropertyEntity propertyEntity) throws Exception {


        Map<String, Object> params = new LinkedHashMap<>();
        params.put("SiteCode", propertyEntity.getSiteCode());
        params.put("Amount", entity.getAmount().doubleValue());
        params.put("MerchantReference", entity.getSerial());
        params.put("CustomerBankReference", String.valueOf(entity.getCId()));
        params.put("IsRtc", false);
        if (entity.getWithdrawInfoType() == null || entity.getWithdrawInfoType().equals(1)) {
            params.put("NotifyUrl", propertyEntity.getNotifyUrl());

        } else {
            params.put("NotifyUrl", propertyEntity.getAgentNotifyUrl());
        }
        Map<String, Object> bankParams = new LinkedHashMap<>();
        List<FinanceWithdrawOzowBankDetailDto> detailDtos = getavailablebanks(propertyEntity);

        for (FinanceWithdrawOzowBankDetailDto detailDto : detailDtos) {
            if (detailDto.getBankGroupId().equals(entity.getBankingCode())) {
                bankParams.put("BankGroupId", detailDto.getBankGroupId());
                bankParams.put("AccountNumber", getAesEncrypt(entity.getAccountNumber(), getKey(entity.getSerial()),
                        getIvString(entity.getSerial(), entity.getAmount().doubleValue(),
                                entity.getSerial())));
                bankParams.put("BranchCode", detailDto.getUniversalBranchCode());
                break;
            }
        }
        params.put("BankingDetails", bankParams);

        String sign = getWithdrawSign(propertyEntity, params);
        params.put("HashCheck", sign);

        String payUrl = propertyEntity.getBaseUrl() + propertyEntity.getPayoutUrl();


        Map<String, String> headers = new HashMap<>();
        headers.put("Accept", "application/json");
        headers.put("ApiKey", propertyEntity.getApiKey());
        headers.put("SiteCode", propertyEntity.getSiteCode());
        headers.put("Content-Type", "application/json");

        log.info(String.format("ozow payout:%s,headers:%s,params:%s", payUrl, JSON.toJSONString(headers),
                JSON.toJSONString(params)));


        Connection.Response response = HttpUtil.post(payUrl, headers, JSON.toJSONString(params));


        String payString = response.body();

        log.info("ozow payout response body:" + payString);

        JSONObject ozowDataEntity = JSON.parseObject(payString);
        if (!ozowDataEntity.getJSONObject("payoutStatus").getString("status").equals("1") || !ozowDataEntity.getJSONObject("payoutStatus").getString("subStatus").equals("201")) {
            throw ServiceException.exception(ozowDataEntity.getJSONObject("payoutStatus").getString("errorMessage"));
        }
    }

    private List<FinanceWithdrawOzowBankDetailDto> getavailablebanks(OzowWithdrawPropertyEntity propertyEntity) throws Exception {


        Map<String, String> headers = new HashMap<>();
        headers.put("Accept", "application/json");
        headers.put("ApiKey", propertyEntity.getApiKey());
        headers.put("SiteCode", propertyEntity.getSiteCode());
        headers.put("Content-Type", "application/x-www-form-urlencoded");

        String availablebankUrl = propertyEntity.getBaseUrl() + propertyEntity.getAvailablebankUrl();


        log.info(String.format("ozow get availablebanks info url:%s,headers:%s", availablebankUrl,
                JSON.toJSONString(headers)));
        Connection.Response response = HttpUtil.get(availablebankUrl, headers);

        String payString = response.body();

        log.info(String.format("ozow get availablebanks info:%s", payString));

        List<FinanceWithdrawOzowBankDetailDto> detailDtos = JSON.parseArray(payString,
                FinanceWithdrawOzowBankDetailDto.class);

        return detailDtos;
    }

    private boolean queryValidateWithdrawOrderInfo(OzowPayoutCallbackEntity callbackEntity) throws Exception {

        JSONObject ozowOrderInfoDataEntity = queryWithdrawOrderInfo(callbackEntity);
        if (!ozowOrderInfoDataEntity.getJSONObject("payoutStatus").getInteger("status").equals(5) &&
                !ozowOrderInfoDataEntity.getJSONObject("payoutStatus").getInteger("subStatus").equals(203)) {
            return false;
        }
        return true;
    }

    private boolean queryValidateWithdrawOrderInfo(JSONObject jsonObject) throws Exception {

        if (!jsonObject.getJSONObject("payoutStatus").getInteger("status").equals(5)) {
            return false;
        }
        return true;
    }

    private JSONObject queryWithdrawOrderInfo(OzowPayoutCallbackEntity callbackEntity) throws Exception {
        OzowWithdrawPropertyEntity payProperty = getWithdrawProperty(PayConstants.OZOW_PAY_TYPE_REMIT,
                OzowWithdrawPropertyEntity.class);

        Map<String, String> headers = new HashMap<>();
        headers.put("Accept", "application/json");
        headers.put("ApiKey", payProperty.getApiKey());
        headers.put("SiteCode", payProperty.getSiteCode());

        String queryUrl =
                payProperty.getBaseUrl() + payProperty.getQueryUrl() + "?payoutId=" + callbackEntity.getPayoutId();


        log.info(String.format("ozow get order info url:%s,headers:%s", queryUrl, JSON.toJSONString(headers)));
        Connection.Response response = HttpUtil.get(queryUrl, headers);

        String payString = response.body();

        log.info(String.format("ozow get order info:%s", payString));

        JSONObject ozowOrderInfoDataEntity = JSON.parseObject(payString);
        return ozowOrderInfoDataEntity;
    }


    private boolean validate(OzowPayoutCallbackEntity callbackEntity) throws Exception {
        OzowWithdrawPropertyEntity payProperty = getWithdrawProperty(PayConstants.OZOW_PAY_TYPE_REMIT,
                OzowWithdrawPropertyEntity.class);
        String _sign = callbackEntity.getHashCheck();

        String sign = getSign(payProperty, callbackEntity);

        log.info(String.format("order no:%scallback result sign compare--callback sign:%s, encryption sign result:%s"
                , callbackEntity.getMerchantReference(), _sign, sign));
        return sign.equals(_sign);
    }

    /**
     * 验证回调参数是否被篡改
     *
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private boolean validate(OzowCallbackEntity callbackEntity, OzowPropertyEntity payProperty) throws Exception {

        String _sign = callbackEntity.getHash();

        String sign = getSign(payProperty, callbackEntity);

        log.info(String.format("order no:%scallback result sign compare--callback sign:%s, encryption sign result:%s"
                , callbackEntity.getTransactionReference(), _sign, sign));
        return sign.equals(_sign);
    }

    /**
     * 支付请求加密sign
     *
     * @param propertyEntity
     * @return
     * @throws ServiceException
     */
    private String getSign(OzowPropertyEntity propertyEntity, Map<String, Object> map) throws Exception {

        String signString = null;

        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (entry.getValue() != null) {
                if (Strings.isNullOrEmpty(signString)) {
                    signString = entry.getValue().toString();
                } else {
                    signString += entry.getValue();
                }
            }
        }
        signString += propertyEntity.getKey();
        String sign = ShaUtil.sha512(signString.toLowerCase()).toLowerCase();
        String order = map.get("TransactionReference").toString();

        log.info(String.format("orderNo:%s,signString:%s,sign:%s", order, signString, sign));

        return sign;
    }

    private String getBankSign(Map<String, String> map) throws Exception {
        OzowWithdrawPropertyEntity propertyEntity = getWithdrawProperty(PayConstants.OZOW_PAY_TYPE_REMIT,
                OzowWithdrawPropertyEntity.class);
        String signString = "";

        for (Map.Entry<String, String> entry : map.entrySet()) {
            if (entry.getKey().equals("Amount")) {
                signString += String.format("%.0f", Double.parseDouble(entry.getValue().toString()) * 100);
            } else {
                signString += entry.getValue();
            }
        }
        signString += propertyEntity.getApiKey();
        String sign = ShaUtil.sha512(signString.toLowerCase()).toLowerCase();
        String order = map.get("MerchantReference").toString();

        log.info(String.format("orderNo:%s,signString:%s,sign:%s", order, signString, sign));

        return sign;
    }

    private String getWithdrawSign(OzowWithdrawPropertyEntity propertyEntity, Map<String, Object> map) throws Exception {

        String signString = "";
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (entry.getValue() != null) {
                if (entry.getValue() instanceof Map) {
                    Map<String, Object> map1 = (Map<String, Object>) entry.getValue();
                    for (Map.Entry<String, Object> entry1 : map1.entrySet()) {
                        signString += entry1.getValue();
                    }
                } else {
                    if (entry.getKey().equals("Amount")) {
                        signString += String.format("%.0f", Double.parseDouble(entry.getValue().toString()) * 100);
                    } else {
                        signString += entry.getValue();
                    }

                }

            }
        }
        signString += propertyEntity.getApiKey();
        String sign = ShaUtil.sha512(signString.toLowerCase()).toLowerCase();
        String order = map.get("MerchantReference").toString();

        log.info(String.format("orderNo:%s,signString:%s,sign:%s", order, signString, sign));

        return sign;
    }

    /**
     * 回调请求加密sign
     *
     * @param propertyEntity
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private String getSign(OzowPropertyEntity propertyEntity, OzowCallbackEntity callbackEntity) throws Exception {
        Map<String, String> map = new LinkedHashMap<>();
        map.put("SiteCode", callbackEntity.getSiteCode());
        map.put("TransactionId", callbackEntity.getTransactionId());
        map.put("TransactionReference", callbackEntity.getTransactionReference());
        map.put("Amount", callbackEntity.getAmount());
        map.put("Status", callbackEntity.getStatus());
        map.put("Optional1", callbackEntity.getOptional1());
        map.put("Optional2", callbackEntity.getOptional2());
        map.put("Optional3", callbackEntity.getOptional3());
        map.put("Optional4", callbackEntity.getOptional4());
        map.put("Optional5", callbackEntity.getOptional5());
        map.put("CurrencyCode", callbackEntity.getCurrencyCode());
        map.put("IsTest", callbackEntity.getIsTest());
        map.put("StatusMessage", callbackEntity.getStatusMessage());
        String signString = null;

        for (Map.Entry<String, String> entry : map.entrySet()) {
            if (!Strings.isNullOrEmpty(entry.getValue())) {
                if (Strings.isNullOrEmpty(signString)) {
                    signString = entry.getValue();
                } else {
                    signString += entry.getValue();
                }
            }
        }
        signString += propertyEntity.getKey();
        String sign = ShaUtil.sha512(signString.toLowerCase()).toLowerCase();

        log.info(String.format("orderNo:%s,signString:%s,sign:%s", callbackEntity.getTransactionReference(), signString,
                sign));

        return sign;
    }

    private String getSign(OzowWithdrawPropertyEntity propertyEntity, OzowPayoutCallbackEntity callbackEntity) throws Exception {
        Map<String, String> map = new LinkedHashMap<>();
        map.put("PayoutId", callbackEntity.getPayoutId());
        map.put("SiteCode", callbackEntity.getSiteCode());
        map.put("MerchantReference", callbackEntity.getMerchantReference());
        map.put("CustomerMerchantReference", callbackEntity.getCustomerMerchantReference());
        map.put("PayoutStatus", String.valueOf(callbackEntity.getPayoutStatus().getStatus()));
        map.put("payoutSubStatus", String.valueOf(callbackEntity.getPayoutStatus().getSubStatus()));
        String signString = null;

        for (Map.Entry<String, String> entry : map.entrySet()) {
            if (!Strings.isNullOrEmpty(entry.getValue())) {
                if (Strings.isNullOrEmpty(signString)) {
                    signString = entry.getValue();
                } else {
                    signString += entry.getValue();
                }
            }
        }
        signString += propertyEntity.getApiKey();
        String sign = ShaUtil.sha512(signString.toLowerCase()).toLowerCase();

        log.info(String.format("orderNo:%s,signString:%s,sign:%s", callbackEntity.getMerchantReference(), signString,
                sign));

        return sign;
    }
}
