# 💰 سیستم مالی، پرداخت و درگاه‌ها > **منابع ادغام‌شده:** `payment-gateway.md`, `payment-architecture-pyms.md`, `daya-loan-integration.md`, `manual-payment-system.md`, `discount-shop-business.md` > **آخرین بروزرسانی:** اسفند ۱۴۰۴ --- ## ۱. معماری کلی مالی ```mermaid 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 فقط در سه جا استفاده می‌شود: > 1. **فروشگاه تخفیفی** — باقیمانده بعد از کسر DiscountBalance (اگر > 0) > 2. **شارژ کیف‌پول** — واریز مستقیم از پروفایل کاربر > 3. **خرید پکیج** — (فعلاً غیرفعال: "درگاه پرداخت متصل نیست") > > ❌ **فروشگاه عادی (Regular Store) از ZarinPal استفاده نمی‌کند** — فقط کسر از Balance کیف‌پول ### ۲.۱ فلوی پرداخت ```mermaid 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` | از appsettings | | `CallbackUrl` | `/payment/callback` | | `Sandbox` | true (staging) / false (production) | | `Currency` | IRR (ریال → تبدیل به تومان در UI) | --- ## ۳. سیستم وام دایا (DayaLoan) ### ۳.۱ معماری ```mermaid 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 ```csharp // appsettings.json "DayaLoan": { "UseMock": true, // staging "BaseUrl": "https://api.dayaloan.ir", "ApiKey": "***", "AutoApproveInMock": true } ``` ### ۳.۳ مقادیر | آیتم | مقدار | |------|-------| | مبلغ وام (DayaLoanAmount) | ۵۶,۰۰۰,۰۰۰ ریال | | شارژ Balance | ۵۶,۰۰۰,۰۰۰ ریال | | شارژ DiscountBalance | ۱۱۲,۰۰۰,۰۰۰ ریال (دو برابر — DayaLoanAmount × 2) | | مجموع شارژ | ۱۶۸,۰۰۰,۰۰۰ ریال | | NetworkBalance | شارژ نمی‌شود | | بازپرداخت | طبق شرایط دایا | --- ## ۴. پرداخت دستی (کارت‌به‌کارت) > ⚠️ **وضعیت: طراحی‌شده — پیاده‌سازی نشده** ```mermaid flowchart TD A["کاربر → انتخاب کارت‌به‌کارت"] --> B["نمایش شماره‌کارت مقصد\n+ مبلغ"] B --> C["کاربر → واریز\n+ آپلود تصویر رسید"] C --> D["ادمین BackOffice\nمشاهده لیست درخواست‌ها"] D --> E{"تأیید / رد؟"} E -->|تأیید| F["✅ شارژ خودکار کیف‌پول"] E -->|رد| G["❌ اطلاع‌رسانی به کاربر"] ``` **موجودیت‌های مورد نیاز:** - `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 باقیمانده → ZarinPal IPG = 1,000,000 - 400,000 = 600,000 ───────── مجموع = 1,000,000 ℹ️ تخفیف ثابت ۳۰% نیست — فیلد Product.MaxDiscountPercent (0-100) تعیین‌کننده است. ``` ### ۵.۲ فلوی خرید فروشگاه تخفیفی ```mermaid flowchart TD A["کاربر عضو باشگاه\nمشاهده محصول"] --> B["قیمت تخفیف‌خورده نمایش داده می‌شود"] B --> C["افزودن به سبد\nمحاسبه MaxDiscount% هر محصول"] C --> D["سهم تخفیف = MIN(قیمت×MaxDiscount%, DiscountBalance)"] D --> E["باقیمانده = مجموع - سهم تخفیف"] E --> F{"باقیمانده > 0?"} F -->|بله| G["کسر DiscountBalance\n+ Redirect → ZarinPal IPG\nباقیمانده + 9% VAT"] F -->|خیر| H["فقط کسر از DiscountBalance\nبدون درگاه → ثبت مستقیم"] ``` ### ۵.۳ دسترسی فروشگاه تخفیفی | شرط | نتیجه | |------|--------| | `IsClubMember = true` | دسترسی به Discount Store | | `IsClubMember = false` | فقط Regular Store | | `DiscountBalance > 0` | می‌تواند از تخفیف استفاده کند | | `DiscountBalance = 0` | پرداخت ۱۰۰% از طریق ZarinPal IPG | --- ## ۶. 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 ``` هر دو فروشگاه از نرخ 9% استفاده می‌کنند: Regular Store → const vatRate = 0.09m (hardcoded در SubmitShopBuyOrderCommandHandler) Discount Store → VatCalculator.VAT_RATE = 0.09m ⚠️ SystemConstants.ShopVAT = 0.1 (10%) — تعریف‌شده ولی استفاده نمی‌شود (stale constant) قیمت نمایشی = قیمت پایه × (1 + 0.09) در صورتحساب: قیمت پایه + مالیات جداگانه نمایش داده می‌شود ``` --- ## ۸. خلاصه وضعیت پیاده‌سازی | ماژول | وضعیت | یادداشت | |-------|--------|---------| | ZarinPal IPG | ✅ کامل | Production ready | | وام دایا | ✅ کامل | Mock mode فعال در staging | | پرداخت ترکیبی | ✅ کامل | Discount + IPG | | Pool هفتگی | ✅ کامل | SP + Hangfire | | پرداخت دستی | ⬜ طراحی | نیاز به تصمیم مدیریت | | Refund | ⬜ طراحی | فقط در PYMS تعریف‌شده |