Files
docs/technical/TECH-05-API-INTEGRATION.md
T
masoodafar-web 3c729304db docs: fix all discrepancies based on comprehensive code audit
Corrections verified against actual CMS/BackOffice/FrontOffice source code:

- ClubActivationFee: 25,200,000 (not 25,000,000)
- Tree depth: no limit (15 is commission calculation depth only)
- IPG wallet charge: Balance=56M + Discount=56M
- DayaLoan wallet charge: Balance=56M + Discount=112M (2×)
- Discount: per-product MaxDiscountPercent (not fixed 30%)
- VAT: 10% (ShopVAT) vs 9% (discount store PlaceOrder)
- Kavenegar template: 'Afrino' only (not verify-foursat)
- SMS sender: 1000001110100
- DayaLoan job: every 20min (not 15min)
- Commission job: Sunday 00:05 (not Saturday)
- Network tree: on User entity (not separate NetworkNode table)
- UserWallets entity (not UserWalletBalances)
- OTP: 6 digits, 5 attempts, 2min TTL, 60s cooldown
- Removed non-existent constants (ClubJoiningPercentage, ClubActivationThreshold)
- Fixed Hangfire Chatika interval: every 5min
- Removed InventorySync from recurring jobs list
2026-02-18 22:58:40 +03:30

12 KiB
Raw Blame History

🔌 API، Protobuf و یکپارچه‌سازی خارجی

منابع ادغام‌شده: FRONTOFFICE-CMS-API-COMPATIBILITY.md, REMAINING-TASKS.md, chatika-integration.md, payment-gateway.md, club-feature-management-services.md
آخرین بروزرسانی: اسفند ۱۴۰۴


۱. معماری ارتباطات

┌──────────────────────────────────────────────────────────────────┐
│                    External Services                              │
│  ┌─────────┐ ┌──────────┐ ┌─────────┐ ┌─────────┐              │
│  │ ZarinPal│ │ Kavenegar│ │ DayaLoan│ │ Chatika │              │
│  │ (IPG)   │ │ (SMS)    │ │ (Loan)  │ │ (AI)    │              │
│  └────┬────┘ └────┬─────┘ └────┬────┘ └────┬────┘              │
│       │           │            │            │                    │
│  ┌────▼───────────▼────────────▼────────────▼────┐              │
│  │              CMS Microservice                  │              │
│  │    (gRPC Server + Hangfire + EF Core)          │              │
│  └────────────────┬───────────────────────────────┘              │
│                   │ gRPC (Protobuf v3)                           │
│       ┌───────────┼───────────┐                                  │
│  ┌────▼────┐ ┌────▼────┐                                        │
│  │BackOffice│ │FrontOffice│                                      │
│  │(Blazor  │ │(Blazor   │                                       │
│  │ WASM)   │ │ Server)  │                                       │
│  └─────────┘ └──────────┘                                       │
└──────────────────────────────────────────────────────────────────┘

۲. gRPC Proto Definitions

۲.۱ لیست کامل سرویس‌ها

// ===== product.proto =====
service ProductService {
    rpc GetProducts (GetProductsRequest) returns (GetProductsResponse);
    rpc GetProductById (GetProductByIdRequest) returns (ProductMessage);
    rpc CreateProduct (CreateProductRequest) returns (CreateProductResponse);
    rpc UpdateProduct (UpdateProductRequest) returns (UpdateProductResponse);
    rpc DeleteProduct (DeleteProductRequest) returns (Empty);
    rpc GetProductsPaged (GetProductsPagedRequest) returns (GetProductsPagedResponse);
}

// ===== order.proto =====
service OrderService {
    rpc CreateOrder (CreateOrderRequest) returns (CreateOrderResponse);
    rpc GetOrders (GetOrdersRequest) returns (GetOrdersResponse);
    rpc GetOrderById (GetOrderByIdRequest) returns (OrderMessage);
    rpc UpdateOrderStatus (UpdateOrderStatusRequest) returns (Empty);
}

