237 lines
11 KiB
C#
237 lines
11 KiB
C#
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// تأیید و پردازش callback درگاه پرداخت سپهر
|
|
/// </summary>
|
|
public async Task<OperationResult> 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<IDictionary<string, object>>(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}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// مدیریت آپدیت قراردادهای نهادی
|
|
/// </summary>
|
|
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();
|
|
}
|
|
} |