You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 14:36:05