longz
2026-07-13 6fc77feef6337bc478423efad5de91fa520b4990
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/service/impl/IcbcPayServiceImpl.java
@@ -31,6 +31,7 @@
import com.ruoyi.payment.api.vo.IcbcSubOrderSplitReq;
import com.ruoyi.payment.api.vo.IcbcSubOrderSplitResp;
import com.ruoyi.payment.icbc.config.IcbcPayProperties;
import com.ruoyi.payment.icbc.service.IcbcPaymentFlowRecorder;
import com.ruoyi.payment.icbc.service.IcbcPayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -53,6 +54,8 @@
public class IcbcPayServiceImpl implements IcbcPayService {
    @Resource
    private IcbcPayProperties properties;
    @Resource
    private IcbcPaymentFlowRecorder flowRecorder;
    @Override
    public IcbcPaymentResp payment(IcbcPaymentReq req) {
@@ -67,28 +70,34 @@
            CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2 request = new CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2();
            request.setServiceUrl(properties.getGatewayUrl());
            request.setBizContent(buildConsumePurchaseBiz(req));
            flowRecorder.record("PAY_REQUEST", req.getOutTradeNo(), req.getPaymentType(), req.getAmount(),
                    request.getBizContent(), null, "SENT", null);
            CardbusinessAggregatepayB2cOnlineConsumepurchaseResponseV2 response = executeIcbc("聚合支付下单", request);
            resp.setOrderId(firstNotBlank(response.getOut_trade_no(), req.getOutTradeNo()));
            resp.setTradeNo(firstNotBlank(response.getOrder_id(), response.getMsgId()));
            resp.setTradeNo(response.getOrder_id());
            resp.setStatus(response.isSuccess() ? "SUCCESS" : "FAIL");
            resp.setPayData(buildConsumePurchasePayData(response, req.getPaymentType()));
            resp.setRawResponse(JSON.toJSONString(response));
            flowRecorder.record("PAY_RESPONSE", resp.getOrderId(), req.getPaymentType(), req.getAmount(),
                    request.getBizContent(), response, resp.getStatus(), null);
        } catch (Exception e) {
            log.error("工行聚合支付调用失败,订单号:{}", req == null ? null : req.getOutTradeNo(), e);
            resp.setStatus("FAIL");
            resp.setRawResponse(e.getMessage());
            flowRecorder.record("PAY_RESPONSE", req == null ? null : req.getOutTradeNo(), req == null ? null : req.getPaymentType(),
                    req == null ? null : req.getAmount(), req, null, "FAIL", e.getMessage());
        }
        return resp;
    }
    @Override
    public IcbcPaymentResp query(String outTradeNo) {
        validateCommonConfig();
        validateConfig("icbc.pay.orderQueryUrl", properties.getOrderQueryUrl());
        IcbcPaymentResp resp = new IcbcPaymentResp();
        resp.setOrderId(outTradeNo);
        try {
            validateCommonConfig();
            validateConfig("icbc.pay.orderQueryUrl", properties.getOrderQueryUrl());
            CardbusinessAggregatepayB2cOnlineOrderqryRequestV2 request = new CardbusinessAggregatepayB2cOnlineOrderqryRequestV2();
            request.setServiceUrl(properties.getOrderQueryUrl());
            request.setBizContent(buildOrderQueryBiz(outTradeNo));
@@ -98,10 +107,13 @@
            resp.setStatus(firstNotBlank(response.getOrderStatus(), response.getPay_status()));
            resp.setPayData(JSON.toJSONString(response));
            resp.setRawResponse(JSON.toJSONString(response));
            flowRecorder.record("QUERY", outTradeNo, null, null, buildOrderQueryBiz(outTradeNo), response,
                    response.isSuccess() ? "SUCCESS" : "FAIL", response.isSuccess() ? null : response.getReturnMsg());
        } catch (Exception e) {
            log.error("工行支付订单查询失败,订单号:{}", outTradeNo, e);
            resp.setStatus("QUERY_FAIL");
            resp.setRawResponse(e.getMessage());
            flowRecorder.record("QUERY", outTradeNo, null, null, null, null, "FAIL", e.getMessage());
        }
        return resp;
    }