// ===== user.proto =====
service UserService {
    rpc Register (RegisterRequest) returns (AuthResponse);
    rpc Login (LoginRequest) returns (AuthResponse);
    rpc GetProfile (GetProfileRequest) returns (UserProfileMessage);
    rpc UpdateProfile (UpdateProfileRequest) returns (Empty);
    rpc SendOtp (SendOtpRequest) returns (SendOtpResponse);
    rpc VerifyOtp (VerifyOtpRequest) returns (VerifyOtpResponse);
}

// ===== club.proto =====
service ClubService {
    rpc GetNetworkTree (GetNetworkTreeRequest) returns (NetworkTreeResponse);
    rpc GetBalance (GetBalanceRequest) returns (BalanceResponse);
    rpc ReadContract (ReadContractRequest) returns (ContractResponse);
    rpc RequestContractOtp (RequestOtpRequest) returns (OtpResponse);
    rpc VerifyContractOtp (VerifyOtpRequest) returns (VerifyOtpResponse);
    rpc AcceptContract (AcceptContractRequest) returns (AcceptContractResponse);
    rpc GetClubFeatures (GetFeaturesRequest) returns (FeaturesResponse);
}

// ===== payment.proto =====
service PaymentService {
    rpc CreatePayment (CreatePaymentRequest) returns (CreatePaymentResponse);
    rpc VerifyPayment (VerifyPaymentRequest) returns (VerifyPaymentResponse);
    rpc GetPaymentStatus (PaymentStatusRequest) returns (PaymentStatusResponse);
}

// ===== blog.proto =====
service BlogService {
    rpc GetPosts (GetPostsRequest) returns (GetPostsResponse);
    rpc GetPostBySlug (GetPostBySlugRequest) returns (BlogPostMessage);
    rpc CreatePost (CreatePostRequest) returns (CreatePostResponse);
    rpc UpdatePost (UpdatePostRequest) returns (Empty);
    rpc DeletePost (DeletePostRequest) returns (Empty);
}

// ===== inventory.proto =====
service InventoryService {
    rpc GetInventory (GetInventoryRequest) returns (InventoryMessage);
    rpc UpdateStock (UpdateStockRequest) returns (Empty);
    rpc GetAllInventories (GetAllRequest) returns (InventoryListResponse);
}

// ===== sitepage.proto =====
service SitePageService {
    rpc GetPage (GetPageRequest) returns (SitePageMessage);
    rpc SaveSettings (SaveSettingsRequest) returns (Empty);
    rpc GetAllPages (Empty) returns (PageListResponse);
}

// ===== file.proto =====
service FileService {
    rpc Upload (stream UploadRequest) returns (UploadResponse);
    rpc Download (DownloadRequest) returns (stream DownloadResponse);
    rpc Delete (DeleteFileRequest) returns (Empty);
}

// ===== category.proto =====
service CategoryService {
    rpc GetCategories (GetCategoriesRequest) returns (CategoryListResponse);
    rpc CreateCategory (CreateCategoryRequest) returns (CreateCategoryResponse);
    rpc UpdateCategory (UpdateCategoryRequest) returns (Empty);
}

// ===== config.proto =====
service SystemConfigService {
    rpc GetConfig (GetConfigRequest) returns (ConfigResponse);
    rpc UpdateConfig (UpdateConfigRequest) returns (Empty);
    rpc GetAllConfigs (Empty) returns (ConfigListResponse);
}

۲.۲ Shared Messages

// ===== common.proto =====
message PaginationState {
    int32 skip = 1;
    int32 take = 2;
}

message PaginatedResponse {
    int32 totalCount = 1;
    int32 pageSize = 2;
    int32 currentPage = 3;
}

message Empty {}

۳. External Service Integration

۳.۱ ZarinPal (پرداخت)

public class ZarinPalService : IPaymentGateway
{
    // Config
    private readonly string _merchantId;
    private readonly bool _isSandbox;
    
    // Endpoints
    const string PAYMENT_URL = "https://api.zarinpal.com/pg/v4/payment/request.json";
    const string VERIFY_URL = "https://api.zarinpal.com/pg/v4/payment/verify.json";
    const string SANDBOX_URL = "https://sandbox.zarinpal.com/pg/v4/payment/request.json";
    
