Add validators and services for Product Galleries and Product Tags

- Implemented Create, Delete, Get, and Update validators for Product Galleries.
- Added Create, Delete, Get, and Update validators for Product Tags.
- Created service classes for handling Discount Categories, Discount Orders, Discount Products, Discount Shopping Cart, Product Categories, Product Galleries, and Product Tags.
- Each service class integrates with CQRS for command and query handling.
- Established mapping profiles for Product Galleries.
This commit is contained in:
masoodafar-web
2025-12-04 02:40:49 +03:30
parent 40d54d08fc
commit f0f48118e7
436 changed files with 33159 additions and 2005 deletions
@@ -1,367 +0,0 @@
using CMSMicroservice.Application.Common.Interfaces;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text;
using System.Xml.Linq;
namespace CMSMicroservice.Infrastructure.Services.Payment;
/// <summary>
/// Real Implementation برای درگاه پرداخت بانک ملت (IPG)
/// بانک ملت از SOAP Web Service استفاده می‌کند
/// برای فعال‌سازی: باید TerminalId, Username, Password را در appsettings.json تنظیم کنید
/// </summary>
public class BankMellatPaymentService : IPaymentGatewayService
{
private readonly HttpClient _httpClient;
private readonly IConfiguration _configuration;
private readonly ILogger<BankMellatPaymentService> _logger;
private readonly string _terminalId;
private readonly string _username;
private readonly string _password;
private readonly string _serviceUrl;
public BankMellatPaymentService(
HttpClient httpClient,
IConfiguration configuration,
ILogger<BankMellatPaymentService> logger)
{
_httpClient = httpClient;
_configuration = configuration;
_logger = logger;
// خواندن تنظیمات از appsettings.json
_terminalId = _configuration["BankMellat:TerminalId"] ?? throw new InvalidOperationException(
"BankMellat:TerminalId is not configured");
_username = _configuration["BankMellat:Username"] ?? throw new InvalidOperationException(
"BankMellat:Username is not configured");
_password = _configuration["BankMellat:Password"] ?? throw new InvalidOperationException(
"BankMellat:Password is not configured");
_serviceUrl = _configuration["BankMellat:ServiceUrl"] ?? "https://bpm.shaparak.ir/pgwchannel/services/pgw";
_httpClient.Timeout = TimeSpan.FromSeconds(30);
}
public async Task<PaymentInitiateResult> InitiatePaymentAsync(
PaymentRequest request,
CancellationToken cancellationToken = default)
{
try
{
_logger.LogInformation(
"Initiating Bank Mellat payment: UserId={UserId}, Amount={Amount}",
request.UserId, request.Amount);
// تبدیل مبلغ به ریال (بانک ملت ریال می‌خواهد)
var amountInRials = (long)(request.Amount * 10);
var localDate = DateTime.Now.ToString("yyyyMMdd");
var localTime = DateTime.Now.ToString("HHmmss");
var orderId = $"{request.UserId}_{DateTime.Now.Ticks}";
// ساخت SOAP Request
var soapRequest = $@"
<soap:Envelope xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/""
xmlns:ns=""http://interfaces.core.sw.bps.com/"">
<soap:Body>
<ns:bpPayRequest>
<terminalId>{_terminalId}</terminalId>
<userName>{_username}</userName>
<userPassword>{_password}</userPassword>
<orderId>{orderId}</orderId>
<amount>{amountInRials}</amount>
<localDate>{localDate}</localDate>
<localTime>{localTime}</localTime>
<additionalData>{request.Description}</additionalData>
<callBackUrl>{request.CallbackUrl}</callBackUrl>
<payerId>0</payerId>
</ns:bpPayRequest>
</soap:Body>
</soap:Envelope>";
var content = new StringContent(soapRequest, Encoding.UTF8, "text/xml");
content.Headers.Add("SOAPAction", "http://interfaces.core.sw.bps.com/IPaymentGateway/bpPayRequest");
var response = await _httpClient.PostAsync(_serviceUrl, content, cancellationToken);
if (!response.IsSuccessStatusCode)
{
_logger.LogError(
"Bank Mellat API error: StatusCode={StatusCode}",
response.StatusCode);
return new PaymentInitiateResult
{
IsSuccess = false,
ErrorMessage = $"خطا در ارتباط با بانک ملت: {response.StatusCode}"
};
}
var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
var refId = ParseSoapResponse(responseContent, "return");
// بررسی کد خطا
if (string.IsNullOrEmpty(refId) || !long.TryParse(refId, out var refIdNumber))
{
_logger.LogError("Invalid RefId from Bank Mellat: {RefId}", refId);
return new PaymentInitiateResult
{
IsSuccess = false,
ErrorMessage = "پاسخ نامعتبر از بانک ملت"
};
}
if (refIdNumber < 0)
{
var errorMessage = GetBankMellatErrorMessage(refIdNumber.ToString());
_logger.LogError("Bank Mellat error code: {ErrorCode} - {Message}", refIdNumber, errorMessage);
return new PaymentInitiateResult
{
IsSuccess = false,
ErrorMessage = errorMessage
};
}
_logger.LogInformation(
"Bank Mellat payment initiated successfully: RefId={RefId}",
refId);
// URL درگاه بانک ملت
var gatewayUrl = $"https://bpm.shaparak.ir/pgwchannel/startpay.mellat?RefId={refId}";
return new PaymentInitiateResult
{
IsSuccess = true,
RefId = refId,
GatewayUrl = gatewayUrl,
ErrorMessage = null
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in InitiatePaymentAsync");
return new PaymentInitiateResult
{
IsSuccess = false,
ErrorMessage = "خطای غیرمنتظره در برقراری ارتباط با بانک"
};
}
}
public async Task<PaymentVerificationResult> VerifyPaymentAsync(
string refId,
string verificationToken,
CancellationToken cancellationToken = default)
{
try
{
_logger.LogInformation("Verifying Bank Mellat payment: RefId={RefId}", refId);
// ساخت SOAP Request برای Verify
var soapRequest = $@"
<soap:Envelope xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/""
xmlns:ns=""http://interfaces.core.sw.bps.com/"">
<soap:Body>
<ns:bpVerifyRequest>
<terminalId>{_terminalId}</terminalId>
<userName>{_username}</userName>
<userPassword>{_password}</userPassword>
<orderId>{verificationToken}</orderId>
<saleOrderId>{verificationToken}</saleOrderId>
<saleReferenceId>{refId}</saleReferenceId>
</ns:bpVerifyRequest>
</soap:Body>
</soap:Envelope>";
var content = new StringContent(soapRequest, Encoding.UTF8, "text/xml");
content.Headers.Add("SOAPAction", "http://interfaces.core.sw.bps.com/IPaymentGateway/bpVerifyRequest");
var response = await _httpClient.PostAsync(_serviceUrl, content, cancellationToken);
var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
var result = ParseSoapResponse(responseContent, "return");
var isSuccess = result == "0"; // 0 = موفق
if (isSuccess)
{
// اگر Verify موفق بود، باید Settle کنیم
await SettlePaymentAsync(refId, verificationToken, cancellationToken);
}
_logger.LogInformation(
"Bank Mellat verification result: RefId={RefId}, IsSuccess={IsSuccess}",
refId, isSuccess);
return new PaymentVerificationResult
{
IsSuccess = isSuccess,
RefId = refId,
TrackingCode = refId,
Amount = 0, // مبلغ باید از Database بیاید
Message = isSuccess ? "تراکنش موفق" : GetBankMellatErrorMessage(result)
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in VerifyPaymentAsync");
return new PaymentVerificationResult
{
IsSuccess = false,
RefId = refId,
Message = "خطا در تأیید پرداخت"
};
}
}
private async Task SettlePaymentAsync(string refId, string orderId, CancellationToken cancellationToken)
{
try
{
var soapRequest = $@"
<soap:Envelope xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/""
xmlns:ns=""http://interfaces.core.sw.bps.com/"">
<soap:Body>
<ns:bpSettleRequest>
<terminalId>{_terminalId}</terminalId>
<userName>{_username}</userName>
<userPassword>{_password}</userPassword>
<orderId>{orderId}</orderId>
<saleOrderId>{orderId}</saleOrderId>
<saleReferenceId>{refId}</saleReferenceId>
</ns:bpSettleRequest>
</soap:Body>
</soap:Envelope>";
var content = new StringContent(soapRequest, Encoding.UTF8, "text/xml");
content.Headers.Add("SOAPAction", "http://interfaces.core.sw.bps.com/IPaymentGateway/bpSettleRequest");
var response = await _httpClient.PostAsync(_serviceUrl, content, cancellationToken);
var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
var result = ParseSoapResponse(responseContent, "return");
var isSuccess = result == "0";
_logger.LogInformation(
"Bank Mellat settle result: RefId={RefId}, IsSuccess={IsSuccess}",
refId, isSuccess);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in SettlePaymentAsync");
}
}
public async Task<PayoutResult> ProcessPayoutAsync(
PayoutRequest request,
CancellationToken cancellationToken = default)
{
try
{
_logger.LogInformation(
"Processing Bank Mellat payout: UserId={UserId}, Amount={Amount}, IBAN={Iban}",
request.UserId, request.Amount, request.Iban);
// Validation
if (!request.Iban.StartsWith("IR") || request.Iban.Length != 26)
{
return new PayoutResult
{
IsSuccess = false,
Message = "فرمت شماره شبا نامعتبر است",
ProcessedAt = DateTime.UtcNow
};
}
if (request.Amount < 10_000)
{
return new PayoutResult
{
IsSuccess = false,
Message = "حداقل مبلغ برداشت 10,000 تومان است",
ProcessedAt = DateTime.UtcNow
};
}
// TODO: بانک ملت ممکن است API واریز مستقیم نداشته باشد
// در این صورت باید از Shaparak Paya (سامانه پایا) استفاده کرد
// یا از سرویس‌های واسط مانند Fanapay, IPG.ir استفاده شود
_logger.LogWarning(
"Bank Mellat direct payout is not supported. Use Shaparak Paya or third-party service.");
return new PayoutResult
{
IsSuccess = false,
Message = "واریز مستقیم از طریق بانک ملت پشتیبانی نمی‌شود. از سامانه پایا استفاده کنید.",
ProcessedAt = DateTime.UtcNow
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in ProcessPayoutAsync");
return new PayoutResult
{
IsSuccess = false,
Message = "خطا در پردازش واریز",
ProcessedAt = DateTime.UtcNow
};
}
}
// Helper method to parse SOAP XML response
private string ParseSoapResponse(string soapResponse, string elementName)
{
try
{
var doc = XDocument.Parse(soapResponse);
var ns = doc.Root?.GetDefaultNamespace();
var element = doc.Descendants(ns + elementName).FirstOrDefault();
return element?.Value ?? string.Empty;
}
catch
{
return string.Empty;
}
}
// کدهای خطای بانک ملت
private string GetBankMellatErrorMessage(string errorCode)
{
return errorCode switch
{
"0" => "تراکنش موفق",
"11" => "شماره کارت نامعتبر است",
"12" => "موجودی کافی نیست",
"13" => "رمز نادرست است",
"14" => "تعداد دفعات وارد کردن رمز بیش از حد مجاز است",
"15" => "کارت نامعتبر است",
"16" => "دفعات برداشت وجه بیش از حد مجاز است",
"17" => "کاربر از انجام تراکنش منصرف شده است",
"18" => "تاریخ انقضای کارت گذشته است",
"19" => "مبلغ برداشت وجه بیش از حد مجاز است",
"21" => "پذیرنده نامعتبر است",
"23" => "خطای امنیتی رخ داده است",
"24" => "اطلاعات کاربری پذیرنده نامعتبر است",
"25" => "مبلغ نامعتبر است",
"31" => "پاسخ نامعتبر است",
"32" => "فرمت اطلاعات وارد شده صحیح نمی‌باشد",
"33" => "حساب نامعتبر است",
"34" => "خطای سیستمی",
"35" => "تاریخ نامعتبر است",
"41" => "شماره درخواست تکراری است",
"42" => "تراکنش یافت نشد",
"43" => "قبلا درخواست Verify داده شده است",
"44" => "درخواست Verify یافت نشد",
"45" => "تراکنش Settle شده است",
"46" => "تراکنش Settle نشده است",
"47" => "تراکنش Settle یافت نشد",
"48" => "تراکنش Reverse شده است",
"49" => "تراکنش Refund یافت نشد",
"51" => "تراکنش تکراری است",
"54" => "تراکنش مرجع موجود نیست",
"55" => "تراکنش نامعتبر است",
"61" => "خطا در واریز",
_ => $"خطای ناشناخته: {errorCode}"
};
}
}