From 207f53ef80e9ddf83b2bb2b290cd6111a3b93b88 Mon Sep 17 00:00:00 2001 From: masoodafar-web Date: Sun, 15 Feb 2026 23:25:20 +0330 Subject: [PATCH] refactor: remove PYMS microservice, use ZarinPal directly in CMS - Remove PYMSPaymentService.cs and PYMS proto files - Remove 'pyms' case from DI ConfigureServices - Remove PYMS config from appsettings - Switch PaymentProvider to 'zarinpal' (direct integration) - ZarinPalPaymentService handles sandbox/production, verify, errors - PYMS deployment/service/ingress removed from K8s --- .../ConfigureServices.cs | 7 +- .../Services/Payment/PYMSPaymentService.cs | 284 ------------------ .../CMSMicroservice.Protobuf.csproj | 3 - .../Protos/pyms/pyms_public_messages.proto | 41 --- .../Protos/pyms/pyms_transaction.proto | 279 ----------------- .../appsettings.Development.json | 5 +- src/CMSMicroservice.WebApi/appsettings.json | 5 +- 7 files changed, 3 insertions(+), 621 deletions(-) delete mode 100644 src/CMSMicroservice.Infrastructure/Services/Payment/PYMSPaymentService.cs delete mode 100644 src/CMSMicroservice.Protobuf/Protos/pyms/pyms_public_messages.proto delete mode 100644 src/CMSMicroservice.Protobuf/Protos/pyms/pyms_transaction.proto diff --git a/src/CMSMicroservice.Infrastructure/ConfigureServices.cs b/src/CMSMicroservice.Infrastructure/ConfigureServices.cs index c6cb923..314e26c 100644 --- a/src/CMSMicroservice.Infrastructure/ConfigureServices.cs +++ b/src/CMSMicroservice.Infrastructure/ConfigureServices.cs @@ -75,7 +75,7 @@ public static class ConfigureServices } // Payment Gateway Service - Multi-Provider Architecture - // پشتیبانی از درگاه‌های مختلف: ZarinPal, Daya, PYMS, Mock + // پشتیبانی از درگاه‌های مختلف: ZarinPal, Daya, Mock var paymentProvider = configuration.GetValue("PaymentProvider", "Mock")?.ToLowerInvariant(); switch (paymentProvider) @@ -90,11 +90,6 @@ public static class ConfigureServices .SetHandlerLifetime(TimeSpan.FromMinutes(5)); break; - case "pyms": - // PYMS (Payment Microservice) — ارتباط gRPC با سرویس پرداخت مستقل - services.AddSingleton(); - break; - case "mock": default: services.AddScoped(); diff --git a/src/CMSMicroservice.Infrastructure/Services/Payment/PYMSPaymentService.cs b/src/CMSMicroservice.Infrastructure/Services/Payment/PYMSPaymentService.cs deleted file mode 100644 index 59dd20d..0000000 --- a/src/CMSMicroservice.Infrastructure/Services/Payment/PYMSPaymentService.cs +++ /dev/null @@ -1,284 +0,0 @@ -using CMSMicroservice.Application.Common.Interfaces; -using CMSMicroservice.Protobuf.Protos.PYMS; -using CMSMicroservice.Protobuf.Protos.PYMS.Transaction; -using Grpc.Net.Client; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using System.Net.Http; - -namespace CMSMicroservice.Infrastructure.Services.Payment; - -/// -/// پیاده‌سازی درگاه پرداخت از طریق PYMS (Payment Microservice) -/// CMS به جای اتصال مستقیم به ZarinPal، از PYMS استفاده می‌کند. -/// PYMS تراکنش‌ها را ذخیره و با ZarinPal ارتباط برقرار می‌کند. -/// -public class PYMSPaymentService : IPaymentGatewayService, IDisposable -{ - private readonly ILogger _logger; - private readonly GrpcChannel _channel; - private readonly TransactionContract.TransactionContractClient _client; - private readonly string _merchantId; - private readonly bool _useSandbox; - - public PYMSPaymentService( - IConfiguration configuration, - ILogger logger) - { - _logger = logger; - - var pymsAddress = configuration["PYMS:Address"] - ?? throw new InvalidOperationException("PYMS:Address is not configured."); - - _merchantId = configuration["ZarinPal:MerchantId"] - ?? throw new InvalidOperationException("ZarinPal:MerchantId is not configured."); - - _useSandbox = configuration.GetValue("ZarinPal:UseSandbox", true); - - // ایجاد کانال gRPC به PYMS - _channel = GrpcChannel.ForAddress(pymsAddress, new GrpcChannelOptions - { - HttpHandler = new SocketsHttpHandler - { - EnableMultipleHttp2Connections = true, - PooledConnectionIdleTimeout = TimeSpan.FromMinutes(5), - KeepAlivePingDelay = TimeSpan.FromSeconds(60), - KeepAlivePingTimeout = TimeSpan.FromSeconds(30), - } - }); - - _client = new TransactionContract.TransactionContractClient(_channel); - - _logger.LogInformation( - "PYMS Payment Service initialized. Address={Address}, Mode={Mode}", - pymsAddress, _useSandbox ? "🧪 Sandbox" : "🏦 Production"); - } - - /// - /// مرحله ۱: ارسال درخواست پرداخت به PYMS - /// PYMS تراکنش را ایجاد و URL درگاه را برمی‌گرداند - /// - public async Task InitiatePaymentAsync( - PaymentRequest request, - CancellationToken cancellationToken = default) - { - try - { - // CMS مبالغ را به تومان نگه‌داری می‌کند - // PYMS مبلغ را به ریال می‌خواهد — تبدیل تومان به ریال - var amountInRials = (long)(request.Amount * 10); - - var grpcRequest = new PaymentRequestRequest - { - MerchantId = _merchantId, - Amount = amountInRials, - CallbackUrl = request.CallbackUrl ?? string.Empty, - Description = request.Description ?? string.Empty, - OrderId = request.UserId.ToString(), - // نوع تراکنش: Sandbox برای تست، Real برای Production - Type = _useSandbox ? TransactionTypeEnum.Sandbox : TransactionTypeEnum.Real, - Currency = CurrencyEnum.Irt, // تومان - }; - - if (!string.IsNullOrWhiteSpace(request.Mobile)) - grpcRequest.Mobile = request.Mobile; - - _logger.LogInformation( - "PYMS payment request: Amount={AmountToman} Toman ({AmountRial} Rial), User={UserId}, Sandbox={Sandbox}", - request.Amount, amountInRials, request.UserId, _useSandbox); - - var response = await _client.PaymentRequestAsync(grpcRequest, cancellationToken: cancellationToken); - - if (!string.IsNullOrEmpty(response.PaymentGWUrl)) - { - _logger.LogInformation( - "PYMS payment initiated successfully: GatewayUrl={Url}", - response.PaymentGWUrl); - - // از URL درگاه، Authority را استخراج می‌کنیم (آخرین بخش URL) - var authority = ExtractAuthorityFromUrl(response.PaymentGWUrl); - - return new PaymentInitiateResult - { - IsSuccess = true, - RefId = authority, - GatewayUrl = response.PaymentGWUrl - }; - } - - _logger.LogError("PYMS payment request failed: Empty gateway URL returned"); - - return new PaymentInitiateResult - { - IsSuccess = false, - ErrorMessage = "خطا در دریافت آدرس درگاه از PYMS" - }; - } - catch (Grpc.Core.RpcException ex) - { - _logger.LogError(ex, "PYMS gRPC error in InitiatePayment: Status={Status}, Detail={Detail}", - ex.StatusCode, ex.Status.Detail); - - return new PaymentInitiateResult - { - IsSuccess = false, - ErrorMessage = $"خطا در ارتباط با سرویس پرداخت: {ex.Status.Detail}" - }; - } - catch (Exception ex) - { - _logger.LogError(ex, "PYMS InitiatePayment exception"); - return new PaymentInitiateResult - { - IsSuccess = false, - ErrorMessage = $"خطا در ارتباط با سرویس پرداخت: {ex.Message}" - }; - } - } - - /// - /// تأیید پرداخت بدون مبلغ — PYMS خودش مبلغ را از تراکنش ذخیره‌شده می‌خواند - /// - public async Task VerifyPaymentAsync( - string refId, - string verificationToken, - CancellationToken cancellationToken = default) - { - return await VerifyPaymentInternalAsync(refId, verificationToken, cancellationToken); - } - - /// - /// تأیید پرداخت با مبلغ — PYMS خودش verify را انجام می‌دهد - /// refId = Authority, verificationToken = Status (OK/NOK) - /// - public async Task VerifyPaymentAsync( - string refId, - string verificationToken, - decimal amountInToman, - CancellationToken cancellationToken = default) - { - return await VerifyPaymentInternalAsync(refId, verificationToken, cancellationToken); - } - - private async Task VerifyPaymentInternalAsync( - string refId, - string verificationToken, - CancellationToken cancellationToken) - { - try - { - // اگر کاربر لغو کرده - if (!string.Equals(verificationToken, "OK", StringComparison.OrdinalIgnoreCase)) - { - _logger.LogWarning("Payment cancelled by user: Authority={Authority}", refId); - return new PaymentVerificationResult - { - IsSuccess = false, - RefId = refId, - Message = "پرداخت توسط کاربر لغو شد" - }; - } - - var grpcRequest = new PaymentVerificationRequest - { - Authority = refId, - Status = verificationToken - }; - - _logger.LogInformation("PYMS verify request: Authority={Authority}, Status={Status}", - refId, verificationToken); - - var response = await _client.PaymentVerificationAsync(grpcRequest, cancellationToken: cancellationToken); - - if (response.PaymentStatus) - { - _logger.LogInformation( - "PYMS payment verified: Id={Id}, RefId={RefId}, OrderId={OrderId}, StatusCode={StatusCode}", - response.Id, response.RefId, response.OrderId, response.VerificationStatusCode); - - return new PaymentVerificationResult - { - IsSuccess = true, - RefId = refId, - TrackingCode = response.RefId, - Amount = 0, // مبلغ از DB خوانده می‌شود - Message = response.Message ?? "تراکنش موفق" - }; - } - - _logger.LogError( - "PYMS verify failed: Authority={Authority}, StatusCode={StatusCode}, Message={Message}", - refId, response.VerificationStatusCode, response.Message); - - return new PaymentVerificationResult - { - IsSuccess = false, - RefId = refId, - Message = response.Message ?? "تأیید پرداخت ناموفق" - }; - } - catch (Grpc.Core.RpcException ex) - { - _logger.LogError(ex, "PYMS gRPC error in VerifyPayment: Status={Status}, Detail={Detail}", - ex.StatusCode, ex.Status.Detail); - - return new PaymentVerificationResult - { - IsSuccess = false, - RefId = refId, - Message = $"خطا در تأیید تراکنش: {ex.Status.Detail}" - }; - } - catch (Exception ex) - { - _logger.LogError(ex, "PYMS VerifyPayment exception: Authority={Authority}", refId); - return new PaymentVerificationResult - { - IsSuccess = false, - RefId = refId, - Message = $"خطا در تأیید تراکنش: {ex.Message}" - }; - } - } - - /// - /// PYMS فعلاً قابلیت Payout ندارد - /// - public Task ProcessPayoutAsync( - PayoutRequest request, - CancellationToken cancellationToken = default) - { - _logger.LogWarning("PYMS does not support direct payout yet."); - return Task.FromResult(new PayoutResult - { - IsSuccess = false, - Message = "سرویس پرداخت (PYMS) فعلاً از قابلیت واریز مستقیم پشتیبانی نمی‌کند", - ProcessedAt = DateTime.UtcNow - }); - } - - /// - /// استخراج Authority از URL درگاه - /// مثال: https://sandbox.zarinpal.com/pg/StartPay/A00000000000000000000000000123456789 → A00000000000000000000000000123456789 - /// - private static string ExtractAuthorityFromUrl(string gatewayUrl) - { - if (string.IsNullOrEmpty(gatewayUrl)) - return string.Empty; - - // Authority معمولاً آخرین بخش URL است - var uri = new Uri(gatewayUrl); - var segments = uri.Segments; - if (segments.Length > 0) - { - return segments[^1].TrimEnd('/'); - } - - return gatewayUrl; - } - - public void Dispose() - { - _channel?.Dispose(); - } -} diff --git a/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj b/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj index 27ba442..351869b 100644 --- a/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj +++ b/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj @@ -73,9 +73,6 @@ - - - diff --git a/src/CMSMicroservice.Protobuf/Protos/pyms/pyms_public_messages.proto b/src/CMSMicroservice.Protobuf/Protos/pyms/pyms_public_messages.proto deleted file mode 100644 index 5fa68f3..0000000 --- a/src/CMSMicroservice.Protobuf/Protos/pyms/pyms_public_messages.proto +++ /dev/null @@ -1,41 +0,0 @@ -syntax = "proto3"; - -package pyms_messages; - -option csharp_namespace = "CMSMicroservice.Protobuf.Protos.PYMS"; - -service PYMSPublicMessageContract{} - -message PaginationState -{ - int32 page_number = 1; - int32 page_size = 2; -} - -message MetaData -{ - int64 current_page = 1; - int64 total_page = 2; - int64 page_size = 3; - int64 total_count = 4; - bool has_previous = 5; - bool has_next = 6; -} - -message DecimalValue -{ - int64 units = 1; - sfixed32 nanos = 2; -} - -enum TransactionTypeEnum -{ - Real = 0; - Sandbox = 1; -} - -enum CurrencyEnum -{ - IRR = 0; - IRT = 1; -} diff --git a/src/CMSMicroservice.Protobuf/Protos/pyms/pyms_transaction.proto b/src/CMSMicroservice.Protobuf/Protos/pyms/pyms_transaction.proto deleted file mode 100644 index 1fcb587..0000000 --- a/src/CMSMicroservice.Protobuf/Protos/pyms/pyms_transaction.proto +++ /dev/null @@ -1,279 +0,0 @@ -syntax = "proto3"; - -package pyms_transaction; - -import "pyms/pyms_public_messages.proto"; -import "google/protobuf/empty.proto"; -import "google/protobuf/wrappers.proto"; -import "google/protobuf/duration.proto"; -import "google/protobuf/timestamp.proto"; -import "google/api/annotations.proto"; - -option csharp_namespace = "CMSMicroservice.Protobuf.Protos.PYMS.Transaction"; - -service TransactionContract -{ - rpc CreateNewTransaction(CreateNewTransactionRequest) returns (CreateNewTransactionResponse){ - option (google.api.http) = { - post: "/CreateNewTransaction" - body: "*" - }; - }; - rpc UpdateTransaction(UpdateTransactionRequest) returns (google.protobuf.Empty){ - option (google.api.http) = { - put: "/UpdateTransaction" - body: "*" - }; - }; - rpc DeleteTransaction(DeleteTransactionRequest) returns (google.protobuf.Empty){ - option (google.api.http) = { - delete: "/DeleteTransaction" - body: "*" - }; - }; - rpc GetTransaction(GetTransactionRequest) returns (GetTransactionResponse){ - option (google.api.http) = { - get: "/GetTransaction" - }; - }; - rpc GetAllTransactionByFilter(GetAllTransactionByFilterRequest) returns (GetAllTransactionByFilterResponse){ - option (google.api.http) = { - get: "/GetAllTransactionByFilter" - }; - }; - rpc PaymentRequest(PaymentRequestRequest) returns (PaymentRequestResponse){ - option (google.api.http) = { - post: "/PaymentRequest" - body: "*" - }; - }; - rpc PaymentVerification(PaymentVerificationRequest) returns (PaymentVerificationResponse){ - option (google.api.http) = { - post: "/PaymentVerification" - body: "*" - }; - }; -} - -message CreateNewTransactionRequest -{ - string merchant_id = 1; - int64 amount = 2; - string callback_url = 3; - string description = 4; - google.protobuf.StringValue mobile = 5; - google.protobuf.StringValue email = 6; - google.protobuf.Int32Value request_status_code = 7; - google.protobuf.StringValue request_status_message = 8; - google.protobuf.StringValue authority = 9; - google.protobuf.StringValue fee_type = 10; - google.protobuf.Int64Value fee = 11; - oneof Currency_item - { - pyms_messages.CurrencyEnum currency = 12; - } - bool payment_status = 13; - google.protobuf.Int32Value verification_status_code = 14; - google.protobuf.StringValue verification_status_message = 15; - google.protobuf.StringValue card_hash = 16; - google.protobuf.StringValue card_pan = 17; - google.protobuf.StringValue ref_id = 18; - google.protobuf.StringValue order_id = 19; - oneof Type_item - { - pyms_messages.TransactionTypeEnum type = 20; - } -} - -message CreateNewTransactionResponse -{ - int64 id = 1; -} - -message UpdateTransactionRequest -{ - int64 id = 1; - string merchant_id = 2; - int64 amount = 3; - string callback_url = 4; - string description = 5; - google.protobuf.StringValue mobile = 6; - google.protobuf.StringValue email = 7; - google.protobuf.Int32Value request_status_code = 8; - google.protobuf.StringValue request_status_message = 9; - google.protobuf.StringValue authority = 10; - google.protobuf.StringValue fee_type = 11; - google.protobuf.Int64Value fee = 12; - oneof Currency_item - { - pyms_messages.CurrencyEnum currency = 13; - } - bool payment_status = 14; - google.protobuf.Int32Value verification_status_code = 15; - google.protobuf.StringValue verification_status_message = 16; - google.protobuf.StringValue card_hash = 17; - google.protobuf.StringValue card_pan = 18; - google.protobuf.StringValue ref_id = 19; - google.protobuf.StringValue order_id = 20; - oneof Type_item - { - pyms_messages.TransactionTypeEnum type = 21; - } -} - -message DeleteTransactionRequest -{ - int64 id = 1; -} - -message GetTransactionRequest -{ - google.protobuf.Int64Value id = 1; - google.protobuf.StringValue authority = 2; -} - -message GetTransactionResponse -{ - int64 id = 1; - string merchant_id = 2; - int64 amount = 3; - string callback_url = 4; - string description = 5; - google.protobuf.StringValue mobile = 6; - google.protobuf.StringValue email = 7; - google.protobuf.Int32Value request_status_code = 8; - google.protobuf.StringValue request_status_message = 9; - google.protobuf.StringValue authority = 10; - google.protobuf.StringValue fee_type = 11; - google.protobuf.Int64Value fee = 12; - oneof Currency_item - { - pyms_messages.CurrencyEnum currency = 13; - } - bool payment_status = 14; - google.protobuf.Int32Value verification_status_code = 15; - google.protobuf.StringValue verification_status_message = 16; - google.protobuf.StringValue card_hash = 17; - google.protobuf.StringValue card_pan = 18; - google.protobuf.StringValue ref_id = 19; - google.protobuf.StringValue order_id = 20; - oneof Type_item - { - pyms_messages.TransactionTypeEnum type = 21; - } -} - -message GetAllTransactionByFilterRequest -{ - pyms_messages.PaginationState pagination_state = 1; - google.protobuf.StringValue sort_by = 2; - GetAllTransactionByFilterFilter filter = 3; -} - -message GetAllTransactionByFilterFilter -{ - google.protobuf.Int64Value id = 1; - google.protobuf.StringValue merchant_id = 2; - google.protobuf.Int64Value amount = 3; - google.protobuf.StringValue callback_url = 4; - google.protobuf.StringValue description = 5; - google.protobuf.StringValue mobile = 6; - google.protobuf.StringValue email = 7; - google.protobuf.Int32Value request_status_code = 8; - google.protobuf.StringValue request_status_message = 9; - google.protobuf.StringValue authority = 10; - google.protobuf.StringValue fee_type = 11; - google.protobuf.Int64Value fee = 12; - oneof Currency_item - { - pyms_messages.CurrencyEnum currency = 13; - } - google.protobuf.BoolValue payment_status = 14; - google.protobuf.Int32Value verification_status_code = 15; - google.protobuf.StringValue verification_status_message = 16; - google.protobuf.StringValue card_hash = 17; - google.protobuf.StringValue card_pan = 18; - google.protobuf.StringValue ref_id = 19; - google.protobuf.StringValue order_id = 20; - oneof Type_item - { - pyms_messages.TransactionTypeEnum type = 21; - } -} - -message GetAllTransactionByFilterResponse -{ - pyms_messages.MetaData meta_data = 1; - repeated GetAllTransactionByFilterResponseModel models = 2; -} - -message GetAllTransactionByFilterResponseModel -{ - int64 id = 1; - string merchant_id = 2; - int64 amount = 3; - string callback_url = 4; - string description = 5; - google.protobuf.StringValue mobile = 6; - google.protobuf.StringValue email = 7; - google.protobuf.Int32Value request_status_code = 8; - google.protobuf.StringValue request_status_message = 9; - google.protobuf.StringValue authority = 10; - google.protobuf.StringValue fee_type = 11; - google.protobuf.Int64Value fee = 12; - oneof Currency_item - { - pyms_messages.CurrencyEnum currency = 13; - } - bool payment_status = 14; - google.protobuf.Int32Value verification_status_code = 15; - google.protobuf.StringValue verification_status_message = 16; - google.protobuf.StringValue card_hash = 17; - google.protobuf.StringValue card_pan = 18; - google.protobuf.StringValue ref_id = 19; - google.protobuf.StringValue order_id = 20; - oneof Type_item - { - pyms_messages.TransactionTypeEnum type = 21; - } -} - -message PaymentRequestRequest -{ - google.protobuf.StringValue merchant_id = 1; - int64 amount = 2; - string callback_url = 3; - google.protobuf.StringValue description = 4; - google.protobuf.StringValue mobile = 5; - google.protobuf.StringValue email = 6; - oneof Currency_item - { - pyms_messages.CurrencyEnum currency = 7; - } - oneof Type_item - { - pyms_messages.TransactionTypeEnum type = 8; - } - google.protobuf.StringValue order_id = 9; -} - -message PaymentRequestResponse -{ - string payment_g_w_url = 1; -} - -message PaymentVerificationRequest -{ - string authority = 1; - string status = 2; -} - -message PaymentVerificationResponse -{ - int64 id = 1; - bool payment_status = 2; - string message = 3; - google.protobuf.StringValue ref_id = 4; - google.protobuf.StringValue order_id = 5; - google.protobuf.Int32Value verification_status_code = 6; -} diff --git a/src/CMSMicroservice.WebApi/appsettings.Development.json b/src/CMSMicroservice.WebApi/appsettings.Development.json index ec93e69..7465984 100644 --- a/src/CMSMicroservice.WebApi/appsettings.Development.json +++ b/src/CMSMicroservice.WebApi/appsettings.Development.json @@ -1,8 +1,5 @@ { - "PaymentProvider": "pyms", - "PYMS": { - "Address": "https://pyms.se.kbs1.ir" - }, + "PaymentProvider": "zarinpal", "ZarinPal": { "MerchantId": "00000000-0000-0000-0000-000000000000", "UseSandbox": true diff --git a/src/CMSMicroservice.WebApi/appsettings.json b/src/CMSMicroservice.WebApi/appsettings.json index 15e0f3b..c0f39e7 100644 --- a/src/CMSMicroservice.WebApi/appsettings.json +++ b/src/CMSMicroservice.WebApi/appsettings.json @@ -1,8 +1,5 @@ { - "PaymentProvider": "pyms", - "PYMS": { - "Address": "http://pyms-svc.default.svc.cluster.local:80" - }, + "PaymentProvider": "zarinpal", "ZarinPal": { "MerchantId": "6b098fc8-f490-47a1-aac3-1de1a1b84404", "UseSandbox": true