add: implement Sepehr payment gateway integration and enhance financial invoice handling

This commit is contained in:
2025-12-31 18:12:27 +03:30
parent 7e3ea39d5b
commit 490a1a69d5
12 changed files with 352 additions and 181 deletions

View File

@@ -10,4 +10,6 @@ public interface IFinancialInvoiceRepository : IRepository<long, FinancialInvoic
EditFinancialInvoice GetDetails(long id); EditFinancialInvoice GetDetails(long id);
List<FinancialInvoiceViewModel> Search(FinancialInvoiceSearchModel searchModel); List<FinancialInvoiceViewModel> Search(FinancialInvoiceSearchModel searchModel);
Task<FinancialInvoice> GetUnPaidByEntityId(long entityId, FinancialInvoiceItemType financialInvoiceItemType); Task<FinancialInvoice> GetUnPaidByEntityId(long entityId, FinancialInvoiceItemType financialInvoiceItemType);
Task<FinancialInvoice> GetUnPaidFinancialInvoiceByContractingPartyIdAndAmount(long contractingPartyId,
double amount);
} }

View File

@@ -8,7 +8,7 @@ public class CreateFinancialInvoice
public double Amount { get; set; } public double Amount { get; set; }
public long ContractingPartyId { get; set; } public long ContractingPartyId { get; set; }
public string Description { get; set; } public string Description { get; set; }
public List<CreateFinancialInvoiceItem>? Items { get; set; } public List<CreateFinancialInvoiceItem> Items { get; set; }
} }
public class CreateFinancialInvoiceItem public class CreateFinancialInvoiceItem

View File

