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
This commit is contained in:
masoodafar-web
2026-02-19 02:08:28 +03:30
parent b861b66cda
commit a94d0dbe95
3 changed files with 620 additions and 7 deletions
+18 -7
View File
@@ -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 |
---
## نقشه ارتباط فایل‌ها
+205
View File
@@ -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
```
+397
View File
@@ -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:<br/>WalletMode == Magic<br/>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<br/>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;
}
```