fix(order): restrict V5Pay order prefix to letters

This commit is contained in:
hy001 2026-07-10 20:42:14 +08:00
parent ac18a90da4
commit 8922491cbe
2 changed files with 20 additions and 5 deletions

View File

@ -43,11 +43,13 @@ public final class V5PayOrderNumber {
} }
private static String normalizedPrefix(String prefix) { private static String normalizedPrefix(String prefix) {
String normalized = Objects.toString(prefix, "").trim().toUpperCase(Locale.ROOT); String rawPrefix = Objects.toString(prefix, "").trim();
if (StringUtils.isBlank(normalized) || !isAsciiLettersOrDigits(normalized)) { // 无分隔符拼接时前缀若包含数字会产生命名空间碰撞例如 YUMI+123 YUMI1+23
// 必须在大小写转换前校验 ASCII避免 Unicode 字母 dotless i转换成 ASCII 后绕过限制
if (StringUtils.isBlank(rawPrefix) || !isAsciiLetters(rawPrefix)) {
throw new IllegalArgumentException("V5Pay order prefix is invalid"); throw new IllegalArgumentException("V5Pay order prefix is invalid");
} }
return normalized; return rawPrefix.toUpperCase(Locale.ROOT);
} }
private static boolean isAsciiDigits(String value) { private static boolean isAsciiDigits(String value) {
@ -60,11 +62,11 @@ public final class V5PayOrderNumber {
return true; return true;
} }
private static boolean isAsciiLettersOrDigits(String value) { private static boolean isAsciiLetters(String value) {
for (int index = 0; index < value.length(); index++) { for (int index = 0; index < value.length(); index++) {
char item = value.charAt(index); char item = value.charAt(index);
boolean letter = (item >= 'A' && item <= 'Z') || (item >= 'a' && item <= 'z'); boolean letter = (item >= 'A' && item <= 'Z') || (item >= 'a' && item <= 'z');
if (!letter && (item < '0' || item > '9')) { if (!letter) {
return false; return false;
} }
} }

View File

@ -48,6 +48,19 @@ public class V5PayOrderNumberTest {
assertIllegalArgument(() -> V5PayOrderNumber.providerOrderNo("YUMI", -1L)); 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) { private void assertIllegalArgument(Runnable action) {
try { try {
action.run(); action.run();