yeg
2026-09-12 d32e0dee609ab13220908ab28a11390bede07c84
talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuPayload.java
File was renamed from talent-card-server/src/main/java/com/talentcard/server/business/payment/HuifuPaymentPayload.java
@@ -1,4 +1,4 @@
package com.talentcard.server.business.payment;
package com.talentcard.server.business.payment.huifu;
import java.math.BigDecimal;
import java.math.RoundingMode;
@@ -10,17 +10,18 @@
/**
 * Shared readings of a Huifu payload.
 *
 * <p>A payment callback and a payment query carry the same business fields, so both are interpreted
 * here instead of each caller keeping its own field names, money conversion and status rules. The
 * provider reports money in yuan with two decimals while local amounts are integer cents.
 * <p>A callback and a synchronous response carry the same business fields, so payment, refund and
 * withdrawal results are all interpreted here instead of each caller keeping its own field names,
 * money conversion and status rules. The provider reports money in yuan with two decimals while
 * local amounts are integer cents.
 */
final class HuifuPaymentPayload {
public final class HuifuPayload {
    private HuifuPaymentPayload() {
    private HuifuPayload() {
    }
    /** How the provider describes the state of a transaction. */
    enum TransStatus {
    public enum TransStatus {
        /** Money moved: {@code S}. */
        SUCCESS,
        /** The provider declared the transaction failed or closed: {@code F}. */
@@ -31,7 +32,7 @@
        UNKNOWN
    }
    static TransStatus status(String transStatus) {
    public static TransStatus status(String transStatus) {
        String value = text(transStatus).toUpperCase(Locale.ROOT);
        return switch (value) {
            case "S", "SUCCESS", "PAID" -> TransStatus.SUCCESS;
@@ -42,25 +43,25 @@
    }
    /** Whether a success statement may be believed: the transaction status and the code must agree. */
    static boolean paid(String transStatus, String code) {
    public static boolean paid(String transStatus, String code) {
        return status(transStatus) == TransStatus.SUCCESS && successCode(code);
    }
    /** A notification that will never settle by itself, used to close refunds. */
    static boolean terminalFailure(String transStatus, String code) {
    public static boolean terminalFailure(String transStatus, String code) {
        TransStatus status = status(transStatus);
        if (status == TransStatus.PENDING) return false;
        if (status == TransStatus.FAILED) return true;
        return !successCode(code);
    }
    static boolean successCode(String code) {
    public static boolean successCode(String code) {
        String value = text(code);
        return "00000000".equals(value) || "10000".equals(value) || "0".equals(value) || "SUCCESS".equalsIgnoreCase(value);
    }
    /** Provider amount in yuan with two decimals, converted to integer cents. Null when unusable. */
    static Long amountInCent(String yuan) {
    public static Long amountInCent(String yuan) {
        String value = text(yuan);
        if (value.isBlank()) return null;
        try {
@@ -71,7 +72,7 @@
    }
    /** First non-blank value of the candidate keys, looking up the provider's snake and camel spellings. */
    static String value(Map<String, Object> data, String... keys) {
    public static String value(Map<String, Object> data, String... keys) {
        for (String key : keys) {
            Object value = data.get(key);
            if (value != null && !String.valueOf(value).isBlank()) return String.valueOf(value).trim();
@@ -80,19 +81,18 @@
    }
    /** Response code, preferring the business sub-code. */
    static String code(Map<String, Object> data) {
        String code = value(data, "sub_resp_code", "resp_code", "code");
        return code;
    public static String code(Map<String, Object> data) {
        return value(data, "sub_resp_code", "resp_code", "code");
    }
    /** Response message, preferring the business sub-description. */
    static String message(Map<String, Object> data) {
    public static String message(Map<String, Object> data) {
        return value(data, "sub_resp_desc", "resp_desc", "message");
    }
    /** Huifu may nest the business fields under {@code data}, as an object or as a JSON string. */
    @SuppressWarnings("unchecked")
    static Map<String, Object> businessData(Map<String, Object> payload) {
    public static Map<String, Object> businessData(Map<String, Object> payload) {
        Object nested = payload.get("data");
        if (nested instanceof Map<?, ?> value) return (Map<String, Object>) value;
        if (nested instanceof String value && !value.isBlank()) {
@@ -105,11 +105,11 @@
        return payload;
    }
    static String firstNonBlank(String first, String second) {
    public static String firstNonBlank(String first, String second) {
        return first == null || first.isBlank() ? (second == null ? "" : second) : first;
    }
    static String text(Object value) {
    public static String text(Object value) {
        return value == null ? "" : String.valueOf(value).trim();
    }
}