Implement Persian Date Conversion and Enhance User Network Information Service
- Added PersianDateTimeService for converting Gregorian dates to Persian format in the BackOffice frontend. - Updated multiple frontend pages (Dashboard, UserPayouts, WorkerControl, UserNetworkInfo) to utilize the new Persian date service. - Enhanced GetUserNetworkPositionDto with 28+ new fields for comprehensive user network data. - Updated GetUserNetworkPositionQueryHandler to include new methods for calculating network statistics. - Modified Protobuf messages to accommodate the new fields, increasing from 14 to 42. - Refined week number calculation algorithm to ensure consistency across C# and SQL implementations. - Created new CSV and Excel files for binary plan calculations. - Ensured all changes are tested and validated for accuracy and performance.
This commit is contained in:
@@ -0,0 +1,656 @@
|
||||
# Base Package Payment System - سیستم پرداخت پکیج پایه
|
||||
|
||||
**تاریخ ایجاد:** 2024-12-16
|
||||
**تاریخ آخرین بهروزرسانی:** 2024-12-16
|
||||
**وضعیت:** ✅ پیادهسازی شده
|
||||
**اولویت:** 🔴 بسیار بالا
|
||||
|
||||
---
|
||||
|
||||
## 📋 فهرست
|
||||
|
||||
1. [خلاصه سیستم](#خلاصه-سیستم)
|
||||
2. [Business Requirements](#business-requirements)
|
||||
3. [معماری سیستم](#معماری-سیستم)
|
||||
4. [Implementation Details](#implementation-details)
|
||||
5. [Club Membership Contract System](#club-membership-contract-system)
|
||||
6. [API Endpoints](#api-endpoints)
|
||||
7. [Flow Diagram](#flow-diagram)
|
||||
8. [نکات مهم](#نکات-مهم)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 خلاصه سیستم
|
||||
|
||||
سیستم پرداخت پکیج پایه امکان پرداخت **56 میلیون تومان** را برای کاربران فراهم میکند تا بتوانند:
|
||||
1. کیف پول خود را شارژ کنند (Balance + DiscountBalance)
|
||||
2. **امضای قرارداد باشگاه مشتریان** (گام الزامی بعد از پرداخت)
|
||||
3. **فعالسازی لینک دعوت** (Referral Link) - تنها بعد از امضای قرارداد
|
||||
4. دسترسی کامل به امکانات باشگاه مشتریان
|
||||
|
||||
### دو روش پرداخت:
|
||||
1. **پرداخت مستقیم (Direct Payment)** - از طریق درگاه بانکی (زرینپال)
|
||||
2. **اعتبار الماسی دایا (Daya Loan)** - از طریق سایت دایا
|
||||
|
||||
---
|
||||
|
||||
## 📊 Business Requirements
|
||||
|
||||
### شرایط نمایش لینک دعوت:
|
||||
```
|
||||
CanShowReferralLink = HasPurchasedPackage && IsClubMemberActive
|
||||
```
|
||||
|
||||
- **HasPurchasedPackage**: کاربر پکیج پایه را خریداری کرده (PackagePurchaseMethod != None)
|
||||
- **IsClubMemberActive**: قرارداد باشگاه مشتریان امضا شده (ClubMembership.IsActive = true)
|
||||
|
||||
⚠️ **نکته مهم**: پرداخت پکیج به تنهایی کافی نیست! کاربر باید قرارداد باشگاه مشتریان را نیز امضا کند.
|
||||
|
||||
### مقدار پکیج:
|
||||
- **مبلغ**: 56,000,000 تومان
|
||||
- **شارژ Balance**: 56,000,000 تومان
|
||||
- **شارژ DiscountBalance**: 56,000,000 تومان
|
||||
|
||||
### PackagePurchaseMethod Enum:
|
||||
```csharp
|
||||
public enum PackagePurchaseMethod
|
||||
{
|
||||
None = 0, // هنوز خرید نکرده
|
||||
DirectPurchase = 1, // پرداخت مستقیم
|
||||
DayaLoan = 2 // اعتبار دایا
|
||||
}
|
||||
```
|
||||
|
||||
### ContractType Enum:
|
||||
```csharp
|
||||
public enum ContractType
|
||||
{
|
||||
Main = 0, // قرارداد ثبتنام اولیه
|
||||
ClubMembership = 1, // قرارداد باشگاه مشتریان
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ معماری سیستم
|
||||
|
||||
### Architecture Pattern:
|
||||
```
|
||||
Frontend (Blazor)
|
||||
↓
|
||||
BFF (Backend For Frontend)
|
||||
↓ ↘
|
||||
CMS PYMS (Payment Gateway)
|
||||
```
|
||||
|
||||
### Layer Responsibilities:
|
||||
|
||||
#### 1️⃣ Frontend (Blazor)
|
||||
- نمایش UI برای انتخاب روش پرداخت
|
||||
- فراخوانی BFF برای شروع پرداخت
|
||||
- مدیریت Callback از درگاه
|
||||
- نمایش نتیجه پرداخت
|
||||
- **Modal غیرقابل بسته شدن برای امضای قرارداد باشگاه** (جدید ✨)
|
||||
|
||||
#### 2️⃣ BFF (Middle Layer)
|
||||
- **InitiateBasePackagePayment**: هماهنگی بین CMS و PYMS
|
||||
- فراخوانی CMS برای ثبت Transaction + Order
|
||||
- فراخوانی PYMS برای دریافت URL درگاه
|
||||
- برگرداندن URL به Frontend
|
||||
|
||||
- **VerifyBasePackagePayment**: تأیید پرداخت
|
||||
- فراخوانی PYMS برای Verify
|
||||
- فراخوانی CMS برای شارژ یا Reject
|
||||
|
||||
- **RequestClubContractOtp**: ارسال OTP برای امضای قرارداد (جدید ✨)
|
||||
- **AcceptClubMembershipContract**: امضای قرارداد و فعالسازی باشگاه (جدید ✨)
|
||||
|
||||
#### 3️⃣ CMS (Core Business)
|
||||
- **InitiateBasePackagePayment**: ثبت Transaction + Order با Pending
|
||||
- **VerifyBasePackagePayment**: شارژ کیف پول یا Reject بر اساس نتیجه
|
||||
- **AcceptClubMembershipContract**: ثبت UserContract و فعالسازی ClubMembership (جدید ✨)
|
||||
|
||||
#### 4️⃣ PYMS (Payment Gateway Service)
|
||||
- **PaymentRequest**: دریافت URL درگاه زرینپال
|
||||
- **PaymentVerification**: تأیید پرداخت از بانک
|
||||
|
||||
---
|
||||
|
||||
## 💻 Implementation Details
|
||||
|
||||
### CMS Layer
|
||||
|
||||
#### Commands:
|
||||
1. **InitiateBasePackagePaymentCommand**
|
||||
```csharp
|
||||
// Input
|
||||
public record InitiateBasePackagePaymentCommand
|
||||
{
|
||||
public long UserId { get; init; }
|
||||
}
|
||||
|
||||
// Output
|
||||
public class InitiateBasePackagePaymentResponseDto
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; }
|
||||
public long OrderId { get; set; }
|
||||
public long TransactionId { get; set; }
|
||||
public long Amount { get; set; } // 56,000,000
|
||||
}
|
||||
```
|
||||
|
||||
**Handler Logic:**
|
||||
- بررسی عدم خرید قبلی: `user.PackagePurchaseMethod == None`
|
||||
- بررسی عدم Order Pending قبلی
|
||||
- ایجاد Transaction با PaymentStatus.Pending
|
||||
- ایجاد UserOrder با PackageId=4, PaymentStatus.Pending
|
||||
- Return OrderId + TransactionId
|
||||
|
||||
2. **VerifyBasePackagePaymentCommand**
|
||||
```csharp
|
||||
// Input
|
||||
public record VerifyBasePackagePaymentCommand
|
||||
{
|
||||
public long OrderId { get; init; }
|
||||
public long TransactionId { get; init; }
|
||||
public bool PaymentSuccess { get; init; } // از BFF میآید
|
||||
public string? RefId { get; init; }
|
||||
public string? Message { get; init; }
|
||||
}
|
||||
|
||||
// Output
|
||||
public class VerifyBasePackagePaymentResponseDto
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; }
|
||||
public long OrderId { get; set; }
|
||||
public long TransactionId { get; set; }
|
||||
public string? ReferenceCode { get; set; }
|
||||
public long WalletBalance { get; set; }
|
||||
public long DiscountBalance { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
**Handler Logic (Success):**
|
||||
- شارژ `wallet.Balance += 56,000,000`
|
||||
- شارژ `wallet.DiscountBalance += 56,000,000`
|
||||
- ثبت Transaction با PaymentStatus.Success
|
||||
- ثبت UserWalletChangeLog (Balance + Discount)
|
||||
- Update Order: PaymentStatus.Success, PaymentMethod.IPG
|
||||
- Update User: PackagePurchaseMethod.DirectPurchase
|
||||
|
||||
**Handler Logic (Failed):**
|
||||
- Update Transaction: PaymentStatus.Reject
|
||||
- Update Order: PaymentStatus.Reject
|
||||
|
||||
#### Proto Definition:
|
||||
```protobuf
|
||||
// package.proto
|
||||
service PackageContract {
|
||||
rpc InitiateBasePackagePayment(InitiateBasePackagePaymentRequest)
|
||||
returns (InitiateBasePackagePaymentResponse);
|
||||
|
||||
rpc VerifyBasePackagePayment(VerifyBasePackagePaymentRequest)
|
||||
returns (VerifyBasePackagePaymentResponse);
|
||||
}
|
||||
|
||||
message InitiateBasePackagePaymentRequest {
|
||||
int64 user_id = 1;
|
||||
}
|
||||
|
||||
message InitiateBasePackagePaymentResponse {
|
||||
bool success = 1;
|
||||
string message = 2;
|
||||
int64 order_id = 3;
|
||||
int64 transaction_id = 4;
|
||||
int64 amount = 5;
|
||||
}
|
||||
|
||||
message VerifyBasePackagePaymentRequest {
|
||||
int64 order_id = 1;
|
||||
int64 transaction_id = 2;
|
||||
bool payment_success = 3;
|
||||
google.protobuf.StringValue ref_id = 4;
|
||||
google.protobuf.StringValue message = 5;
|
||||
}
|
||||
|
||||
message VerifyBasePackagePaymentResponse {
|
||||
bool success = 1;
|
||||
string message = 2;
|
||||
int64 order_id = 3;
|
||||
int64 transaction_id = 4;
|
||||
google.protobuf.StringValue reference_code = 5;
|
||||
int64 wallet_balance = 6;
|
||||
int64 discount_balance = 7;
|
||||
}
|
||||
```
|
||||
|
||||
#### Files Created/Modified:
|
||||
```
|
||||
CMS/src/CMSMicroservice.Application/PackageCQ/Commands/
|
||||
├── InitiateBasePackagePayment/
|
||||
│ ├── InitiateBasePackagePaymentCommand.cs
|
||||
│ ├── InitiateBasePackagePaymentCommandValidator.cs
|
||||
│ └── InitiateBasePackagePaymentCommandHandler.cs
|
||||
└── VerifyBasePackagePayment/
|
||||
├── VerifyBasePackagePaymentCommand.cs
|
||||
├── VerifyBasePackagePaymentCommandValidator.cs
|
||||
└── VerifyBasePackagePaymentCommandHandler.cs
|
||||
|
||||
CMS/src/CMSMicroservice.Protobuf/Protos/
|
||||
└── package.proto (updated)
|
||||
|
||||
CMS/src/CMSMicroservice.WebApi/
|
||||
├── Services/PackageService.cs (updated)
|
||||
└── Common/Mappings/PackageProfile.cs (updated)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### BFF Layer
|
||||
|
||||
#### Commands:
|
||||
1. **InitiateBasePackagePaymentCommand**
|
||||
```csharp
|
||||
// Input (UserId از CurrentUserService گرفته میشود)
|
||||
public record InitiateBasePackagePaymentCommand
|
||||
{
|
||||
public string CallbackUrl { get; init; }
|
||||
}
|
||||
|
||||
// Output
|
||||
public class InitiateBasePackagePaymentResponseDto
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; }
|
||||
public long OrderId { get; set; }
|
||||
public long TransactionId { get; set; }
|
||||
public long Amount { get; set; }
|
||||
public string PaymentGatewayUrl { get; set; }
|
||||
public string Authority { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
**Handler Logic:**
|
||||
```csharp
|
||||
// 1. فراخوانی CMS
|
||||
var cmsResponse = await _context.Package.InitiateBasePackagePaymentAsync(
|
||||
new InitiateBasePackagePaymentRequest {
|
||||
UserId = _currentUserService.UserId.Value
|
||||
});
|
||||
|
||||
// 2. فراخوانی PYMS
|
||||
var paymentResponse = await _context.ZarinTransactions.PaymentRequestAsync(
|
||||
new PaymentRequestRequest {
|
||||
MerchantId = "...",
|
||||
Amount = cmsResponse.Amount * 10, // تبدیل به ریال
|
||||
CallbackUrl = $"{request.CallbackUrl}?orderId={...}&transactionId={...}",
|
||||
Description = "پرداخت پکیج پایه",
|
||||
Currency = CurrencyEnum.Irr,
|
||||
Type = TransactionTypeEnum.Real
|
||||
});
|
||||
|
||||
// 3. Return URL + Authority
|
||||
return new InitiateBasePackagePaymentResponseDto {
|
||||
PaymentGatewayUrl = paymentResponse.PaymentGWUrl,
|
||||
Authority = ExtractAuthorityFromUrl(paymentResponse.PaymentGWUrl),
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
2. **VerifyBasePackagePaymentCommand**
|
||||
```csharp
|
||||
// Input
|
||||
public record VerifyBasePackagePaymentCommand
|
||||
{
|
||||
public long OrderId { get; init; }
|
||||
public long TransactionId { get; init; }
|
||||
public string Authority { get; init; }
|
||||
public string Status { get; init; } // OK یا NOK
|
||||
}
|
||||
```
|
||||
|
||||
**Handler Logic:**
|
||||
```csharp
|
||||
// 1. بررسی Status
|
||||
if (request.Status != "OK") {
|
||||
await NotifyCmsPaymentFailed(...);
|
||||
return Failed;
|
||||
}
|
||||
|
||||
// 2. Verify از PYMS
|
||||
var verifyResponse = await _context.ZarinTransactions
|
||||
.PaymentVerificationAsync(...);
|
||||
|
||||
// 3. فراخوانی CMS
|
||||
if (verifyResponse.PaymentStatus) {
|
||||
var cmsResponse = await _context.Package.VerifyBasePackagePaymentAsync(
|
||||
new VerifyBasePackagePaymentRequest {
|
||||
OrderId = request.OrderId,
|
||||
TransactionId = request.TransactionId,
|
||||
PaymentSuccess = true,
|
||||
RefId = verifyResponse.RefId,
|
||||
Message = verifyResponse.Message
|
||||
});
|
||||
return Success;
|
||||
} else {
|
||||
await NotifyCmsPaymentFailed(...);
|
||||
return Failed;
|
||||
}
|
||||
```
|
||||
|
||||
#### Proto Definition:
|
||||
```protobuf
|
||||
// package.proto
|
||||
service PackageContract {
|
||||
rpc InitiateBasePackagePayment(InitiateBasePackagePaymentRequest)
|
||||
returns (InitiateBasePackagePaymentResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/InitiateBasePackagePayment"
|
||||
body: "*"
|
||||
};
|
||||
};
|
||||
|
||||
rpc VerifyBasePackagePayment(VerifyBasePackagePaymentRequest)
|
||||
returns (VerifyBasePackagePaymentResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/VerifyBasePackagePayment"
|
||||
body: "*"
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
message InitiateBasePackagePaymentRequest {
|
||||
string callback_url = 1;
|
||||
// UserId از JWT token گرفته میشود
|
||||
}
|
||||
|
||||
message InitiateBasePackagePaymentResponse {
|
||||
bool success = 1;
|
||||
string message = 2;
|
||||
int64 order_id = 3;
|
||||
int64 transaction_id = 4;
|
||||
int64 amount = 5;
|
||||
string payment_gateway_url = 6;
|
||||
string authority = 7;
|
||||
}
|
||||
|
||||
message VerifyBasePackagePaymentRequest {
|
||||
int64 order_id = 1;
|
||||
int64 transaction_id = 2;
|
||||
string authority = 3;
|
||||
string status = 4;
|
||||
}
|
||||
|
||||
message VerifyBasePackagePaymentResponse {
|
||||
bool success = 1;
|
||||
string message = 2;
|
||||
int64 order_id = 3;
|
||||
int64 transaction_id = 4;
|
||||
google.protobuf.StringValue ref_id = 5;
|
||||
int64 wallet_balance = 6;
|
||||
int64 discount_balance = 7;
|
||||
}
|
||||
```
|
||||
|
||||
#### Files Created/Modified:
|
||||
```
|
||||
FrontOffice.BFF/src/FrontOffice.BFF.Application/PackageCQ/Commands/
|
||||
├── InitiateBasePackagePayment/
|
||||
│ ├── InitiateBasePackagePaymentCommand.cs
|
||||
│ ├── InitiateBasePackagePaymentCommandValidator.cs
|
||||
│ └── InitiateBasePackagePaymentCommandHandler.cs
|
||||
└── VerifyBasePackagePayment/
|
||||
├── VerifyBasePackagePaymentCommand.cs
|
||||
├── VerifyBasePackagePaymentCommandValidator.cs
|
||||
└── VerifyBasePackagePaymentCommandHandler.cs
|
||||
|
||||
FrontOffice.BFF/src/Protobufs/FrontOffice.BFF.Package.Protobuf/Protos/
|
||||
└── package.proto (updated)
|
||||
|
||||
FrontOffice.BFF/src/FrontOffice.BFF.WebApi/
|
||||
├── Services/PackageService.cs (updated)
|
||||
└── Common/Mappings/PackageProfile.cs (updated)
|
||||
|
||||
FrontOffice.BFF/src/FrontOffice.BFF.Domain/
|
||||
└── FrontOffice.BFF.Domain.csproj (updated - added CMS Proto reference)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Frontend Layer
|
||||
|
||||
#### Pages:
|
||||
1. **Profile/Index.razor.cs**
|
||||
- نمایش دکمه "خرید پکیج پایه"
|
||||
- Bottom Sheet با دو گزینه: پرداخت مستقیم / اعتبار الماسی
|
||||
- فراخوانی BFF.InitiateBasePackagePayment
|
||||
|
||||
```csharp
|
||||
private async Task DirectPayment()
|
||||
{
|
||||
var callbackUrl = $"{Navigation.BaseUri}profile/payment-callback";
|
||||
|
||||
var response = await PackageContract.InitiateBasePackagePaymentAsync(
|
||||
new InitiateBasePackagePaymentRequest {
|
||||
CallbackUrl = callbackUrl
|
||||
});
|
||||
|
||||
if (response.Success) {
|
||||
Navigation.NavigateTo(response.PaymentGatewayUrl, forceLoad: true);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **Profile/PaymentCallback.razor**
|
||||
- دریافت Query Parameters: orderId, transactionId, Authority, Status
|
||||
- فراخوانی BFF.VerifyBasePackagePayment
|
||||
- نمایش نتیجه (موفق/ناموفق)
|
||||
|
||||
```csharp
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender) {
|
||||
var response = await PackageContract.VerifyBasePackagePaymentAsync(
|
||||
new VerifyBasePackagePaymentRequest {
|
||||
OrderId = OrderId,
|
||||
TransactionId = TransactionId,
|
||||
Authority = Authority,
|
||||
Status = Status
|
||||
});
|
||||
|
||||
// نمایش نتیجه
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Files Created/Modified:
|
||||
```
|
||||
FrontOffice/src/FrontOffice.Main/Pages/Profile/
|
||||
├── Index.razor.cs (updated)
|
||||
└── PaymentCallback.razor (new)
|
||||
|
||||
FrontOffice/src/FrontOffice.Main/Utilities/
|
||||
├── UserAuthInfo.cs (updated - added UserId)
|
||||
└── AuthService.cs (updated - extract UserId from JWT)
|
||||
|
||||
FrontOffice/src/FrontOffice.Main/
|
||||
└── FrontOffice.Main.csproj (updated - added BFF Package Proto reference)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔌 API Endpoints
|
||||
|
||||
### BFF Endpoints (gRPC-Web + HTTP):
|
||||
|
||||
```
|
||||
POST /InitiateBasePackagePayment
|
||||
Body: {
|
||||
"callback_url": "https://example.com/profile/payment-callback"
|
||||
}
|
||||
|
||||
Response: {
|
||||
"success": true,
|
||||
"message": "...",
|
||||
"order_id": 123,
|
||||
"transaction_id": 456,
|
||||
"amount": 56000000,
|
||||
"payment_gateway_url": "https://www.zarinpal.com/pg/StartPay/...",
|
||||
"authority": "A00000000000000000000000000123456"
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
POST /VerifyBasePackagePayment
|
||||
Body: {
|
||||
"order_id": 123,
|
||||
"transaction_id": 456,
|
||||
"authority": "A00000000000000000000000000123456",
|
||||
"status": "OK"
|
||||
}
|
||||
|
||||
Response: {
|
||||
"success": true,
|
||||
"message": "پرداخت با موفقیت تایید شد",
|
||||
"order_id": 123,
|
||||
"transaction_id": 456,
|
||||
"ref_id": "789",
|
||||
"wallet_balance": 56000000,
|
||||
"discount_balance": 56000000
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Flow Diagram
|
||||
|
||||
### Complete Payment Flow:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User as کاربر
|
||||
participant FE as Frontend
|
||||
participant BFF as BFF
|
||||
participant CMS as CMS
|
||||
participant PYMS as PYMS
|
||||
participant Bank as درگاه بانک
|
||||
|
||||
User->>FE: کلیک "پرداخت مستقیم"
|
||||
FE->>BFF: InitiateBasePackagePayment(CallbackUrl)
|
||||
BFF->>BFF: استخراج UserId از JWT
|
||||
BFF->>CMS: InitiateBasePackagePayment(UserId)
|
||||
CMS->>CMS: ثبت Transaction (Pending)
|
||||
CMS->>CMS: ثبت Order (Pending)
|
||||
CMS-->>BFF: OrderId, TransactionId, Amount
|
||||
|
||||
BFF->>PYMS: PaymentRequest(Amount, Callback)
|
||||
PYMS-->>BFF: PaymentGWUrl, Authority
|
||||
BFF-->>FE: PaymentGWUrl, OrderId, TransactionId
|
||||
|
||||
FE->>Bank: Redirect to PaymentGWUrl
|
||||
User->>Bank: پرداخت
|
||||
Bank-->>FE: Redirect to Callback?Authority=...&Status=OK
|
||||
|
||||
FE->>BFF: VerifyBasePackagePayment(OrderId, TransactionId, Authority, Status)
|
||||
BFF->>PYMS: PaymentVerification(Authority)
|
||||
PYMS-->>BFF: PaymentStatus, RefId
|
||||
|
||||
alt پرداخت موفق
|
||||
BFF->>CMS: VerifyBasePackagePayment(PaymentSuccess=true, RefId)
|
||||
CMS->>CMS: شارژ Balance (56M)
|
||||
CMS->>CMS: شارژ DiscountBalance (56M)
|
||||
CMS->>CMS: ثبت Transaction (Success)
|
||||
CMS->>CMS: ثبت WalletChangeLog
|
||||
CMS->>CMS: Update Order (Success)
|
||||
CMS->>CMS: Update User.PackagePurchaseMethod
|
||||
CMS-->>BFF: Success, WalletBalance, DiscountBalance
|
||||
BFF-->>FE: Success
|
||||
FE-->>User: نمایش پیام موفقیت + موجودی
|
||||
else پرداخت ناموفق
|
||||
BFF->>CMS: VerifyBasePackagePayment(PaymentSuccess=false)
|
||||
CMS->>CMS: Update Transaction (Reject)
|
||||
CMS->>CMS: Update Order (Reject)
|
||||
CMS-->>BFF: Failed
|
||||
BFF-->>FE: Failed
|
||||
FE-->>User: نمایش پیام خطا
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ نکات مهم
|
||||
|
||||
### Security:
|
||||
1. **UserId از JWT گرفته میشود** نه از Request - امنیت بالاتر
|
||||
2. **Validation در هر لایه** انجام میشود
|
||||
3. **Transaction Idempotency** - چک میشود که Order Pending قبلی وجود نداشته باشد
|
||||
|
||||
### Business Logic:
|
||||
1. کاربر **فقط یک بار** میتواند پکیج پایه بخرد
|
||||
2. **شارژ همزمان** Balance و DiscountBalance انجام میشود
|
||||
3. **PackagePurchaseMethod** بعد از پرداخت موفق به `DirectPurchase` تغییر میکند
|
||||
4. برای فعالسازی لینک دعوت، باید **هم پکیج خریداری شود هم باشگاه فعال شود**
|
||||
|
||||
### Error Handling:
|
||||
1. اگر CMS خطا برگرداند، به درگاه نمیرویم
|
||||
2. اگر PYMS URL ندهد، Transaction در CMS باقی میماند (Pending)
|
||||
3. اگر Callback با Status=NOK بیاید، مستقیماً Reject میشود
|
||||
4. اگر Verification ناموفق باشد، Transaction و Order به Reject تغییر میکند
|
||||
|
||||
### Project References:
|
||||
برای development، از Project Reference استفاده میشود:
|
||||
- BFF → CMS.Protobuf (Project Reference)
|
||||
- Frontend → BFF.Package.Protobuf (Project Reference)
|
||||
|
||||
برای production، باید به NuGet Package تبدیل شوند.
|
||||
|
||||
---
|
||||
|
||||
## ✅ Checklist پیادهسازی
|
||||
|
||||
### CMS:
|
||||
- [x] InitiateBasePackagePaymentCommand
|
||||
- [x] InitiateBasePackagePaymentCommandValidator
|
||||
- [x] InitiateBasePackagePaymentCommandHandler
|
||||
- [x] VerifyBasePackagePaymentCommand
|
||||
- [x] VerifyBasePackagePaymentCommandValidator
|
||||
- [x] VerifyBasePackagePaymentCommandHandler
|
||||
- [x] Proto messages و RPCs
|
||||
- [x] PackageService implementation
|
||||
- [x] Mapster mappings
|
||||
|
||||
### BFF:
|
||||
- [x] InitiateBasePackagePaymentCommand
|
||||
- [x] InitiateBasePackagePaymentCommandValidator
|
||||
- [x] InitiateBasePackagePaymentCommandHandler
|
||||
- [x] VerifyBasePackagePaymentCommand
|
||||
- [x] VerifyBasePackagePaymentCommandValidator
|
||||
- [x] VerifyBasePackagePaymentCommandHandler
|
||||
- [x] Proto messages و RPCs
|
||||
- [x] PackageService implementation
|
||||
- [x] Mapster mappings
|
||||
- [x] CurrentUserService integration
|
||||
|
||||
### Frontend:
|
||||
- [x] Bottom Sheet UI برای انتخاب روش پرداخت
|
||||
- [x] DirectPayment method
|
||||
- [x] PaymentCallback page
|
||||
- [x] UserAuthInfo.UserId
|
||||
- [x] AuthService extract UserId
|
||||
- [x] Navigation to payment gateway
|
||||
- [x] Display payment result
|
||||
|
||||
### Testing:
|
||||
- [ ] Test پرداخت موفق
|
||||
- [ ] Test پرداخت ناموفق
|
||||
- [ ] Test لغو پرداخت توسط کاربر
|
||||
- [ ] Test خرید مجدد (باید خطا دهد)
|
||||
- [ ] Test شارژ کیف پول
|
||||
- [ ] Test فعالسازی لینک دعوت
|
||||
|
||||
---
|
||||
|
||||
**تاریخ آخرین بهروزرسانی:** 2024-12-16
|
||||
**نگارنده:** Development Team
|
||||
Reference in New Issue
Block a user