@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using CompanyManagment.App.Contracts.SepehrPaymentGateway;
namespace CompanyManagment.App.Contracts.FinancialStatment; namespace CompanyManagment.App.Contracts.FinancialStatment;
@@ -62,7 +63,17 @@ public interface IFinancialStatmentApplication
/// <returns></returns> /// <returns></returns>
Task<FinancialStatmentDetailsByContractingPartyViewModel> GetDetailsByContractingParty(long contractingPartyId, Task<FinancialStatmentDetailsByContractingPartyViewModel> GetDetailsByContractingParty(long contractingPartyId,
FinancialStatementSearchModel searchModel); FinancialStatementSearchModel searchModel);
/// <summary>
/// پردازش شارژ حساب از طریق درگاه پرداخت سپهر
/// </summary>
/// <param name="request"></param>
/// <param name="gateWayCallBackUrl">مسیر برگشت درگاه پرداخت</param>
///
Task<OperationResult<CreateSepehrPaymentGatewayResponse>> CreatePaymentGateWayAndCreateInvoice(
CreateFinancialPayRequest request, string gateWayCallBackUrl);
} }
public record CreateFinancialPayRequest(long Id, string BaseUrl);
public class FinancialStatmentDetailsByContractingPartyViewModel public class FinancialStatmentDetailsByContractingPartyViewModel
{ {

View File

@@ -0,0 +1,22 @@
namespace CompanyManagment.App.Contracts.SepehrPaymentGateway;
/// <summary>
/// پاسخ ایجاد درگاه پرداخت سپهر
/// </summary>
public class CreateSepehrPaymentGatewayResponse
{
/// <summary>
/// توکن درگاه
/// </summary>
public string Token { get; set; }
/// <summary>
/// شناسه تراکنش
/// </summary>
public long TransactionId { get; set; }
/// <summary>
/// URL درگاه پرداخت
/// </summary>
public string PaymentUrl { get; set; }
}

View File

@@ -0,0 +1,32 @@
using _0_Framework.Application;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace CompanyManagment.App.Contracts.SepehrPaymentGateway;
/// <summary>
/// رابط برای سرویس مشترک ایجاد درگاه پرداخت سپهر
/// </summary>
public interface ISepehrPaymentGatewayService
{
/// <summary>
/// ایجاد درگاه پرداخت سپهر برای یک تراکنش
/// </summary>
/// <param name="amount">مبلغ</param>
/// <param name="contractingPartyId">شناسه طرف قرارداد</param>
/// <param name="frontCallbackUrl">آدرس بازگشتی به فرانت برای نمایش نتیجه</param>
/// <param name="gatewayCallbackUrl">آدرس بازگشتی درگاه پرداخت</param>
/// <param name="financialInvoiceId">شناسه فاکتور مالی (اختیاری) - این پارامتر مستقیماً به درگاه فرستاده می‌شود</param>
/// <param name="extraData">داده‌های اضافی سفارشی برای payload (اختیاری)</param>
/// <param name="cancellationToken">توکن لغو</param>
/// <returns>شامل Token درگاه یا OperationResult با خطا</returns>
Task<OperationResult<CreateSepehrPaymentGatewayResponse>> CreateSepehrPaymentGateway(
double amount,
long contractingPartyId,
long financialInvoiceId,
string gatewayCallbackUrl,
string frontCallbackUrl="https://client.gozareshgir.ir",
Dictionary<string, object> extraData = null,
CancellationToken cancellationToken = default);
}

View File

@@ -9,15 +9,13 @@ using CompanyManagment.EFCore;
namespace CompanyManagment.Application; namespace CompanyManagment.Application;
public class FinancialInvoiceApplication : RepositoryBase<long, FinancialInvoice>, IFinancialInvoiceApplication public class FinancialInvoiceApplication : IFinancialInvoiceApplication
{ {
private readonly IFinancialInvoiceRepository _financialInvoiceRepository; private readonly IFinancialInvoiceRepository _financialInvoiceRepository;
private readonly CompanyContext _context;
public FinancialInvoiceApplication(IFinancialInvoiceRepository financialInvoiceRepository, CompanyContext context) : base(context) public FinancialInvoiceApplication(IFinancialInvoiceRepository financialInvoiceRepository)
{ {
_financialInvoiceRepository = financialInvoiceRepository; _financialInvoiceRepository = financialInvoiceRepository;
_context = context;
} }
public OperationResult Create(CreateFinancialInvoice command) public OperationResult Create(CreateFinancialInvoice command)
@@ -185,6 +183,7 @@ public class FinancialInvoiceApplication : RepositoryBase<long, FinancialInvoice
{ {
return _financialInvoiceRepository.Search(searchModel); return _financialInvoiceRepository.Search(searchModel);
} }
//public OperationResult Remove(long id) //public OperationResult Remove(long id)
//{ //{

View File

@@ -3,9 +3,12 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using _0_Framework.Application; using _0_Framework.Application;
using Company.Domain.ContarctingPartyAgg; using Company.Domain.ContarctingPartyAgg;
using Company.Domain.FinancialInvoiceAgg;
using Company.Domain.FinancialStatmentAgg; using Company.Domain.FinancialStatmentAgg;
using CompanyManagment.App.Contracts.FinancialInvoice;
using CompanyManagment.App.Contracts.FinancialStatment; using CompanyManagment.App.Contracts.FinancialStatment;
using CompanyManagment.App.Contracts.FinancilTransaction; using CompanyManagment.App.Contracts.FinancilTransaction;
using CompanyManagment.App.Contracts.SepehrPaymentGateway;
using Microsoft.AspNetCore.Components.Forms; using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -16,12 +19,20 @@ public class FinancialStatmentApplication : IFinancialStatmentApplication
private readonly IFinancialStatmentRepository _financialStatmentRepository; private readonly IFinancialStatmentRepository _financialStatmentRepository;
private readonly IFinancialTransactionApplication _financialTransactionApplication; private readonly IFinancialTransactionApplication _financialTransactionApplication;
private readonly IPersonalContractingPartyRepository _contractingPartyRepository; private readonly IPersonalContractingPartyRepository _contractingPartyRepository;
private readonly IFinancialInvoiceRepository _financialInvoiceRepository;
private readonly ISepehrPaymentGatewayService _sepehrPaymentGatewayService;
public FinancialStatmentApplication(IFinancialStatmentRepository financialStatmentRepository, IFinancialTransactionApplication financialTransactionApplication, IPersonalContractingPartyRepository contractingPartyRepository) public FinancialStatmentApplication(IFinancialStatmentRepository financialStatmentRepository,
IFinancialTransactionApplication financialTransactionApplication,
IPersonalContractingPartyRepository contractingPartyRepository,
IFinancialInvoiceRepository financialInvoiceRepository,
ISepehrPaymentGatewayService sepehrPaymentGatewayService)
{ {
_financialStatmentRepository = financialStatmentRepository; _financialStatmentRepository = financialStatmentRepository;
_financialTransactionApplication = financialTransactionApplication; _financialTransactionApplication = financialTransactionApplication;
_contractingPartyRepository = contractingPartyRepository; _contractingPartyRepository = contractingPartyRepository;
_financialInvoiceRepository = financialInvoiceRepository;
_sepehrPaymentGatewayService = sepehrPaymentGatewayService;
} }
public OperationResult CreateFromBankGateway(CreateFinancialStatment command) public OperationResult CreateFromBankGateway(CreateFinancialStatment command)
@@ -51,7 +62,6 @@ public class FinancialStatmentApplication : IFinancialStatmentApplication
if (createTransaction.IsSuccedded) if (createTransaction.IsSuccedded)
return op.Succcedded(); return op.Succcedded();
return op.Failed("خطا در انجام عملیات"); return op.Failed("خطا در انجام عملیات");
} }
else else
{ {
@@ -71,8 +81,6 @@ public class FinancialStatmentApplication : IFinancialStatmentApplication
Balance = 0, Balance = 0,
TypeOfTransaction = command.TypeOfTransaction, TypeOfTransaction = command.TypeOfTransaction,
DescriptionOption = command.DescriptionOption DescriptionOption = command.DescriptionOption
}; };
var createTransaction = _financialTransactionApplication.Create(transaction); var createTransaction = _financialTransactionApplication.Create(transaction);
if (createTransaction.IsSuccedded) if (createTransaction.IsSuccedded)
@@ -98,22 +106,22 @@ public class FinancialStatmentApplication : IFinancialStatmentApplication
{ {
debtor = 0; debtor = 0;
creditor = command.CreditorString.MoneyToDouble(); creditor = command.CreditorString.MoneyToDouble();
} }
else if (command.TypeOfTransaction == "debt") else if (command.TypeOfTransaction == "debt")
{ {
creditor = 0; creditor = 0;
debtor = command.DeptorString.MoneyToDouble(); debtor = command.DeptorString.MoneyToDouble();
} }
if (!command.TdateFa.TryToGeorgianDateTime(out var tDateGr)) if (!command.TdateFa.TryToGeorgianDateTime(out var tDateGr))
{ {
return op.Failed("تاریخ وارد شده صحیح نمی باشد"); return op.Failed("تاریخ وارد شده صحیح نمی باشد");
} }
if (_financialStatmentRepository.Exists(x => x.ContractingPartyId == command.ContractingPartyId)) if (_financialStatmentRepository.Exists(x => x.ContractingPartyId == command.ContractingPartyId))
{ {
var financialStatment = _financialStatmentRepository.GetDetailsByContractingPartyId(command.ContractingPartyId); var financialStatment =
_financialStatmentRepository.GetDetailsByContractingPartyId(command.ContractingPartyId);
var transaction = new CreateFinancialTransaction() var transaction = new CreateFinancialTransaction()
{ {
FinancialStatementId = financialStatment.Id, FinancialStatementId = financialStatment.Id,
@@ -124,20 +132,15 @@ public class FinancialStatmentApplication : IFinancialStatmentApplication
Creditor = creditor, Creditor = creditor,
TypeOfTransaction = command.TypeOfTransaction, TypeOfTransaction = command.TypeOfTransaction,
DescriptionOption = command.DescriptionOption DescriptionOption = command.DescriptionOption
}; };
var createTransaction = _financialTransactionApplication.Create(transaction); var createTransaction = _financialTransactionApplication.Create(transaction);
if (createTransaction.IsSuccedded) if (createTransaction.IsSuccedded)
return op.Succcedded(); return op.Succcedded();
return op.Failed("خطا در انجام عملیات"); return op.Failed("خطا در انجام عملیات");
} }
else else
{ {
var statement = new FinancialStatment(command.ContractingPartyId, command.ContractingPartyName); var statement = new FinancialStatment(command.ContractingPartyId, command.ContractingPartyName);
_financialStatmentRepository.Create(statement); _financialStatmentRepository.Create(statement);
_financialStatmentRepository.SaveChanges(); _financialStatmentRepository.SaveChanges();
@@ -153,20 +156,14 @@ public class FinancialStatmentApplication : IFinancialStatmentApplication
Balance = 0, Balance = 0,
TypeOfTransaction = command.TypeOfTransaction, TypeOfTransaction = command.TypeOfTransaction,
DescriptionOption = command.DescriptionOption DescriptionOption = command.DescriptionOption
}; };
var createTransaction = _financialTransactionApplication.Create(transaction); var createTransaction = _financialTransactionApplication.Create(transaction);
if (createTransaction.IsSuccedded) if (createTransaction.IsSuccedded)
return op.Succcedded(); return op.Succcedded();
return op.Failed("خطا در انجام عملیات"); return op.Failed("خطا در انجام عملیات");
} }
} }
public List<FinancialStatmentViewModel> Search(FinancialStatmentSearchModel searchModel) public List<FinancialStatmentViewModel> Search(FinancialStatmentSearchModel searchModel)
{ {
@@ -203,6 +200,45 @@ public class FinancialStatmentApplication : IFinancialStatmentApplication
public async Task<FinancialStatmentDetailsByContractingPartyViewModel> GetDetailsByContractingParty( public async Task<FinancialStatmentDetailsByContractingPartyViewModel> GetDetailsByContractingParty(
long contractingPartyId, FinancialStatementSearchModel searchModel) long contractingPartyId, FinancialStatementSearchModel searchModel)
{ {
return await _financialStatmentRepository.GetDetailsByContractingParty(contractingPartyId,searchModel); return await _financialStatmentRepository.GetDetailsByContractingParty(contractingPartyId, searchModel);
}
public async Task<OperationResult<CreateSepehrPaymentGatewayResponse>> CreatePaymentGateWayAndCreateInvoice(
CreateFinancialPayRequest request, string gateWayCallBackUrl)
{
var op = new OperationResult<CreateSepehrPaymentGatewayResponse>();
// گام 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;
} }
} }

