This commit is contained in:
payacom
2026-10-02 18:13:34 +03:30
parent d4ebc20aa8
commit fade8543e2
9 changed files with 180 additions and 63 deletions

View File

@@ -9,16 +9,28 @@ function canUseFreeBillboard (user) {
return used < BILLBOARD_FREE_LIMIT
}
async function consumeFreeBillboard (user) {
if (!canUseFreeBillboard(user)) {
/**
* نسخه‌ی atomic — چک (free_billboard_count < سقف) و افزایش توی یه عملیاتِ واحدِ
* MongoDB (findOneAndUpdate با شرط توی query) انجام می‌شه، نه در دو مرحله‌ی جدا
* (قبلاً canUseFreeBillboard روی یه user از قبل fetch‌شده چک می‌شد و بعد جداگانه
* $inc می‌خورد) — وگرنه دو درخواستِ هم‌زمان (دابل‌کلیکِ «تلاشِ دوباره» روی صفحه‌ی
* پرداختِ ناموفق، که تا امروز گاردِ submitting هم نداشت) هر دو می‌تونستن همون
* user رو با free_billboard_count زیرِ سقف بخونن و هر دو از سقف رد بشن
*/
async function tryConsumeFreeBillboard (UserModel, userId) {
const updated = await UserModel.findOneAndUpdate(
{ _id: userId, free_billboard_count: { $lt: BILLBOARD_FREE_LIMIT } },
{ $inc: { free_billboard_count: 1 } },
{ new: true }
)
if (!updated) {
return { ok: false, message: `سقف ${BILLBOARD_FREE_LIMIT} بیلبورد رایگان به پایان رسیده است.` }
}
user.free_billboard_count = (user.free_billboard_count || 0) + 1
return { ok: true }
return { ok: true, user: updated }
}
module.exports = {
BILLBOARD_FREE_LIMIT,
canUseFreeBillboard,
consumeFreeBillboard
tryConsumeFreeBillboard
}

View File

@@ -13,6 +13,13 @@ const { getBucketBalance } = require('../controllers/application/wallet/walletCo
* این‌طوری اگه کاربر پرداختِ درگاه رو نصفه ول کنه، هیچ مبلغی از کیف پول کم نشده که
* لازم باشه برگردونده بشه
*/
// زرین‌پال حداقل مبلغِ درگاه رو ۱۵٬۰۰۰ ریال (۱٬۵۰۰ تومان) می‌خواد — اگه بعدِ کسرِ
// کیف‌پول فقط یه باقی‌مونده‌ی مثبتِ کوچیک‌تر از این بمونه، کلِ درخواستِ initiate به
// زرین‌پال با خطای 422 رد می‌شه (نه فقط همون بخشِ کوچیک) — این دقیقاً همون چیزی بود
// که باعث می‌شد «ثبت درخواست» (و هر مسیرِ دیگه‌ای که از این تابع استفاده می‌کنه) با
// بعضی ترکیب‌های قیمت/موجودیِ کیف‌پول روی سرور ارور بده
const ZARINPAL_MIN_GATEWAY_TOMAN = 1500
async function resolveWalletSplit (userId, amount, useWallet) {
if (!useWallet) {
return { walletAmount: 0, gatewayAmount: amount, fullyCovered: false }
@@ -21,8 +28,25 @@ async function resolveWalletSplit (userId, amount, useWallet) {
if (!balance || balance <= 0) {
return { walletAmount: 0, gatewayAmount: amount, fullyCovered: false }
}
const walletAmount = Math.min(balance, amount)
const gatewayAmount = amount - walletAmount
let walletAmount = Math.min(balance, amount)
let gatewayAmount = amount - walletAmount
if (gatewayAmount > 0 && gatewayAmount < ZARINPAL_MIN_GATEWAY_TOMAN) {
const neededWalletAmount = amount - ZARINPAL_MIN_GATEWAY_TOMAN
if (neededWalletAmount >= 0 && balance >= neededWalletAmount) {
// از کیف‌پول کمتر استفاده کن تا باقی‌مونده دقیقاً به حداقلِ مجازِ درگاه برسه
walletAmount = neededWalletAmount
gatewayAmount = ZARINPAL_MIN_GATEWAY_TOMAN
} else {
// یا کلِ مبلغ از حداقلِ درگاه کمتره، یا کیف‌پول برای رسوندنِ باقی‌مونده به اون
// حداقل کافی نیست — اصلاً از کیف‌پول استفاده نکن، همه چیز از درگاه. این‌طوری
// حداقل یه خطای واضحِ «حداقل مبلغ» از خودِ زرین‌پال می‌گیریم، نه یه 422ِ گنگ
walletAmount = 0
gatewayAmount = amount
}
}
return { walletAmount, gatewayAmount, fullyCovered: gatewayAmount <= 0 }
}