@@ -118,13 +130,18 @@
    @Override
    public IcbcRefundResp refund(IcbcRefundReq req) {
        validateCommonConfig();
        validateConfig("icbc.pay.refundUrl", properties.getRefundUrl());
        IcbcRefundResp resp = new IcbcRefundResp();
        resp.setOutTradeNo(req.getOutTradeNo());
        resp.setOrderId(req.getOrderId());
        resp.setOutRefundNo(req.getOutRefundNo());
        if (req != null) {
            resp.setOutTradeNo(req.getOutTradeNo());
            resp.setOrderId(req.getOrderId());
            resp.setOutRefundNo(req.getOutRefundNo());
        }
        try {
            validateCommonConfig();
            validateConfig("icbc.pay.refundUrl", properties.getRefundUrl());
            if (req == null) {
                throw new IllegalArgumentException("工行退款请求不能为空");
            }
            CardbusinessAggregatepayB2cOnlineMerrefundRequestV1 request = new CardbusinessAggregatepayB2cOnlineMerrefundRequestV1();
            request.setServiceUrl(properties.getRefundUrl());
            request.setBizContent(buildRefundBiz(req));
@@ -137,23 +154,33 @@
            resp.setRefundId(firstNotBlank(response.getIntrx_serial_no(), response.getOuttrxSerialNo()));
            resp.setRefundTime(response.getRefund_time());
            resp.setRefundAmount(firstNotBlank(response.getRealRejectAmt(), response.getRejectAmt(), toFen(req.getRefundAmount())));
            flowRecorder.record("REFUND", req.getOutTradeNo(), null, req.getRefundAmount(), request.getBizContent(), response,
                    resp.getStatus(), null);
        } catch (Exception e) {
            log.error("工行退款失败,订单号:{},退款单号:{}", req.getOutTradeNo(), req.getOutRefundNo(), e);
            log.error("工行退款失败,订单号:{},退款单号:{}",
                    req == null ? null : req.getOutTradeNo(), req == null ? null : req.getOutRefundNo(), e);
            resp.setStatus("FAIL");
            resp.setRawResponse(e.getMessage());
            flowRecorder.record("REFUND", req == null ? null : req.getOutTradeNo(), null, req == null ? null : req.getRefundAmount(),
                    req, null, "FAIL", e.getMessage());
        }
        return resp;
    }
    @Override
    public IcbcSubOrderSplitResp splitSubOrder(IcbcSubOrderSplitReq req) {
        validateCommonConfig();
        validateConfig("icbc.pay.merPrtclNo", properties.getMerPrtclNo());
        validateConfig("icbc.pay.subOrderSplitUrl", properties.getSubOrderSplitUrl());
        IcbcSubOrderSplitResp resp = new IcbcSubOrderSplitResp();
        resp.setChargingOrderCode(req.getChargingOrderCode());
        resp.setSubOrderId(req.getSubOrderId());
        if (req != null) {
            resp.setChargingOrderCode(req.getChargingOrderCode());
            resp.setSubOrderId(req.getSubOrderId());
        }
        try {
            validateCommonConfig();
            validateConfig("icbc.pay.merPrtclNo", properties.getMerPrtclNo());
            validateConfig("icbc.pay.subOrderSplitUrl", properties.getSubOrderSplitUrl());
            if (req == null) {
                throw new IllegalArgumentException("工行分账请求不能为空");
            }
            CardbusinessAggregatepayB2cOnlineSersuborderRequestV1 request = new CardbusinessAggregatepayB2cOnlineSersuborderRequestV1();
            request.setServiceUrl(properties.getSubOrderSplitUrl());
            request.setBizContent(buildSubOrderSplitBiz(req));
@@ -161,24 +188,33 @@
            resp.setStatus(response.isSuccess() ? "SUCCESS" : "FAIL");
            resp.setRawResponse(JSON.toJSONString(response));
            fillSplitOutput(resp, response.getOrderSubmitActOutput());
            flowRecorder.record("SPLIT", req.getChargingOrderCode(), null, req.getClassifyAmt(), request.getBizContent(), response,
                    resp.getStatus(), null);
        } catch (Exception e) {
            log.error("工行小订单拆分失败,订单号:{}", req.getChargingOrderCode(), e);
            log.error("工行小订单拆分失败,订单号:{}", req == null ? null : req.getChargingOrderCode(), e);
            resp.setStatus("FAIL");
            resp.setRawResponse(e.getMessage());
            flowRecorder.record("SPLIT", req == null ? null : req.getChargingOrderCode(), null, req == null ? null : req.getClassifyAmt(),
                    req, null, "FAIL", e.getMessage());
        }
        return resp;
    }
    @Override
    public IcbcSubOrderRefundResp refundSubOrder(IcbcSubOrderRefundReq req) {
        validateCommonConfig();
        validateConfig("icbc.pay.subOrderRefundUrl", properties.getSubOrderRefundUrl());
        IcbcSubOrderRefundResp resp = new IcbcSubOrderRefundResp();
        resp.setChargingOrderCode(req.getChargingOrderCode());
        resp.setSubOrderId(req.getSubOrderId());
        resp.setRetSubOrderId(req.getRetSubOrderId());
        resp.setOuttrxSerialNo(req.getOuttrxSerialNo());
        if (req != null) {
            resp.setChargingOrderCode(req.getChargingOrderCode());
            resp.setSubOrderId(req.getSubOrderId());
            resp.setRetSubOrderId(req.getRetSubOrderId());
            resp.setOuttrxSerialNo(req.getOuttrxSerialNo());
        }
        try {
            validateCommonConfig();
            validateConfig("icbc.pay.subOrderRefundUrl", properties.getSubOrderRefundUrl());
            if (req == null) {
                throw new IllegalArgumentException("工行分账退款请求不能为空");
            }
            CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1 request = new CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1();
            request.setServiceUrl(properties.getSubOrderRefundUrl());
            request.setBizContent(buildSubOrderRefundBiz(req));
@@ -189,20 +225,29 @@
            resp.setSubOrderSeq(response.getSub_order_seq());
            resp.setSubOrderTrxid(response.getSub_order_trxid());
            resp.setRefundAmount(toFen(req.getRefundAmount()));
            flowRecorder.record("SPLIT_REFUND", req.getChargingOrderCode(), null, req.getRefundAmount(), request.getBizContent(), response,
                    resp.getStatus(), null);
        } catch (Exception e) {
            log.error("工行小订单拆分退款失败,订单号:{},子订单号:{},退款流水:{}", req.getChargingOrderCode(), req.getSubOrderId(), req.getOuttrxSerialNo(), e);
            log.error("工行小订单拆分退款失败,订单号:{},子订单号:{},退款流水:{}",
                    req == null ? null : req.getChargingOrderCode(), req == null ? null : req.getSubOrderId(),
                    req == null ? null : req.getOuttrxSerialNo(), e);
            resp.setStatus("FAIL");
            resp.setRawResponse(e.getMessage());
            flowRecorder.record("SPLIT_REFUND", req == null ? null : req.getChargingOrderCode(), null,
                    req == null ? null : req.getRefundAmount(), req, null, "FAIL", e.getMessage());
        }
        return resp;
    }
    @Override
    public IcbcSubOrderQueryResp querySubOrder(IcbcSubOrderQueryReq req) {
        validateCommonConfig();
        validateConfig("icbc.pay.subOrderQueryUrl", properties.getSubOrderQueryUrl());
        IcbcSubOrderQueryResp resp = new IcbcSubOrderQueryResp();
        try {
            validateCommonConfig();
            validateConfig("icbc.pay.subOrderQueryUrl", properties.getSubOrderQueryUrl());
            if (req == null) {
                throw new IllegalArgumentException("工行子订单查询请求不能为空");
            }
            CardbusinessOrderSuborderqueryRequestV1 request = new CardbusinessOrderSuborderqueryRequestV1();
            request.setServiceUrl(properties.getSubOrderQueryUrl());
            request.setBizContent(buildSubOrderQueryBiz(req));
@@ -215,13 +260,18 @@
            resp.setSubOrderNo(response.getSubOrderNo());
            resp.setSubMerOrderId(response.getSubMerOrderId());
            resp.setOrderAmnt(response.getOrderAmnt());
            resp.setOrderRefundAmnt(response.getOrderRefundAmnt());
            resp.setPayDate(response.getPayDate());
            resp.setStatus(response.isSuccess() ? "SUCCESS" : "FAIL");
            resp.setRawResponse(JSON.toJSONString(response));
            flowRecorder.record("SPLIT_QUERY", firstNotBlank(req.getMerOrderId(), req.getOrderId(), req.getSubOrderNo()), null, null, request.getBizContent(), response,
                    resp.getStatus(), null);
        } catch (Exception e) {
            log.error("工行子订单查询失败,子订单号:{}", req.getSubOrderNo(), e);
            log.error("工行子订单查询失败,子订单号:{}", req == null ? null : req.getSubOrderNo(), e);
            resp.setStatus("FAIL");
            resp.setRawResponse(e.getMessage());
            flowRecorder.record("SPLIT_QUERY", req == null ? null : firstNotBlank(req.getMerOrderId(), req.getOrderId(), req.getSubOrderNo()), null, null,
                    req, null, "FAIL", e.getMessage());
        }
        return resp;
    }
