JavaScript Firebase提现系统余额负数Bug求修复方案
问题根源
- 核心问题是
ValidationAmount校验逻辑的异步操作没有被正确处理:你在函数内部调用的auth.onAuthStateChanged、onSnapshot都是异步方法,回调里的return false根本不会返回到ValidationAmount外层,函数直接走到后面的判断逻辑返回true,余额超量的校验完全没生效。 - 你用了实时监听方法
onSnapshot做单次余额查询,本身就不符合场景,应该用单次读取的getDoc。 - 前端校验只做第一层防护,必须补Firestore安全规则做后端校验,防止用户绕过前端直接发起修改请求。
修复方案
步骤1:重写异步校验函数
把ValidationAmount改成异步函数,正确等待数据库查询结果再返回校验状态:
async function ValidationAmount(val) { // 先做基础同步校验 const amount = Number(val); if (isNaN(amount) || amount < 5 || amount <= 0 || !val.trim()) { return false; } // 异步校验用户余额 return new Promise((resolve) => { auth.onAuthStateChanged(async (user) => { if (!user) return resolve(false); const userRef = doc(db, "users", user.uid); const userSnap = await getDoc(userRef); if (!userSnap.exists()) return resolve(false); const userBalance = userSnap.data().earningsCount; // 余额足够才返回true resolve(amount <= userBalance); }); }); }
步骤2:修改调用处的异步处理
调用ValidationAmount的地方要加await等待校验结果:
requestBtn.addEventListener("click", async function (){ if(ValidationWallet(requestInput.value)){ const { value: Amount } = await Swal.fire({ icon:'question', title:'Amount', inputLabel: 'Select The Withdrawl Amount', showCancelButton: true, showCloseButton: true, cancelButtonColor: '#dc3545', confirmButtonText: 'Confirm', confirmButtonColor: '#0088FF', cancelButtonAriaLabel: 'Thumbs down', input:'text' }); // 加await等待异步校验结果 if(Amount && await ValidationAmount(Amount)){ Swal.fire("Success","The Draw Is Waiting For Review","success"); sendWithdrawlRequest(Amount) }else{ Swal.fire("Ops!","Please Put The Valid Amount","error"); } }else{ Swal.fire("Ops!","Please Check Your Wallet","error"); } });
步骤3:加兜底校验(可选但推荐)
在sendWithdrawlRequest里也加一层余额判断,双重保险:
function sendWithdrawlRequest(Amount){ Amount = +Amount; auth.onAuthStateChanged(async (user) => { if (!user) return; const userRef = doc(db, "users", user.uid); const userSnap = await getDoc(userRef); // 兜底校验,余额不够直接终止操作 if (!userSnap.exists() || userSnap.data().earningsCount < Amount) { Swal.fire("Ops!", "余额不足", "error"); return; } let today = new Date(); let date = today.getFullYear()+'-'+(today.getMonth()+1)+'-'+today.getDate(); const requestCode = Math.random().toString(36).substr(2, 9); await setDoc(doc(db, "payments", requestCode),{ amount: Amount, walletID: requestInput.value, requestID: requestCode, createdAt: date, }); await updateDoc(userRef, { earningsCount: userSnap.data().earningsCount - Amount, payments: arrayUnion({ amount: Amount, walletID: requestInput.value, createdAt: date,}), }); }); };
步骤4:补Firestore安全规则
务必在Firestore控制台添加以下安全规则,从后端层面禁止余额扣成负数:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /users/{userId} { allow update: if request.auth != null && request.auth.uid == userId && request.resource.data.earningsCount >= 0 && request.resource.data.earningsCount <= resource.data.earningsCount; } match /payments/{paymentId} { allow create: if request.auth != null; } } }
内容的提问来源于stack exchange,提问作者Kareem Gameel
相关产品推荐
相关产品推荐

