# مرحله ۹ — کیف‌پول کاربر و جریان واریز: خلاصه

⚠️ **این اولین مرحله‌ایه که پول واقعی وارد سیستم می‌شه** و کاملاً از Ledger مرحله‌ی ۲ (روی MySQL، نه SQLite) استفاده می‌کنه.

## معماری
- جدول جدید `wallet_deposit_requests` (روی همون SQLite قبلی) فقط گردش‌کار «درخواست → تایید/رد» رو نگه می‌داره — دقیقاً مثل جدول `transactions` که برای اشتراک (مرحله ۷) استفاده می‌شه.
- **خودِ پول هیچ‌وقت اینجا نیست.** موجودی واقعی فقط از طریق `FinDB::postLedgerEntry()` (مرحله ۲) روی MySQL ثبت می‌شه.
- این کاملاً از سیستم پرداخت اشتراک (مرحله ۷، جدول `transactions`/`Tx`) جداست — طبق پیش‌نیاز صریح خود سند.

## یه اصلاح ایمنی مهم قبل از شروع
چون کیف‌پول اولین فیچریه که واقعاً `FinDB` (MySQL) رو در لحظه‌ی اجرا صدا می‌زنه، یه خطر واقعی بود: اگه ادمین هنوز MySQL رو روی سرور تنظیم نکرده باشه، `requiredEnv()` قبلی با `exit(1)` **کل پردازه‌ی ربات رو می‌کشت** (نه فقط فیچر کیف‌پول، بلکه اسکن سیگنال و مانیتور پوزیشن هم می‌خوابیدن) — چون `exit()` برخلاف Exception، از `try/catch` رد می‌شه. یه نسخه‌ی «نرم» به اسم `requiredEnvSoft()` اضافه کردم که به‌جاش یه Exception قابل‌گرفتن پرت می‌کنه؛ `FinDB::conn()` داخل خودِ `traderpro.php` (نه در `findb.php` که فقط اسکریپت‌های CLI ازش استفاده می‌کنن) به این نسخه‌ی نرم سوییچ شد. حالا اگه MySQL تنظیم نشده باشه، فقط کاربری که کیف‌پول رو باز می‌کنه پیام «موقتاً در دسترس نیست» می‌بینه، بقیه‌ی ربات دست‌نخورده کار می‌کنه.

## ۹.۱ درخواست واریز
کاربر → «👛 کیف پول من» (زیر «امکانات بیشتر») → «➕ واریز به کیف پول» → مبلغ رو تایپ می‌کنه → اطلاعات کارت (همون کارت مرحله ۱) با مبلغ دقیق نشون داده می‌شه → «واریز را انجام دادم» → راهنمای ۳ قدمی ارسال رسید (مثل مرحله ۷) → عکس رسید. همین‌جا ردیف `wallet_deposit_requests` با وضعیت `pending` ساخته می‌شه — **هیچ اثری روی موجودی نداره.**

## ۹.۲ تایید ادمین — ایمن در برابر دابل‌کلیک
ادمین رسید رو با ۳ گزینه می‌بینه: ✅ تایید همین مبلغ / ✏️ وارد کردن مبلغ واقعی / ❌ رد.
- **محافظت اول:** قبل از هر کاری، وضعیت درخواست چک می‌شه؛ اگه دیگه `pending` نباشه (یعنی قبلاً تصمیم‌گیری شده)، بلافاصله برمی‌گرده و کاری نمی‌کنه.
- **محافظت دوم (طبق چک‌لیست):** خودِ `idempotencyKey` ثابت (`wallet_deposit_{شناسه‌ی درخواست}`) به `FinDB::postLedgerEntry()` داده می‌شه. حتی اگه به‌خاطر یه سناریوی لبه‌ای (مثلاً کرش پردازه‌ی بین ثبت Ledger و آپدیت SQLite) دوباره صدا زده بشه، Ledger خودش تشخیص می‌ده و دومین بار فقط `idempotent_replay: true` برمی‌گردونه، نه واریز تکراری.
- **مبلغ متفاوت (چک‌لیست ردیف ۳):** روی «✏️ وارد کردن مبلغ واقعی»، ادمین عدد واقعی رو تایپ می‌کنه؛ همون تابع approve با override صدا زده می‌شه، با همون idempotency key.

## ۹.۳ ثبت در Ledger
`FinDB::postLedgerEntry(type: 'deposit', ...)` — موجودی جدید از `SUM(ledger_entries)` محاسبه می‌شه، نه نوشتن مستقیم. پیام تایید به کاربر شامل **مبلغ دقیق + موجودی جدید واقعی** (از خروجی خودِ Ledger، نه یه عدد حدسی) است.

## ۹.۴ نمایش کیف‌پول
- موجودی کل، موجودی در دسترس، قفل‌شده در معامله‌ی باز.
- **قفل‌شده فعلاً همیشه ۰ است** — چون موتور معامله (مرحله ۱۰) هنوز ساخته نشده؛ تابعش (`walletLockedAmount`) عمداً جدا نوشته شده تا وقتی مرحله ۱۰ اومد، فقط همون یه تابع پر بشه.
- **تاریخچه‌ی واریز مستقیم از `ledger_entries` خونده می‌شه** (نه یه لیست جدا)، دقیقاً طبق چک‌لیست ردیف ۴.

## وضعیت چک‌لیست
- [x] دابل‌کلیک تایید ادمین → فقط یک‌بار اثر می‌کنه (دو لایه محافظت).
- [x] رد درخواست → هیچ اثری روی موجودی، پیام رد به کاربر.
- [x] ادمین می‌تونه مبلغ واقعی متفاوت وارد کنه.
- [x] تاریخچه دقیقاً از Ledger میاد.
- [x] پیام تایید شامل مبلغ دقیق + موجودی جدید واقعیه.

## کارهای دستی روی VPS (قبل از تست)
1. مطمئن بشید مرحله ۲ (MySQL + `migrate-to-ledger.php`) قبلاً درست انجام شده.
2. `FIN_DB_*` توی `.env` پر شده باشه.
3. یه واریز تست کامل انجام بدید: درخواست → تایید ادمین → چک کردن پیام تایید → باز کردن «کیف پول من» و دیدن موجودی/تاریخچه‌ی به‌روزشده.
4. یه‌بار هم رد کردن رو تست کنید.
5. یه‌بار هم روی دکمه‌ی تایید دوبار سریع کلیک کنید (تست دابل‌کلیک) و مطمئن بشید موجودی فقط یک‌بار بالا رفته.

## فایل تحویلی
- `traderpro.php` — به‌روزرسانی‌شده.
