From a94d0dbe95a2e3ded9d76b9d2c70eb4c3f65deed Mon Sep 17 00:00:00 2001 From: masoodafar-web Date: Thu, 19 Feb 2026 02:08:28 +0330 Subject: [PATCH] docs: add Magic Wallet spec & implementation plan - New folder: roadmap/ for upcoming features - MAGIC-WALLET-SPEC: full business rules, state machine, x2.5 multiplier, 100M deposit cap, API flow, data model changes, transaction logging - MAGIC-WALLET-PLAN: 6-phase implementation, checklist, time estimates - Note: ChargeDiscountWallet completion is independent (not Magic Wallet) - Note: WalletChangeLog is mandatory (not optional) - Updated master index with roadmap section --- overview/OVERVIEW-02-INDEX.md | 25 ++- roadmap/MAGIC-WALLET-PLAN.md | 205 ++++++++++++++++++ roadmap/MAGIC-WALLET-SPEC.md | 397 ++++++++++++++++++++++++++++++++++ 3 files changed, 620 insertions(+), 7 deletions(-) create mode 100644 roadmap/MAGIC-WALLET-PLAN.md create mode 100644 roadmap/MAGIC-WALLET-SPEC.md diff --git a/overview/OVERVIEW-02-INDEX.md b/overview/OVERVIEW-02-INDEX.md index 5fb5a71..aa66105 100644 --- a/overview/OVERVIEW-02-INDEX.md +++ b/overview/OVERVIEW-02-INDEX.md @@ -3,7 +3,7 @@ > **فهرست کامل ۱۵ فایل مستند پروژه FourSat (کارا بازار سلامت)** > **تاریخ تجمیع:** اسفند ۱۴۰۴ > **تعداد فایل‌های مبدأ:** ۵۳ فایل (~۳۲,۰۰۰ خط) -> **تعداد فایل‌های نهایی:** ۱۵ فایل +> **تعداد فایل‌های نهایی:** ۱۷ فایل (۱۵ اصلی + ۲ roadmap) --- @@ -25,12 +25,16 @@ totalDoc/ │ ├── TECH-04-MIGRATION.md مهاجرت داده │ └── TECH-05-API-INTEGRATION.md API و یکپارچه‌سازی │ -└── 📁 overview/ (نمای کلان — ۵ فایل) - ├── OVERVIEW-01-FLOWCHARTS.md فلوچارت‌ها و دیاگرام‌ها - ├── OVERVIEW-02-INDEX.md ← همین فایل - ├── OVERVIEW-03-CHANGELOG.md تاریخچه و درصد تکمیل - ├── OVERVIEW-04-GLOSSARY.md واژه‌نامه و استانداردها - └── OVERVIEW-05-ROADMAP.md نقشه راه و ریسک‌ها +├── 📁 overview/ (نمای کلان — ۵ فایل) +│ ├── OVERVIEW-01-FLOWCHARTS.md فلوچارت‌ها و دیاگرام‌ها +│ ├── OVERVIEW-02-INDEX.md ← همین فایل +│ ├── OVERVIEW-03-CHANGELOG.md تاریخچه و درصد تکمیل +│ ├── OVERVIEW-04-GLOSSARY.md واژه‌نامه و استانداردها +│ └── OVERVIEW-05-ROADMAP.md نقشه راه و ریسک‌ها +│ +└── 📁 roadmap/ (فیچرهای جدید — در حال توسعه) + ├── MAGIC-WALLET-SPEC.md مشخصات کیف‌پول جادویی + └── MAGIC-WALLET-PLAN.md پلن پیاده‌سازی + checklist ``` --- @@ -67,6 +71,13 @@ totalDoc/ | O4 | [OVERVIEW-04-GLOSSARY](OVERVIEW-04-GLOSSARY.md) | واژه‌نامه | اصطلاحات فارسی/انگلیسی، استانداردهای کد | | O5 | [OVERVIEW-05-ROADMAP](OVERVIEW-05-ROADMAP.md) | نقشه راه | ریسک‌ها، وابستگی‌ها، کارهای باقیمانده، اولویت‌ها | +### 🪄 Roadmap (فیچرهای جدید) + +| # | فایل | موضوع | خلاصه | +|---|------|--------|--------| +| R1 | [MAGIC-WALLET-SPEC](../roadmap/MAGIC-WALLET-SPEC.md) | کیف‌پول جادویی — مشخصات | State Machine، ضریب ×2.5، سقف 100M، قوانین، API، مدل داده | +| R2 | [MAGIC-WALLET-PLAN](../roadmap/MAGIC-WALLET-PLAN.md) | کیف‌پول جادویی — پلن | ۶ فاز، checklist، تخمین زمان، وابستگی ChargeDiscountWallet | + --- ## نقشه ارتباط فایل‌ها diff --git a/roadmap/MAGIC-WALLET-PLAN.md b/roadmap/MAGIC-WALLET-PLAN.md new file mode 100644 index 0000000..bf44aa2 --- /dev/null +++ b/roadmap/MAGIC-WALLET-PLAN.md @@ -0,0 +1,205 @@ +# 🪄 پلن پیاده‌سازی کیف‌پول جادویی + +> **مرجع:** [MAGIC-WALLET-SPEC](./MAGIC-WALLET-SPEC.md) +> **تخمین کل:** ~۷ روز کاری +> **وابستگی مستقل:** تکمیل ChargeDiscountWallet (ربطی به جادویی ندارد) + +--- + +## فازبندی + +### فاز ۱ — مدل داده و Migration (روز ۱) + +**هدف:** زیرساخت دیتابیس و enum‌ها + +``` +فایل‌های تغییری: +├── UserWallet.cs → + WalletMode, MagicTotalDeposited, MagicTotalCredited, MagicActivatedAt, MagicCompletedAt +├── WalletMode.cs → enum جدید (Normal=0, Magic=1) +├── TransactionType.cs → + MagicWalletDeposit=14, MagicWalletBonus=15 +├── SystemConstants.cs → + MagicWalletMultiplier, MagicWalletMaxDeposit, MagicWalletMaxCredit +├── UserWalletConfiguration.cs → EF config برای فیلدهای جدید +└── Migration: AddMagicWalletFields → dotnet ef migrations add +``` + +**تست:** Migration اجرا بشه، فیلدها در DB ایجاد بشن، default‌ها درست باشن. + +--- + +### فاز ۲ — Trigger ورود/خروج Magic (روز ۲) + +**هدف:** State Machine خودکار + +``` +فایل‌های تغییری: +├── SubmitShopBuyOrderCommandHandler.cs +│ ├── بعد از کسر Balance: check ورود به Magic +│ └── بعد از کسر Balance: check خروج از Magic +│ +├── (Optional) Domain Event: WalletModeChangedEvent +│ └── برای لاگ و نوتیفیکیشن +│ +└── User.cs (یا UserWallet) + └── + PurchaseCycleCount (int) — تعداد دور خرید پکیج +``` + +**تست:** +- سناریو ۱: Balance=0 بعد از خرید → WalletMode=Magic ✅ +- سناریو ۲: بدون پکیج + Balance=0 → نباید Magic بشه ❌ +- سناریو ۳: Magic + Balance=0 + TotalDeposited>0 → خروج ✅ + +--- + +### فاز ۳ — API شارژ جادویی (روز ۳-۴) + +**هدف:** مسیر کامل شارژ از درگاه با ضریب ×2.5 + +``` +فایل‌های جدید: +├── InitiateMagicChargeCommand.cs +├── InitiateMagicChargeCommandHandler.cs +├── InitiateMagicChargeCommandValidator.cs +├── VerifyMagicChargeCommand.cs +├── VerifyMagicChargeCommandHandler.cs +├── MagicWalletController.cs → GET /api/wallet/verify-magic-charge +│ +├── userwallet.proto → + InitiateMagicCharge, GetMagicWalletStatus RPCs +└── UserWalletService.cs → implement new RPCs + +نکات مهم: +├── هر شارژ = ۲ تراکنش (Deposit + Bonus) +├── هر شارژ = ۱ WalletChangeLog (اجباری) +├── Validation: WalletMode==Magic && TotalDeposited+Amount <= Cap +└── Callback: /api/wallet/verify-magic-charge → redirect FrontOffice +``` + +**تست:** +- واریز 10M → Balance += 25M, TotalDeposited += 10M ✅ +- واریز بیشتر از سقف → خطا ❌ +- واریز در Normal Mode → خطا ❌ +- ۲ تراکنش + ۱ لاگ ثبت شده ✅ + +--- + +### فاز ۴ — غیرفعال‌سازی کمیسیون (روز ۴.۵) + +**هدف:** کاربرهای Magic از کمیسیون خارج بشن + +``` +فایل‌های تغییری: +├── CalculateWeeklyBalancesCommandHandler.cs +│ └── فیلتر: WHERE wallet.WalletMode != Magic +│ +└── (یا SP اگه از Stored Procedure استفاده میکنه) + └── sp_CalculateWeeklyBalances → + JOIN UserWallets WHERE WalletMode = 0 +``` + +**تست:** کاربر Magic در محاسبات هفتگی شرکت نکنه ✅ + +--- + +### فاز ۵ — صفحات FrontOffice (روز ۵-۶) + +**هدف:** UI شارژ جادویی + نمایش وضعیت + +``` +فایل‌های جدید: +├── Pages/Profile/MagicWallet.razor → فرم شارژ + پروگرس‌بار سقف +├── Pages/Profile/MagicWallet.razor.cs → code-behind +├── Pages/Profile/MagicPaymentCallback.razor → نتیجه پرداخت +└── Pages/Profile/MagicPaymentCallback.razor.cs + +فایل‌های تغییری: +├── WalletService.cs → + InitiateMagicChargeAsync, GetMagicWalletStatusAsync +├── RouteConstants.cs → + MagicWallet, MagicPaymentCallback +├── Pages/Profile/Index.razor → بنر Magic Mode +├── Pages/Profile/Wallet.razor → پروگرس سقف + لینک شارژ +└── NavMenu / Sidebar → لینک شرطی به صفحه جادویی +``` + +**UI شارژ جادویی:** +``` +┌──────────────────────────────────────────────┐ +│ 🪄 کیف‌پول جادویی │ +│ │ +│ وضعیت: فعال ✅ │ +│ مجموع واریزی: 30M / 100M تومان │ +│ ██████████░░░░░░░░░░░░░░░░░░░░ 30% │ +│ مجموع اعتبار دریافتی: 75M تومان │ +│ │ +│ ┌──────────────────────────────────────┐ │ +│ │ مبلغ واریز: [________] تومان │ │ +│ │ اعتبار دریافتی: 0 × 2.5 = 0 تومان │ │ +│ │ باقیمانده سقف: 70M تومان │ │ +│ │ │ │ +│ │ [ 🔒 پرداخت از درگاه ] │ │ +│ └──────────────────────────────────────┘ │ +└──────────────────────────────────────────────┘ +``` + +--- + +### فاز ۶ — محدودیت خرید مجدد پکیج (روز ۷) + +**هدف:** بعد از Magic فقط IPG مجاز باشه + +``` +فایل‌های تغییری: +├── CheckAndProcessDayaLoansCommandHandler.cs +│ └── if PurchaseCycleCount > 0 → reject +│ +├── Package Purchase UI (FrontOffice) +│ └── if PurchaseCycleCount > 0 → hide Daya button +│ +└── ActivateClubMembershipCommandHandler.cs + └── if WalletMode == Magic → "ابتدا جادویی تمام شود" +``` + +--- + +## Checklist پیاده‌سازی + +- [ ] **فاز ۱:** WalletMode enum +- [ ] **فاز ۱:** UserWallet entity + 5 فیلد جدید +- [ ] **فاز ۱:** TransactionType + 2 مقدار +- [ ] **فاز ۱:** SystemConstants + 3 ثابت +- [ ] **فاز ۱:** EF Configuration +- [ ] **فاز ۱:** Migration +- [ ] **فاز ۲:** Trigger ورود Magic (SubmitShopBuyOrder) +- [ ] **فاز ۲:** Trigger خروج Magic +- [ ] **فاز ۲:** PurchaseCycleCount +- [ ] **فاز ۳:** InitiateMagicChargeCommand + Handler +- [ ] **فاز ۳:** VerifyMagicChargeCommand + Handler +- [ ] **فاز ۳:** MagicWalletController (HTTP callback) +- [ ] **فاز ۳:** gRPC Proto + Service +- [ ] **فاز ۳:** ۲ تراکنش + ۱ لاگ (اجباری) +- [ ] **فاز ۴:** فیلتر کمیسیون (C# یا SP) +- [ ] **فاز ۵:** MagicWallet.razor +- [ ] **فاز ۵:** MagicPaymentCallback.razor +- [ ] **فاز ۵:** WalletService gRPC client +- [ ] **فاز ۵:** Profile + Wallet page updates +- [ ] **فاز ۶:** Daya restriction +- [ ] **فاز ۶:** Club activation restriction + +--- + +## وابستگی مستقل: تکمیل ChargeDiscountWallet + +> ⚠️ **این کار ربطی به کیف‌پول جادویی ندارد** و باید مستقل انجام شود. + +``` +مشکل فعلی: +├── ChargeDiscountWalletCommandHandler — CQRS handler موجوده ✅ +├── VerifyDiscountWalletChargeCommandHandler — موجوده ✅ +├── Callback URL = "/api/wallet/verify-discount-charge" — ست شده +├── HTTP Controller endpoint — ❌ وجود ندارد +├── gRPC RPC — ❌ در proto تعریف نشده +└── FrontOffice page — ❌ صفحه شارژ وجود ندارد + +کار لازم: +├── WalletController.cs → GET /api/wallet/verify-discount-charge +├── userwallet.proto → rpc ChargeDiscountWallet +├── UserWalletService.cs → implement RPC +├── FrontOffice → صفحه شارژ DiscountBalance + callback +└── تست end-to-end +``` diff --git a/roadmap/MAGIC-WALLET-SPEC.md b/roadmap/MAGIC-WALLET-SPEC.md new file mode 100644 index 0000000..1e66ba3 --- /dev/null +++ b/roadmap/MAGIC-WALLET-SPEC.md @@ -0,0 +1,397 @@ +# 🪄 کیف‌پول جادویی (Magic Wallet) + +> **وضعیت:** طراحی — آماده پیاده‌سازی +> **تاریخ:** اسفند ۱۴۰۴ +> **وابستگی:** خرید پکیج پایه، فروشگاه عادی، سیستم کمیسیون + +--- + +## ۱. خلاصه بیزینسی + +کاربر بعد از خرید پکیج پایه (۵۶M) و خرج کردن کامل Balance از فروشگاه عادی، وارد **حالت جادویی** می‌شه. در این حالت می‌تونه کیف‌پولش رو از درگاه شارژ کنه و **۲.۵ برابر** اعتبار بگیره — بدون هیچ کمیسیون یا پورسانتی. + +```mermaid +stateDiagram-v2 + [*] --> NewUser: ثبت‌نام + NewUser --> Normal: خرید پکیج 56M\n(IPG یا دایا) + Normal --> Magic: Balance = 0\n(همه رو خرج کرد) + Magic --> PostMagic: Balance = 0\n(250M رو خرج کرد) + PostMagic --> Normal: خرید مجدد پکیج 56M\n(فقط IPG — دایا ❌) + Normal --> Magic: Balance = 0\n(دوباره خرج کرد) + + state Normal { + [*] --> خرید_عادی + خرید_عادی: Balance -= مبلغ خرید + خرید_عادی --> کمیسیون_فعال + کمیسیون_فعال: ✅ پورسانت + 25.2M Pool + } + + state Magic { + [*] --> شارژ_جادویی + شارژ_جادویی: واریز × 2.5 = اعتبار + شارژ_جادویی --> خرید_با_اعتبار + خرید_با_اعتبار: Balance -= مبلغ خرید + خرید_با_اعتبار --> بدون_کمیسیون + بدون_کمیسیون: ❌ هیچ پورسانتی آزاد نمیشه + } +``` + +--- + +## ۲. چرخه کامل کیف‌پول + +### فاز ۱ — خرید پکیج (Normal Mode) + +| مرحله | عملیات | نتیجه | +|--------|---------|--------| +| ۱ | کاربر پکیج ۵۶M می‌خره (IPG یا دایا) | `Balance += 56M`, `DiscountBalance += 112M` | +| ۲ | فعال‌سازی باشگاه | `25.2M → Pool`, فیچرها باز میشه | +| ۳ | کمیسیون هفتگی | `NetworkBalance += سهم` ✅ | +| ۴ | خرید از فروشگاه عادی | `Balance -= مبلغ` | +| ۵ | Balance = 0 | **→ ورود به Magic Mode** | + +### فاز ۲ — کیف‌پول جادویی (Magic Mode) + +| مرحله | عملیات | نتیجه | +|--------|---------|--------| +| ۱ | کاربر از صفحه شارژ جادویی مبلغ واریز میکنه | درگاه ZarinPal | +| ۲ | تراکنش واریز ثبت میشه (مبلغ اصلی) | `Transaction(MagicDeposit, 10M)` | +| ۳ | اعتبار ×2.5 به Balance اضافه میشه | `Balance += 25M` | +| ۴ | تراکنش بونوس ثبت میشه | `Transaction(MagicBonus, 15M)` | +| ۵ | لاگ کیف‌پول ثبت میشه | `WalletChangeLog` ✅ (اجباری) | +| ۶ | MagicTotalDeposited += مبلغ واریزی | ترک سقف | +| ۷ | خرید از فروشگاه عادی | `Balance -= مبلغ` | +| ۸ | Balance = 0 و سقف پر شده | **→ خروج از Magic Mode** | + +### فاز ۳ — بازگشت (Post-Magic) + +| مرحله | عملیات | نتیجه | +|--------|---------|--------| +| ۱ | کیف‌پول جادویی تمام شد | `WalletMode = Normal` | +| ۲ | برای ادامه باید دوباره پکیج ۵۶M بخره | **فقط IPG** (دایا ❌) | +| ۳ | خرید مجدد پکیج | `Balance += 56M`, `DiscountBalance += 112M` | +| ۴ | همه آپشن‌ها دوباره فعال | کمیسیون ✅, Pool ✅ | +| ۵ | دوباره Balance = 0 بشه | **→ Magic Mode مجدد** | + +--- + +## ۳. قوانین Magic Mode + +### ۳.۱ ضریب و سقف + +| پارامتر | مقدار | ثابت پیشنهادی | +|----------|-------|---------------| +| ضریب شارژ | **×2.5** | `MagicWalletMultiplier = 2.5m` | +| سقف ورودی | **100M تومان** (1B ریال) | `MagicWalletMaxDeposit = 1_000_000_000` | +| سقف خروجی | **250M تومان** (2.5B ریال) | `MagicWalletMaxCredit = 2_500_000_000` | +| سود کاربر | **150%** | — | + +### ۳.۲ مثال عددی + +``` +واریز: 10,000,000 تومان (100M ریال) + ├── تراکنش واریز: 10,000,000 تومان (Transaction: MagicDeposit) + ├── بونوس داخلی: 15,000,000 تومان (Transaction: MagicBonus) + ├── اعتبار نهایی: 25,000,000 تومان (Balance += 250M ریال) + └── WalletChangeLog: BalanceChange = +250,000,000 ریال ✅ + +سقف: + ├── مجموع واریزی: MagicTotalDeposited += 100,000,000 ریال + ├── مجموع اعتبار: MagicTotalCredited += 250,000,000 ریال + └── باقیمانده سقف: MaxDeposit - TotalDeposited +``` + +### ۳.۳ چه چیزهایی غیرفعال میشه + +| قابلیت | Normal Mode | Magic Mode | +|--------|-------------|------------| +| خرید از فروشگاه عادی | ✅ | ✅ | +| خرید از فروشگاه تخفیفی | ✅ | ✅ (DiscountBalance قبلی) | +| کمیسیون هفتگی | ✅ | ❌ | +| پورسانت ۲۵.۲M | ✅ | ❌ | +| شارژ جادویی ×2.5 | ❌ | ✅ | +| خرید مجدد پکیج | ✅ | ❌ | + +--- + +## ۴. شرایط ورود و خروج + +### ۴.۱ ورود به Magic Mode + +``` +شرط‌ها (همه باید true باشن): + ├── wallet.Balance == 0 (کیف‌پول خالی شد) + ├── user.PackagePurchaseMethod != None (قبلاً پکیج خریده) + ├── wallet.WalletMode == Normal (الان عادیه) + └── user.ClubMembership.IsActive == true (باشگاه فعاله) + +نتیجه: + ├── wallet.WalletMode = Magic + ├── wallet.MagicActivatedAt = DateTime.UtcNow + ├── wallet.MagicTotalDeposited = 0 + └── wallet.MagicTotalCredited = 0 +``` + +### ۴.۲ خروج از Magic Mode + +``` +شرط‌ها (هرکدام کافیه): + ├── wallet.Balance == 0 && MagicTotalDeposited > 0 (همه رو خرج کرد) + └── MagicTotalDeposited >= MagicWalletMaxDeposit (سقف پر شد) + +نتیجه: + ├── wallet.WalletMode = Normal + ├── wallet.MagicCompletedAt = DateTime.UtcNow + └── user.PurchaseCycleCount++ +``` + +--- + +## ۵. تراکنش‌ها و لاگ + +### ۵.۱ انواع تراکنش جدید + +| TransactionType | کد | توضیح | +|-----------------|-----|--------| +| `MagicWalletDeposit` | 14 | واریز اصلی از درگاه (مبلغ واقعی) | +| `MagicWalletBonus` | 15 | بونوس داخلی (مبلغ × 1.5) | + +### ۵.۲ لاگ کیف‌پول (اجباری) + +هر شارژ جادویی **باید** یک رکورد `UserWalletChangeLog` ایجاد کنه: + +``` +UserWalletChangeLog: + ├── UserWalletId = wallet.Id + ├── CurrentBalance = wallet.Balance (بعد از تغییر) + ├── BalanceChange = creditAmount (مبلغ × 2.5) + ├── IsIncrement = true + ├── ReferenceId = transaction.Id + ├── CurrentDiscountBalance = wallet.DiscountBalance (بدون تغییر) + ├── DiscountBalanceChange = 0 + ├── CurrentNetworkBalance = wallet.NetworkBalance (بدون تغییر) + └── NetworkBalanceChange = 0 +``` + +> ⚠️ **لاگ کیف‌پول دلخواه نیست — اجباریه.** هر تغییر Balance باید لاگ بخوره. + +--- + +## ۶. مسیر شارژ جادویی (API) + +### ۶.۱ فلوی کامل + +```mermaid +sequenceDiagram + participant U as کاربر + participant FO as FrontOffice + participant CMS as CMS (gRPC) + participant PYMS as PYMS + participant ZP as ZarinPal + + U->>FO: مبلغ واریز (مثلاً 10M) + FO->>CMS: InitiateMagicCharge(userId, amount) + + Note over CMS: Validations:
WalletMode == Magic
TotalDeposited + amount <= 100M + + CMS->>PYMS: CreatePaymentRequest(amount) + PYMS->>ZP: Request Authority + ZP-->>PYMS: Authority + PYMS-->>CMS: PaymentUrl + CMS-->>FO: PaymentUrl + FO->>U: Redirect to ZarinPal + + U->>ZP: پرداخت + ZP->>CMS: Callback /api/wallet/verify-magic-charge + + Note over CMS: creditAmount = amount × 2.5
bonusAmount = amount × 1.5 + + CMS->>CMS: Balance += creditAmount + CMS->>CMS: Transaction #1 (MagicDeposit, amount) + CMS->>CMS: Transaction #2 (MagicBonus, bonusAmount) + CMS->>CMS: WalletChangeLog ✅ + CMS->>CMS: MagicTotalDeposited += amount + + CMS-->>FO: Redirect to callback page + FO->>U: نتیجه + بالانس جدید +``` + +### ۶.۲ تفاوت با ChargeDiscountWallet + +| ویژگی | ChargeDiscountWallet | MagicCharge | +|--------|---------------------|-------------| +| **هدف** | شارژ DiscountBalance | شارژ Balance (جادویی) | +| **ضریب** | ×1 (مبلغ واقعی) | ×2.5 | +| **سقف** | ندارد | 100M تومان ورودی | +| **شرط** | همیشه فعال | فقط WalletMode == Magic | +| **کمیسیون** | — | ❌ غیرفعال | +| **Callback** | `/api/wallet/verify-discount-charge` | `/api/wallet/verify-magic-charge` | +| **وضعیت** | ⚠️ نیمه‌کاره (controller ندارد) | 🆕 باید ساخته بشه | + +> ⚠️ **ChargeDiscountWallet ناقصه و باید مستقل کامل بشه — ربطی به کیف‌پول جادویی نداره.** + +--- + +## ۷. تغییرات مدل داده + +### ۷.۱ UserWallet — فیلدهای جدید + +```csharp +// اضافه به UserWallet entity: +public WalletMode WalletMode { get; set; } = WalletMode.Normal; +public long MagicTotalDeposited { get; set; } // مجموع واریزی واقعی (ریال) +public long MagicTotalCredited { get; set; } // مجموع اعتبار داده‌شده (ریال) +public DateTime? MagicActivatedAt { get; set; } +public DateTime? MagicCompletedAt { get; set; } +``` + +### ۷.۲ WalletMode enum (جدید) + +```csharp +public enum WalletMode +{ + Normal = 0, // حالت عادی — کمیسیون فعال + Magic = 1 // حالت جادویی — شارژ ×2.5، بدون کمیسیون +} +``` + +### ۷.۳ TransactionType — مقادیر جدید + +```csharp +// اضافه به TransactionType enum: +MagicWalletDeposit = 14, // واریز از درگاه (مبلغ واقعی) +MagicWalletBonus = 15 // بونوس داخلی (مبلغ × 1.5) +``` + +### ۷.۴ SystemConstants — ثابت‌های جدید + +```csharp +public const decimal MagicWalletMultiplier = 2.5m; +public const long MagicWalletMaxDeposit = 1_000_000_000; // 100M تومان = 1B ریال +public const long MagicWalletMaxCredit = 2_500_000_000; // 250M تومان = 2.5B ریال +``` + +### ۷.۵ EF Migration + +``` +Migration: AddMagicWalletFields + ├── ALTER TABLE UserWallets ADD WalletMode int NOT NULL DEFAULT 0 + ├── ALTER TABLE UserWallets ADD MagicTotalDeposited bigint NOT NULL DEFAULT 0 + ├── ALTER TABLE UserWallets ADD MagicTotalCredited bigint NOT NULL DEFAULT 0 + ├── ALTER TABLE UserWallets ADD MagicActivatedAt datetime2 NULL + └── ALTER TABLE UserWallets ADD MagicCompletedAt datetime2 NULL +``` + +--- + +## ۸. تغییرات Handler‌ها + +### ۸.۱ SubmitShopBuyOrderCommandHandler (تغییر) + +``` +بعد از کسر Balance: + if (wallet.Balance == 0 + && user.PackagePurchaseMethod != None + && wallet.WalletMode == Normal + && user.ClubMembership?.IsActive == true) + { + → ورود به Magic Mode + } + + if (wallet.WalletMode == Magic + && wallet.Balance == 0 + && wallet.MagicTotalDeposited > 0) + { + → خروج از Magic Mode + } +``` + +### ۸.۲ CalculateWeeklyBalancesCommandHandler (تغییر) + +``` +فیلتر اضافه: + فقط کاربرهایی که wallet.WalletMode == Normal + (کاربرهای Magic از محاسبه کمیسیون خارج میشن) +``` + +### ۸.۳ CheckAndProcessDayaLoansCommandHandler (تغییر) + +``` +Validation اضافه: + if (user.PurchaseCycleCount > 0) + → reject: "وام دایا فقط برای خرید اولین پکیج" +``` + +### ۸.۴ InitiateMagicChargeCommandHandler (جدید) + +``` +Input: UserId, Amount +Validations: + ├── wallet.WalletMode == Magic + ├── Amount > 0 + └── MagicTotalDeposited + Amount <= MagicWalletMaxDeposit +Action: + ├── PaymentTransaction → PYMS → ZarinPal + └── CallbackUrl = "/api/wallet/verify-magic-charge" +Return: PaymentUrl +``` + +### ۸.۵ VerifyMagicChargeCommandHandler (جدید) + +``` +Input: Authority, Status +On Success: + ├── creditAmount = amount × 2.5 + ├── bonusAmount = creditAmount - amount + ├── wallet.Balance += creditAmount + ├── wallet.MagicTotalDeposited += amount + ├── wallet.MagicTotalCredited += creditAmount + ├── Transaction #1 (MagicWalletDeposit, amount) + ├── Transaction #2 (MagicWalletBonus, bonusAmount) + └── WalletChangeLog ✅ (اجباری) +``` + +--- + +## ۹. تغییرات UI (FrontOffice) + +### ۹.۱ صفحات جدید + +| صفحه | Route | توضیح | +|-------|-------|--------| +| MagicWallet.razor | `/profile/magic-wallet` | فرم شارژ + پروگرس‌بار سقف | +| MagicPaymentCallback.razor | `/profile/magic-payment-callback` | نتیجه پرداخت شارژ جادویی | + +### ۹.۲ تغییر صفحات موجود + +| صفحه | تغییر | +|-------|--------| +| Profile/Index.razor | بنر "🪄 کیف‌پول جادویی فعال" + لینک شارژ | +| Profile/Wallet.razor | نمایش وضعیت Magic + پروگرس (deposited/100M) | +| Club membership page | اگه Magic → پیام "بعد از اتمام جادویی می‌تونید پکیج بخرید" | + +### ۹.۳ gRPC Proto اضافات + +```protobuf +// userwallet.proto — RPCهای جدید: +rpc InitiateMagicCharge (MagicChargeRequest) returns (MagicChargeResponse); +rpc GetMagicWalletStatus (MagicWalletStatusRequest) returns (MagicWalletStatusResponse); + +message MagicChargeRequest { + int64 user_id = 1; + int64 amount = 2; +} + +message MagicChargeResponse { + string payment_url = 1; + int64 remaining_deposit_cap = 2; +} + +message MagicWalletStatusResponse { + bool is_magic_mode = 1; + int64 total_deposited = 2; + int64 total_credited = 3; + int64 remaining_cap = 4; + string activated_at = 5; +} +```