shinnosuke
2026-08-13 dbfab7f57fbd0189b31f5f16854b4d725ee79a12
talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuMiniProgramPaymentGateway.java
@@ -4,6 +4,7 @@
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.alibaba.fastjson.JSON;
@@ -52,8 +53,21 @@
        extendInfo.put("delay_acct_flag", properties.isDelayAccount() ? "Y" : "N");
        extendInfo.put("time_expire", command.expiresAt().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
        request.setExtendInfo(extendInfo);
        log.info("Huifu mini-program payment request: {}", logJson(Map.of(
                "req_date", REQUEST_DATE.format(LocalDateTime.now()),
                "req_seq_id", command.paymentNo(),
                "huifu_id", properties.getMerchantId().trim(),
                "goods_desc", command.goodsDescription(),
                "trans_amt", BigDecimal.valueOf(command.amountInCent(), 2).toPlainString(),
                "trade_type", "T_MINIAPP",
                "system_id", properties.getSystemId().trim(),
                "product_id", properties.getProductId().trim(),
                "extend_info", extendInfo
        )));
        try {
            return BasePayClient.request(request, false);
            Map<String, Object> response = BasePayClient.request(request, false);
            log.info("Huifu mini-program payment response: {}", logJson(response));
            return response;
        } catch (Exception exception) {
            // 支付参数包含用户 OpenID 与签名材料,日志仅保留 SDK 错误类型及平台消息。
            log.warn("Huifu mini-program payment request failed: type={}, message={}",
@@ -121,6 +135,46 @@
        return message.length() > 500 ? message.substring(0, 500) : message;
    }
    private String logJson(Object value) {
        try {
            return JSON.toJSONString(sanitizeForLog(value));
        } catch (Exception ignored) {
            return "{\"log_error\":\"Unable to serialize payment payload\"}";
        }
    }
    private Object sanitizeForLog(Object value) {
        if (value instanceof Map<?, ?> source) {
            Map<String, Object> result = new LinkedHashMap<>();
            for (Map.Entry<?, ?> entry : source.entrySet()) {
                String key = String.valueOf(entry.getKey());
                result.put(key, isSensitiveKey(key) ? mask(String.valueOf(entry.getValue())) : sanitizeForLog(entry.getValue()));
            }
            return result;
        }
        if (value instanceof List<?> source) return source.stream().map(this::sanitizeForLog).toList();
        if (value instanceof String text && (text.startsWith("{") || text.startsWith("["))) {
            try {
                return sanitizeForLog(JSON.parse(text));
            } catch (Exception ignored) {
                return text;
            }
        }
        return value;
    }
    private boolean isSensitiveKey(String key) {
        String normalized = key == null ? "" : key.toLowerCase();
        return normalized.contains("private") || normalized.contains("secret") || normalized.contains("sign")
                || normalized.contains("openid") || normalized.contains("authorization") || normalized.contains("certificate");
    }
    private String mask(String value) {
        if (value == null || value.isBlank()) return "";
        if (value.length() <= 8) return "***";
        return value.substring(0, 4) + "***" + value.substring(value.length() - 4);
    }
    private boolean isOutcomeUnknown(Exception exception) {
        String message = safeMessage(exception.getMessage()).toLowerCase();
        return message.contains("no response") || message.contains("timeout") || message.contains("timed out");