From 66a6c411d667aa3aade72209dc1c94550fdc15ab Mon Sep 17 00:00:00 2001 From: mahan Date: Wed, 31 Dec 2025 15:49:44 +0330 Subject: [PATCH 1/4] add: implement payment callback handling and enhance financial invoice structure --- .../IInstitutionContractRepository.cs | 1 + .../FinancialInvoice/EditFinancialInvoice.cs | 3 +- .../IPaymentCallbackHandler.cs | 21 ++ .../VerifyPaymentCallbackCommand.cs | 53 ++++ .../PaymentCallbackHandler.cs | 233 ++++++++++++++++ .../Repository/FinancialInvoiceRepository.cs | 1 + .../InstitutionContractRepository.cs | 6 + .../PersonalBootstrapper.cs | 142 +--------- ServiceHost/Controllers/GeneralController.cs | 264 ++---------------- 9 files changed, 346 insertions(+), 378 deletions(-) create mode 100644 CompanyManagment.App.Contracts/PaymentCallback/IPaymentCallbackHandler.cs create mode 100644 CompanyManagment.App.Contracts/PaymentCallback/VerifyPaymentCallbackCommand.cs create mode 100644 CompanyManagment.Application/PaymentCallbackHandler.cs diff --git a/Company.Domain/InstitutionContractAgg/IInstitutionContractRepository.cs b/Company.Domain/InstitutionContractAgg/IInstitutionContractRepository.cs index c578d7be..be22916f 100644 --- a/Company.Domain/InstitutionContractAgg/IInstitutionContractRepository.cs +++ b/Company.Domain/InstitutionContractAgg/IInstitutionContractRepository.cs @@ -171,4 +171,5 @@ public interface IInstitutionContractRepository : IRepository SetDiscountForCreation(InstitutionContractSetDiscountForCreationRequest request); Task ResetDiscountForCreation(InstitutionContractResetDiscountForExtensionRequest request); Task CreationComplete(InstitutionContractExtensionCompleteRequest request); + Task GetIncludeInstallments(long id); } \ No newline at end of file diff --git a/CompanyManagment.App.Contracts/FinancialInvoice/EditFinancialInvoice.cs b/CompanyManagment.App.Contracts/FinancialInvoice/EditFinancialInvoice.cs index 1975c32a..a24b49f3 100644 --- a/CompanyManagment.App.Contracts/FinancialInvoice/EditFinancialInvoice.cs +++ b/CompanyManagment.App.Contracts/FinancialInvoice/EditFinancialInvoice.cs @@ -10,7 +10,8 @@ public class EditFinancialInvoice public double Amount { get; set; } public FinancialInvoiceStatus Status { get; set; } - public List? Items { get; set; } + public List Items { get; set; } + public long ContractingPartyId { get; set; } } public class EditFinancialInvoiceItem diff --git a/CompanyManagment.App.Contracts/PaymentCallback/IPaymentCallbackHandler.cs b/CompanyManagment.App.Contracts/PaymentCallback/IPaymentCallbackHandler.cs new file mode 100644 index 00000000..c435cf5f --- /dev/null +++ b/CompanyManagment.App.Contracts/PaymentCallback/IPaymentCallbackHandler.cs @@ -0,0 +1,21 @@ +using _0_Framework.Application; +using System.Threading; +using System.Threading.Tasks; + +namespace CompanyManagment.App.Contracts.PaymentCallback; + +/// +/// رابط برای مدیریت Callback درگاه‌های پرداخت +/// +public interface IPaymentCallbackHandler +{ + /// + /// تأیید و پردازش callback درگاه پرداخت سپهر + /// + /// داده‌های callback درگاه + /// توکن لغو عملیات + /// نتیجه عملیات + Task VerifySepehrPaymentCallback(VerifyPaymentCallbackCommand command, + CancellationToken cancellationToken = default); +} + diff --git a/CompanyManagment.App.Contracts/PaymentCallback/VerifyPaymentCallbackCommand.cs b/CompanyManagment.App.Contracts/PaymentCallback/VerifyPaymentCallbackCommand.cs new file mode 100644 index 00000000..9dbe583c --- /dev/null +++ b/CompanyManagment.App.Contracts/PaymentCallback/VerifyPaymentCallbackCommand.cs @@ -0,0 +1,53 @@ +namespace CompanyManagment.App.Contracts.PaymentCallback; + +/// +/// دستور تأیید callback درگاه پرداخت +/// +public class VerifyPaymentCallbackCommand +{ + /// + /// کد پاسخ درگاه (0 = موفق) + /// + public int ResponseCode { get; set; } + + /// + /// شناسه فاکتور/تراکنش + /// + public long InvoiceId { get; set; } + + /// + /// داده‌های اضافی JSON + /// + public string Payload { get; set; } + + /// + /// مبلغ تراکنش + /// + public long Amount { get; set; } + + /// + /// شماره پیگیری درگاه + /// + public long TraceNumber { get; set; } + + /// + /// شماره سند بانکی (RRN) + /// + public long Rrn { get; set; } + + /// + /// رسید دیجیتال + /// + public string DigitalReceipt { get; set; } + + /// + /// بانک صادر کننده کارت + /// + public string IssuerBank { get; set; } + + /// + /// شماره کارت + /// + public string CardNumber { get; set; } +} + diff --git a/CompanyManagment.Application/PaymentCallbackHandler.cs b/CompanyManagment.Application/PaymentCallbackHandler.cs new file mode 100644 index 00000000..8e327201 --- /dev/null +++ b/CompanyManagment.Application/PaymentCallbackHandler.cs @@ -0,0 +1,233 @@ +using _0_Framework.Application; +using Company.Domain.PaymentTransactionAgg; +using CompanyManagment.App.Contracts.FinancialInvoice; +using CompanyManagment.App.Contracts.FinancialStatment; +using CompanyManagment.App.Contracts.InstitutionContract; +using CompanyManagment.App.Contracts.PaymentCallback; +using CompanyManagment.App.Contracts.PaymentTransaction; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using System.Transactions; +using _0_Framework.Application.PaymentGateway; +using Company.Domain.FinancialStatmentAgg; +using Company.Domain.FinancialTransactionAgg; +using Company.Domain.InstitutionContractAgg; +using CompanyManagment.EFCore.Migrations; + +namespace CompanyManagment.Application; + +public class PaymentCallbackHandler : IPaymentCallbackHandler +{ + private readonly IPaymentTransactionApplication _paymentTransactionApplication; + private readonly IFinancialStatmentApplication _financialStatmentApplication; + private readonly IFinancialStatmentRepository _financialStatmentRepository; + private readonly IFinancialInvoiceApplication _financialInvoiceApplication; + private readonly IInstitutionContractApplication _institutionContractApplication; + private readonly IInstitutionContractRepository _institutionContractRepository; + private readonly IPaymentGateway _paymentGateway; + + public PaymentCallbackHandler( + IPaymentTransactionApplication paymentTransactionApplication, + IFinancialStatmentApplication financialStatmentApplication, + IFinancialInvoiceApplication financialInvoiceApplication, + IInstitutionContractApplication institutionContractApplication, + IHttpClientFactory httpClientFactory, IInstitutionContractRepository institutionContractRepository, + IFinancialStatmentRepository financialStatmentRepository) + { + _paymentTransactionApplication = paymentTransactionApplication; + _financialStatmentApplication = financialStatmentApplication; + _financialInvoiceApplication = financialInvoiceApplication; + _institutionContractApplication = institutionContractApplication; + _institutionContractRepository = institutionContractRepository; + _financialStatmentRepository = financialStatmentRepository; + _paymentGateway = new SepehrPaymentGateway(httpClientFactory); + } + + /// + /// تأیید و پردازش callback درگاه پرداخت سپهر + /// + public async Task VerifySepehrPaymentCallback(VerifyPaymentCallbackCommand command, + CancellationToken cancellationToken = default) + { + var operation = new OperationResult(); + + try + { + await using var transactionScope =await _financialStatmentRepository.BeginTransactionAsync(); + + // گام 1: دریافت اطلاعات تراکنش + var transaction = await _paymentTransactionApplication.GetDetails(command.InvoiceId); + + if (transaction == null) + return operation.Failed("تراکنش مورد نظر یافت نشد"); + + // گام 2: بررسی وضعیت قبلی تراکنش + if (transaction.Status != PaymentTransactionStatus.Pending) + return operation.Failed("این تراکنش قبلا پرداخت شده است"); + + // گام 3: بررسی کد پاسخ درگاه + if (command.ResponseCode != 0) + { + var failResult = _paymentTransactionApplication.SetFailed(command.InvoiceId); + return failResult.IsSuccedded + ? operation.Failed("تراکنش توسط درگاه رد شد") + : operation.Failed("خطا در به‌روزرسانی وضعیت تراکنش"); + } + + // گام 4: استخراج اطلاعات فاکتور مالی + var extraData = JsonConvert.DeserializeObject>(command.Payload ?? "{}"); + + extraData.TryGetValue("financialInvoiceId", out var financialInvoiceIdObj); + + if (financialInvoiceIdObj == null || + !long.TryParse(financialInvoiceIdObj.ToString(), out var financialInvoiceId)) + return operation.Failed("فاکتور مالی نامعتبر است"); + + // گام 5: دریافت اطلاعات فاکتور مالی + var financialInvoice = _financialInvoiceApplication.GetDetails(financialInvoiceId); + + if (financialInvoice == null) + return operation.Failed("فاکتور مالی نامعتبر است"); + + if (financialInvoice.Status != FinancialInvoiceStatus.Unpaid) + return operation.Failed("فاکتور مالی نامعتبر است"); + + // گام 6: بررسی تطابق مبلغ + if ((long)financialInvoice.Amount != command.Amount) + { + var failResult = _paymentTransactionApplication.SetFailed(command.InvoiceId); + return operation.Failed("مبلغ تراکنش با مبلغ فاکتور مطابقت ندارد"); + } + + // گام 7: به‌روزرسانی فاکتور مالی + var setPaidResult = _financialInvoiceApplication.SetPaid(financialInvoiceId, DateTime.Now); + if (!setPaidResult.IsSuccedded) + { + var failResult = _paymentTransactionApplication.SetFailed(command.InvoiceId); + return operation.Failed("خطا در به‌روزرسانی فاکتور مالی"); + } + + // گام 8: ایجاد سند مالی (Financial Statement) + var createCreditStatementCommand = new CreateFinancialStatment() + { + ContractingPartyId = transaction.ContractingPartyId, + Deptor = 0, + Creditor = command.Amount, + DeptorString = "0", + TypeOfTransaction = "credit", + DescriptionOption = (financialInvoice.Description ?? "") + " شماره فاکتور: " + + (financialInvoice.InvoiceNumber ?? ""), + Description = "درگاه بانکی", + }; + + var statementResult = _financialStatmentApplication.CreateFromBankGateway(createCreditStatementCommand); + if (!statementResult.IsSuccedded) + { + var failResult = _paymentTransactionApplication.SetFailed(command.InvoiceId); + return operation.Failed("خطا در ایجاد سند مالی"); + } + + // گام 9: به‌روزرسانی وضعیت تراکنش + var setSuccessResult = _paymentTransactionApplication.SetSuccess( + command.InvoiceId, + command.CardNumber, + command.IssuerBank, + command.Rrn.ToString(), + command.DigitalReceipt); + + if (!setSuccessResult.IsSuccedded) + { + return operation.Failed("خطا در به‌روزرسانی وضعیت تراکنش"); + } + + // گام 10: به‌روزرسانی وضعیت قرارداد‌های نهادی (اگر وجود داشته باشند) + if (financialInvoice.Items?.Any(x => + x.Type is FinancialInvoiceItemType.BuyInstitutionContract + or FinancialInvoiceItemType.BuyInstitutionContractInstallment) ?? false) + { + await HandleInstitutionContractItems(financialInvoice); + } + + // گام 11: تأیید نهایی با درگاه پرداخت + var verifyCommand = new VerifyPaymentGateWayRequest() + { + Amount = transaction.Amount, + TransactionId = command.InvoiceId.ToString(), + DigitalReceipt = command.DigitalReceipt + }; + + var verifyRes = await _paymentGateway.Verify(verifyCommand, cancellationToken); +#if DEBUG + verifyRes.IsSuccess = true; +#endif + if (!verifyRes.IsSuccess) + { + return operation.Failed("خطا در تایید پرداخت از درگاه"); + } + + // تمام عملیات موفق - تایید transaction + await transactionScope.CommitAsync(cancellationToken); + return operation.Succcedded(); + } + catch (Exception ex) + { + // در صورت بروز هرگونه خطا، transaction خودکار rollback می‌شود + return operation.Failed($"خطا در پردازش callback: {ex.Message}"); + } + } + + /// + /// مدیریت آپدیت قرارداد‌های نهادی + /// + private async Task HandleInstitutionContractItems(EditFinancialInvoice financialInvoice) + { + // قرارداد‌های خریداری مستقیم + var directContractItems = financialInvoice.Items + .Where(x => x.Type == FinancialInvoiceItemType.BuyInstitutionContract); + var financialStatement = + await _financialStatmentRepository.GetByContractingPartyId(financialInvoice.ContractingPartyId); + + var today = DateTime.Now; + foreach (var item in directContractItems) + { + var institutionContract = _institutionContractRepository.Get(item.EntityId); + + await _institutionContractApplication.SetPendingWorkflow(item.EntityId, + InstitutionContractSigningType.OtpBased); + + var financialTransaction = new FinancialTransaction(0, today, today.ToFarsi(), + "پرداخت کل سرویس", "debt", "بابت خدمات", institutionContract.TotalAmount, 0, 0); + + financialStatement.AddFinancialTransaction(financialTransaction); + } + + // قرارداد‌های خریداری با اقساط + var installmentItems = financialInvoice.Items + .Where(x => x.Type == FinancialInvoiceItemType.BuyInstitutionContractInstallment); + + foreach (var item in installmentItems) + { + var institutionContractId =await _institutionContractRepository.GetIdByInstallmentId(item.EntityId); + var institutionContract = _institutionContractRepository.Get(institutionContractId); + + + await _institutionContractApplication.SetPendingWorkflow(institutionContractId, + InstitutionContractSigningType.OtpBased); + + var firstInstallment = institutionContract.Installments.First(); + + var firstInstallmentAmount = firstInstallment.Amount; + + var financialTransaction = new FinancialTransaction(0, today, today.ToFarsi(), + "قسط اول سرویس", "debt", "بابت خدمات", firstInstallmentAmount, 0, 0); + + financialStatement.AddFinancialTransaction(financialTransaction); + } + await _financialStatmentRepository.SaveChangesAsync(); + } +} \ No newline at end of file diff --git a/CompanyManagment.EFCore/Repository/FinancialInvoiceRepository.cs b/CompanyManagment.EFCore/Repository/FinancialInvoiceRepository.cs index 66b05f02..b2db131a 100644 --- a/CompanyManagment.EFCore/Repository/FinancialInvoiceRepository.cs +++ b/CompanyManagment.EFCore/Repository/FinancialInvoiceRepository.cs @@ -34,6 +34,7 @@ public class FinancialInvoiceRepository : RepositoryBase Amount = financialInvoice.Amount, Status = financialInvoice.Status, InvoiceNumber = financialInvoice.InvoiceNumber, + ContractingPartyId = financialInvoice.ContractingPartyId, Items = financialInvoice.Items?.Select(x => new EditFinancialInvoiceItem { Id = x.id, diff --git a/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs b/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs index e2197ae7..1a105585 100644 --- a/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs +++ b/CompanyManagment.EFCore/Repository/InstitutionContractRepository.cs @@ -6040,6 +6040,12 @@ public class InstitutionContractRepository : RepositoryBase GetIncludeInstallments(long id) + { + return _context.InstitutionContractSet.Include(x => x.Installments) + .FirstOrDefaultAsync(x => x.id == id); + } + private async Task> CreateLegalContractingPartyEntity( InstitutionContractCreationTempLegalParty request, long representativeId, string address, string city, string state) diff --git a/PersonalContractingParty.Config/PersonalBootstrapper.cs b/PersonalContractingParty.Config/PersonalBootstrapper.cs index ebb0e49f..e7256455 100644 --- a/PersonalContractingParty.Config/PersonalBootstrapper.cs +++ b/PersonalContractingParty.Config/PersonalBootstrapper.cs @@ -229,153 +229,14 @@ using CompanyManagment.Application; using CompanyManagment.EFCore; using CompanyManagment.EFCore._common; using CompanyManagment.EFCore.Repository; -using CompanyManagment.EFCore.Repository; using File.EfCore.Repository; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using P_TextManager.Domin.TextManagerAgg; -using CompanyManagment.App.Contracts.CrossJobItems; -using Company.Domain.CrossJobItemsAgg; -using Company.Domain.DateSalaryAgg; -using Company.Domain.DateSalaryItemAgg; -using Company.Domain.FinancialStatmentAgg; -using Company.Domain.FinancialTransactionAgg; -using Company.Domain.GroupPlanAgg; -using Company.Domain.GroupPlanJobItemAgg; -using Company.Domain.InstitutionContractAgg; -using Company.Domain.InstitutionContractContactInfoAgg; -using CompanyManagment.App.Contracts.Insurance; -using Company.Domain.InsuranceAgg; -using Company.Domain.InsuranceEmployeeInfoAgg; -using Company.Domain.InsuranceJobItemAgg; -using Company.Domain.InsuranceListAgg; -using Company.Domain.InsurancJobAgg; -using Company.Domain.InsurancWorkshopInfoAgg; -using Company.Domain.LeftWorkInsuranceAgg; -using Company.Domain.PaymentToEmployeeAgg; -using Company.Domain.PaymentToEmployeeItemAgg; -using Company.Domain.PercentageAgg; -using Company.Domain.PersonnelCodeAgg; -using Company.Domain.SmsResultAgg; -using Company.Domain.WorkingHoursTempAgg; -using Company.Domain.WorkingHoursTempItemAgg; -using Company.Domain.WorkshopPlanAgg; -using Company.Domain.WorkshopPlanEmployeeAgg; -using Company.Domain.ZoneAgg; -using CompanyManagment.App.Contracts.ClassifiedSalary; -using CompanyManagment.App.Contracts.DateSalary; -using CompanyManagment.App.Contracts.DateSalaryItem; -using CompanyManagment.App.Contracts.EmployeeInsurancListData; -using CompanyManagment.App.Contracts.FinancialStatment; -using CompanyManagment.App.Contracts.FinancilTransaction; -using CompanyManagment.App.Contracts.InstitutionContract; -using CompanyManagment.App.Contracts.InstitutionContractContactinfo; -using CompanyManagment.App.Contracts.InsuranceEmployeeInfo; -using CompanyManagment.App.Contracts.InsuranceJob; -using CompanyManagment.App.Contracts.InsuranceList; -using CompanyManagment.App.Contracts.InsuranceWorkshopInfo; -using CompanyManagment.App.Contracts.LeftWorkInsurance; -using CompanyManagment.App.Contracts.PaymentToEmployee; -using CompanyManagment.App.Contracts.Percentage; -using CompanyManagment.App.Contracts.PersonnleCode; -using CompanyManagment.App.Contracts.SmsResult; -using CompanyManagment.App.Contracts.WorkingHoursTemp; -using CompanyManagment.App.Contracts.WorkingHoursTempItem; -using CompanyManagment.App.Contracts.WorkshopPlan; -using CompanyManagment.App.Contracts.Zone; -using CompanyManagment.App.Contracts.EmployeeComputeOptions; -using Company.Domain.EmployeeComputeOptionsAgg; -using Company.Domain.InsuranceYearlySalaryAgg; -using Company.Domain.ReportAgg; -using Company.Domain.RollCallAgg; -using Company.Domain.RollCallEmployeeAgg; -using Company.Domain.RollCallPlanAgg; -using Company.Domain.RollCallServiceAgg; -using CompanyManagment.App.Contracts.InsuranceYearlySalary; -using CompanyManagment.App.Contracts.Report; -using CompanyManagment.App.Contracts.RollCall; -using CompanyManagment.App.Contracts.RollCallEmployee; -using CompanyManagment.App.Contracts.RollCallService; -using CompanyManagment.App.Contracts.RollCallPlan; -using Company.Domain.ReportClientAgg; -using Company.Domain.TaxJobCategoryAgg; -using Company.Domain.WorkshopAccountAgg; -using CompanyManagment.App.Contracts.ReportClient; -using CompanyManagment.App.Contracts.TaxJobCategory; -using Company.Domain.RollCallEmployeeStatusAgg; -using CompanyManagment.App.Contracts.RollCallEmployeeStatus; -using Company.Domain.CustomizeWorkshopEmployeeSettingsAgg; -using Company.Domain.CustomizeWorkshopGroupSettingsAgg; -using Company.Domain.CustomizeWorkshopSettingsAgg; -using Company.Domain.FineAgg; -using Company.Domain.LoanAgg; -using Company.Domain.RewardAgg; -using Company.Domain.SalaryAidAgg; -using CompanyManagment.App.Contracts.CustomizeWorkshopSettings; -using CompanyManagment.App.Contracts.Fine; -using CompanyManagment.App.Contracts.Loan; -using CompanyManagment.App.Contracts.Reward; -using CompanyManagment.App.Contracts.SalaryAid; -using Company.Domain.AndroidApkVersionAgg; -using Company.Domain.BankAgg; -using CompanyManagment.App.Contracts.AndroidApkVersion; -using Company.Domain.FineSubjectAgg; -using CompanyManagment.App.Contracts.FineSubject; -using Company.Domain.CustomizeCheckoutAgg; -using CompanyManagment.App.Contracts.CustomizeCheckout; -using Company.Domain.WorkshopSubAccountAgg; -using Company.Domain.CustomizeCheckoutTempAgg; -using Company.Domain.EmployeeBankInformationAgg; -using Company.Domain.RollCallAgg.DomainService; -using CompanyManagment.App.Contracts.Bank; -using CompanyManagment.App.Contracts.EmployeeBankInformation; -using Company.Domain.EmployeeDocumentItemAgg; -using Company.Domain.EmployeeDocumentsAdminSelectionAgg; -using Company.Domain.EmployeeDocumentsAgg; -using CompanyManagement.Infrastructure.Excel.SalaryAid; -using CompanyManagment.App.Contracts.EmployeeDocuments; -using CompanyManagment.App.Contracts.EmployeeDocumentsAdminSelection; -using Company.Domain.EmployeeClientTempAgg; -using Company.Domain.InstitutionPlanAgg; -using Company.Domain.LeftWorkTempAgg; -using Company.Domain.TemporaryClientRegistrationAgg; -using CompanyManagment.App.Contracts.EmployeeClientTemp; -using CompanyManagment.App.Contracts.InstitutionPlan; -using CompanyManagment.App.Contracts.LeftWorkTemp; -using CompanyManagment.App.Contracts.TemporaryClientRegistration; -using Company.Domain.ContactUsAgg; -using CompanyManagment.App.Contracts.ContactUs; -using Company.Domain.EmployeeAuthorizeTempAgg; -using Company.Domain.AdminMonthlyOverviewAgg; -using Company.Domain.AuthorizedBankDetailsAgg; -using Company.Domain.ContractingPartyBankAccountsAgg; -using Company.Domain.PaymentInstrumentAgg; -using Company.Domain.PaymentTransactionAgg; -using Company.Domain.FinancialInvoiceAgg; -using CompanyManagment.App.Contracts.AdminMonthlyOverview; -using CompanyManagment.App.Contracts.ContractingPartyBankAccounts; -using CompanyManagment.App.Contracts.PaymentInstrument; -using CompanyManagment.App.Contracts.PaymentTransaction; -using CompanyManagment.App.Contracts.AuthorizedPerson; -using Company.Domain.AuthorizedPersonAgg; -using Company.Domain.EmployeeFaceEmbeddingAgg; -using Company.Domain.InstitutionContractExtensionTempAgg; -using Company.Domain.LawAgg; -using CompanyManagement.Infrastructure.Mongo.EmployeeFaceEmbeddingRepo; -using CompanyManagement.Infrastructure.Mongo.InstitutionContractInsertTempRepo; -using CompanyManagment.App.Contracts.EmployeeFaceEmbedding; -using CompanyManagment.App.Contracts.Law; -using CompanyManagment.EFCore.Repository; -using CompanyManagment.App.Contracts.FinancialInvoice; -using _0_Framework.Application.FaceEmbedding; -using _0_Framework.Infrastructure; -using _0_Framework.InfraStructure; +using CompanyManagment.App.Contracts.PaymentCallback; using Company.Domain.CameraBugReportAgg; using CompanyManagment.App.Contracts.CameraBugReport; -using CompanyManagement.Infrastructure.Mongo.CameraBugReportRepo; using CameraBugReportRepository = CompanyManagement.Infrastructure.Mongo.CameraBugReportRepo.CameraBugReportRepository; -using Company.Domain._common; -using CompanyManagment.EFCore._common; using CompanyManagment.EFCore.Services; using Shared.Contracts.Holidays; @@ -622,6 +483,7 @@ public class PersonalBootstrapper services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/ServiceHost/Controllers/GeneralController.cs b/ServiceHost/Controllers/GeneralController.cs index 4854bd4c..7871eef9 100644 --- a/ServiceHost/Controllers/GeneralController.cs +++ b/ServiceHost/Controllers/GeneralController.cs @@ -1,49 +1,25 @@ using _0_Framework.Application; -using _0_Framework.Application.PaymentGateway; -using Company.Domain.BankAgg; -using Company.Domain.PaymentTransactionAgg; -using CompanyManagment.App.Contracts.FinancialStatment; -using CompanyManagment.App.Contracts.FinancilTransaction; +using CompanyManagment.App.Contracts.PaymentCallback; using CompanyManagment.App.Contracts.PaymentTransaction; -using CompanyManagment.App.Contracts.Workshop; -using CompanyManagment.EFCore.Migrations; -using Microsoft.AspNetCore.Authorization; +using GozareshgirProgramManager.Application._Common.Constants; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Options; -using Newtonsoft.Json; -using NuGet.Protocol; -using Parbad; using ServiceHost.BaseControllers; using System.ComponentModel.DataAnnotations; using System.Globalization; -using System.Net.Http; -using System.Security.Cryptography; using System.Threading; -using CompanyManagment.App.Contracts.FinancialInvoice; -using CompanyManagment.App.Contracts.InstitutionContract; -using GozareshgirProgramManager.Application._Common.Constants; -using GozareshgirProgramManager.Infrastructure.Persistence.Context; namespace ServiceHost.Controllers; public class GeneralController : GeneralBaseController { private readonly IPaymentTransactionApplication _paymentTransactionApplication; - private readonly IPaymentGateway _paymentGateway; - private readonly IFinancialStatmentApplication _financialStatmentApplication; - private readonly IFinancialInvoiceApplication _financialInvoiceApplication; - private readonly IInstitutionContractApplication _institutionContractApplication; + private readonly IPaymentCallbackHandler _paymentCallbackHandler; public GeneralController(IPaymentTransactionApplication paymentTransactionApplication, - IHttpClientFactory clientFactory, IFinancialStatmentApplication financialStatmentApplication, - IFinancialInvoiceApplication financialInvoiceApplication, - IInstitutionContractApplication institutionContractApplication) + IPaymentCallbackHandler paymentCallbackHandler) { _paymentTransactionApplication = paymentTransactionApplication; - _paymentGateway = new SepehrPaymentGateway(clientFactory); - _financialStatmentApplication = financialStatmentApplication; - _financialInvoiceApplication = financialInvoiceApplication; - _institutionContractApplication = institutionContractApplication; + _paymentCallbackHandler = paymentCallbackHandler; } /// @@ -67,15 +43,15 @@ public class GeneralController : GeneralBaseController }); } - [HttpGet("pm-permissions")] - public IActionResult GetPMPermissions() - { - var permissions = ProgramManagerPermissionCode.GetAllCodes(); - return new JsonResult(permissions); - } + // [HttpGet("pm-permissions")] + // public IActionResult GetPMPermissions() + // { + // var permissions = ProgramManagerPermissionCode.GetAllCodes(); + // return new JsonResult(permissions); + // } [HttpGet("/api/callback"), HttpPost("/api/callback")] - public async Task Verify([FromForm]SepehrGatewayPayResponse payResponse) + public async Task Verify([FromForm] SepehrGatewayPayResponse payResponse) { if (!long.TryParse(payResponse.invoiceid, out var paymentTransactionId)) { @@ -89,215 +65,29 @@ public class GeneralController : GeneralBaseController return NotFound("Transaction not found"); } - if (transaction.Status != PaymentTransactionStatus.Pending) + // ایجاد command برای ارسال به PaymentCallbackHandler + var command = new VerifyPaymentCallbackCommand { - return BadRequest("این تراکنش قبلا پرداخت شده است"); - } - - - if (payResponse.respcode != 0) - { - return await HandleFailedTransaction(transaction); - } - - var extraData = JsonConvert.DeserializeObject>(payResponse.payload); - extraData.TryGetValue("financialInvoiceId", out var financialInvoiceIdObj); - if (financialInvoiceIdObj == null || - !long.TryParse(financialInvoiceIdObj.ToString(), out var financialInvoiceId)) - { - return BadRequest("فاکتور مالی نامعتبر است"); - } - - - var financialInvoice = _financialInvoiceApplication.GetDetails(financialInvoiceId); - - if (financialInvoice == null) - { - return BadRequest("فاکتور مالی نامعتبر است"); - } - - if (financialInvoice.Status != FinancialInvoiceStatus.Unpaid) - { - return BadRequest("فاکتور مالی نامعتبر است"); - } - - if (financialInvoice.Amount != transaction.Amount) - { - return await HandleFailedTransaction(transaction); - } - - var verifyCommand = new VerifyPaymentGateWayRequest() - { - Amount = transaction.Amount, - TransactionId = payResponse.invoiceid, - DigitalReceipt = payResponse.digitalreceipt + ResponseCode = payResponse.respcode, + InvoiceId = paymentTransactionId, + Payload = payResponse.payload, + Amount = payResponse.amount, + TraceNumber = payResponse.tracenumber, + Rrn = payResponse.rrn, + DigitalReceipt = payResponse.digitalreceipt, + IssuerBank = payResponse.issuerbank, + CardNumber = payResponse.cardnumber }; - - var verifyRes = await _paymentGateway.Verify(verifyCommand, CancellationToken.None); -#if DEBUG - verifyRes.IsSuccess = true; -#endif + // پردازش callback در Application Layer + var result = await _paymentCallbackHandler.VerifySepehrPaymentCallback(command, CancellationToken.None); - _financialInvoiceApplication.SetPaid(financialInvoiceId, DateTime.Now); - - if (verifyRes.IsSuccess) + if (result.IsSuccedded) { - var command = new CreateFinancialStatment() - { - ContractingPartyId = transaction.ContractingPartyId, - Deptor = 0, - Creditor = transaction.Amount, - DeptorString = "0", - TypeOfTransaction = "credit", - DescriptionOption = financialInvoice.Description + "شماره فاکتور" + financialInvoice.InvoiceNumber, - Description = "درگاه بانکی", - }; - var statementResult = _financialStatmentApplication.CreateFromBankGateway(command); - if (!statementResult.IsSuccedded) - { - return new JsonResult(statementResult); - } - - var setSuccessResult = _paymentTransactionApplication.SetSuccess(paymentTransactionId, - payResponse.cardnumber, payResponse.issuerbank, payResponse.rrn.ToString(), - payResponse.digitalreceipt); - - if (financialInvoice.Items?.Any(x => - x.Type is FinancialInvoiceItemType.BuyInstitutionContract - or FinancialInvoiceItemType.BuyInstitutionContractInstallment) ?? false) - { - var financialItems = financialInvoice.Items - .Where(x => x.Type == FinancialInvoiceItemType.BuyInstitutionContract); - foreach (var editFinancialInvoiceItem in financialItems) - { - await _institutionContractApplication.SetPendingWorkflow(editFinancialInvoiceItem.EntityId, - InstitutionContractSigningType.OtpBased); - } - - var financialInstallmentItems = financialInvoice.Items - .Where(x => x.Type == FinancialInvoiceItemType.BuyInstitutionContractInstallment); - - foreach (var editFinancialInvoiceItem in financialInstallmentItems) - { - var institutionContractId = - await _institutionContractApplication.GetIdByInstallmentId( - editFinancialInvoiceItem.EntityId); - await _institutionContractApplication.SetPendingWorkflow(institutionContractId, - InstitutionContractSigningType.OtpBased); - } - } - - if (!setSuccessResult.IsSuccedded) - { - return await HandleFailedTransaction(transaction); - } - return Redirect(BuildCallbackUrl(transaction.CallBackUrl, true, transaction.Id)); } - // در غیر این صورت تراکنش ناموفق است - return await HandleFailedTransaction(transaction); - - - //var data = JsonConvert.SerializeObject(invoice.AdditionalData); - //var statics = - //JsonConvert.SerializeObject(res); - - //await _onlinePayment.CancelAsync(invoice); - //return new JsonResult(new - //{ - // data, - // statics - //}); - - //// Check if the invoice is new, or it's already processed before. - //if (invoice.Status != PaymentFetchResultStatus.ReadyForVerifying) - //{ - // // You can also see if the invoice is already verified before. - // var isAlreadyVerified = invoice.IsAlreadyVerified; - - // return Content("The payment was not successful."); - //} - - //// Note: Save the verifyResult.TransactionCode in your database. - } - - [HttpPost("/api/callback3232")] - public async Task OnGetCallBack(string? transid, string? cardnumber, string? tracking_number, - string bank, string invoice_id, string? status, CancellationToken cancellationToken) - { - if (!long.TryParse(invoice_id, out var paymentTransactionId)) - { - return BadRequest("Invalid invoice_id"); - } - - var transaction = await _paymentTransactionApplication.GetDetails(paymentTransactionId); - if (transaction == null) - { - return NotFound("Transaction not found"); - } - - if (transaction.Status != PaymentTransactionStatus.Pending) - { - return BadRequest("این تراکنش قبلا پرداخت شده است"); - } - - // اگر شماره کارت یا شماره پیگیری خالی باشد، تراکنش ناموفق است - if (string.IsNullOrWhiteSpace(cardnumber) || string.IsNullOrWhiteSpace(tracking_number)) - { - return await HandleFailedTransaction(transaction); - } - - var verifyCommand = new VerifyPaymentGateWayRequest() - { - Amount = transaction.Amount / 10, - TransactionId = transid - }; - var verifyRes = await _paymentGateway.Verify(verifyCommand, cancellationToken); - - // اگر استاتوس 1 باشد، تراکنش موفق است - if (verifyRes.IsSuccess) - { - var command = new CreateFinancialStatment() - { - ContractingPartyId = transaction.ContractingPartyId, - Deptor = 0, - Creditor = transaction.Amount, - DeptorString = "0", - TypeOfTransaction = "credit", - DescriptionOption = "بابت قرارداد مابین (روابط کار)", - Description = "درگاه بانکی", - }; - var statementResult = _financialStatmentApplication.CreateFromBankGateway(command); - if (!statementResult.IsSuccedded) - { - return await HandleFailedTransaction(transaction); - } - - var setSuccessResult = - _paymentTransactionApplication.SetSuccess(paymentTransactionId, cardnumber, bank, null, null); - - if (!setSuccessResult.IsSuccedded) - { - return new JsonResult(setSuccessResult); - } - - return Redirect(BuildCallbackUrl(transaction.CallBackUrl, true, transaction.Id)); - } - - // در غیر این صورت تراکنش ناموفق است - return await HandleFailedTransaction(transaction); - } - - private async Task HandleFailedTransaction(PaymentTransactionDetailsViewModel transaction) - { - var result = _paymentTransactionApplication.SetFailed(transaction.Id); - if (!result.IsSuccedded) - { - return new JsonResult(result); - } - + // در صورت ناموفق بودن return Redirect(BuildCallbackUrl(transaction.CallBackUrl, false, transaction.Id)); } From 7e3ea39d5b96dd9dc4d5796147a323d8534094a9 Mon Sep 17 00:00:00 2001 From: mahan Date: Wed, 31 Dec 2025 16:08:52 +0330 Subject: [PATCH 2/4] add: update payment gateway integration and enhance transaction handling in callback --- .../PaymentCallbackHandler.cs | 52 ++++++++++--------- .../Client/Controllers/FinancialController.cs | 9 ++-- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/CompanyManagment.Application/PaymentCallbackHandler.cs b/CompanyManagment.Application/PaymentCallbackHandler.cs index 8e327201..5bd85425 100644 --- a/CompanyManagment.Application/PaymentCallbackHandler.cs +++ b/CompanyManagment.Application/PaymentCallbackHandler.cs @@ -112,7 +112,33 @@ public class PaymentCallbackHandler : IPaymentCallbackHandler return operation.Failed("خطا در به‌روزرسانی فاکتور مالی"); } - // گام 8: ایجاد سند مالی (Financial Statement) + + + // گام 8: به‌روزرسانی وضعیت تراکنش + var setSuccessResult = _paymentTransactionApplication.SetSuccess( + command.InvoiceId, + command.CardNumber, + command.IssuerBank, + command.Rrn.ToString(), + command.DigitalReceipt); + + if (!setSuccessResult.IsSuccedded) + { + return operation.Failed("خطا در به‌روزرسانی وضعیت تراکنش"); + } + + // گام 9: به‌روزرسانی وضعیت قرارداد‌های نهادی (اگر وجود داشته باشند) + var institutionContractItems = financialInvoice.Items.Where(x => + x.Type is FinancialInvoiceItemType.BuyInstitutionContract + or FinancialInvoiceItemType.BuyInstitutionContractInstallment).ToList(); + + if (institutionContractItems.Any()) + { + await HandleInstitutionContractItems(financialInvoice); + } + + + // گام 10: ایجاد سند مالی (Financial Statement) var createCreditStatementCommand = new CreateFinancialStatment() { ContractingPartyId = transaction.ContractingPartyId, @@ -128,31 +154,9 @@ public class PaymentCallbackHandler : IPaymentCallbackHandler var statementResult = _financialStatmentApplication.CreateFromBankGateway(createCreditStatementCommand); if (!statementResult.IsSuccedded) { - var failResult = _paymentTransactionApplication.SetFailed(command.InvoiceId); + _paymentTransactionApplication.SetFailed(command.InvoiceId); return operation.Failed("خطا در ایجاد سند مالی"); } - - // گام 9: به‌روزرسانی وضعیت تراکنش - var setSuccessResult = _paymentTransactionApplication.SetSuccess( - command.InvoiceId, - command.CardNumber, - command.IssuerBank, - command.Rrn.ToString(), - command.DigitalReceipt); - - if (!setSuccessResult.IsSuccedded) - { - return operation.Failed("خطا در به‌روزرسانی وضعیت تراکنش"); - } - - // گام 10: به‌روزرسانی وضعیت قرارداد‌های نهادی (اگر وجود داشته باشند) - if (financialInvoice.Items?.Any(x => - x.Type is FinancialInvoiceItemType.BuyInstitutionContract - or FinancialInvoiceItemType.BuyInstitutionContractInstallment) ?? false) - { - await HandleInstitutionContractItems(financialInvoice); - } - // گام 11: تأیید نهایی با درگاه پرداخت var verifyCommand = new VerifyPaymentGateWayRequest() { diff --git a/ServiceHost/Areas/Client/Controllers/FinancialController.cs b/ServiceHost/Areas/Client/Controllers/FinancialController.cs index 96b001be..8736c764 100644 --- a/ServiceHost/Areas/Client/Controllers/FinancialController.cs +++ b/ServiceHost/Areas/Client/Controllers/FinancialController.cs @@ -23,7 +23,7 @@ public class FinancialController : ClientBaseController _financialStatementApplication = financialStatementApplication; _authHelper = authHelper; _paymentTransactionApplication = paymentTransactionApplication; - _paymentGateway = new AqayePardakhtPaymentGateway(httpClientFactory, appSetting); + _paymentGateway = new SepehrPaymentGateway(httpClientFactory); } [HttpGet] @@ -63,7 +63,7 @@ public class FinancialController : ClientBaseController } var callbackUrl = Url.Action( - action: "OnGetCallBack", + action: "Verify", controller: "General", // نام کنترلر بدون کلمه‌ی "Controller" values: null, protocol: Request.Scheme); // http یا https @@ -72,7 +72,8 @@ public class FinancialController : ClientBaseController { Amount = balanceAmount.Amount, ContractingPartyId = balanceAmount.ContractingPartyId, - CallBackUrl = request.BaseUrl + CallBackUrl = request.BaseUrl, + Gateway = PaymentTransactionGateWay.SepehrPay }; var transaction = await _paymentTransactionApplication.Create(transactionCommand); @@ -85,7 +86,7 @@ public class FinancialController : ClientBaseController var command = new CreatePaymentGatewayRequest() { CallBackUrl = callbackUrl, - Amount = balanceAmount.Amount/10, + Amount = balanceAmount.Amount, TransactionId = transaction.SendId.ToString(), }; From 490a1a69d54bacf4615a36a0dc9cc39b8cf9dd47 Mon Sep 17 00:00:00 2001 From: mahan Date: Wed, 31 Dec 2025 18:12:27 +0330 Subject: [PATCH 3/4] add: implement Sepehr payment gateway integration and enhance financial invoice handling --- .../IFinancialInvoiceRepository.cs | 2 + .../CreateFinancialInvoice.cs | 2 +- .../IFinancialStatmentApplication.cs | 11 ++ .../CreateSepehrPaymentGatewayResponse.cs | 22 +++ .../ISepehrPaymentGatewayService.cs | 32 +++ .../FinancialInvoiceApplication.cs | 7 +- .../FinancialStatmentApplication.cs | 78 ++++++-- .../InstitutionContractApplication.cs | 184 ++++++++---------- .../SepehrPaymentGatewayService.cs | 117 +++++++++++ .../Repository/FinancialInvoiceRepository.cs | 8 + .../PersonalBootstrapper.cs | 2 + .../Client/Controllers/FinancialController.cs | 68 ++----- 12 files changed, 352 insertions(+), 181 deletions(-) create mode 100644 CompanyManagment.App.Contracts/SepehrPaymentGateway/CreateSepehrPaymentGatewayResponse.cs create mode 100644 CompanyManagment.App.Contracts/SepehrPaymentGateway/ISepehrPaymentGatewayService.cs create mode 100644 CompanyManagment.Application/SepehrPaymentGatewayService.cs diff --git a/Company.Domain/FinancialInvoiceAgg/IFinancialInvoiceRepository.cs b/Company.Domain/FinancialInvoiceAgg/IFinancialInvoiceRepository.cs index 96edcc14..6f1812eb 100644 --- a/Company.Domain/FinancialInvoiceAgg/IFinancialInvoiceRepository.cs +++ b/Company.Domain/FinancialInvoiceAgg/IFinancialInvoiceRepository.cs @@ -10,4 +10,6 @@ public interface IFinancialInvoiceRepository : IRepository Search(FinancialInvoiceSearchModel searchModel); Task GetUnPaidByEntityId(long entityId, FinancialInvoiceItemType financialInvoiceItemType); + Task GetUnPaidFinancialInvoiceByContractingPartyIdAndAmount(long contractingPartyId, + double amount); } \ No newline at end of file diff --git a/CompanyManagment.App.Contracts/FinancialInvoice/CreateFinancialInvoice.cs b/CompanyManagment.App.Contracts/FinancialInvoice/CreateFinancialInvoice.cs index 0ce5a82b..6c28c66e 100644 --- a/CompanyManagment.App.Contracts/FinancialInvoice/CreateFinancialInvoice.cs +++ b/CompanyManagment.App.Contracts/FinancialInvoice/CreateFinancialInvoice.cs @@ -8,7 +8,7 @@ public class CreateFinancialInvoice public double Amount { get; set; } public long ContractingPartyId { get; set; } public string Description { get; set; } - public List? Items { get; set; } + public List Items { get; set; } } public class CreateFinancialInvoiceItem diff --git a/CompanyManagment.App.Contracts/FinancialStatment/IFinancialStatmentApplication.cs b/CompanyManagment.App.Contracts/FinancialStatment/IFinancialStatmentApplication.cs index 749a38a8..4cd5fd77 100644 --- a/CompanyManagment.App.Contracts/FinancialStatment/IFinancialStatmentApplication.cs +++ b/CompanyManagment.App.Contracts/FinancialStatment/IFinancialStatmentApplication.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using CompanyManagment.App.Contracts.SepehrPaymentGateway; namespace CompanyManagment.App.Contracts.FinancialStatment; @@ -62,7 +63,17 @@ public interface IFinancialStatmentApplication /// Task GetDetailsByContractingParty(long contractingPartyId, FinancialStatementSearchModel searchModel); + + /// + /// پردازش شارژ حساب از طریق درگاه پرداخت سپهر + /// + /// + /// مسیر برگشت درگاه پرداخت + /// + Task> CreatePaymentGateWayAndCreateInvoice( + CreateFinancialPayRequest request, string gateWayCallBackUrl); } +public record CreateFinancialPayRequest(long Id, string BaseUrl); public class FinancialStatmentDetailsByContractingPartyViewModel { diff --git a/CompanyManagment.App.Contracts/SepehrPaymentGateway/CreateSepehrPaymentGatewayResponse.cs b/CompanyManagment.App.Contracts/SepehrPaymentGateway/CreateSepehrPaymentGatewayResponse.cs new file mode 100644 index 00000000..8e44123f --- /dev/null +++ b/CompanyManagment.App.Contracts/SepehrPaymentGateway/CreateSepehrPaymentGatewayResponse.cs @@ -0,0 +1,22 @@ +namespace CompanyManagment.App.Contracts.SepehrPaymentGateway; + +/// +/// پاسخ ایجاد درگاه پرداخت سپهر +/// +public class CreateSepehrPaymentGatewayResponse +{ + /// + /// توکن درگاه + /// + public string Token { get; set; } + + /// + /// شناسه تراکنش + /// + public long TransactionId { get; set; } + + /// + /// URL درگاه پرداخت + /// + public string PaymentUrl { get; set; } +} \ No newline at end of file diff --git a/CompanyManagment.App.Contracts/SepehrPaymentGateway/ISepehrPaymentGatewayService.cs b/CompanyManagment.App.Contracts/SepehrPaymentGateway/ISepehrPaymentGatewayService.cs new file mode 100644 index 00000000..b5af2f4e --- /dev/null +++ b/CompanyManagment.App.Contracts/SepehrPaymentGateway/ISepehrPaymentGatewayService.cs @@ -0,0 +1,32 @@ +using _0_Framework.Application; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace CompanyManagment.App.Contracts.SepehrPaymentGateway; + +/// +/// رابط برای سرویس مشترک ایجاد درگاه پرداخت سپهر +/// +public interface ISepehrPaymentGatewayService +{ + /// + /// ایجاد درگاه پرداخت سپهر برای یک تراکنش + /// + /// مبلغ + /// شناسه طرف قرارداد + /// آدرس بازگشتی به فرانت برای نمایش نتیجه + /// آدرس بازگشتی درگاه پرداخت + /// شناسه فاکتور مالی (اختیاری) - این پارامتر مستقیماً به درگاه فرستاده می‌شود + /// داده‌های اضافی سفارشی برای payload (اختیاری) + /// توکن لغو + /// شامل Token درگاه یا OperationResult با خطا + Task> CreateSepehrPaymentGateway( + double amount, + long contractingPartyId, + long financialInvoiceId, + string gatewayCallbackUrl, + string frontCallbackUrl="https://client.gozareshgir.ir", + Dictionary extraData = null, + CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/CompanyManagment.Application/FinancialInvoiceApplication.cs b/CompanyManagment.Application/FinancialInvoiceApplication.cs index 59a7fdd9..c2b6ffbf 100644 --- a/CompanyManagment.Application/FinancialInvoiceApplication.cs +++ b/CompanyManagment.Application/FinancialInvoiceApplication.cs @@ -9,15 +9,13 @@ using CompanyManagment.EFCore; namespace CompanyManagment.Application; -public class FinancialInvoiceApplication : RepositoryBase, IFinancialInvoiceApplication +public class FinancialInvoiceApplication : IFinancialInvoiceApplication { private readonly IFinancialInvoiceRepository _financialInvoiceRepository; - private readonly CompanyContext _context; - public FinancialInvoiceApplication(IFinancialInvoiceRepository financialInvoiceRepository, CompanyContext context) : base(context) + public FinancialInvoiceApplication(IFinancialInvoiceRepository financialInvoiceRepository) { _financialInvoiceRepository = financialInvoiceRepository; - _context = context; } public OperationResult Create(CreateFinancialInvoice command) @@ -185,6 +183,7 @@ public class FinancialInvoiceApplication : RepositoryBase x.ContractingPartyId == command.ContractingPartyId)) { - var financialStatment = _financialStatmentRepository.GetDetailsByContractingPartyId(command.ContractingPartyId); + var financialStatment = + _financialStatmentRepository.GetDetailsByContractingPartyId(command.ContractingPartyId); var transaction = new CreateFinancialTransaction() { FinancialStatementId = financialStatment.Id, @@ -124,20 +132,15 @@ public class FinancialStatmentApplication : IFinancialStatmentApplication Creditor = creditor, TypeOfTransaction = command.TypeOfTransaction, DescriptionOption = command.DescriptionOption - - - }; - - var createTransaction = _financialTransactionApplication.Create(transaction); + + var createTransaction = _financialTransactionApplication.Create(transaction); if (createTransaction.IsSuccedded) return op.Succcedded(); return op.Failed("خطا در انجام عملیات"); - } else { - var statement = new FinancialStatment(command.ContractingPartyId, command.ContractingPartyName); _financialStatmentRepository.Create(statement); _financialStatmentRepository.SaveChanges(); @@ -153,20 +156,14 @@ public class FinancialStatmentApplication : IFinancialStatmentApplication Balance = 0, TypeOfTransaction = command.TypeOfTransaction, DescriptionOption = command.DescriptionOption - - }; var createTransaction = _financialTransactionApplication.Create(transaction); if (createTransaction.IsSuccedded) return op.Succcedded(); return op.Failed("خطا در انجام عملیات"); - } - } - - public List Search(FinancialStatmentSearchModel searchModel) { @@ -203,6 +200,45 @@ public class FinancialStatmentApplication : IFinancialStatmentApplication public async Task GetDetailsByContractingParty( long contractingPartyId, FinancialStatementSearchModel searchModel) { - return await _financialStatmentRepository.GetDetailsByContractingParty(contractingPartyId,searchModel); + return await _financialStatmentRepository.GetDetailsByContractingParty(contractingPartyId, searchModel); + } + + public async Task> CreatePaymentGateWayAndCreateInvoice( + CreateFinancialPayRequest request, string gateWayCallBackUrl) + { + var op = new OperationResult(); + // گام 1: دریافت موجودی حساب + var balanceAmount = await GetBalanceAmount(request.Id); + if (balanceAmount.Amount <= 0) + { + return op.Failed("موجودی حساب شما صفر است"); + } + // گام 2: ایجاد درگاه پرداخت سپهر + + var financialInvoice = await _financialInvoiceRepository + .GetUnPaidFinancialInvoiceByContractingPartyIdAndAmount(balanceAmount.ContractingPartyId, + balanceAmount.Amount); + + if (financialInvoice == null) + { + financialInvoice = new FinancialInvoice(balanceAmount.Amount, balanceAmount.ContractingPartyId, + "پرداخت بدهی صورت حساب مالی"); + + var items = new FinancialInvoiceItem("پرداخت بدهی صورت حساب مالی", balanceAmount.Amount, + financialInvoice.id, FinancialInvoiceItemType.PreviousDebt, 0); + financialInvoice.AddItem(items); + await _financialInvoiceRepository.CreateAsync(financialInvoice); + await _financialInvoiceRepository.SaveChangesAsync(); + } + + var gatewayResult = await _sepehrPaymentGatewayService.CreateSepehrPaymentGateway( + amount: balanceAmount.Amount, + contractingPartyId: balanceAmount.ContractingPartyId, + frontCallbackUrl: request.BaseUrl, + gatewayCallbackUrl: gateWayCallBackUrl, + financialInvoiceId: financialInvoice.id, + extraData: null); + + return gatewayResult; } } \ No newline at end of file diff --git a/CompanyManagment.Application/InstitutionContractApplication.cs b/CompanyManagment.Application/InstitutionContractApplication.cs index c4ea7ce4..e844c1ff 100644 --- a/CompanyManagment.Application/InstitutionContractApplication.cs +++ b/CompanyManagment.Application/InstitutionContractApplication.cs @@ -24,6 +24,7 @@ using CompanyManagment.App.Contracts.FinancialStatment; using CompanyManagment.App.Contracts.InstitutionContract; using CompanyManagment.App.Contracts.InstitutionContractContactinfo; using CompanyManagment.App.Contracts.PaymentTransaction; +using CompanyManagment.App.Contracts.SepehrPaymentGateway; using CompanyManagment.App.Contracts.Workshop; using PersianTools.Core; using ConnectedPersonnelViewModel = CompanyManagment.App.Contracts.Workshop.ConnectedPersonnelViewModel; @@ -48,6 +49,7 @@ public class InstitutionContractApplication : IInstitutionContractApplication private readonly IPaymentGateway _paymentGateway; private readonly IPaymentTransactionRepository _paymentTransactionRepository; private readonly IRollCallServiceRepository _rollCallServiceRepository; + private readonly ISepehrPaymentGatewayService _sepehrPaymentGatewayService; public InstitutionContractApplication(IInstitutionContractRepository institutionContractRepository, @@ -58,7 +60,8 @@ public class InstitutionContractApplication : IInstitutionContractApplication IFinancialStatmentRepository financialStatmentRepository, IContactInfoApplication contactInfoApplication, IAccountApplication accountApplication, ISmsService smsService, IFinancialInvoiceRepository financialInvoiceRepository, IHttpClientFactory httpClientFactory, - IPaymentTransactionRepository paymentTransactionRepository, IRollCallServiceRepository rollCallServiceRepository) + IPaymentTransactionRepository paymentTransactionRepository, IRollCallServiceRepository rollCallServiceRepository, + ISepehrPaymentGatewayService sepehrPaymentGatewayService) { _institutionContractRepository = institutionContractRepository; _contractingPartyRepository = contractingPartyRepository; @@ -74,6 +77,7 @@ public class InstitutionContractApplication : IInstitutionContractApplication _financialInvoiceRepository = financialInvoiceRepository; _paymentTransactionRepository = paymentTransactionRepository; _rollCallServiceRepository = rollCallServiceRepository; + _sepehrPaymentGatewayService = sepehrPaymentGatewayService; _paymentGateway = new SepehrPaymentGateway(httpClientFactory); } @@ -1258,119 +1262,93 @@ public class InstitutionContractApplication : IInstitutionContractApplication if (contractingParty == null) throw new NotFoundException("طرف قرارداد یافت نشد"); - if (institutionContract.VerifyCode != code) - return op.Failed("کد وارد شده صحیح نمی باشد"); - - var financialStatement =await _financialStatmentRepository.GetByContractingPartyId(contractingParty.id); + if (institutionContract.VerifyCode != code) + return op.Failed("کد وارد شده صحیح نمی باشد"); - var dbTransaction = await _institutionContractRepository.BeginTransactionAsync(); - FinancialInvoice financialInvoice; - FinancialInvoiceItem financialInvoiceItem; - var today = DateTime.Today; - double invoiceAmount = 0; - string invoiceItemDescription = string.Empty; - FinancialInvoiceItemType invoiceItemType = FinancialInvoiceItemType.BuyInstitutionContract; - long invoiceItemEntityId = 0; + var financialStatement = await _financialStatmentRepository.GetByContractingPartyId(contractingParty.id); - if (institutionContract.IsInstallment) - { - var firstInstallment = institutionContract.Installments.First(); - var firstInstallmentAmount = firstInstallment.Amount; + var dbTransaction = await _institutionContractRepository.BeginTransactionAsync(); + FinancialInvoice financialInvoice; + FinancialInvoiceItem financialInvoiceItem; + var today = DateTime.Today; + double invoiceAmount = 0; + string invoiceItemDescription = string.Empty; + FinancialInvoiceItemType invoiceItemType = FinancialInvoiceItemType.BuyInstitutionContract; + long invoiceItemEntityId = 0; - financialInvoice = await _financialInvoiceRepository.GetUnPaidByEntityId(firstInstallment.Id, FinancialInvoiceItemType.BuyInstitutionContractInstallment); - if (financialInvoice == null) - { - var financialTransaction = new FinancialTransaction(0, today, today.ToFarsi(), - "قسط اول سرویس", "debt", "بابت خدمات", firstInstallmentAmount, 0, 0); - financialStatement.AddFinancialTransaction(financialTransaction); - invoiceAmount = firstInstallmentAmount; - invoiceItemDescription = $"پرداخت قسط اول قرارداد شماره {institutionContract.ContractNo}"; - invoiceItemType = FinancialInvoiceItemType.BuyInstitutionContractInstallment; - invoiceItemEntityId = firstInstallment.Id; - } - else - { - invoiceAmount = financialInvoice.Amount; - invoiceItemDescription = financialInvoice.Items.First().Description; - invoiceItemType = financialInvoice.Items.First().Type; - invoiceItemEntityId = financialInvoice.Items.First().EntityId; - } - } - else - { - financialInvoice = await _financialInvoiceRepository.GetUnPaidByEntityId(institutionContract.id, FinancialInvoiceItemType.BuyInstitutionContract); - if (financialInvoice == null) - { - var financialTransaction = new FinancialTransaction(0, today, today.ToFarsi(), - "پرداخت کل سرویس", "debt", "بابت خدمات", institutionContract.TotalAmount, 0, 0); - financialStatement.AddFinancialTransaction(financialTransaction); - invoiceAmount = institutionContract.TotalAmount; - invoiceItemDescription = $"پرداخت کل قرارداد شماره {institutionContract.ContractNo}"; - invoiceItemType = FinancialInvoiceItemType.BuyInstitutionContract; - invoiceItemEntityId = institutionContract.id; - } - else - { - invoiceAmount = financialInvoice.Amount; - invoiceItemDescription = financialInvoice.Items.First().Description; - invoiceItemType = financialInvoice.Items.First().Type; - invoiceItemEntityId = financialInvoice.Items.First().EntityId; - } - } + if (institutionContract.IsInstallment) + { + var firstInstallment = institutionContract.Installments.First(); + var firstInstallmentAmount = firstInstallment.Amount; - if (financialInvoice == null) - { - financialInvoice = new FinancialInvoice(invoiceAmount, contractingParty.id, $"خرید قرارداد مالی شماره {institutionContract.ContractNo}"); - financialInvoiceItem = new FinancialInvoiceItem(invoiceItemDescription, invoiceAmount, 0, invoiceItemType, invoiceItemEntityId); - financialInvoice.AddItem(financialInvoiceItem); - await _financialInvoiceRepository.CreateAsync(financialInvoice); - } + financialInvoice = await _financialInvoiceRepository.GetUnPaidByEntityId(firstInstallment.Id, FinancialInvoiceItemType.BuyInstitutionContractInstallment); + if (financialInvoice == null) + { + var financialTransaction = new FinancialTransaction(0, today, today.ToFarsi(), + "قسط اول سرویس", "debt", "بابت خدمات", firstInstallmentAmount, 0, 0); + financialStatement.AddFinancialTransaction(financialTransaction); + invoiceAmount = firstInstallmentAmount; + invoiceItemDescription = $"پرداخت قسط اول قرارداد شماره {institutionContract.ContractNo}"; + invoiceItemType = FinancialInvoiceItemType.BuyInstitutionContractInstallment; + invoiceItemEntityId = firstInstallment.Id; + } + else + { + invoiceAmount = financialInvoice.Amount; + invoiceItemDescription = financialInvoice.Items.First().Description; + invoiceItemType = financialInvoice.Items.First().Type; + invoiceItemEntityId = financialInvoice.Items.First().EntityId; + } + } + else + { + financialInvoice = await _financialInvoiceRepository.GetUnPaidByEntityId(institutionContract.id, FinancialInvoiceItemType.BuyInstitutionContract); + if (financialInvoice == null) + { + var financialTransaction = new FinancialTransaction(0, today, today.ToFarsi(), + "پرداخت کل سرویس", "debt", "بابت خدمات", institutionContract.TotalAmount, 0, 0); + financialStatement.AddFinancialTransaction(financialTransaction); + invoiceAmount = institutionContract.TotalAmount; + invoiceItemDescription = $"پرداخت کل قرارداد شماره {institutionContract.ContractNo}"; + invoiceItemType = FinancialInvoiceItemType.BuyInstitutionContract; + invoiceItemEntityId = institutionContract.id; + } + else + { + invoiceAmount = financialInvoice.Amount; + invoiceItemDescription = financialInvoice.Items.First().Description; + invoiceItemType = financialInvoice.Items.First().Type; + invoiceItemEntityId = financialInvoice.Items.First().EntityId; + } + } - await _financialInvoiceRepository.SaveChangesAsync(); + if (financialInvoice == null) + { + financialInvoice = new FinancialInvoice(invoiceAmount, contractingParty.id, $"خرید قرارداد مالی شماره {institutionContract.ContractNo}"); + financialInvoiceItem = new FinancialInvoiceItem(invoiceItemDescription, invoiceAmount, 0, invoiceItemType, invoiceItemEntityId); + financialInvoice.AddItem(financialInvoiceItem); + await _financialInvoiceRepository.CreateAsync(financialInvoice); + } - var transaction = new PaymentTransaction(institutionContract.ContractingPartyId, invoiceAmount, - institutionContract.ContractingPartyName, "https://client.gozareshgir.ir", - PaymentTransactionGateWay.SepehrPay); - await _paymentTransactionRepository.CreateAsync(transaction); - await _financialInvoiceRepository.SaveChangesAsync(); + await _financialInvoiceRepository.SaveChangesAsync(); - var createPayment = new CreatePaymentGatewayRequest() - { - Amount = invoiceAmount, - TransactionId = transaction.id.ToString(), - CallBackUrl = callbackUrl, - FinancialInvoiceId = financialInvoice.id, - }; - var gatewayResponse = await _paymentGateway.Create(createPayment); - if (!gatewayResponse.IsSuccess) - return op.Failed("خطا در ایجاد درگاه پرداخت: " + gatewayResponse.Message + gatewayResponse.ErrorCode); + // استفاده از سرویس مشترک برای ایجاد درگاه پرداخت + var gatewayResult = await _sepehrPaymentGatewayService.CreateSepehrPaymentGateway( + amount: (long)invoiceAmount, + contractingPartyId: institutionContract.ContractingPartyId, + gatewayCallbackUrl: callbackUrl, + financialInvoiceId: financialInvoice.id, + extraData: null); - - // institutionContract.SetPendingWorkflow(); - // - // var phone = institutionContract.ContactInfoList.FirstOrDefault(x => - // x.SendSms && x.Position == "طرف قرارداد" && x.PhoneType == "شماره همراه"); - // if (phone !=null) - // { - // var userPass = contractingParty.IsLegal == "حقیقی" - // ? contractingParty.Nationalcode - // : contractingParty.NationalId; - // var createAcc = new RegisterAccount - // { - // Fullname = contractingParty.LName, - // Username = userPass, - // Password = userPass, - // Mobile = phone.PhoneNumber, - // NationalCode = userPass - // }; - // var res = _accountApplication.RegisterClient(createAcc); - // if (res.IsSuccedded) - // CreateContractingPartyAccount(contractingParty.id, res.SendId); - // } + if (!gatewayResult.IsSuccedded) + { + await dbTransaction.RollbackAsync(); + return op.Failed(gatewayResult.Message); + } await dbTransaction.CommitAsync(); await _institutionContractRepository.SaveChangesAsync(); - return op.Succcedded(gatewayResponse.Token); + return op.Succcedded(gatewayResult.Data.Token); } public async Task GetWorkshopInitialDetails(long workshopDetailsId) diff --git a/CompanyManagment.Application/SepehrPaymentGatewayService.cs b/CompanyManagment.Application/SepehrPaymentGatewayService.cs new file mode 100644 index 00000000..dc9749cf --- /dev/null +++ b/CompanyManagment.Application/SepehrPaymentGatewayService.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using _0_Framework.Application; +using _0_Framework.Application.PaymentGateway; +using CompanyManagment.App.Contracts.PaymentTransaction; +using CompanyManagment.App.Contracts.SepehrPaymentGateway; + +namespace CompanyManagment.Application; + +/// +/// سرویس مشترک برای ایجاد درگاه پرداخت سپهر +/// +public class SepehrPaymentGatewayService : ISepehrPaymentGatewayService +{ + private readonly IPaymentGateway _paymentGateway; + private readonly IPaymentTransactionApplication _paymentTransactionApplication; + + public SepehrPaymentGatewayService( + IPaymentTransactionApplication paymentTransactionApplication, + IHttpClientFactory httpClientFactory) + { + _paymentGateway = new SepehrPaymentGateway(httpClientFactory); + _paymentTransactionApplication = paymentTransactionApplication; + } + + /// + /// ایجاد درگاه پرداخت سپهر برای یک تراکنش + /// + /// مبلغ + /// شناسه طرف قرارداد + /// آدرس بازگشتی به فرانت برای نمایش نتیجه + /// آدرس بازگشتی درگاه پرداخت + /// شناسه فاکتور مالی (اختیاری) + /// داده‌های اضافی (اختیاری) + /// توکن لغو + /// شامل Token درگاه یا OperationResult با خطا + public async Task> CreateSepehrPaymentGateway( + double amount, + long contractingPartyId, + long financialInvoiceId, + string gatewayCallbackUrl, + string frontCallbackUrl="https://client.gozareshgir.ir", + Dictionary extraData = null, + CancellationToken cancellationToken = default) + { + var op = new OperationResult(); + + try + { + // گام 1: ایجاد تراکنش پرداخت + var transactionCommand = new CreatePaymentTransaction() + { + Amount = amount, + ContractingPartyId = contractingPartyId, + CallBackUrl = frontCallbackUrl, + Gateway = PaymentTransactionGateWay.SepehrPay + }; + + var transactionResult = await _paymentTransactionApplication.Create(transactionCommand); + + if (!transactionResult.IsSuccedded) + { + return op.Failed(transactionResult.Message); + } + + // گام 2: ایجاد درخواست درگاه پرداخت + extraData ??= new Dictionary(); + + var createPaymentCommand = new CreatePaymentGatewayRequest() + { + Amount = amount, + TransactionId = transactionResult.SendId.ToString(), + CallBackUrl = gatewayCallbackUrl, + FinancialInvoiceId = financialInvoiceId, + ExtraData = extraData + }; + + // گام 3: ارسال درخواست به درگاه سپهر + var gatewayResponse = await _paymentGateway.Create(createPaymentCommand, cancellationToken); + +#if DEBUG + gatewayResponse.IsSuccess = true; +#endif + if (!gatewayResponse.IsSuccess) + { + return op.Failed($"خطا در ایجاد درگاه پرداخت: {gatewayResponse.Message ?? gatewayResponse.ErrorCode?.ToString()}"); + } + + // گام 4: ذخیره Token در تراکنش + var setTokenResult = await _paymentTransactionApplication.SetTransactionId( + transactionResult.SendId, + gatewayResponse.Token); + + if (!setTokenResult.IsSuccedded) + { + return op.Failed("خطا در ذخیره Token درگاه"); + } + + // گام 5: بازگشت اطلاعات درگاه پرداخت + var response = new CreateSepehrPaymentGatewayResponse + { + Token = gatewayResponse.Token, + TransactionId = transactionResult.SendId, + PaymentUrl = _paymentGateway.GetStartPayUrl(gatewayResponse.Token) + }; + + return op.Succcedded(response); + } + catch (Exception ex) + { + return op.Failed($"خطا در ایجاد درگاه پرداخت: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/CompanyManagment.EFCore/Repository/FinancialInvoiceRepository.cs b/CompanyManagment.EFCore/Repository/FinancialInvoiceRepository.cs index b2db131a..a5e65908 100644 --- a/CompanyManagment.EFCore/Repository/FinancialInvoiceRepository.cs +++ b/CompanyManagment.EFCore/Repository/FinancialInvoiceRepository.cs @@ -101,4 +101,12 @@ public class FinancialInvoiceRepository : RepositoryBase .Where(x => x.Status == FinancialInvoiceStatus.Unpaid).FirstOrDefaultAsync(x => x.Items .Any(y => y.Type == financialInvoiceItemType && y.EntityId == entityId)); } + + public async Task GetUnPaidFinancialInvoiceByContractingPartyIdAndAmount(long contractingPartyId, + double amount) + { + return await _context.FinancialInvoices.FirstOrDefaultAsync(x=>x.ContractingPartyId == contractingPartyId && + x.Amount == amount && + x.Status == FinancialInvoiceStatus.Unpaid); + } } \ No newline at end of file diff --git a/PersonalContractingParty.Config/PersonalBootstrapper.cs b/PersonalContractingParty.Config/PersonalBootstrapper.cs index e7256455..92e63a87 100644 --- a/PersonalContractingParty.Config/PersonalBootstrapper.cs +++ b/PersonalContractingParty.Config/PersonalBootstrapper.cs @@ -234,6 +234,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using P_TextManager.Domin.TextManagerAgg; using CompanyManagment.App.Contracts.PaymentCallback; +using CompanyManagment.App.Contracts.SepehrPaymentGateway; using Company.Domain.CameraBugReportAgg; using CompanyManagment.App.Contracts.CameraBugReport; using CameraBugReportRepository = CompanyManagement.Infrastructure.Mongo.CameraBugReportRepo.CameraBugReportRepository; @@ -484,6 +485,7 @@ public class PersonalBootstrapper services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/ServiceHost/Areas/Client/Controllers/FinancialController.cs b/ServiceHost/Areas/Client/Controllers/FinancialController.cs index 8736c764..68d8d494 100644 --- a/ServiceHost/Areas/Client/Controllers/FinancialController.cs +++ b/ServiceHost/Areas/Client/Controllers/FinancialController.cs @@ -1,8 +1,10 @@ using _0_Framework.Application; using _0_Framework.Application.PaymentGateway; +using CompanyManagment.App.Contracts.FinancialInvoice; using CompanyManagment.App.Contracts.FinancialStatment; using CompanyManagment.App.Contracts.FinancilTransaction; using CompanyManagment.App.Contracts.PaymentTransaction; +using CompanyManagment.App.Contracts.SepehrPaymentGateway; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; @@ -10,20 +12,19 @@ using ServiceHost.BaseControllers; namespace ServiceHost.Areas.Client.Controllers; -public record CreateFinancialPayRequest(long Id, string BaseUrl); public class FinancialController : ClientBaseController { private readonly IFinancialStatmentApplication _financialStatementApplication; private readonly IAuthHelper _authHelper; - private readonly IPaymentGateway _paymentGateway; - private readonly IPaymentTransactionApplication _paymentTransactionApplication; + private readonly ISepehrPaymentGatewayService _sepehrPaymentGatewayService; + private readonly IFinancialInvoiceApplication _financialInvoiceApplication; - public FinancialController(IFinancialStatmentApplication financialStatementApplication, IAuthHelper authHelper,IHttpClientFactory httpClientFactory, IPaymentTransactionApplication paymentTransactionApplication,IOptions appSetting) + public FinancialController(IFinancialStatmentApplication financialStatementApplication, IAuthHelper authHelper, ISepehrPaymentGatewayService sepehrPaymentGatewayService, IFinancialInvoiceApplication financialInvoiceApplication) { _financialStatementApplication = financialStatementApplication; _authHelper = authHelper; - _paymentTransactionApplication = paymentTransactionApplication; - _paymentGateway = new SepehrPaymentGateway(httpClientFactory); + _sepehrPaymentGatewayService = sepehrPaymentGatewayService; + _financialInvoiceApplication = financialInvoiceApplication; } [HttpGet] @@ -45,65 +46,28 @@ public class FinancialController : ClientBaseController return result; } /// - /// ساخت + /// ساخت درگاه پرداخت برای موجودی حساب کلاینت /// - /// - /// - /// - /// [HttpPost("CreatePay")] [AllowAnonymous] - public async Task>> CreatePay([FromForm] CreateFinancialPayRequest request, CancellationToken cancellationToken) + public async Task>> CreatePay([FromForm] CreateFinancialPayRequest request) { var op = new OperationResult(); - var balanceAmount = await _financialStatementApplication.GetBalanceAmount(request.Id); - if (balanceAmount.Amount<=0) - { - return op.Failed("موجودی حساب شما صفر است"); - } - var callbackUrl = Url.Action( + var gateWayCallBackUrl = Url.Action( action: "Verify", controller: "General", // نام کنترلر بدون کلمه‌ی "Controller" values: null, protocol: Request.Scheme); // http یا https + // گام 2: ایجاد درگاه پرداخت سپهر - var transactionCommand = new CreatePaymentTransaction() + var gatewayResult = await _financialStatementApplication.CreatePaymentGateWayAndCreateInvoice(request, gateWayCallBackUrl); + if (!gatewayResult.IsSuccedded) { - Amount = balanceAmount.Amount, - ContractingPartyId = balanceAmount.ContractingPartyId, - CallBackUrl = request.BaseUrl, - Gateway = PaymentTransactionGateWay.SepehrPay - }; - - var transaction = await _paymentTransactionApplication.Create(transactionCommand); - - if (!transaction.IsSuccedded) - { - return op.Failed(transaction.Message); + return op.Failed(gatewayResult.Message); } - var command = new CreatePaymentGatewayRequest() - { - CallBackUrl = callbackUrl, - Amount = balanceAmount.Amount, - TransactionId = transaction.SendId.ToString(), - }; - - var gatewayResponse = await _paymentGateway.Create(command, cancellationToken); - - if (gatewayResponse.IsSuccess) - { - _ = await _paymentTransactionApplication.SetTransactionId(transaction.SendId, gatewayResponse.Token); - return Redirect(_paymentGateway.GetStartPayUrl(gatewayResponse.Token)); - } - - if (gatewayResponse.ErrorCode.HasValue) - { - return op.Failed($"خطا در ایجاد درگاه پرداخت: {gatewayResponse.ErrorCode.Value}"); - } - - return op.Failed("خطا در ایجاد درگاه پرداخت"); - + // گام 3: بازگشتی به درگاه پرداخت + return Redirect(gatewayResult.Data.PaymentUrl); } } \ No newline at end of file From 8e72b56758bf7f893dd6d4535bc434a6d530945c Mon Sep 17 00:00:00 2001 From: mahan Date: Wed, 31 Dec 2025 18:16:51 +0330 Subject: [PATCH 4/4] refactor: format BlockMessageForElectronicContract method parameters for improved readability --- 0_Framework/Application/Sms/ISmsService.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/0_Framework/Application/Sms/ISmsService.cs b/0_Framework/Application/Sms/ISmsService.cs index 53137858..9590bf27 100644 --- a/0_Framework/Application/Sms/ISmsService.cs +++ b/0_Framework/Application/Sms/ISmsService.cs @@ -85,7 +85,9 @@ public interface ISmsService /// /// /// - Task<(byte status, string message, int messaeId, bool isSucceded)> BlockMessageForElectronicContract(string number, string fullname, string amount, string code1, string code2) + Task<(byte status, string message, int messaeId, bool isSucceded)> BlockMessageForElectronicContract(string number, + string fullname, + string amount, string code1, string code2); #endregion #region AlarmMessage