Files
docs/business/BUSINESS-02-PAYMENT-FINANCE.md
T
masoodafar-web b861b66cda docs: fix Regular Store payment — wallet-only, no IPG/ZarinPal
- Regular Store: Balance deduction only (SubmitShopBuyOrderCommandHandler)
  NO ZarinPal redirect, NO payment gateway
- Discount Store: DiscountBalance + ZarinPal IPG for remainder
  Added gatewayAmount=0 edge case (discount covers 100%)
- VAT: Both stores use 9% (ShopVAT=0.1 is stale/unused constant)
- Added ZarinPal usage scope note (only: Discount Store, Wallet Top-up, Package Purchase)
- Fixed BUSINESS-02: sections 2, 5.2, 5.3, 7
- Fixed BUSINESS-03: store comparison chart
- Fixed OVERVIEW-01: user journey + both store flowcharts
- Fixed OVERVIEW-03: timeline entry
2026-02-18 23:52:35 +03:30

219 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 💰 سیستم مالی، پرداخت و درگاه‌ها
> **منابع ادغام‌شده:** `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 تعریف‌شده |