package com.seer.teach.pay.app.client.wechat.core;

import cn.hutool.core.map.MapUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.seer.teach.common.enums.ResultCodeEnum;
import com.seer.teach.common.enums.pay.RefundStatusEnum;
import com.seer.teach.common.exception.CommonException;
import com.seer.teach.common.config.WxConfig;
import com.seer.teach.common.utils.DateUtils;
import com.seer.teach.pay.app.client.dto.PayOrderReqDTO;
import com.seer.teach.pay.app.client.dto.RefundNotificationRespDTO;
import com.seer.teach.pay.app.client.convert.PayClientConvert;
import com.seer.teach.pay.app.client.wechat.core.resp.WechatPayOrderResp;
import com.seer.teach.pay.app.client.wechat.core.resp.WechatPrepayTransactionResp;
import com.seer.teach.pay.dto.RefundSubmitRespDTO;
import com.seer.teach.pay.app.client.wechat.core.resp.WechatTransferDetailResp;
import com.seer.teach.pay.app.client.wechat.core.resp.WechatTransferQueryResp;
import com.seer.teach.pay.app.client.wechat.core.resp.WechatTransferResp;
import com.seer.teach.pay.app.util.ConfigDecryptUtil;
import com.seer.teach.pay.entity.PayOrderEntity;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAPublicKeyConfig;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.*;
import com.wechat.pay.java.service.transferbatch.TransferBatchService;
import com.wechat.pay.java.service.transferbatch.model.*;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;

import java.util.*;

/**
 *  微信支付客户端
 *  <p>
 * 服务商模式JSAPI支付，服务商为子商户（也叫特约商户）提供在微信客户端内部浏览器网页中使用微信支付收款的能力
 *  <see><a href='https://pay.weixin.qq.com/doc/v3/partner/4012069859'>微信JSAPI/小程序 产品介绍</a></see>
 * </p>
 * 微信支付客户端
 */
@Setter
@Getter
@Slf4j
public class WechatPlayClient {


    private JsapiServiceExtension jsapiServiceExtension;

    private NotificationParser parser;

    private RefundService refundService;

    private TransferBatchService transferBatchService;

    private ApplicationContext applicationContext;

    private String configJson;

    private String appId;

    private String mchId;

    private String jsPayNotifyUrl;

    private String jsRefundNotifyUrl;

    public WechatPlayClient(String configJson) {
        this.configJson = configJson;
        JSONObject channelConfig = JSONUtil.parseObj(configJson);
        // 从渠道配置获取必要参数，并进行解密处理
        String appId = channelConfig.getStr("appId");
        String mchId = channelConfig.getStr("mchId");
        String apiV3Key = channelConfig.getStr("apiV3Key");
        String publicKeyId = channelConfig.getStr("publicKeyId");
        String merchantSerialNumber = channelConfig.getStr("merchantSerialNumber");
        String privateKeyPem = channelConfig.getStr("privateKeyPem");
        String publicKeyPem = channelConfig.getStr("publicKeyPem");
        this.jsPayNotifyUrl = channelConfig.getStr("jsPayNotifyUrl");
        this.jsRefundNotifyUrl = channelConfig.getStr("jsRefundNotifyUrl");
        // 使用安全解密方法处理敏感配置参数
        this.appId = ConfigDecryptUtil.safeDecrypt(appId);
        this.mchId = ConfigDecryptUtil.safeDecrypt(mchId);
        apiV3Key = ConfigDecryptUtil.safeDecrypt(apiV3Key);
        publicKeyId = ConfigDecryptUtil.safeDecrypt(publicKeyId);
        merchantSerialNumber = ConfigDecryptUtil.safeDecrypt(merchantSerialNumber);
        // 对PEM格式证书使用特殊处理
        privateKeyPem = cleanPemContent(ConfigDecryptUtil.safeDecrypt(privateKeyPem, true));
        publicKeyPem = cleanPemContent(ConfigDecryptUtil.safeDecrypt(publicKeyPem, true));

        // 定义证书路径（支持通配符）
        Config config =
                new RSAPublicKeyConfig.Builder()
                        .merchantId(this.mchId)
                        .privateKey(privateKeyPem)
                        .publicKey(publicKeyPem)
                        .publicKeyId(publicKeyId)
                        .merchantSerialNumber(merchantSerialNumber)
                        .apiV3Key(apiV3Key)
                        .build();
        // 初始化证书服务
        jsapiServiceExtension = new JsapiServiceExtension.Builder().config(config).build();
        NotificationConfig notificationConfig = new RSAPublicKeyConfig.Builder()
                .merchantId(mchId)
                .privateKey(privateKeyPem)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3Key)
                .publicKey(publicKeyPem)
                .publicKeyId(publicKeyId)
                .build();
        parser = new NotificationParser(notificationConfig);
        // 初始化退款服务
        refundService = new RefundService.Builder()
                .config(config)
                .build();