View File

@@ -24,6 +24,7 @@ using CompanyManagment.App.Contracts.FinancialStatment;
using CompanyManagment.App.Contracts.InstitutionContract; using CompanyManagment.App.Contracts.InstitutionContract;
using CompanyManagment.App.Contracts.InstitutionContractContactinfo; using CompanyManagment.App.Contracts.InstitutionContractContactinfo;
using CompanyManagment.App.Contracts.PaymentTransaction; using CompanyManagment.App.Contracts.PaymentTransaction;
using CompanyManagment.App.Contracts.SepehrPaymentGateway;
using CompanyManagment.App.Contracts.Workshop; using CompanyManagment.App.Contracts.Workshop;
using PersianTools.Core; using PersianTools.Core;
using ConnectedPersonnelViewModel = CompanyManagment.App.Contracts.Workshop.ConnectedPersonnelViewModel; using ConnectedPersonnelViewModel = CompanyManagment.App.Contracts.Workshop.ConnectedPersonnelViewModel;
@@ -48,6 +49,7 @@ public class InstitutionContractApplication : IInstitutionContractApplication
private readonly IPaymentGateway _paymentGateway; private readonly IPaymentGateway _paymentGateway;
private readonly IPaymentTransactionRepository _paymentTransactionRepository; private readonly IPaymentTransactionRepository _paymentTransactionRepository;
private readonly IRollCallServiceRepository _rollCallServiceRepository; private readonly IRollCallServiceRepository _rollCallServiceRepository;
private readonly ISepehrPaymentGatewayService _sepehrPaymentGatewayService;
public InstitutionContractApplication(IInstitutionContractRepository institutionContractRepository, public InstitutionContractApplication(IInstitutionContractRepository institutionContractRepository,
@@ -58,7 +60,8 @@ public class InstitutionContractApplication : IInstitutionContractApplication
IFinancialStatmentRepository financialStatmentRepository, IContactInfoApplication contactInfoApplication, IFinancialStatmentRepository financialStatmentRepository, IContactInfoApplication contactInfoApplication,
IAccountApplication accountApplication, ISmsService smsService, IAccountApplication accountApplication, ISmsService smsService,
IFinancialInvoiceRepository financialInvoiceRepository, IHttpClientFactory httpClientFactory, IFinancialInvoiceRepository financialInvoiceRepository, IHttpClientFactory httpClientFactory,
IPaymentTransactionRepository paymentTransactionRepository, IRollCallServiceRepository rollCallServiceRepository) IPaymentTransactionRepository paymentTransactionRepository, IRollCallServiceRepository rollCallServiceRepository,
ISepehrPaymentGatewayService sepehrPaymentGatewayService)
{ {
_institutionContractRepository = institutionContractRepository; _institutionContractRepository = institutionContractRepository;
_contractingPartyRepository = contractingPartyRepository; _contractingPartyRepository = contractingPartyRepository;
@@ -74,6 +77,7 @@ public class InstitutionContractApplication : IInstitutionContractApplication
_financialInvoiceRepository = financialInvoiceRepository; _financialInvoiceRepository = financialInvoiceRepository;
_paymentTransactionRepository = paymentTransactionRepository; _paymentTransactionRepository = paymentTransactionRepository;
_rollCallServiceRepository = rollCallServiceRepository; _rollCallServiceRepository = rollCallServiceRepository;
_sepehrPaymentGatewayService = sepehrPaymentGatewayService;
_paymentGateway = new SepehrPaymentGateway(httpClientFactory); _paymentGateway = new SepehrPaymentGateway(httpClientFactory);
} }
@@ -1258,119 +1262,93 @@ public class InstitutionContractApplication : IInstitutionContractApplication
if (contractingParty == null) if (contractingParty == null)
throw new NotFoundException("طرف قرارداد یافت نشد"); throw new NotFoundException("طرف قرارداد یافت نشد");
if (institutionContract.VerifyCode != code) if (institutionContract.VerifyCode != code)
return op.Failed("کد وارد شده صحیح نمی باشد"); return op.Failed("کد وارد شده صحیح نمی باشد");
var financialStatement =await _financialStatmentRepository.GetByContractingPartyId(contractingParty.id);
var dbTransaction = await _institutionContractRepository.BeginTransactionAsync(); var financialStatement = await _financialStatmentRepository.GetByContractingPartyId(contractingParty.id);
FinancialInvoice financialInvoice;
FinancialInvoiceItem financialInvoiceItem;
var today = DateTime.Today;
double invoiceAmount = 0;
string invoiceItemDescription = string.Empty;
FinancialInvoiceItemType invoiceItemType = FinancialInvoiceItemType.BuyInstitutionContract;
long invoiceItemEntityId = 0;
if (institutionContract.IsInstallment) var dbTransaction = await _institutionContractRepository.BeginTransactionAsync();
{ FinancialInvoice financialInvoice;
var firstInstallment = institutionContract.Installments.First(); FinancialInvoiceItem financialInvoiceItem;
var firstInstallmentAmount = firstInstallment.Amount; 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 (institutionContract.IsInstallment)
if (financialInvoice == null) {
{ var firstInstallment = institutionContract.Installments.First();
var financialTransaction = new FinancialTransaction(0, today, today.ToFarsi(), var firstInstallmentAmount = firstInstallment.Amount;
"قسط اول سرویس", "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 (financialInvoice == null) financialInvoice = await _financialInvoiceRepository.GetUnPaidByEntityId(firstInstallment.Id, FinancialInvoiceItemType.BuyInstitutionContractInstallment);
{ if (financialInvoice == null)
financialInvoice = new FinancialInvoice(invoiceAmount, contractingParty.id, $"خرید قرارداد مالی شماره {institutionContract.ContractNo}"); {
financialInvoiceItem = new FinancialInvoiceItem(invoiceItemDescription, invoiceAmount, 0, invoiceItemType, invoiceItemEntityId); var financialTransaction = new FinancialTransaction(0, today, today.ToFarsi(),
financialInvoice.AddItem(financialInvoiceItem); "قسط اول سرویس", "debt", "بابت خدمات", firstInstallmentAmount, 0, 0);
await _financialInvoiceRepository.CreateAsync(financialInvoice); 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, await _financialInvoiceRepository.SaveChangesAsync();
institutionContract.ContractingPartyName, "https://client.gozareshgir.ir",
PaymentTransactionGateWay.SepehrPay);
await _paymentTransactionRepository.CreateAsync(transaction);
await _financialInvoiceRepository.SaveChangesAsync();
var createPayment = new CreatePaymentGatewayRequest() // استفاده از سرویس مشترک برای ایجاد درگاه پرداخت
{ var gatewayResult = await _sepehrPaymentGatewayService.CreateSepehrPaymentGateway(
Amount = invoiceAmount, amount: (long)invoiceAmount,
TransactionId = transaction.id.ToString(), contractingPartyId: institutionContract.ContractingPartyId,
CallBackUrl = callbackUrl, gatewayCallbackUrl: callbackUrl,
FinancialInvoiceId = financialInvoice.id, financialInvoiceId: financialInvoice.id,
}; extraData: null);
var gatewayResponse = await _paymentGateway.Create(createPayment);
if (!gatewayResponse.IsSuccess)
return op.Failed("خطا در ایجاد درگاه پرداخت: " + gatewayResponse.Message + gatewayResponse.ErrorCode);
if (!gatewayResult.IsSuccedded)
// institutionContract.SetPendingWorkflow(); {
// await dbTransaction.RollbackAsync();
// var phone = institutionContract.ContactInfoList.FirstOrDefault(x => return op.Failed(gatewayResult.Message);
// 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);
// }
await dbTransaction.CommitAsync(); await dbTransaction.CommitAsync();
await _institutionContractRepository.SaveChangesAsync(); await _institutionContractRepository.SaveChangesAsync();
return op.Succcedded(gatewayResponse.Token); return op.Succcedded(gatewayResult.Data.Token);
} }
public async Task<InstitutionContractWorkshopDetailViewModel> GetWorkshopInitialDetails(long workshopDetailsId) public async Task<InstitutionContractWorkshopDetailViewModel> GetWorkshopInitialDetails(long workshopDetailsId)

View File

@@ -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;
/// <summary>
/// سرویس مشترک برای ایجاد درگاه پرداخت سپهر
/// </summary>
public class SepehrPaymentGatewayService : ISepehrPaymentGatewayService
{
private readonly IPaymentGateway _paymentGateway;
private readonly IPaymentTransactionApplication _paymentTransactionApplication;
public SepehrPaymentGatewayService(
IPaymentTransactionApplication paymentTransactionApplication,
IHttpClientFactory httpClientFactory)
{
_paymentGateway = new SepehrPaymentGateway(httpClientFactory);
_paymentTransactionApplication = paymentTransactionApplication;
}
/// <summary>
/// ایجاد درگاه پرداخت سپهر برای یک تراکنش
/// </summary>
/// <param name="amount">مبلغ</param>
/// <param name="contractingPartyId">شناسه طرف قرارداد</param>
/// <param name="frontCallbackUrl">آدرس بازگشتی به فرانت برای نمایش نتیجه</param>
/// <param name="gatewayCallbackUrl">آدرس بازگشتی درگاه پرداخت</param>
/// <param name="financialInvoiceId">شناسه فاکتور مالی (اختیاری)</param>
/// <param name="extraData">داده‌های اضافی (اختیاری)</param>
/// <param name="cancellationToken">توکن لغو</param>
/// <returns>شامل Token درگاه یا OperationResult با خطا</returns>
public async Task<OperationResult<CreateSepehrPaymentGatewayResponse>> CreateSepehrPaymentGateway(
double amount,
long contractingPartyId,
long financialInvoiceId,
string gatewayCallbackUrl,
string frontCallbackUrl="https://client.gozareshgir.ir",
Dictionary<string, object> extraData = null,
CancellationToken cancellationToken = default)
{
var op = new OperationResult<CreateSepehrPaymentGatewayResponse>();
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<string, object>();
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}");
}
}
}

View File

@@ -101,4 +101,12 @@ public class FinancialInvoiceRepository : RepositoryBase<long, FinancialInvoice>
.Where(x => x.Status == FinancialInvoiceStatus.Unpaid).FirstOrDefaultAsync(x => x.Items .Where(x => x.Status == FinancialInvoiceStatus.Unpaid).FirstOrDefaultAsync(x => x.Items
.Any(y => y.Type == financialInvoiceItemType && y.EntityId == entityId)); .Any(y => y.Type == financialInvoiceItemType && y.EntityId == entityId));
} }
public async Task<FinancialInvoice> GetUnPaidFinancialInvoiceByContractingPartyIdAndAmount(long contractingPartyId,
double amount)
{
return await _context.FinancialInvoices.FirstOrDefaultAsync(x=>x.ContractingPartyId == contractingPartyId &&
x.Amount == amount &&
x.Status == FinancialInvoiceStatus.Unpaid);
}
} }

