From 66a6c411d667aa3aade72209dc1c94550fdc15ab Mon Sep 17 00:00:00 2001 From: mahan Date: Wed, 31 Dec 2025 15:49:44 +0330 Subject: [PATCH] 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)); }