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 API_BASE_URL = "https://jvapi.haiyihy.com";
|
||||||
const EXCHANGE_RATE = 80000;
|
const EXCHANGE_RATE = 80000;
|
||||||
const WITHDRAW_MIN_AMOUNT = 50;
|
const WITHDRAW_MIN_AMOUNT = 50;
|
||||||
|
const AMOUNT_FRACTION_DIGITS = 2;
|
||||||
const supportedLanguages = ["en", "ar", "tr", "id"];
|
const supportedLanguages = ["en", "ar", "tr", "id"];
|
||||||
const languageLabels = {
|
const languageLabels = {
|
||||||
en: "EN",
|
en: "EN",
|
||||||
@ -483,7 +484,32 @@
|
|||||||
|
|
||||||
function formatMoneyInput(value) {
|
function formatMoneyInput(value) {
|
||||||
const number = Number(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) {
|
function moneyNumber(value) {
|
||||||
@ -502,7 +528,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function activeAmount() {
|
function activeAmount() {
|
||||||
return moneyNumber(activeInput()?.value);
|
return parseAmountValue(activeInput()?.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatExchangeRate() {
|
function formatExchangeRate() {
|
||||||
@ -613,12 +639,12 @@
|
|||||||
submitButton.textContent = submitLabels[currentMode];
|
submitButton.textContent = submitLabels[currentMode];
|
||||||
|
|
||||||
const amount = activeAmount();
|
const amount = activeAmount();
|
||||||
|
const invalidAmount = !Number.isFinite(amount) || amount <= 0;
|
||||||
const exceedsBalance = amount > availableBalance;
|
const exceedsBalance = amount > availableBalance;
|
||||||
const missingWithdrawInfo = currentMode === "withdraw" && !selectedPaymentInfo?.id;
|
const missingWithdrawInfo = currentMode === "withdraw" && !selectedPaymentInfo?.id;
|
||||||
const missingPayee = currentMode === "transfer" && !selectedPayee?.id;
|
const missingPayee = currentMode === "transfer" && !selectedPayee?.id;
|
||||||
|
|
||||||
submitButton.disabled = !amount ||
|
submitButton.disabled = invalidAmount ||
|
||||||
amount <= 0 ||
|
|
||||||
exceedsBalance ||
|
exceedsBalance ||
|
||||||
missingWithdrawInfo ||
|
missingWithdrawInfo ||
|
||||||
missingPayee ||
|
missingPayee ||
|
||||||
@ -1167,10 +1193,8 @@
|
|||||||
function normalizeAmountInput(event) {
|
function normalizeAmountInput(event) {
|
||||||
const input = event?.target || activeInput();
|
const input = event?.target || activeInput();
|
||||||
if (!input) return;
|
if (!input) return;
|
||||||
const raw = String(input.value || "").replace(/[^\d.]/g, "");
|
let nextValue = normalizeAmountValue(input.value);
|
||||||
const parts = raw.split(".");
|
const amount = parseAmountValue(nextValue);
|
||||||
let nextValue = parts.length > 2 ? `${parts[0]}.${parts.slice(1).join("")}` : raw;
|
|
||||||
const amount = Number(nextValue);
|
|
||||||
if (Number.isFinite(amount) && amount > availableBalance) nextValue = formatMoneyInput(availableBalance);
|
if (Number.isFinite(amount) && amount > availableBalance) nextValue = formatMoneyInput(availableBalance);
|
||||||
input.value = nextValue;
|
input.value = nextValue;
|
||||||
updateSubmitState();
|
updateSubmitState();
|
||||||
@ -1178,7 +1202,7 @@
|
|||||||
|
|
||||||
function validateAmount(requireMinimum) {
|
function validateAmount(requireMinimum) {
|
||||||
const amount = activeAmount();
|
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 (requireMinimum && amount < WITHDRAW_MIN_AMOUNT) return "amount_too_low";
|
||||||
if (amount > availableBalance) return "amount_exceeds_balance";
|
if (amount > availableBalance) return "amount_exceeds_balance";
|
||||||
return "";
|
return "";
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user