fix(withdrawal): validate decimal amount input

This commit is contained in:
zhx 2026-07-20 15:46:26 +08:00
parent 22c188b84f
commit b0a0124448

View File

@ -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 "";