39590d2cbe
Updated 8 docs: - CHANGELOG: Phase 10a-d (DataMigration tool, EF staging migrations, PackagePurchaseDialog, 4 UI fixes) - PAYMENT-FINANCE: Rial→Toman conversion chain documented, PackagePurchaseDialog status - TECH-02: Added PackagePurchaseDialog to folder structure + status table - TECH-04: DataMigration tool features (smart retry, FK handling, fallback tables), EF staging - PACKAGE-TASKS: Phase 10 graph, NuGet v0.0.189, T4.1+T4.2 marked ✅ - BIZ-PACKAGE: v6→v7, commits updated, T4.1+T4.2 marked ✅ - INDEX: Updated last-update + R3 description - ROADMAP: Progress bars updated, DONE section + NOW section refreshed
12 KiB
12 KiB
💰 سیستم مالی، پرداخت و درگاهها
منابع ادغامشده:
payment-gateway.md,payment-architecture-pyms.md,daya-loan-integration.md,manual-payment-system.md,discount-shop-business.md
آخرین بروزرسانی: اسفند ۱۴۰۴ (بروزرسانی: فیکس Toman/Rial + PackagePurchaseDialog + اعمال migrations بر staging)
۱. معماری کلی مالی
flowchart TD
subgraph GATEWAYS["درگاهها"]
ZP["ZarinPal\nIPG"]
DL["Daya Loan\nAPI"]
MP["Manual Pay\nCard2Card"]
DW["Discount Wallet\nInternal"]
end
ZP --> PYMS["PYMS — Payment Service\ngRPC ↔ CMS ↔ FrontOffice/BackOffice"]
DL --> PYMS
MP --> PYMS
DW --> PYMS
PYMS --> WALLETS
subgraph WALLETS["3 Wallet System"]
W1["💰 Balance\nکیف پول اصلی"]
W2["🌟 NetworkBalance\nپاداش تیمی"]
W3["🏷️ DiscountBalance\nکیف پول اعتباری"]
end
۲. درگاه ZarinPal (IPG)
✅ محل استفاده: ZarinPal در چهار جا فعال است:
- فروشگاه اعتباری — باقیمانده بعد از کسر DiscountBalance (اگر > 0)
- شارژ کیفپول اعتباری — واریز مستقیم از پروفایل کاربر
- خرید پکیج — پرداخت مستقیم با کارت بانکی (هر دو شاخه فعال)
- شارژ کیفپول جادویی — واریز با ضریب ×2.5 (فقط در حالت Magic)
❌ فروشگاه عادی (Regular Store) از ZarinPal استفاده نمیکند — فقط کسر از Balance کیفپول
۲.۱ فلوی پرداخت
flowchart TD
A["کاربر → انتخاب محصول\nدرخواست پرداخت"] --> B["CMS → CreatePaymentRequest\ngRPC to PYMS"]
B --> C["PYMS → ZarinPal API\nدریافت Authority"]
C --> D["Redirect کاربر\nصفحه پرداخت ZarinPal"]
D --> E["بازگشت با Authority\nCMS VerifyPayment"]
E -->|موفق| F["✅ ثبت سفارش\n+ شارژ کیفپول"]
E -->|ناموفق| G["❌ نمایش پیام خطا"]
۲.۲ تنظیمات ZarinPal
| پارامتر | مقدار |
|---|---|
MerchantId |
4225d555-5fa9-4df0-9b61-1ce152cbbba8 |
CallbackUrl |
/payment/callback |
Sandbox |
true (staging) / false (production) |
Currency |
IRR (ریال → تبدیل به تومان در UI) |
✅ توضیح تبدیل Rial → Toman:
- DB:
Package.Priceبه ریال ذخیره میشود (/// قیمت پکیج (ریال))- CMS → ZarinPal: مستقیم ریال ارسال میشود (
Amount = package.Price→amountInRials = (long)request.Amount)- FrontOffice UI:
PackageDto.FormattedPrice=Price / 10+ «تومان» (فیکس شد در3c1a8ff)- باگ قبلی: FO مقدار خام ریال را با برچسب «تومان» نمایش میداد (مثلاً ۵۶۰،۰۰۰،۰۰۰ تومان بجای ۵۶،۰۰۰،۰۰۰ تومان)
تنظیمات محیطی:
appsettings.json+appsettings.Staging.json:UseSandbox: true(تست)appsettings.Production.json:UseSandbox: false(واقعی)- Production URL:
cms.kbs1.ir| FrontOffice GwUrl:cms.kbs2.ir
۳. سیستم وام دایا (DayaLoan)
۳.۱ معماری
flowchart TD
A["Hangfire Recurring Job\nهر ۲۰ دقیقه — */20 * * * *"] --> B["DayaLoanProcessorJob.Execute"]
B --> C["بررسی LoanRequests\nStatus = Pending"]
C --> D["برای هر درخواست:"]
D --> E["ارسال به DayaLoan API\nAutomaticRetry Attempts=3"]
E -->|تأیید| F["✅ Balance += 56M\nDiscountBalance += 112M\n+ ثبت Transaction + Log"]
E -->|رد| G["❌ Status = Rejected\n+ ارسال SMS"]
۳.۲ Mock Mode
// appsettings.json
"DayaLoan": {
"UseMock": true, // staging
"BaseUrl": "https://api.dayaloan.ir",
"ApiKey": "***",
"AutoApproveInMock": true
}
۳.۳ مقادیر
| آیتم | مقدار |
|---|---|
| مبلغ وام (DayaLoanAmount) | ۵۶,۰۰۰,۰۰۰ ریال |
| شارژ Balance | ۵۶,۰۰۰,۰۰۰ ریال |
| شارژ DiscountBalance | ۱۱۲,۰۰۰,۰۰۰ ریال (دو برابر — DayaLoanAmount × 2) |
| مجموع شارژ | ۱۶۸,۰۰۰,۰۰۰ ریال |
| NetworkBalance | شارژ نمیشود |
| بازپرداخت | طبق شرایط دایا |
۴. پرداخت دستی (کارتبهکارت)
⚠️ وضعیت: طراحیشده — پیادهسازی نشده
flowchart TD
A["کاربر → انتخاب کارتبهکارت"] --> B["نمایش شمارهکارت مقصد\n+ مبلغ"]
B --> C["کاربر → واریز\n+ آپلود تصویر رسید"]
C --> D["ادمین BackOffice\nمشاهده لیست درخواستها"]
D --> E{"تأیید / رد؟"}
E -->|تأیید| F["✅ شارژ خودکار کیفپول"]
E -->|رد| G["❌ اطلاعرسانی به کاربر"]
موجودیتهای مورد نیاز:
ManualPaymentRequest(UserId, Amount, ReceiptImage, Status, AdminNote)ManualPaymentStatusenum: Pending, Approved, Rejected
۵. پرداخت ترکیبی فروشگاه اعتباری (Hybrid Payment)
۵.۱ فرمول
قیمت محصول = 1,000,000 ریال
MaxDiscountPercent محصول = 40% (هر محصول درصد تخفیف مخصوص خود را دارد)
سهم تخفیف = قیمت × MaxDiscountPercent% = 400,000
⚠️ اگر DiscountBalance < سهم تخفیف → خطا: «موجودی کیف پول اعتباری کافی نیست»
(دیگر MIN استفاده نمیشود — کاربر باید موجودی کافی داشته باشد)
باقیمانده → ZarinPal IPG = 1,000,000 - 400,000 = 600,000
─────────
مجموع = 1,000,000
ℹ️ تخفیف ثابت ۳۰% نیست — فیلد Product.MaxDiscountPercent (0-100) تعیینکننده است.
۵.۲ فلوی خرید فروشگاه اعتباری
flowchart TD
A["کاربر عضو باشگاه\nمشاهده محصول"] --> B["قیمت تخفیفخورده نمایش داده میشود"]
B --> C["افزودن به سبد\nمحاسبه MaxDiscount% هر محصول"]
C --> D["سهم تخفیف = قیمت × MaxDiscount%"]
D --> V{"DiscountBalance >= سهم تخفیف?"}
V -->|خیر| X["❌ خطا: موجودی کیف پول اعتباری کافی نیست"]
V -->|بله| E["باقیمانده = مجموع - سهم تخفیف"]
E --> F{"باقیمانده > 0?"}
F -->|بله| G["کسر DiscountBalance\n+ Redirect → ZarinPal IPG\nباقیمانده + 10% VAT"]
F -->|خیر| H["فقط کسر از DiscountBalance\nبدون درگاه → ثبت مستقیم"]
G --> LOG["ثبت UserWalletChangeLog"]
H --> LOG
۵.۳ دسترسی فروشگاه اعتباری
| شرط | نتیجه |
|---|---|
IsClubMember = true |
دسترسی به Discount Store |
IsClubMember = false |
فقط Regular Store |
DiscountBalance >= سهم تخفیف |
خرید مجاز |
DiscountBalance < سهم تخفیف |
❌ خطا: موجودی کیف پول اعتباری کافی نیست |
۵.۴ UserWalletChangeLog (اسفند ۱۴۰۴ — فیکس)
باگ: هنگام خرید از فروشگاه اعتباری،
DiscountBalanceدر دیتابیس کم میشد ولی هیچUserWalletChangeLogثبت نمیشد → کاربر در تاریخچه کیفپول چیزی نمیدید.
فیکس در ۳ هندلر:
| هندلر | سناریو | فیکس |
|---|---|---|
PlaceOrderCommandHandler |
پرداخت کامل با DiscountBalance (بدون درگاه) | ✅ ثبت log با ChangeDiscountValue = -amount |
CompleteOrderPaymentCommandHandler |
پرداخت ترکیبی (درگاه + DiscountBalance) | ✅ ثبت log بعد از verify موفق درگاه |
VerifyDiscountWalletChargeCommandHandler |
شارژ کیفپول اعتباری | ✅ ثبت log با ChangeDiscountValue = +amount |
۶. PYMS — سرویس پرداخت مرکزی
۶.۱ gRPC Services
service PaymentService {
rpc CreatePayment (CreatePaymentRequest) returns (CreatePaymentResponse);
rpc VerifyPayment (VerifyPaymentRequest) returns (VerifyPaymentResponse);
rpc GetPaymentStatus (GetPaymentStatusRequest) returns (PaymentStatusResponse);
rpc RefundPayment (RefundPaymentRequest) returns (RefundPaymentResponse);
}
۶.۲ Transaction Types
| نوع | کد | توضیح |
|---|---|---|
| PackagePurchase | 1 | خرید پکیج طلایی |
| StorePurchase | 2 | خرید از فروشگاه |
| DiscountStorePurchase | 3 | خرید از فروشگاه اعتباری |
| CommissionPayout | 4 | واریز کمیسیون هفتگی |
| WalletCharge | 5 | شارژ مستقیم کیفپول |
| ActivationFee | 6 | هزینه فعالسازی |
| DayaLoanCharge | 7 | شارژ از وام دایا |
| MagicWalletDeposit | 14 | واریز به کیفپول جادویی |
| MagicWalletBonus | 15 | بونوس ضریب ×2.5 کیفپول جادویی |
۷. مالیات و VAT
هر دو فروشگاه از نرخ 10% استفاده میکنند:
Regular Store → const vatRate = 0.10m (hardcoded در SubmitShopBuyOrderCommandHandler)
Discount Store → VatCalculator.VAT_RATE = 0.10m
SystemConstants.ShopVAT = 0.1 (10%)
قیمت نمایشی = قیمت پایه × (1 + 0.10)
در صورتحساب: قیمت پایه + مالیات جداگانه نمایش داده میشود
۸. خلاصه وضعیت پیادهسازی
| ماژول | وضعیت | یادداشت |
|---|---|---|
| ZarinPal IPG | ✅ کامل | Production فعال — MerchantId: 4225d555... |
| وام دایا | ✅ کامل | Mock mode فعال در staging |
| پرداخت ترکیبی | ✅ کامل | Discount + IPG |
| Pool هفتگی | ✅ کامل | SP + Hangfire |
| WalletChangeLog | ✅ فیکس شده | لاگ تغییرات کیفپول در ۳ هندلر اضافه شد |
| Validation کیفپول اعتباری | ✅ فیکس شده | ارور اگر موجودی کافی نباشد |
| Toman/Rial تبدیل نمایش | ✅ فیکس شده | Price / 10 در FO — درگاه ریال میگیرد، UI تومان نمایش میدهد |
| PackagePurchaseDialog | ✅ کامل | دیالوگ داینامیک کاشیای با انتخاب روش پرداخت |
| پرداخت دستی | ⬜ طراحی | نیاز به تصمیم مدیریت |
| Refund | ⬜ طراحی | فقط در PYMS تعریفشده |
| کیفپول جادویی (Magic) | ✅ کامل | فاز 1-6 پیادهسازی شده — Production فعال |
۸.۱ نامگذاری استاندارد کیفپولها (اسفند ۱۴۰۴)
| فیلد دیتابیس | نام قدیم (UI) | نام جدید (UI) |
|---|---|---|
Balance |
عادی / اعتباری / نقدی | کیف پول اصلی |
DiscountBalance |
تخفیفی / تخفیف | کیف پول اعتباری |
NetworkBalance |
شبکه / طلایی / پورسانت | پاداش تیمی |
تغییرات UI در ۱۲ فایل (FrontOffice: 5, BackOffice: 7) اعمال شد.