Files
docs/business/BUSINESS-02-PAYMENT-FINANCE.md
T
masoodafar-web 3c729304db docs: fix all discrepancies based on comprehensive code audit
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
2026-02-18 22:58:40 +03:30

8.0 KiB
Raw Blame History

💰 سیستم مالی، پرداخت و درگاه‌ها

منابع ادغام‌شده: 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

// 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

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 تعریف‌شده