yeg
2026-09-12 d879291f56eb64adffa44f763238ea736b4be99b
talent-card-server/src/main/java/com/talentcard/server/business/payment/HuifuRefundService.java
@@ -5,6 +5,7 @@
import com.talentcard.server.business.MoneyUtils;
import com.talentcard.server.business.order.OrderRefundRequest;
import com.talentcard.server.business.payment.huifu.HuifuPaymentGatewayException;
import com.talentcard.server.business.payment.huifu.HuifuPaymentProperties;
import com.talentcard.server.business.payment.huifu.HuifuRefundCommand;
import com.talentcard.server.business.payment.huifu.HuifuRefundGateway;
import com.talentcard.server.common.BusinessException;
@@ -30,11 +31,13 @@
    private final JdbcTemplate jdbc;
    private final TransactionTemplate transactions;
    private final HuifuRefundGateway gateway;
    private final HuifuPaymentProperties huifuPaymentProperties;
    public HuifuRefundService(JdbcTemplate jdbc, TransactionTemplate transactions, HuifuRefundGateway gateway) {
    public HuifuRefundService(JdbcTemplate jdbc, TransactionTemplate transactions, HuifuRefundGateway gateway, HuifuPaymentProperties huifuPaymentProperties) {
        this.jdbc = jdbc;
        this.transactions = transactions;
        this.gateway = gateway;
        this.huifuPaymentProperties = huifuPaymentProperties;
    }
    public Map<String, Object> request(String orderNo, OrderRefundRequest request) {
@@ -44,7 +47,7 @@
        Map<String, Object> response;
        try {
            response = gateway.createRefund(new HuifuRefundCommand(
                    prepared.refundNo(), prepared.paymentNo(), prepared.originalProviderNo(),
                    prepared.refundNo(), prepared.huifuId(), prepared.paymentNo(), prepared.originalProviderNo(),
                    prepared.originalRequestDate(), prepared.refundAmount()));
        } catch (HuifuPaymentGatewayException exception) {
            if (!exception.isOutcomeUnknown()) {
@@ -76,6 +79,7 @@
        if (number(refund.get("refundStatus")) != 1) return refund;
        HuifuRefundCommand command = new HuifuRefundCommand(
                refundNo,
                refundHuifuId(refund.get("providerHuifuId")),
                text(refund.get("providerOriginalReqSeqId")),
                text(refund.get("providerOriginalNo")),
                text(refund.get("providerOriginalReqDate")),
@@ -94,7 +98,7 @@
    public Map<String, Object> detail(String refundNo) {
        List<Map<String, Object>> rows = rows("""
                select r.id, r.order_id, r.refund_no, r.order_no, r.order_type, r.paid_amount, r.refund_amount,
                       r.refundable_amount, r.refund_status, r.provider, r.provider_refund_no,
                       r.refundable_amount, r.refund_status, r.provider, r.provider_refund_no, r.provider_huifu_id,
                       r.provider_original_no, r.provider_original_req_seq_id, r.provider_original_req_date,
                       r.provider_response_code, r.provider_response_message, r.reason, r.operator_name, r.refund_time
                from order_refund r
@@ -108,7 +112,8 @@
        List<Map<String, Object>> orders = rows("""
                select o.id, o.order_no, o.talent_id, o.card_id, o.benefit_id, o.merchant_id, o.order_type,
                       o.pay_amount, o.order_status, o.verify_status,
                       p.payment_no, p.provider_transaction_no, p.created_at as payment_created_at
                       p.payment_no, p.provider_transaction_no, p.created_at as payment_created_at,
                       coalesce(p.provider_huifu_id, o.collect_huifu_id) as huifu_id
                from benefit_order o
                left join payment_transaction p on p.order_id = o.id and p.provider = ? and p.status = 'SUCCEEDED'
                where o.deleted = 0 and o.order_no = ?
@@ -135,19 +140,20 @@
        String paymentNo = text(order.get("paymentNo"));
        String providerNo = text(order.get("providerTransactionNo"));
        if (paymentNo.isBlank() && providerNo.isBlank()) throw new BusinessException(ErrorCode.CONFLICT, "Payment transaction is not ready for refund");
        String huifuId = refundHuifuId(order.get("huifuId"));
        String refundNo = BusinessIdGenerator.next("R");
        String requestDate = requestDate(order.get("paymentCreatedAt"));
        jdbc.update("""
                insert into order_refund(refund_no, order_id, order_no, talent_id, card_id, benefit_id, merchant_id,
                                         order_type, paid_amount, refund_amount, refundable_amount, refund_status,
                                         provider, provider_original_no, provider_original_req_seq_id, provider_original_req_date,
                                         provider, provider_huifu_id, provider_original_no, provider_original_req_seq_id, provider_original_req_date,
                                         reason, operator_name)
                values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?)
                values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?)
                """,
                refundNo, order.get("id"), order.get("orderNo"), order.get("talentId"), order.get("cardId"),
                order.get("benefitId"), order.get("merchantId"), order.get("orderType"), paidAmount, refundAmount,
                refundableAmount - refundAmount, PROVIDER, providerNo, paymentNo, requestDate, request.reason(), request.operatorName());
        return new PreparedRefund(refundNo, number(order.get("id")), paymentNo, providerNo, requestDate, refundAmount);
                refundableAmount - refundAmount, PROVIDER, huifuId, providerNo, paymentNo, requestDate, request.reason(), request.operatorName());
        return new PreparedRefund(refundNo, number(order.get("id")), huifuId, paymentNo, providerNo, requestDate, refundAmount);
    }
    private void markSucceeded(PreparedRefund prepared, ProviderResult result) {
@@ -179,6 +185,16 @@
        return total == null ? 0 : total;
    }
    /**
     * The merchant a refund must be requested from: the sub-merchant fixed on the payment or the
     * order. Payments made before the collecting sub-merchant was recorded were collected by the
     * configured platform merchant, so those fall back to it instead of the refund failing.
     */
    private String refundHuifuId(Object storedHuifuId) {
        String resolved = text(storedHuifuId);
        return resolved.isBlank() ? text(huifuPaymentProperties.getMerchantId()) : resolved;
    }
    private String requestDate(Object value) {
        if (value instanceof LocalDateTime dateTime) return REQUEST_DATE.format(dateTime);
        if (value instanceof LocalDate date) return date.format(DateTimeFormatter.BASIC_ISO_DATE);
@@ -189,8 +205,8 @@
    private long number(Object value) { return value instanceof Number number ? number.longValue() : 0; }
    private String text(Object value) { return value == null ? "" : String.valueOf(value).trim(); }
    private record PreparedRefund(String refundNo, long orderId, String paymentNo, String originalProviderNo, String originalRequestDate, long refundAmount) {
        private PreparedRefund(String refundNo, long orderId, long refundAmount) { this(refundNo, orderId, "", "", "", refundAmount); }
    private record PreparedRefund(String refundNo, long orderId, String huifuId, String paymentNo, String originalProviderNo, String originalRequestDate, long refundAmount) {
        private PreparedRefund(String refundNo, long orderId, long refundAmount) { this(refundNo, orderId, "", "", "", "", refundAmount); }
    }
    private record ProviderResult(boolean success, boolean failed, String code, String message, String providerNo) {