View File

@@ -234,6 +234,7 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using P_TextManager.Domin.TextManagerAgg; using P_TextManager.Domin.TextManagerAgg;
using CompanyManagment.App.Contracts.PaymentCallback; using CompanyManagment.App.Contracts.PaymentCallback;
using CompanyManagment.App.Contracts.SepehrPaymentGateway;
using Company.Domain.CameraBugReportAgg; using Company.Domain.CameraBugReportAgg;
using CompanyManagment.App.Contracts.CameraBugReport; using CompanyManagment.App.Contracts.CameraBugReport;
using CameraBugReportRepository = CompanyManagement.Infrastructure.Mongo.CameraBugReportRepo.CameraBugReportRepository; using CameraBugReportRepository = CompanyManagement.Infrastructure.Mongo.CameraBugReportRepo.CameraBugReportRepository;
@@ -484,6 +485,7 @@ public class PersonalBootstrapper
services.AddTransient<IPaymentTransactionRepository, PaymentTransactionRepository>(); services.AddTransient<IPaymentTransactionRepository, PaymentTransactionRepository>();
services.AddTransient<IPaymentTransactionApplication, PaymentTransactionApplication>(); services.AddTransient<IPaymentTransactionApplication, PaymentTransactionApplication>();
services.AddTransient<IPaymentCallbackHandler, PaymentCallbackHandler>(); services.AddTransient<IPaymentCallbackHandler, PaymentCallbackHandler>();
services.AddTransient<ISepehrPaymentGatewayService, SepehrPaymentGatewayService>();
services.AddTransient<IContractingPartyBankAccountsApplication, ContractingPartyBankAccountsApplication>(); services.AddTransient<IContractingPartyBankAccountsApplication, ContractingPartyBankAccountsApplication>();
services.AddTransient<IContractingPartyBankAccountsRepository, ContractingPartyBankAccountsRepository>(); services.AddTransient<IContractingPartyBankAccountsRepository, ContractingPartyBankAccountsRepository>();

