Files
docs/roadmap/MAGIC-WALLET-PLAN.md
masoodafar-web f5173a4def docs: فعال‌سازی درگاه ZarinPal + مرج پروداکشن + بهبود UI ادمین
- CHANGELOG: اضافه Phase 18 BackOffice + فعال‌سازی درگاه FO + تنظیمات محیطی CMS + مرج پروداکشن
- PAYMENT-FINANCE: MerchantId واقعی + تنظیمات محیطی Staging/Production + Magic فاز 6 کامل
- ROADMAP: بروزرسانی درصدها + Magic 100% + Payment 97% + DONE section
- TECH-02: ساختار فولدر + UserAutoComplete + WalletManagement + دکمه‌های درگاه
- BUSINESS-01: Magic Wallet فاز 1-6 کامل
- TECH-04: Migration پروداکشن ExpandDiscountProductFullInformation + حذف u21 تکراری
- TECH-03: جزئیات مرج پروداکشن + تنظیمات appsettings.Production.json
- MAGIC-WALLET-PLAN: وضعیت کامل
- INDEX: بروزرسانی تاریخ
2026-02-22 23:26:36 +03:30

10 KiB
Raw Permalink Blame History

🪄 پلن پیاده‌سازی کیف‌پول جادویی

مرجع: MAGIC-WALLET-SPEC
تخمین کل: ~۷ روز کاری
وضعیت: کامل — همه ۶ فاز پیاده‌سازی و مرج شده
وابستگی مستقل: تکمیل 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 پیاده‌سازی

  • فاز ۱: WalletMode enum
  • فاز ۱: UserWallet entity + 5 فیلد جدید
  • فاز ۱: ClubMembershipCycle entity (جدید)
  • فاز ۱: TransactionType + 2 مقدار
  • فاز ۱: SystemConstants + 3 ثابت
  • فاز ۱: EF Configuration (UserWallet + ClubMembershipCycle)
  • فاز ۱: Migration: AddMagicWalletFields (u21 — اعمال شده )
  • فاز ۱: Migration: AddClubMembershipCycle + data seed (74 رکورد seed شده )
  • فاز ۲: Trigger ورود Magic (SubmitShopBuyOrder)
  • فاز ۲: Trigger خروج Magic
  • فاز ۲: ActivateClubMembership → ActivatedAt نگه‌داشته بشه + Cycle جدید
  • فاز ۲: PurchaseCycleCount
  • فاز ۳: InitiateMagicChargeCommand + Handler
  • فاز ۳: VerifyMagicChargeCommand + Handler
  • فاز ۳: MagicWalletController (HTTP callback)
  • فاز ۳: gRPC Proto + Service
  • فاز ۳: ۲ تراکنش + ۱ لاگ (اجباری)
  • فاز ۴: فیلتر کمیسیون Magic (C# handler + SP)
  • فاز ۴: تاریخ کمیسیون: ActivatedAt → Cycle.PackagePurchasedAt (C# + SP)
  • فاز ۵: MagicWallet.razor
  • فاز ۵: MagicPaymentCallback — نتیجه پرداخت از طریق ?payment= query param در همان MagicWallet.razor هندل میشه
  • فاز ۵: WalletService gRPC client
  • فاز ۵: Profile + Wallet page updates
  • فاز ۶: Daya restriction (CheckAndProcessDayaLoansCommandHandler + FO Purchase UI)
  • فاز ۶: Club activation restriction (ActivateClubMembershipCommandHandler + WalletMode guard)

وابستگی مستقل: تکمیل ChargeDiscountWallet

تکمیل شد — مستقل از کیف‌پول جادویی پیاده‌سازی شد.

انجام شده:
├── ChargeDiscountWalletCommandHandler — CQRS handler ✅
├── VerifyDiscountWalletChargeCommandHandler — ✅
├── PaymentCallbackController → GET /api/wallet/verify-discount-charge ✅
├── userwallet.proto → rpc InitiateDiscountCharge ✅
├── UserWalletService.cs → InitiateDiscountCharge override ✅
├── WalletService.cs (FO) → InitiateDiscountChargeAsync ✅
├── ChargeDiscountWallet.razor + .razor.cs (FO) ✅
├── RouteConstants → ChargeDiscountWallet ✅
└── Wallet.razor → دکمه شارژ اعتباری ✅