3c729304db
Corrections verified against actual CMS/BackOffice/FrontOffice source code: - ClubActivationFee: 25,200,000 (not 25,000,000) - Tree depth: no limit (15 is commission calculation depth only) - IPG wallet charge: Balance=56M + Discount=56M - DayaLoan wallet charge: Balance=56M + Discount=112M (2×) - Discount: per-product MaxDiscountPercent (not fixed 30%) - VAT: 10% (ShopVAT) vs 9% (discount store PlaceOrder) - Kavenegar template: 'Afrino' only (not verify-foursat) - SMS sender: 1000001110100 - DayaLoan job: every 20min (not 15min) - Commission job: Sunday 00:05 (not Saturday) - Network tree: on User entity (not separate NetworkNode table) - UserWallets entity (not UserWalletBalances) - OTP: 6 digits, 5 attempts, 2min TTL, 60s cooldown - Removed non-existent constants (ClubJoiningPercentage, ClubActivationThreshold) - Fixed Hangfire Chatika interval: every 5min - Removed InventorySync from recurring jobs list
232 lines
8.0 KiB
Markdown
232 lines
8.0 KiB
Markdown
# 💰 سیستم مالی، پرداخت و درگاهها
|
||
|
||
> **منابع ادغامشده:** `payment-gateway.md`, `payment-architecture-pyms.md`, `daya-loan-integration.md`, `manual-payment-system.md`, `discount-shop-business.md`
|
||
> **آخرین بروزرسانی:** اسفند ۱۴۰۴
|
||
|
||
---
|
||
|
||
## ۱. معماری کلی مالی
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────────────┐
|
||
│ FourSat Payment Architecture │
|
||
├──────────────┬──────────────┬──────────────┬─────────────────────┤
|
||
│ ZarinPal │ Daya Loan │ Manual Pay │ Discount Wallet │
|
||
│ (IPG) │ (API) │ (Card2Card) │ (Internal) │
|
||
├──────────────┴──────────────┴──────────────┴─────────────────────┤
|
||
│ PYMS (Payment Service) │
|
||
│ gRPC ←→ CMS ←→ FrontOffice/BackOffice │
|
||
├──────────────────────────────────────────────────────────────────┤
|
||
│ 3 Wallet System │
|
||
│ Balance (نقدی) │ NetworkBalance (شبکه) │ DiscountBalance │
|
||
└──────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## ۲. درگاه ZarinPal (IPG)
|
||
|
||
### ۲.۱ فلوی پرداخت
|
||
|
||
```
|
||
کاربر → انتخاب محصول → درخواست پرداخت
|
||
│
|
||
▼
|
||
CMS → CreatePaymentRequest (gRPC to PYMS)
|
||
│
|
||
▼
|
||
PYMS → ZarinPal API → دریافت Authority
|
||
│
|
||
▼
|
||
Redirect کاربر → صفحه پرداخت ZarinPal
|
||
│
|
||
▼
|
||
بازگشت با Authority → CMS VerifyPayment
|
||
│
|
||
├─→ موفق: ثبت سفارش + شارژ کیفپول (در صورت نیاز)
|
||
└─→ ناموفق: نمایش پیام خطا
|
||
```
|
||
|
||
### ۲.۲ تنظیمات ZarinPal
|
||
|
||
| پارامتر | مقدار |
|
||
|----------|-------|
|
||
| `MerchantId` | از appsettings |
|
||
| `CallbackUrl` | `/payment/callback` |
|
||
| `Sandbox` | true (staging) / false (production) |
|
||
| `Currency` | IRR (ریال → تبدیل به تومان در UI) |
|
||
|
||
---
|
||
|
||
## ۳. سیستم وام دایا (DayaLoan)
|
||
|
||
### ۳.۱ معماری
|
||
|
||
```
|
||
Hangfire Recurring Job (هر ۲۰ دقیقه — cron: */20 * * * *)
|
||
│
|
||
▼
|
||
DayaLoanProcessorJob.Execute()
|
||
│
|
||
▼
|
||
بررسی LoanRequests با Status=Pending
|
||
│
|
||
▼
|
||
برای هر درخواست:
|
||
├─→ ارسال به DayaLoan API ([AutomaticRetry(Attempts=3)])
|
||
├─→ در صورت تأیید: Balance += 56M, DiscountBalance += 112M (2×)
|
||
├─→ ثبت Transaction + Log
|
||
└─→ در صورت رد: Status=Rejected + ارسال SMS
|
||
```
|
||
|
||
### ۳.۲ Mock Mode
|
||
|
||
```csharp
|
||
// appsettings.json
|
||
"DayaLoan": {
|
||
"UseMock": true, // staging
|
||
"BaseUrl": "https://api.dayaloan.ir",
|
||
"ApiKey": "***",
|
||
"AutoApproveInMock": true
|
||
}
|
||
```
|
||
|
||
### ۳.۳ مقادیر
|
||
|
||
| آیتم | مقدار |
|
||
|------|-------|
|
||
| مبلغ وام (DayaLoanAmount) | ۵۶,۰۰۰,۰۰۰ ریال |
|
||
| شارژ Balance | ۵۶,۰۰۰,۰۰۰ ریال |
|
||
| شارژ DiscountBalance | ۱۱۲,۰۰۰,۰۰۰ ریال (دو برابر — DayaLoanAmount × 2) |
|
||
| مجموع شارژ | ۱۶۸,۰۰۰,۰۰۰ ریال |
|
||
| NetworkBalance | شارژ نمیشود |
|
||
| بازپرداخت | طبق شرایط دایا |
|
||
|
||
---
|
||
|
||
## ۴. پرداخت دستی (کارتبهکارت)
|
||
|
||
> ⚠️ **وضعیت: طراحیشده — پیادهسازی نشده**
|
||
|
||
```
|
||
فلوی پیشنهادی:
|
||
کاربر → انتخاب "کارتبهکارت"
|
||
│
|
||
▼
|
||
نمایش شمارهکارت مقصد + مبلغ
|
||
│
|
||
▼
|
||
کاربر → واریز + آپلود تصویر رسید
|
||
│
|
||
▼
|
||
ادمین BackOffice → مشاهده لیست درخواستها
|
||
│
|
||
▼
|
||
تأیید/رد → شارژ خودکار کیفپول
|
||
```
|
||
|
||
**موجودیتهای مورد نیاز:**
|
||
- `ManualPaymentRequest` (UserId, Amount, ReceiptImage, Status, AdminNote)
|
||
- `ManualPaymentStatus` enum: Pending, Approved, Rejected
|
||
|
||
---
|
||
|
||
## ۵. پرداخت ترکیبی فروشگاه تخفیفی (Hybrid Payment)
|
||
|
||
### ۵.۱ فرمول
|
||
|
||
```
|
||
قیمت محصول = 1,000,000 ریال
|
||
MaxDiscountPercent محصول = 40% (هر محصول درصد تخفیف مخصوص خود را دارد)
|
||
|
||
سهم تخفیف = MIN(1,000,000 × 40%, DiscountBalanceکاربر) = 400,000
|
||
پرداخت نقدی (IPG) = 1,000,000 - 400,000 = 600,000
|
||
─────────
|
||
مجموع = 1,000,000
|
||
|
||
ℹ️ تخفیف ثابت ۳۰% نیست — فیلد Product.MaxDiscountPercent (0-100) تعیینکننده است.
|
||
```
|
||
|
||
### ۵.۲ فلوی خرید فروشگاه تخفیفی
|
||
|
||
```
|
||
کاربر (عضو باشگاه) → مشاهده محصول
|
||
│
|
||
▼
|
||
قیمت تخفیفخورده نمایش داده میشود
|
||
│
|
||
▼
|
||
افزودن به سبد → بررسی DiscountBalance
|
||
│
|
||
├─→ DiscountBalance کافی:
|
||
│ سهم تخفیف از DiscountBalance کسر
|
||
│ باقیمانده → IPG (ZarinPal)
|
||
│
|
||
└─→ DiscountBalance ناکافی:
|
||
فقط به اندازه موجودی از تخفیف
|
||
باقیمانده بیشتر → IPG
|
||
```
|
||
|
||
### ۵.۳ دسترسی فروشگاه تخفیفی
|
||
|
||
| شرط | نتیجه |
|
||
|------|--------|
|
||
| `IsClubMember = true` | دسترسی به Discount Store |
|
||
| `IsClubMember = false` | فقط Regular Store |
|
||
| `DiscountBalance > 0` | میتواند از تخفیف استفاده کند |
|
||
| `DiscountBalance = 0` | پرداخت ۱۰۰% نقدی |
|
||
|
||
---
|
||
|
||
## ۶. PYMS — سرویس پرداخت مرکزی
|
||
|
||
### ۶.۱ gRPC Services
|
||
|
||
```protobuf
|
||
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 | شارژ از وام دایا |
|
||
|
||
---
|
||
|
||
## ۷. مالیات و VAT
|
||
|
||
```
|
||
دو نرخ VAT در کد:
|
||
|
||
SystemConstants.ShopVAT = 0.1 (10%) — تنظیم سیستمی
|
||
VAT_RATE = 0.09 (9%) — در فروشگاه تخفیفی (DiscountShop PlaceOrder)
|
||
|
||
قیمت نمایشی = قیمت پایه × (1 + VAT)
|
||
در صورتحساب: قیمت پایه + مالیات جداگانه نمایش داده میشود
|
||
```
|
||
|
||
---
|
||
|
||
## ۸. خلاصه وضعیت پیادهسازی
|
||
|
||
| ماژول | وضعیت | یادداشت |
|
||
|-------|--------|---------|
|
||
| ZarinPal IPG | ✅ کامل | Production ready |
|
||
| وام دایا | ✅ کامل | Mock mode فعال در staging |
|
||
| پرداخت ترکیبی | ✅ کامل | Discount + IPG |
|
||
| Pool هفتگی | ✅ کامل | SP + Hangfire |
|
||
| پرداخت دستی | ⬜ طراحی | نیاز به تصمیم مدیریت |
|
||
| Refund | ⬜ طراحی | فقط در PYMS تعریفشده |
|