fix(order): prevent V5Pay prefix collisions
This commit is contained in:
parent
0a4ca3df58
commit
534574eb71
@ -8,7 +8,8 @@ import java.util.regex.Pattern;
|
||||
public final class V5PayOrderNoUtils {
|
||||
|
||||
private static final String DEFAULT_PREFIX = "ASLAN";
|
||||
private static final Pattern PREFIX_PATTERN = Pattern.compile("[A-Za-z0-9]+");
|
||||
private static final Pattern PREFIX_PATTERN = Pattern.compile("[A-Za-z]+");
|
||||
private static final Pattern PROVIDER_ORDER_NO_PATTERN = Pattern.compile("[A-Za-z0-9]+");
|
||||
private static final Pattern LOCAL_ORDER_NO_PATTERN = Pattern.compile("[1-9][0-9]{0,18}");
|
||||
|
||||
private V5PayOrderNoUtils() {
|
||||
@ -28,13 +29,13 @@ public final class V5PayOrderNoUtils {
|
||||
public static String localOrderNo(String prefix, String providerOrderNo) {
|
||||
String expected = normalizedPrefix(prefix);
|
||||
String value = Objects.toString(providerOrderNo, "").trim();
|
||||
if (!PREFIX_PATTERN.matcher(value).matches()
|
||||
if (!PROVIDER_ORDER_NO_PATTERN.matcher(value).matches()
|
||||
|| !value.startsWith(expected)
|
||||
|| value.length() == expected.length()) {
|
||||
return "";
|
||||
}
|
||||
String localOrderNo = value.substring(expected.length());
|
||||
// 回调只接受当前前缀后紧跟正数雪花 ID;这同时拒绝其他系统前缀、分隔符和前缀碰撞后的字母尾缀。
|
||||
// 纯字母命名空间后只能紧跟正数雪花 ID;禁止数字前缀才能避免 ASLAN+123 与 ASLAN1+23 产生碰撞。
|
||||
return isValidLocalOrderNo(localOrderNo) ? localOrderNo : "";
|
||||
}
|
||||
|
||||
@ -42,8 +43,8 @@ public final class V5PayOrderNoUtils {
|
||||
String value = Objects.toString(prefix, DEFAULT_PREFIX).trim();
|
||||
String normalized = (value.isBlank() ? DEFAULT_PREFIX : value).toUpperCase(Locale.ROOT);
|
||||
if (!PREFIX_PATTERN.matcher(normalized).matches()) {
|
||||
// 共享商户依赖前缀隔离,配置中出现分隔符或非 ASCII 字符时必须快速失败,不能静默改写。
|
||||
throw new IllegalArgumentException("V5Pay order prefix must contain only ASCII letters and digits");
|
||||
// 无分隔订单号若允许数字前缀会产生命名空间碰撞;符号和 Unicode 同样不符合支付方格式。
|
||||
throw new IllegalArgumentException("V5Pay order prefix must contain only ASCII letters");
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
@ -34,13 +34,20 @@ public class V5PaySignUtilsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orderPrefixNormalizesToUppercaseAlphanumericNamespace() {
|
||||
public void orderPrefixNormalizesToUppercaseAsciiLetterNamespace() {
|
||||
Assert.assertEquals("ASLAN123", V5PayOrderNoUtils.providerOrderNo("aslan", "123"));
|
||||
Assert.assertEquals("A5SLAN42", V5PayOrderNoUtils.providerOrderNo("a5slan", "42"));
|
||||
Assert.assertEquals("ASLAN123", V5PayOrderNoUtils.providerOrderNo("", "123"));
|
||||
Assert.assertEquals("123", V5PayOrderNoUtils.localOrderNo("aslan", "ASLAN123"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void numericPrefixIsRejectedToPreventNamespaceCollision() {
|
||||
Assert.assertEquals("ASLAN123", V5PayOrderNoUtils.providerOrderNo("ASLAN", "123"));
|
||||
// ASLAN1 + 23 会与 ASLAN + 123 形成相同字符串,因此共享商户命名空间不能允许数字前缀。
|
||||
assertInvalidOrderNo("ASLAN1", "23");
|
||||
assertInvalidOrderNo("1ASLAN", "23");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orderParserRejectsOtherNamespaceEmptyAndIllegalCharacters() {
|
||||
Assert.assertEquals("", V5PayOrderNoUtils.localOrderNo("ASLAN", "LALU123"));
|
||||
@ -66,6 +73,8 @@ public class V5PaySignUtilsTest {
|
||||
|
||||
assertInvalidOrderNo("ASLAN-", "123");
|
||||
assertInvalidOrderNo("AS LAN", "123");
|
||||
assertInvalidOrderNo("A5SLAN", "123");
|
||||
assertInvalidOrderNo("阿斯兰", "123");
|
||||
assertInvalidOrderNo("ASLAN", "");
|
||||
assertInvalidOrderNo("ASLAN", "1-23");
|
||||
assertInvalidOrderNo("ASLAN", "9223372036854775808");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user