package com.talentcard.server.business.payment.huifu; import static org.assertj.core.api.Assertions.assertThat; import java.util.LinkedHashMap; import java.util.Map; import com.talentcard.server.business.payment.huifu.HuifuDelayAccountGateway.HuifuDelayRevert; import com.talentcard.server.business.payment.huifu.HuifuDelayAccountGateway.HuifuDelayRevert.Outcome; import org.junit.jupiter.api.Test; /** * 资金回退(交易确认退款)的结果分类。 * *

这一组用例盯的是「同一个业务码在两条路径上语义不同」:交易确认把 {@code 23000003} 当作 * 「已无未确认金额」=幂等成功(有真实回执复验过),但回退路径上没有回执证明它的含义,一律不能算成功 * ——判成成功会让订单翻回「待核销」而钱还在商户基本户,属于资金事故。 */ class HuifuDelayRevertClassificationTests { @Test void aSuccessCodeFromTheProviderIsTheOnlySuccess() { assertThat(outcomeOf("00000000", "S")).isEqualTo(Outcome.SUCCEEDED); // 顶层网关码也代表成功调用。 assertThat(outcomeOf("10000", "S")).isEqualTo(Outcome.SUCCEEDED); } /** * 语义未定的码既不算成功也不算失败:{@code 23000003} 在确认路径上是幂等成功, * 在回退路径上可能是「已无已确认金额」,也可能是被拒的原因(例如余额不足)。 */ @Test void codesWithUndeterminedMeaningAreNeverSuccess() { assertThat(outcomeOf("23000003", "F")).isEqualTo(Outcome.NEEDS_REVIEW); assertThat(outcomeOf("23000004", "F")).isEqualTo(Outcome.NEEDS_REVIEW); assertThat(outcomeOf("23000003", "S")).isEqualTo(Outcome.NEEDS_REVIEW); } @Test void aDeclaredFailureIsAFailure() { assertThat(outcomeOf("22000002", "F")).isEqualTo(Outcome.FAILED); assertThat(outcomeOf("", "F")).isEqualTo(Outcome.FAILED); } @Test void aRequestStillInFlightIsNotASettledResult() { assertThat(outcomeOf("00000100", "P")).isEqualTo(Outcome.PROCESSING); assertThat(outcomeOf("", "")).isEqualTo(Outcome.PROCESSING); // 状态说成功但没有任何业务码:两个证据不一致,按未定处理,不按成功放行。 assertThat(outcomeOf("", "S")).isEqualTo(Outcome.PROCESSING); } @Test void theProviderDescriptionIsKeptForTroubleshooting() { HuifuDelayRevert revert = HuifuDelayRevert.from(response("22000003", "F", "账户余额不足")); assertThat(revert.describe()).isEqualTo("账户余额不足"); assertThat(revert.code()).isEqualTo("22000003"); } private Outcome outcomeOf(String code, String transStat) { return HuifuDelayRevert.from(response(code, transStat, "返回描述")).outcome(); } private Map response(String code, String transStat, String description) { Map response = new LinkedHashMap<>(); response.put("sub_resp_code", code); response.put("sub_resp_desc", description); response.put("trans_stat", transStat); return response; } }