docs: بروزرسانی کامل مستندات — باگ‌ها، فیکس‌ها، دیپلوی Production، CI/CD cross-deploy

- payment-gateway.md: سکشن ۸-۱۳ (ZarinPal callback, تخفیف ۱۰۰٪, VAT, ExpirePendingOrders, DeliveryStatus mapping, Production deploy)
- CICD-PIPELINE-GUIDE.md: باگ cross-deploy, قالب workflow Production, جدول مقایسه دو محیط
- INFRASTRUCTURE-GUIDE.md: سرور Production (45.149.79.127), DB KBS, Proto v0.0.179
- DISCOUNT-STORE-STATUS.md: وضعیت Production Deploy, فلوی پرداخت جدید
- SERVER-MIRRORS-CONFIG.md: registries.yaml سرور Production
- INDEX.md: تاریخ, توضیحات بروز, لینک‌های سریع جدید
This commit is contained in:
masoodafar-web
2026-02-17 01:44:05 +03:30
parent ad31c8be97
commit 4ef4bfbeef
6 changed files with 632 additions and 32 deletions
+286 -3
View File
@@ -772,6 +772,289 @@ catch (Exception ex)
---
**Last Updated**: 2024-12-02
**Version**: 1.0
**Status**: ✅ Production Ready
---
## 🆕 فاز ۲ — ZarinPal + PaymentTransaction (بهمن ۱۴۰۴)
### ۴. ZarinPalPaymentService (فعال)
**Purpose**: درگاه پرداخت مستقیم زرین‌پال — بدون PYMS واسط
**Configuration**:
```json
{
"UseRealPaymentGateway": true,
"PaymentProvider": "zarinpal",
"ZarinPal": {
"MerchantId": "6b098fc8-f490-47a1-aac3-1de1a1b84404",
"UseSandbox": true
}
}
```
**API Endpoints**:
#### InitiatePayment (درخواست پرداخت)
```
POST https://sandbox.zarinpal.com/pg/v4/payment/request.json
{
"merchant_id": "...",
"amount": 100000,
"description": "خرید پکیج طلایی",
"callback_url": "https://cms.se.kbs1.ir/api/payment/callback",
"metadata": { "mobile": "09123456789" }
}
Response:
{
"data": {
"authority": "A00000000000000000000000000123456",
"code": 100
}
}
```
#### VerifyPayment (تأیید پرداخت)
```
POST https://sandbox.zarinpal.com/pg/v4/payment/verify.json
{
"merchant_id": "...",
"authority": "A00000000000000000000000000123456",
"amount": 100000
}
Response:
{
"data": {
"code": 100,
"ref_id": 123456789,
"card_pan": "6037****1234",
"card_hash": "...",
"fee_type": "Merchant",
"fee": 0
}
}
```
**Sandbox URL**: `https://sandbox.zarinpal.com/pg/StartPay/{Authority}`
**Production URL**: `https://zarinpal.com/pg/StartPay/{Authority}`
**PaymentVerificationResult (بروز‌شده)**:
```csharp
public class PaymentVerificationResult
{
public bool IsSuccess { get; set; }
public string RefId { get; set; }
public string? TrackingCode { get; set; }
public decimal Amount { get; set; }
public string? Message { get; set; }
public string? CardPan { get; set; } // 🆕 شماره کارت ماسک‌شده
public string? CardHash { get; set; } // 🆕 هش کارت
public int? VerificationCode { get; set; } // 🆕 کد تأیید زرین‌پال
}
```
**Service Registration (بروز‌شده)**:
```csharp
var paymentProvider = configuration.GetValue<string>("PaymentProvider", "zarinpal");
if (paymentProvider?.ToLower() == "zarinpal")
{
services.AddHttpClient<IPaymentGatewayService, ZarinPalPaymentService>();
}
```
---
### ۵. جدول PaymentTransaction (جداگانه از Transaction)
**Purpose**: ذخیره جزئیات سطح درگاه — جدا از Transaction entity اصلی
**Entity**: `Domain/Entities/Payment/PaymentTransaction.cs`
```csharp
public class PaymentTransaction : BaseEntity
{
public string GatewayProvider { get; set; } // "zarinpal"
public string MerchantId { get; set; }
public long Amount { get; set; }
public string? CallbackUrl { get; set; }
public string? Description { get; set; }
public string? Mobile { get; set; }
public long? UserId { get; set; }
// Request
public int? RequestStatusCode { get; set; } // 100 = success
public string? RequestStatusMessage { get; set; }
public string? Authority { get; set; } // ZarinPal authority
// Verification
public bool PaymentStatus { get; set; }
public int? VerificationStatusCode { get; set; }
public string? VerificationStatusMessage { get; set; }
public string? CardHash { get; set; }
public string? CardPan { get; set; } // ماسک‌شده: 6037****1234
public long? RefId { get; set; }
// Relations
public long? TransactionId { get; set; }
public long? OrderId { get; set; }
}
```
**Indexes**: Authority, GatewayProvider, UserId, TransactionId, RefId
**Migration**: `AddPaymentTransactionTable`
**جریان کامل پرداخت**:
```
1. PlaceOrderCommandHandler → InitiatePayment → PaymentTransaction ایجاد (PaymentStatus=false)
2. کاربر → ریدایرکت به ZarinPal
3. ZarinPal → Callback به /api/payment/callback
4. PaymentCallbackController → VerifyPayment → PaymentTransaction بروز (PaymentStatus=true, CardPan, RefId)
5. CompleteOrderPaymentCommandHandler → Transaction + Order + Wallet بروز
```
**مصرف‌کننده‌ها**:
| سرویس | عملیات |
|--------|--------|
| `PlaceOrderCommandHandler` | ایجاد PaymentTransaction بعد از InitiatePayment |
| `PaymentCallbackController` | بروزرسانی بعد از VerifyPayment |
| `TransactionsService` | ایجاد/بروزرسانی در CustomerPaymentRequest/Verification |
| `PackageService` | ایجاد/بروزرسانی در CustomerPurchasePackage/Verify |
---
### ۶. فیکس نمایش وضعیت پرداخت سفارشات تخفیفی
**مشکل**: `DiscountOrderService.GetOrderById/GetUserOrders` از `Mapster.Adapt<>()` استفاده می‌کرد. نام‌ها متفاوت بودند:
- Domain: `PaymentStatus` (enum: Success=0, Reject=1, Pending=2)
- Proto: `payment_completed` (bool)
Mapster نمی‌تونست enum رو به bool مپ کنه → همیشه `false` (در انتظار پرداخت).
**رفع**: جایگزینی Mapster با مپینگ دستی:
```csharp
PaymentCompleted = result.PaymentStatus == DomainEnums.PaymentStatus.Success
```
### ۷. فیکس DeliveryStatus بعد از پرداخت
**مشکل**: فروشگاه تخفیفی بعد از پرداخت موفق، `DeliveryStatus = InTransit` (ارسال شده) ست می‌کرد. ولی فروشگاه عادی `Pending` نگه می‌داشت.
**رفع**: هر دو handler (`CompleteOrderPaymentCommandHandler` و `PlaceOrderCommandHandler`) به `DeliveryStatus.Pending` تغییر کردند — ادمین باید وضعیت پستی رو مشخص کنه.
---
### ۸. فیکس ZarinPal Callback URL (اسفند ۱۴۰۴)
**مشکل**: `PurchasePackageCommandHandler` از `yourdomain.com` به صورت hardcode استفاده می‌کرد.
**رفع**: خواندن از `IConfiguration`:
```csharp
var cmsBaseUrl = _configuration["CmsBaseUrl"];
var frontOfficeBaseUrl = _configuration["FrontOfficeBaseUrl"];
```
**appsettings.json (Production)**:
```json
{
"CmsBaseUrl": "https://cms.kbs1.ir",
"FrontOfficeBaseUrl": "https://foursat.kbs1.ir"
}
```
**appsettings.json (Staging)**:
```json
{
"CmsBaseUrl": "https://cms.se.kbs1.ir",
"FrontOfficeBaseUrl": "https://foursat.se.kbs1.ir"
}
```
---
### ۹. اجبار تخفیف ۱۰۰٪ (اسفند ۱۴۰۴)
**تغییر بیزینسی**: کاربر دیگه نمی‌تونه درصد تخفیف رو انتخاب کنه — **همیشه حداکثر تخفیف** (MaxDiscountPercent) اعمال می‌شه.
**تغییرات CMS (بکند):**
- `PlaceOrderCommandHandler`: همیشه `MaxDiscountPercent` محصول استفاده می‌شه
- فیلد `requested_discount_percent` از request نادیده گرفته می‌شه
**تغییرات FrontOffice:**
- حذف `MudSlider` و `MudNumericField` از `Checkout.razor`
- حذف کامل بخش نمایش موجودی تخفیفی
- Badge محصولات: نمایش درصد واقعی (مثلاً "۳۰٪ تخفیف") بجای "۱۰۰٪ تخفیفی"
---
### ۱۰. نمایش مالیات (VAT) در Checkout (اسفند ۱۴۰۴)
جدول خلاصه مالی کامل اضافه شد:
| فیلد | توضیح |
|------|-------|
| جمع کل | قبل از تخفیف |
| تخفیف | مجموع DiscountAmount |
| مبلغ پس از تخفیف | بعد از کسر تخفیف |
| مالیات ۹٪ | `VatCalculator` روی مبلغ درگاه |
| **مبلغ قابل پرداخت** | مبلغ درگاه + مالیات |
---
### ۱۱. سرویس Expire سفارشات معلق (اسفند ۱۴۰۴)
**فایل**: `ExpirePendingOrdersService.cs``BackgroundService`
| تنظیم | مقدار |
|-------|-------|
| بررسی | هر ۵ دقیقه |
| انقضا | بعد از ۳۰ دقیقه `PaymentStatus=Pending` |
| عملیات | `PaymentStatus=Reject`, `DeliveryStatus=Cancelled`, آزادسازی رزرو انبار |
| ساعت | `DateTime.Now` (نه UtcNow — DB از ساعت محلی استفاده می‌کنه) |
---
### ۱۲. فیکس DeliveryStatus مپینگ (اسفند ۱۴۰۴)
**مشکل**: Domain `DeliveryStatus.Pending(1)` مستقیم cast به Proto `PROCESSING(1)` می‌شد → سفارشات failed نشون می‌دادن "در حال پردازش".
**راه‌حل**: `MapDeliveryStatus()` و `MapPaymentStatus()` اضافه شدن:
| Domain | Proto |
|--------|-------|
| `PaymentStatus.Success(0)` | `COMPLETED(1)` |
| `PaymentStatus.Reject(1)` | `FAILED(2)` |
| `PaymentStatus.Pending(2)` | `PENDING(0)` |
| `DeliveryStatus.None(0)` | `PENDING(0)` |
| `DeliveryStatus.Pending(1)` | `PROCESSING(1)` |
| `DeliveryStatus.InTransit(2)` | `SHIPPED(2)` |
| `DeliveryStatus.Delivered(3)` | `DELIVERED(3)` |
| `DeliveryStatus.Returned/Cancelled(4,5)` | `CANCELLED(4)` |
+ وقتی پرداخت ناموفقه: `order.DeliveryStatus = DeliveryStatus.Cancelled`
---
### ۱۳. استقرار Production (اسفند ۱۴۰۴)
**Merge از `kub-stage` به `production`** — هر ۳ ریپو:
- CMS: ۳ conflict حل شد (workflow, Dockerfile, appsettings)
- FrontOffice: ۱ conflict (workflow)
- BackOffice: ۲ conflict (workflow, Dockerfile)
**Migration دیتابیس Production**: ۷ migration اعمال شد:
1. `AddDiscountProductImages`
2. `AddInventorySystem`
3. `u19` + `u20`
4. `AddBlogAndContentEntities`
5. `RemoveImagePathMaxLength`
6. `AddPaymentTransactionTable`
**ZarinPal در Production**: `UseSandbox: true` → "درگاه فعال نمیباشد" (عمدی)
---
**Last Updated**: February 17, 2026
**Version**: 3.0
**Proto Version**: 0.0.179
**Status**: ✅ ZarinPal Active (Sandbox) + PaymentTransaction + ExpireOrders + Production Deployed