        // 初始化企业付款服务
        transferBatchService = new TransferBatchService.Builder()
                .config(config)
                .build();
    }

    /**
     * 预支付，获取预支付ID（使用默认配置）
     * <p>
     * JSAPI支付场景(微信内置浏览器打开的网页)或小程序支付场景，服务商需调用该接口在微信支付下单，
     * 生成用于调起支付的预支付交易会话标识(prepay_id)。
     * <see><a href='https://pay.weixin.qq.com/doc/v3/partner/4012738519'>微信JSAPI/小程序下单</a></see>
     *
     *  userId      用户ID
     * totalPrice  订单总金额
     * sn          订单号 【商户订单号】服务商系统内部订单号，要求6-32个字符内，只能是数字、大小写字母_-|* 且在同一个服务商商户号下唯一。
     * description 【商品描述】商品信息描述，用户微信账单的商品字段中可见(可参考JSAPI支付示例说明-账单示意图)，服务商需传递能真实代表商品信息的描述，不能超过127个字符。
     *
     *
     * @return prepayId 调起支付的预支付交易会话标识(prepay_id)
     */
    public WechatPrepayTransactionResp prepayPay(PayOrderReqDTO payOrder, Object extrasRequest) {
        String sn = payOrder.getPayOrderSn();
        Integer userId = payOrder.getUserId();
        Long totalAmount = payOrder.getTotalAmount();
        log.info("开始处理微信预支付请求，订单号: {}, 用户ID: {}, 支付金额: {}分", sn, userId, totalAmount);

        // 构建支付请求
        PrepayRequest request = new PrepayRequest();
        request.setAppid(this.appId);
        request.setMchid(this.mchId);
        request.setNotifyUrl(jsPayNotifyUrl);
        request.setOutTradeNo(sn);
        // 必填   string(127),【商品描述】商品信息描述，用户微信账单的商品字段中可见(可参考JSAPI支付示例说明-账单示意图)，商户需传递能真实代表商品信息的描述，不能超过127个字符
        String description = payOrder.getDescription();
        if(description.length() > 127){
            description = description.substring(0, 126);
        }
        request.setDescription(description);
        Amount amount = new Amount();
        amount.setCurrency("CNY");
        amount.setTotal(totalAmount.intValue());
        request.setAmount(amount);
        Payer payer = new Payer();
        payer.setOpenid(extrasRequest.toString());
        request.setPayer(payer);

        try {
            log.info("开始调用微信预支付接口，订单号: {}", sn);
            PrepayWithRequestPaymentResponse prepayResponse = jsapiServiceExtension.prepayWithRequestPayment(request);
            log.info("微信预支付接口调用完成，订单号: {}", sn);

            if (Objects.nonNull(prepayResponse)) {
                log.info("微信预支付接口调用成功，订单号: {}, prepayResponse: {}", sn, prepayResponse);
                WechatPrepayTransactionResp resp = PayClientConvert.INSTANCE.convert(prepayResponse);
                log.info("转换后的微信预支付响应: {}", resp);

                // 验证响应中的关键字段
                if (resp == null) {
                    log.error("转换后的微信预支付响应为空，订单号: {}", sn);
                    throw new CommonException(ResultCodeEnum.WX_PREPAY_ERROR, "转换后的微信预支付响应为空");
                }

                if (resp.getPackageVal() == null || resp.getPackageVal().isEmpty()) {
                    log.error("转换后的微信预支付响应中packageVal为空，订单号: {}", sn);
                    throw new CommonException(ResultCodeEnum.WX_PREPAY_ERROR, "转换后的微信预支付响应中packageVal为空");
                }
                return resp;
            } else {
                log.error("微信预支付接口返回空响应，订单号: {}", sn);
                throw new CommonException(ResultCodeEnum.WX_PREPAY_ERROR, "微信预支付接口返回空响应");
            }
        } catch (Exception e) {
            log.error("微信预支付接口调用失败，订单号: {}, 错误信息: {}", sn, e.getMessage(), e);
            throw new CommonException(ResultCodeEnum.WX_PREPAY_ERROR, "微信预支付失败: " + e.getMessage());
        }
    }


    /**
     * 清理PEM内容，确保符合微信SDK要求
     *
     * @param pemContent PEM内容
     * @return 清理后的PEM内容
     */
    private String cleanPemContent(String pemContent) {
        if (pemContent == null || pemContent.isEmpty()) {
            return pemContent;
        }

        try {
            // 移除所有反斜杠
            pemContent = pemContent.replace("\\", "");

            // 确保正确的换行符
            pemContent = pemContent.replace("\\n", "\n")
                    .replace("\r\n", "\n")
                    .replace("\r", "\n");

            // 移除多余的空白字符，但要确保BEGIN和END标记格式正确
            pemContent = pemContent.trim();

            // 确保BEGIN和END标记格式正确（包含正确的空格）
            pemContent = pemContent.replace("-----BEGINPRIVATEKEY-----", "-----BEGIN PRIVATE KEY-----")
                    .replace("-----ENDPRIVATEKEY-----", "-----END PRIVATE KEY-----")
                    .replace("-----BEGINPUBLICKEY-----", "-----BEGIN PUBLIC KEY-----")
                    .replace("-----ENDPUBLICKEY-----", "-----END PUBLIC KEY-----");

            // 确保BEGIN和END标记独立成行
            if (pemContent.contains("-----BEGIN")) {
                // 先标准化标记格式
                pemContent = pemContent.replaceAll("-----BEGIN PRIVATE KEY-----", "\n-----BEGIN PRIVATE KEY-----\n")
                        .replaceAll("-----END PRIVATE KEY-----", "\n-----END PRIVATE KEY-----\n")
                        .replaceAll("-----BEGIN PUBLIC KEY-----", "\n-----BEGIN PUBLIC KEY-----\n")
                        .replaceAll("-----END PUBLIC KEY-----", "\n-----END PUBLIC KEY-----\n")
                        .replaceAll("\n+", "\n")  // 将多个连续换行符合并为一个
                        .trim();

                // 确保标记前后有换行符
                pemContent = "\n" + pemContent + "\n";
            }

            log.debug("PEM内容清理完成，长度: {}", pemContent.length());
            return pemContent;
        } catch (Exception e) {
            log.warn("PEM内容清理异常: {}", e.getMessage());
            return pemContent;
        }
    }


    /**
     * 解析微信支付V3版本的支付通知
     *
     * <see><a href='https://pay.weixin.qq.com/doc/v3/partner/4012085146'>支付成功回调通知</a></see>
     *
     */
    public Optional<Transaction> parseOrderNotifyV3(Map<String, String> headers, String requestBody) {
        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(MapUtil.getStr(headers, "wechatpay-serial"))
                .nonce(MapUtil.getStr(headers, "wechatpay-nonce"))
                .signature(MapUtil.getStr(headers, "wechatpay-signature"))
                .timestamp(MapUtil.getStr(headers, "wechatpay-timestamp"))
                .body(requestBody)
                .build();
        try {
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            Transaction transaction = parser.parse(requestParam, Transaction.class);
            log.info("parseOrderNotifyV3 transaction: {}", transaction.toString());
            return Optional.of(transaction);
        } catch (Exception exception) {
            log.error("parseOrderNotifyV3 failed", exception);
            return Optional.empty();
        }
    }

    /**
     * 提交退款
     *
     *  <see><a href='https://pay.weixin.qq.com/doc/v3/partner/4012739034'>申请退款</a></see>
     *
     * @param outTradeNo   订单号
     * @param refundReason 退款原因
     * @param refundAmount 退款金额
     * @param totalAmount  原订单金额
     * @param refundSn     商户退款单号
     * @return RefundSubmitRespDTO 退款结果
     */
    public RefundSubmitRespDTO submitRefund(String outTradeNo, String refundReason, Long refundAmount, Long totalAmount, String refundSn) {
        CreateRequest request = new CreateRequest();
        // 【微信支付订单号】 微信支付侧订单的唯一标识，订单支付成功后，查询订单和支付成功回调通知会返回该参数。transaction_id和out_trade_no必须二选一进行传参。
        request.setOutTradeNo(outTradeNo);
        // 【商户退款单号】 服务商系统内部的退款单号，服务商系统内部唯一，只能是数字、大小写字母_-|*@ ，同一商户退款单号多次请求只退一笔。不可超过64个字节数。
        request.setOutRefundNo(refundSn);
        String reason = refundReason;
        if (StringUtils.isNoneBlank(reason) && reason.length() > 20) {
            reason = reason.substring(0, 20);
        }
        request.setReason(reason);
        // 【退款结果回调url】 异步接收微信支付退款结果通知的回调地址，通知url必须为外网可访问的url，不能携带参数。 如果传了该参数，则商户平台上配置的回调地址（商户平台-交易中心-退款管理-退款配置）将不会生效，优先回调当前传的这个地址。
        request.setNotifyUrl(jsRefundNotifyUrl);
        // 【金额信息】订单退款金额信息
        AmountReq amount = new AmountReq();
        // 【退款金额】 退款金额，币种的最小单位，只能为整数，不能超过原订单支付金额。
        amount.setRefund(refundAmount);
        // 【原订单金额】 原支付交易的订单总金额，币种的最小单位，只能为整数
        amount.setTotal(totalAmount);
        amount.setCurrency("CNY");
        request.setAmount(amount);
        log.info("创建退款参数:{}", request);
        Refund refund = refundService.create(request);
        log.info("创建退款结果:{}", refund);
        return buildWechatRefundResp(refund);
    }

    /**
     * 查询退款
     *
     * @param refundSn 退款单号
     * @return RefundSubmitRespDTO 退款结果
     */
    public RefundSubmitRespDTO queryRefund(String refundSn) {
        QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
        request.setOutRefundNo(refundSn);
        log.info("查询退款参数:{}", request);
        Refund refund = refundService.queryByOutRefundNo(request);
        log.info("查询退款结果:{}", refund);
        return buildWechatRefundResp(refund);
    }

    private static RefundSubmitRespDTO buildWechatRefundResp(Refund refund) {
        RefundSubmitRespDTO refundSubmitRespDTO = new RefundSubmitRespDTO();
        refundSubmitRespDTO.setRefundId(refund.getRefundId());
        //out_trade_no 　必填　string(32) 【商户订单号】 服务商下单时传入的服务商系统内部订单号。
        refundSubmitRespDTO.setPayOrderSn(refund.getOutTradeNo());
        // out_refund_no 　必填　string(64) 【商户退款单号】 服务商申请退款时传的服务商系统内部退款单号。
        refundSubmitRespDTO.setRefundOrderSn(refund.getOutRefundNo());
        // transaction_id 　必填　string(32) 【微信支付订单号】 微信支付侧订单的唯一标识。
        refundSubmitRespDTO.setTransactionId(refund.getTransactionId());
        refundSubmitRespDTO.setRefundAmount(refund.getAmount().getPayerRefund());
        refundSubmitRespDTO.setSuccessTime(DateUtils.parseStringToLocalDateTime(refund.getSuccessTime()));
        refundSubmitRespDTO.setStatus(RefundStatusEnum.fromWxCode(refund.getStatus().name()).getCode());
        refundSubmitRespDTO.setRefundResJson(JSONUtil.toJsonStr(refund));
        return refundSubmitRespDTO;
    }

    /**
     * 解析微信支付V3版本的退款通知
     * 该方法主要用于处理微信支付的退款通知，通过验证通知的合法性并解析通知内容，将其转换为WechatPayOrderResp对象
     * <see><a href='https://pay.weixin.qq.com/doc/v3/partner/4012085298'>微信支付V3版本退款通知</a></see>
     *
     * @param headers     通知的请求头，包含签名、时间戳等信息
     * @param requestBody 通知的请求体，包含具体的退款信息
     * @return 返回一个Optional封装的WechatPayOrderResp对象，如果解析成功则返回包含对象的Optional，否则返回空的Optional
     */
    public Optional<RefundNotificationRespDTO> parseRefundNotifyV3(Map<String, String> headers, String requestBody) {
        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(MapUtil.getStr(headers, "wechatpay-serial"))
                .nonce(MapUtil.getStr(headers, "wechatpay-nonce"))
                .signature(MapUtil.getStr(headers, "wechatpay-signature"))
                .timestamp(MapUtil.getStr(headers, "wechatpay-timestamp"))
                .body(requestBody)
                .build();
        try {
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            RefundNotification refundNotification = parser.parse(requestParam, RefundNotification.class);
            log.info("parseRefundNotifyV3 transaction: {}", refundNotification.toString());

            RefundNotificationRespDTO refundNotificationResp = new RefundNotificationRespDTO();
            refundNotificationResp.setCreateTime(refundNotification.getCreateTime());
            refundNotificationResp.setSuccessTime(DateUtils.parseStringToLocalDateTime(refundNotification.getSuccessTime()));
            refundNotificationResp.setRefundId(refundNotification.getRefundId());
            refundNotificationResp.setOutTradeNo(refundNotification.getOutTradeNo());
            refundNotificationResp.setRefundNo(refundNotification.getOutRefundNo());
            refundNotificationResp.setTransactionId(refundNotification.getTransactionId());
            refundNotificationResp.setUserReceivedAccount(refundNotification.getUserReceivedAccount());
            refundNotificationResp.setRefundStatus(RefundStatusEnum.fromWxCode(refundNotification.getRefundStatus().name()));
            refundNotificationResp.setTotal(refundNotification.getAmount().getTotal());
            refundNotificationResp.setRefundAmount(refundNotification.getAmount().getRefund());
            refundNotificationResp.setActualRefundAmount(refundNotification.getAmount().getPayerRefund());
            refundNotificationResp.setResponseJson(JSONUtil.toJsonStr(refundNotification));
            return Optional.of(refundNotificationResp);
        } catch (Exception exception) {
            log.error("parseRefundNotifyV3 failed", exception);
            return Optional.empty();
        }
    }

    /**
     * 企业付款到零钱
     *
     * @param outTradeNo  商户订单号，需唯一
     * @param openid      用户openid
     * @param amount      付款金额，单位为分
     * @param description 付款描述
     * @param ip          发起付款的客户端IP
     * @return 付款结果
     */
    public WechatTransferResp transferToBalance(String outTradeNo, String openid,
                                                Long amount, String description, String ip) {
        InitiateBatchTransferRequest request = new InitiateBatchTransferRequest();
        request.setAppid(WxConfig.APP_ID);
        request.setOutBatchNo(outTradeNo);
        request.setBatchName(description);
        request.setBatchRemark(description);
        request.setTotalAmount(amount);
        request.setTotalNum(1); // 默认一次只转给一个用户

        // 设置收款用户信息
        TransferDetailInput detail = new TransferDetailInput();
        detail.setOutDetailNo(outTradeNo); // 明细单号与批次单号保持一致
        detail.setTransferAmount(amount);
        detail.setTransferRemark(description);
        detail.setOpenid(openid);
        // 注意：如果需要校验收款人真实姓名，需要设置userName字段
        // detail.setUserName(getUserName(openid));

        request.setTransferDetailList(Collections.singletonList(detail));

        try {
            // 发起付款请求
            InitiateBatchTransferResponse result = transferBatchService.initiateBatchTransfer(request);

            // 构造返回结果
            WechatTransferResp resp = new WechatTransferResp();
            resp.setBatchId(result.getBatchId());
            resp.setOutBatchNo(result.getOutBatchNo());
            resp.setBatchStatus(result.getBatchStatus());
            resp.setCreateTime(result.getCreateTime());
            return resp;
        } catch (Exception e) {
            log.error("微信转账失败，outTradeNo: {}", outTradeNo, e);
            throw new CommonException(ResultCodeEnum.WX_TRANSFER_ERROR);
        }
    }

    /**
     * 查询付款批次状态
     *
     * @param outTradeNo 商户订单号
     * @return 付款批次信息
     */
    public WechatTransferQueryResp queryTransferBatch(String outTradeNo) {
        try {
            GetTransferBatchByOutNoRequest request = new GetTransferBatchByOutNoRequest();
            request.setOutBatchNo(outTradeNo);
            TransferBatchEntity batchInfo = transferBatchService.getTransferBatchByOutNo(request);
            TransferBatchGet transferBatch = batchInfo.getTransferBatch();
            WechatTransferQueryResp resp = new WechatTransferQueryResp();
            resp.setBatchId(transferBatch.getBatchId());
            resp.setOutBatchNo(transferBatch.getOutBatchNo());
            resp.setBatchStatus(transferBatch.getBatchStatus());
            resp.setTotalAmount(transferBatch.getTotalAmount());
            resp.setTotalNum(transferBatch.getTotalNum());
            resp.setSuccessAmount(transferBatch.getSuccessAmount());
            resp.setSuccessNum(transferBatch.getSuccessNum());
            resp.setFailAmount(transferBatch.getFailAmount());
            resp.setFailNum(transferBatch.getFailNum());
            resp.setCreateTime(transferBatch.getCreateTime());
            resp.setUpdateTime(transferBatch.getUpdateTime());

            List<WechatTransferQueryResp.TransferDetailResp> detailRespList = getTransferDetail(batchInfo);
            resp.setDetails(detailRespList);
            return resp;
        } catch (Exception e) {
            log.error("查询微信转账批次失败，outTradeNo: {}", outTradeNo, e);
            throw new CommonException(ResultCodeEnum.WX_TRANSFER_QUERY_ERROR);
        }
    }

    private List<WechatTransferQueryResp.TransferDetailResp> getTransferDetail(TransferBatchEntity batchInfo) {
        List<WechatTransferQueryResp.TransferDetailResp> detailRespList = new ArrayList<>();
        for (TransferDetailCompact detail : batchInfo.getTransferDetailList()) {
            WechatTransferQueryResp.TransferDetailResp detailResp = new WechatTransferQueryResp.TransferDetailResp();
            detailResp.setDetailId(detail.getDetailId());
            detailResp.setOutDetailNo(detail.getOutDetailNo());
            detailResp.setDetailStatus(detail.getDetailStatus());
            detailRespList.add(detailResp);
        }
        return detailRespList;
    }

    /**
     * 查询付款明细状态
     *
     * @param outTradeNo  商户订单号（批次号）
     * @param outDetailNo 明细单号
     * @return 付款明细信息
     */
    public WechatTransferDetailResp queryTransferDetail(String outTradeNo, String outDetailNo) {
        try {
            GetTransferDetailByOutNoRequest request = new GetTransferDetailByOutNoRequest();
            request.setOutBatchNo(outTradeNo);
            request.setOutDetailNo(outDetailNo);
            TransferDetailEntity detailInfo = transferBatchService.getTransferDetailByOutNo(request);

            WechatTransferDetailResp resp = new WechatTransferDetailResp();
            resp.setDetailId(detailInfo.getDetailId());
            resp.setOutDetailNo(detailInfo.getOutDetailNo());
            resp.setDetailStatus(detailInfo.getDetailStatus());
            resp.setTransferAmount((long) detailInfo.getTransferAmount());
            resp.setTransferRemark(detailInfo.getTransferRemark());
            resp.setOpenid(detailInfo.getOpenid());
            resp.setUserName(detailInfo.getUserName());
            resp.setFailReason(detailInfo.getFailReason().toString());
            resp.setUpdateTime(detailInfo.getUpdateTime());

            return resp;
        } catch (Exception e) {
            log.error("查询微信转账明细失败，outTradeNo: {}, outDetailNo: {}", outTradeNo, outDetailNo, e);
            throw new CommonException(ResultCodeEnum.WX_TRANSFER_QUERY_ERROR);
        }
    }

    private static WechatPayOrderResp getWechatPayOrderResp(Transaction transaction) {
        WechatPayOrderResp wechatPayOrderResp = new WechatPayOrderResp();
        wechatPayOrderResp.setSn(transaction.getOutTradeNo());
        wechatPayOrderResp.setTotal(transaction.getAmount().getTotal());
        wechatPayOrderResp.setPayerTotal(transaction.getAmount().getPayerTotal());
        wechatPayOrderResp.setCurrency(transaction.getAmount().getCurrency());
        wechatPayOrderResp.setPaySuccessTime(transaction.getSuccessTime());
        wechatPayOrderResp.setPayStatus(transaction.getTradeState().name());
        wechatPayOrderResp.setTransactionId(transaction.getTransactionId());
        return wechatPayOrderResp;
    }

    /**
     * 关闭订单（使用渠道配置）
     * 当订单超时未支付或用户取消支付时，调用关闭订单
     *
     * @param outTradeNo    商户订单号
     * @return 是否关闭成功
     */
    public boolean closeOrderWithConfig(String outTradeNo) {
        try {
            log.info("开始关闭微信支付订单，订单号: {}", outTradeNo);
            // 构建关闭订单请求
            CloseOrderRequest request = new CloseOrderRequest();
            request.setMchid(mchId);
            request.setOutTradeNo(outTradeNo);
            // 调用关闭订单接口
            jsapiServiceExtension.closeOrder(request);

            log.info("微信支付订单关闭成功，订单号: {}", outTradeNo);
            return true;

        } catch (Exception e) {
            log.error("关闭微信支付订单失败，订单号: {}, 错误信息: {}", outTradeNo, e.getMessage(), e);
            return false;
        }
    }
}
