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:
+286
-3
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user