fix(withdrawal): validate decimal amount input
This commit is contained in:
parent
22c188b84f
commit
b0a0124448
@ -2,6 +2,7 @@
|
||||
const API_BASE_URL = "https://jvapi.haiyihy.com";
|
||||
const EXCHANGE_RATE = 80000;
|
||||
const WITHDRAW_MIN_AMOUNT = 50;
|
||||
const AMOUNT_FRACTION_DIGITS = 2;
|
||||
const supportedLanguages = ["en", "ar", "tr", "id"];
|
||||
const languageLabels = {
|
||||
en: "EN",
|
||||
@ -483,7 +484,32 @@
|
||||
|
||||
function formatMoneyInput(value) {
|
||||
const number = Number(value);
|
||||
return Number.isFinite(number) ? number.toFixed(2) : "0.00";
|
||||
return Number.isFinite(number) ? number.toFixed(AMOUNT_FRACTION_DIGITS) : "0.00";
|
||||
}
|
||||
|
||||
function normalizeAmountValue(value) {
|
||||
const raw = String(value || "").trim();
|
||||
if (!raw) return "";
|
||||
// 金额只接受普通十进制正数;不能把 -50 或 1e2 静默清洗成另一笔正数,避免用户看到值与实际提交值不一致。
|
||||
if (/[eE+-]/.test(raw)) return "";
|
||||
|
||||
const sanitized = raw.replace(/[^\d.]/g, "");
|
||||
const dotIndex = sanitized.indexOf(".");
|
||||
if (dotIndex < 0) return sanitized;
|
||||
|
||||
const integerPart = sanitized.slice(0, dotIndex) || "0";
|
||||
const fractionPart = sanitized.slice(dotIndex + 1)
|
||||
.replace(/\./g, "")
|
||||
.slice(0, AMOUNT_FRACTION_DIGITS);
|
||||
return `${integerPart}.${fractionPart}`;
|
||||
}
|
||||
|
||||
function parseAmountValue(value) {
|
||||
const raw = String(value || "").trim();
|
||||
// input 事件之外仍需校验原始文本,防止脚本赋值或浏览器差异绕过两位小数限制后被后端拒绝。
|
||||
if (!/^\d+(?:\.\d{0,2})?$/.test(raw)) return Number.NaN;
|
||||
const amount = Number(raw);
|
||||
return Number.isFinite(amount) ? amount : Number.NaN;
|
||||
}
|
||||
|
||||
function moneyNumber(value) {
|
||||
@ -502,7 +528,7 @@
|
||||
}
|
||||
|
||||
function activeAmount() {
|
||||
return moneyNumber(activeInput()?.value);
|
||||
return parseAmountValue(activeInput()?.value);
|
||||
}
|
||||
|
||||
function formatExchangeRate() {
|
||||
@ -613,12 +639,12 @@
|
||||
submitButton.textContent = submitLabels[currentMode];
|
||||
|
||||
const amount = activeAmount();
|
||||
const invalidAmount = !Number.isFinite(amount) || amount <= 0;
|
||||
const exceedsBalance = amount > availableBalance;
|
||||
const missingWithdrawInfo = currentMode === "withdraw" && !selectedPaymentInfo?.id;
|
||||
const missingPayee = currentMode === "transfer" && !selectedPayee?.id;
|
||||
|
||||
submitButton.disabled = !amount ||
|
||||
amount <= 0 ||
|
||||
submitButton.disabled = invalidAmount ||
|
||||
exceedsBalance ||
|
||||
missingWithdrawInfo ||
|
||||
missingPayee ||
|
||||
@ -1167,10 +1193,8 @@
|
||||
function normalizeAmountInput(event) {
|
||||
const input = event?.target || activeInput();
|
||||
if (!input) return;
|
||||
const raw = String(input.value || "").replace(/[^\d.]/g, "");
|
||||
const parts = raw.split(".");
|
||||
let nextValue = parts.length > 2 ? `${parts[0]}.${parts.slice(1).join("")}` : raw;
|
||||
const amount = Number(nextValue);
|
||||
let nextValue = normalizeAmountValue(input.value);
|
||||
const amount = parseAmountValue(nextValue);
|
||||
if (Number.isFinite(amount) && amount > availableBalance) nextValue = formatMoneyInput(availableBalance);
|
||||
input.value = nextValue;
|
||||
updateSubmitState();
|
||||
@ -1178,7 +1202,7 @@
|
||||
|
||||
function validateAmount(requireMinimum) {
|
||||
const amount = activeAmount();
|
||||
if (!amount || amount <= 0) return "invalid_amount";
|
||||
if (!Number.isFinite(amount) || amount <= 0) return "invalid_amount";
|
||||
if (requireMinimum && amount < WITHDRAW_MIN_AMOUNT) return "amount_too_low";
|
||||
if (amount > availableBalance) return "amount_exceeds_balance";
|
||||
return "";
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user