View File

@@ -1,8 +1,10 @@
using _0_Framework.Application; using _0_Framework.Application;
using _0_Framework.Application.PaymentGateway; using _0_Framework.Application.PaymentGateway;
using CompanyManagment.App.Contracts.FinancialInvoice;
using CompanyManagment.App.Contracts.FinancialStatment; using CompanyManagment.App.Contracts.FinancialStatment;
using CompanyManagment.App.Contracts.FinancilTransaction; using CompanyManagment.App.Contracts.FinancilTransaction;
using CompanyManagment.App.Contracts.PaymentTransaction; using CompanyManagment.App.Contracts.PaymentTransaction;
using CompanyManagment.App.Contracts.SepehrPaymentGateway;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@@ -10,20 +12,19 @@ using ServiceHost.BaseControllers;
namespace ServiceHost.Areas.Client.Controllers; namespace ServiceHost.Areas.Client.Controllers;
public record CreateFinancialPayRequest(long Id, string BaseUrl);
public class FinancialController : ClientBaseController public class FinancialController : ClientBaseController
{ {
private readonly IFinancialStatmentApplication _financialStatementApplication; private readonly IFinancialStatmentApplication _financialStatementApplication;
private readonly IAuthHelper _authHelper; private readonly IAuthHelper _authHelper;
private readonly IPaymentGateway _paymentGateway; private readonly ISepehrPaymentGatewayService _sepehrPaymentGatewayService;
private readonly IPaymentTransactionApplication _paymentTransactionApplication; private readonly IFinancialInvoiceApplication _financialInvoiceApplication;
public FinancialController(IFinancialStatmentApplication financialStatementApplication, IAuthHelper authHelper,IHttpClientFactory httpClientFactory, IPaymentTransactionApplication paymentTransactionApplication,IOptions<AppSettingConfiguration> appSetting) public FinancialController(IFinancialStatmentApplication financialStatementApplication, IAuthHelper authHelper, ISepehrPaymentGatewayService sepehrPaymentGatewayService, IFinancialInvoiceApplication financialInvoiceApplication)
{ {
_financialStatementApplication = financialStatementApplication; _financialStatementApplication = financialStatementApplication;
_authHelper = authHelper; _authHelper = authHelper;
_paymentTransactionApplication = paymentTransactionApplication; _sepehrPaymentGatewayService = sepehrPaymentGatewayService;
_paymentGateway = new SepehrPaymentGateway(httpClientFactory); _financialInvoiceApplication = financialInvoiceApplication;
} }
[HttpGet] [HttpGet]
@@ -45,65 +46,28 @@ public class FinancialController : ClientBaseController
return result; return result;
} }
/// <summary> /// <summary>
/// ساخت /// ساخت درگاه پرداخت برای موجودی حساب کلاینت
/// </summary> /// </summary>
/// <param name="id"></param>
/// <param name="baseUrl"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpPost("CreatePay")] [HttpPost("CreatePay")]
[AllowAnonymous] [AllowAnonymous]
public async Task<ActionResult<OperationResult<string>>> CreatePay([FromForm] CreateFinancialPayRequest request, CancellationToken cancellationToken) public async Task<ActionResult<OperationResult<string>>> CreatePay([FromForm] CreateFinancialPayRequest request)
{ {
var op = new OperationResult<string>(); var op = new OperationResult<string>();
var balanceAmount = await _financialStatementApplication.GetBalanceAmount(request.Id);
if (balanceAmount.Amount<=0)
{
return op.Failed("موجودی حساب شما صفر است");
}
var callbackUrl = Url.Action( var gateWayCallBackUrl = Url.Action(
action: "Verify", action: "Verify",
controller: "General", // نام کنترلر بدون کلمه‌ی "Controller" controller: "General", // نام کنترلر بدون کلمه‌ی "Controller"
values: null, values: null,
protocol: Request.Scheme); // http یا https protocol: Request.Scheme); // http یا https
// گام 2: ایجاد درگاه پرداخت سپهر
var transactionCommand = new CreatePaymentTransaction() var gatewayResult = await _financialStatementApplication.CreatePaymentGateWayAndCreateInvoice(request, gateWayCallBackUrl);
if (!gatewayResult.IsSuccedded)
{ {
Amount = balanceAmount.Amount, return op.Failed(gatewayResult.Message);
ContractingPartyId = balanceAmount.ContractingPartyId,
CallBackUrl = request.BaseUrl,
Gateway = PaymentTransactionGateWay.SepehrPay
};
var transaction = await _paymentTransactionApplication.Create(transactionCommand);
if (!transaction.IsSuccedded)
{
return op.Failed(transaction.Message);
} }
var command = new CreatePaymentGatewayRequest() // گام 3: بازگشتی به درگاه پرداخت
{ return Redirect(gatewayResult.Data.PaymentUrl);
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("خطا در ایجاد درگاه پرداخت");
} }
} }