@@ -233,28 +283,29 @@
            String[] values = entry.getValue();
            params.put(entry.getKey(), values == null || values.length == 0 ? "" : String.join(",", values));
        }
        if (!verifyNotify(params)) {
            throw new IllegalArgumentException("工行回调验签失败");
        try {
            if (!verifyNotify(params)) {
                throw new IllegalArgumentException("工行回调验签失败");
            }
            mergeBizContent(params);
            Map<String, String> result = IcbcNotifyParser.parseVerified(params);
            flowRecorder.record("PAY_NOTIFY", result.get("out_trade_no"), parseInteger(result.get("attach")), null,
                    params, result, "SUCCESS", null);
            return result;
        } catch (RuntimeException e) {
            flowRecorder.record("PAY_NOTIFY", firstNotBlank(params.get("out_trade_no"), params.get("outTradeNo")),
                    parseInteger(firstNotBlank(params.get("attach"), params.get("paymentType"))), null,
                    params, null, "FAIL", e.getMessage());
            throw e;
        }
        mergeBizContent(params);
        String status = firstNotBlank(params.get("return_code"), params.get("orderStatus"), params.get("pay_status"), params.get("status"));
        if (StringUtils.isNotEmpty(status) && !isPaidStatus(status)) {
            throw new IllegalArgumentException("工行回调支付状态非成功:" + status);
    }
    private Integer parseInteger(String value) {
        try {
            return StringUtils.isEmpty(value) ? null : Integer.valueOf(value);
        } catch (NumberFormatException ignored) {
            return null;
        }
        String outTradeNo = firstNotBlank(params.get("outTradeNo"), params.get("out_trade_no"), params.get("order_id"));
        if (StringUtils.isEmpty(outTradeNo)) {
            throw new IllegalArgumentException("工行回调缺少订单号");
        }
        String tradeNo = firstNotBlank(params.get("tradeNo"), params.get("trade_no"), params.get("msg_id"));
        String attach = firstNotBlank(params.get("attach"), params.get("paymentType"), params.get("payType"));
        if (StringUtils.isEmpty(attach)) {
            throw new IllegalArgumentException("工行回调缺少支付类型");
        }
        Map<String, String> result = new HashMap<>();
        result.put("out_trade_no", outTradeNo);
        result.put("transaction_id", tradeNo);
        result.put("attach", attach);
        return result;
    }
    private DefaultIcbcClient buildClient() {
@@ -442,7 +493,7 @@
        CardbusinessOrderSuborderqueryRequestV1.CardbusinessOrderSuborderqueryRequestV1Biz biz =
                new CardbusinessOrderSuborderqueryRequestV1.CardbusinessOrderSuborderqueryRequestV1Biz();
        biz.setIcbcAppid(properties.getAppId());
        biz.setSerNo(req.getSerNo());
        biz.setSerNo(firstNotBlank(req.getSerNo(), properties.getServiceMerId(), properties.getMerId()));
        biz.setOrderNoType(req.getOrderNoType());
        biz.setOrderId(req.getOrderId());
        biz.setMerOrderId(req.getMerOrderId());