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; using Microsoft.Extensions.Logging; 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, ILogger sepehrGatewayLogger) { _paymentTransactionApplication = paymentTransactionApplication; _financialStatmentApplication = financialStatmentApplication; _financialInvoiceApplication = financialInvoiceApplication; _institutionContractApplication = institutionContractApplication; _institutionContractRepository = institutionContractRepository; _financialStatmentRepository = financialStatmentRepository; _paymentGateway = new SepehrPaymentGateway(httpClientFactory, sepehrGatewayLogger); } /// /// تأیید و پردازش 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: به‌روزرسانی وضعیت تراکنش 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, Deptor = 0, Creditor = command.Amount, DeptorString = "0", TypeOfTransaction = "credit", DescriptionOption = (financialInvoice.Description ?? "") + " شماره فاکتور: " + (financialInvoice.InvoiceNumber ?? ""), Description = "درگاه بانکی", }; var statementResult = _financialStatmentApplication.CreateFromBankGateway(createCreditStatementCommand); if (!statementResult.IsSuccedded) { _paymentTransactionApplication.SetFailed(command.InvoiceId); return operation.Failed("خطا در ایجاد سند مالی"); } // گام 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(); } }