Files
docs/roadmap/MAGIC-WALLET-PLAN.md
T
masoodafar-web 421a651975 docs: Magic Wallet + VAT 10% documentation update
- All 14 totalDoc files updated with Magic Wallet additions
- MAGIC-WALLET-PLAN.md: Phase 1-6 checklist fully marked complete
- Business docs: Magic Wallet section, commission filter, new entities
- Payment docs: VAT 9%→10%, TransactionType 14+15, ZarinPal 4th usage
- Technical docs: UserWallet fields, ClubMembershipCycle, gRPC RPCs
- Overview docs: Magic flowchart, ER diagram, changelog, glossary, roadmap
2026-02-22 20:09:01 +03:30

229 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🪄 پلن پیاده‌سازی کیف‌پول جادویی
> **مرجع:** [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 برای فیلدهای جدید
├── ClubMembershipCycle.cs → 🆕 entity جدید (حل مشکل تاریخ کمیسیون)
├── ClubMembershipCycleConfiguration.cs → EF config
├── Migration: AddMagicWalletFields → dotnet ef migrations add
└── Migration: AddClubMembershipCycle → dotnet ef migrations add + data seed
```
**تست:** Migration اجرا بشه، فیلدها در DB ایجاد بشن، default‌ها درست باشن. هر ClubMembership موجود یه رکورد Cycle=1 داشته باشه.
---
### فاز ۲ — Trigger ورود/خروج Magic (روز ۲)
**هدف:** State Machine خودکار
```
فایل‌های تغییری:
├── SubmitShopBuyOrderCommandHandler.cs
│ ├── بعد از کسر Balance: check ورود به Magic
│ └── بعد از کسر Balance: check خروج از Magic
├── ActivateClubMembershipCommandHandler.cs
│ ├── ActivatedAt فقط بار اول ست بشه (دیگه overwrite نشه)
│ └── هر بار یک ClubMembershipCycle جدید اضافه بشه
├── (Optional) Domain Event: WalletModeChangedEvent
│ └── برای لاگ و نوتیفیکیشن
└── User.cs (یا UserWallet)
└── + PurchaseCycleCount (int) — تعداد دور خرید پکیج
```
**تست:**
- سناریو ۱: Balance=0 بعد از خرید → WalletMode=Magic ✅
- سناریو ۲: بدون پکیج + Balance=0 → نباید Magic بشه ❌
- سناریو ۳: Magic + Balance=0 + **TotalDeposited=50M** (سقف پر نشده) → **هنوز Magic!** نباید خارج بشه ❌
- سناریو ۴: Magic + Balance=0 + **TotalDeposited=100M** (سقف پر) → خروج ✅
- سناریو ۵: Magic + Balance=30M + TotalDeposited=100M → **هنوز Magic!** (بالانس داره) ❌
- سناریو ۶: خروج از Magic → خرید مجدد پکیج → Balance=0 → Magic مجدد با **سقف ریست‌شده**
- سناریو ۷: دور دوم → TotalDeposited, TotalCredited = 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 → خطا ❌
- ۲ تراکنش + ۱ لاگ ثبت شده ✅
---
### فاز ۴ — غیرفعال‌سازی کمیسیون + تاریخ Cycle (روز ۴.۵)
**هدف:** کاربرهای Magic از کمیسیون خارج بشن + تاریخ کمیسیون از Cycle بخونه
```
فایل‌های تغییری:
├── CalculateWeeklyBalancesCommandHandler.cs
│ ├── فیلتر: WHERE wallet.WalletMode != Magic
│ └── تاریخ: ActivatedAt → ClubMembershipCycle.PackagePurchasedAt
├── sp_CalculateWeeklyBalances.sql
│ ├── + JOIN UserWallets WHERE WalletMode = 0
│ └── WHERE cm.ActivatedAt → cc.PackagePurchasedAt (AND cc.IsCurrentCycle = 1)
└── WeekRepository (اگه date range query داره)
└── آپدیت query
```
**تست:**
- کاربر Magic در محاسبات هفتگی شرکت نکنه ✅
- کاربر دور ۲ (پکیج مجدد): با تاریخ PackagePurchasedAt جدید امتیاز بگیره ✅
- تاریخ اصلی ActivatedAt تغییر نکرده باشه ✅
---
### فاز ۵ — صفحات 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 پیاده‌سازی
- [x] **فاز ۱:** WalletMode enum
- [x] **فاز ۱:** UserWallet entity + 5 فیلد جدید
- [x] **فاز ۱:** ClubMembershipCycle entity (جدید)
- [x] **فاز ۱:** TransactionType + 2 مقدار
- [x] **فاز ۱:** SystemConstants + 3 ثابت
- [x] **فاز ۱:** EF Configuration (UserWallet + ClubMembershipCycle)
- [ ] **فاز ۱:** Migration: AddMagicWalletFields
- [ ] **فاز ۱:** Migration: AddClubMembershipCycle + data seed
- [x] **فاز ۲:** Trigger ورود Magic (SubmitShopBuyOrder)
- [x] **فاز ۲:** Trigger خروج Magic
- [x] **فاز ۲:** ActivateClubMembership → ActivatedAt نگه‌داشته بشه + Cycle جدید
- [x] **فاز ۲:** PurchaseCycleCount
- [x] **فاز ۳:** InitiateMagicChargeCommand + Handler
- [x] **فاز ۳:** VerifyMagicChargeCommand + Handler
- [x] **فاز ۳:** MagicWalletController (HTTP callback)
- [x] **فاز ۳:** gRPC Proto + Service
- [x] **فاز ۳:** ۲ تراکنش + ۱ لاگ (اجباری)
- [x] **فاز ۴:** فیلتر کمیسیون Magic (C# handler + SP)
- [x] **فاز ۴:** تاریخ کمیسیون: ActivatedAt → Cycle.PackagePurchasedAt (C# + SP)
- [x] **فاز ۵:** MagicWallet.razor
- [x] **فاز ۵:** MagicPaymentCallback — نتیجه پرداخت از طریق ?payment= query param در همان MagicWallet.razor هندل میشه
- [x] **فاز ۵:** WalletService gRPC client
- [x] **فاز ۵:** Profile + Wallet page updates
- [x] **فاز ۶:** Daya restriction (CheckAndProcessDayaLoansCommandHandler + FO Purchase UI)
- [x] **فاز ۶:** Club activation restriction (ActivateClubMembershipCommandHandler + WalletMode guard)
---
## وابستگی مستقل: تکمیل 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
```