    // Flow
    // 1. CreatePayment → Authority token
    // 2. Redirect → https://www.zarinpal.com/pg/StartPay/{Authority}
    // 3. Callback → VerifyPayment(Authority, Amount)
    // 4. Result → RefID (reference number)
}

۳.۲ Kavenegar (SMS)

public class KavenegarService : ISmsService
{
    // Templates — فقط یک تمپلیت در کد موجود است
    const string OTP_TEMPLATE = "Afrino";  // تنها تمپلیت استفاده‌شده
    // Sender: "1000001110100"
    
    // Rate Limiting
    // ۱ SMS per phone per 60 seconds
    // ۵ SMS per phone per hour
    // ۲۰ SMS per phone per day
    
    public async Task SendOtpAsync(string phone, string code)
    {
        await _api.VerifyLookup(phone, code, OTP_TEMPLATE);
    }
}

۳.۳ Daya Loan (وام)

public class DayaLoanService : ILoanService
{
    // Hangfire job — هر ۲۰ دقیقه (*/20 * * * *)
    // Polly retry: 3 attempts, exponential backoff (2s, 4s, 8s)
    // Mock mode for staging (auto-approve)
    
    public async Task<LoanResult> RequestLoanAsync(Guid userId, decimal amount)
    {
        if (_options.UseMock)
            return LoanResult.Approved(amount);
            
        var response = await _httpClient.PostAsync(
            $"{_baseUrl}/api/loans/request",
            new { UserId = userId, Amount = amount });
        
        return MapResponse(response);
    }
}

۳.۴ Chatika (AI)

public class ChatikaService : IAiChatService
{
    // Hangfire job — هر ۵ دقیقه
    // Polly retry: 3 attempts
    // Only for active club members
    
    public async Task<string> GetResponseAsync(string userMessage)
    {
        var response = await _httpClient.PostAsync(
            $"{_baseUrl}/api/chat",
            new { Message = userMessage });
        
        return response.Content.ReadAsStringAsync();
    }
}

۴. API Compatibility Layer

۴.۱ FrontOffice Service Pattern

// هر سرویس در FrontOffice یک wrapper بر gRPC client است
public class ProductService : IProductService
{
    private readonly ProductServiceClient _client;
    
    public ProductService(ProductServiceClient client)
    {
        _client = client;
    }
    
    public async Task<ProductListResult> GetProductsPagedAsync(
        int skip, int take, Guid? categoryId = null, string? search = null)
    {
        try
        {
            var request = new GetProductsPagedRequest {
                Pagination = new PaginationState { Skip = skip, Take = take },
                CategoryId = categoryId?.ToString() ?? "",
                SearchTerm = search ?? ""
            };
            
            var response = await _client.GetProductsPagedAsync(request);
            
            return new ProductListResult(
                response.Products.Select(MapToDto).ToList(),
                response.TotalCount);
        }
        catch (RpcException ex) when (ex.StatusCode == StatusCode.Unavailable)
        {
            // CMS is down — show cached data or error
            throw new ServiceUnavailableException("CMS service unavailable");
        }
    }
}

۴.۲ Error Handling

gRPC Status HTTP Equivalent Handling
OK 200 Return data
NotFound 404 Show "not found" message
InvalidArgument 400 Show validation errors
Unauthenticated 401 Redirect to login
PermissionDenied 403 Show "access denied"
Unavailable 503 Show "service down"
Internal 500 Show generic error

۵. Proto Package Distribution

CMS/src/Protos/*.proto
    │
    ▼
pack-protos.sh
    │
    ▼
Foursat.CMSMicroservice.Protobuf.nupkg (v1.0.x)
    │
    ▼
Push to BaGet (http://localhost:5555) or Nexus
    │
    ▼
BackOffice: <PackageReference Include="Foursat.CMSMicroservice.Protobuf" />
FrontOffice: <PackageReference Include="Foursat.CMSMicroservice.Protobuf" />

۶. Remaining Tasks / Integration Gaps

آیتم اولویت وضعیت
Product Bundle API Medium Proto + Handler needed
Manual Payment API Low Design only
SignalR for Chatika Low Replace polling
File upload streaming Done
Blog search Done
Inventory autocomplete Done
Lazy load pagination Done
Rate limiting (API level) Medium
API versioning Low