diff --git a/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/common/V5PayOrderNumber.java b/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/common/V5PayOrderNumber.java index 03db259e..b28e9548 100644 --- a/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/common/V5PayOrderNumber.java +++ b/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/common/V5PayOrderNumber.java @@ -43,11 +43,13 @@ public final class V5PayOrderNumber { } private static String normalizedPrefix(String prefix) { - String normalized = Objects.toString(prefix, "").trim().toUpperCase(Locale.ROOT); - if (StringUtils.isBlank(normalized) || !isAsciiLettersOrDigits(normalized)) { + String rawPrefix = Objects.toString(prefix, "").trim(); + // 无分隔符拼接时,前缀若包含数字会产生命名空间碰撞,例如 YUMI+123 与 YUMI1+23; + // 必须在大小写转换前校验 ASCII,避免 Unicode 字母(如 dotless i)转换成 ASCII 后绕过限制。 + if (StringUtils.isBlank(rawPrefix) || !isAsciiLetters(rawPrefix)) { throw new IllegalArgumentException("V5Pay order prefix is invalid"); } - return normalized; + return rawPrefix.toUpperCase(Locale.ROOT); } private static boolean isAsciiDigits(String value) { @@ -60,11 +62,11 @@ public final class V5PayOrderNumber { return true; } - private static boolean isAsciiLettersOrDigits(String value) { + private static boolean isAsciiLetters(String value) { for (int index = 0; index < value.length(); index++) { char item = value.charAt(index); boolean letter = (item >= 'A' && item <= 'Z') || (item >= 'a' && item <= 'z'); - if (!letter && (item < '0' || item > '9')) { + if (!letter) { return false; } } diff --git a/rc-service/rc-service-order/order-application/src/test/java/com/red/circle/order/app/common/V5PayOrderNumberTest.java b/rc-service/rc-service-order/order-application/src/test/java/com/red/circle/order/app/common/V5PayOrderNumberTest.java index 5ea0901d..c3b1afae 100644 --- a/rc-service/rc-service-order/order-application/src/test/java/com/red/circle/order/app/common/V5PayOrderNumberTest.java +++ b/rc-service/rc-service-order/order-application/src/test/java/com/red/circle/order/app/common/V5PayOrderNumberTest.java @@ -48,6 +48,19 @@ public class V5PayOrderNumberTest { assertIllegalArgument(() -> V5PayOrderNumber.providerOrderNo("YUMI", -1L)); } + @Test + public void rejectsDigitBearingPrefixesThatCouldCollideWithoutSeparator() { + assertEquals("YUMI123", V5PayOrderNumber.providerOrderNo("YUMI", 123L)); + + // YUMI1 + 23 会与合法的 YUMI + 123 生成同一个三方单号,因此数字不能出现在前缀任意位置。 + assertIllegalArgument(() -> V5PayOrderNumber.providerOrderNo("YUMI1", 23L)); + assertIllegalArgument(() -> V5PayOrderNumber.localOrderId("YUMI1", "YUMI123")); + assertIllegalArgument(() -> V5PayOrderNumber.providerOrderNo("1YUMI", 23L)); + assertIllegalArgument(() -> V5PayOrderNumber.providerOrderNo("YU1MI", 23L)); + assertIllegalArgument(() -> V5PayOrderNumber.providerOrderNo("123", 23L)); + assertIllegalArgument(() -> V5PayOrderNumber.providerOrderNo("yumı", 23L)); + } + private void assertIllegalArgument(Runnable action) { try { action.run();