Compare commits

...

1 Commits

18 changed files with 11354 additions and 46 deletions

View File

@@ -24,10 +24,10 @@ public class AqayePardakhtPaymentGateway:IPaymentGateway
{
_httpClient = httpClientFactory.CreateClient();
if (appSetting.Value.Domain == ".dadmehrg.ir")
{
_pin = "7349F84E81AB584862D9";
}
// if (appSetting.Value.Domain == ".dadmehrg.ir")
// {
// _pin = "7349F84E81AB584862D9";
// }
}

View File

@@ -73,4 +73,6 @@ public interface IInstitutionContractRepository : IRepository<long, InstitutionC
#endregion
Task<List<InstitutionContractSelectListViewModel>> GetInstitutionContractSelectList(string search, string selected);
Task<(double amount, long contractingPartyId, long institutionContractId)> GetFirstPaymentAmount(Guid id);
InstitutionContract GetIncludeContacts(long institutionContractId);
}

View File

@@ -9,22 +9,30 @@ namespace Company.Domain.PaymentTransactionAgg;
/// </summary>
public class PaymentTransaction:EntityBase
{
private PaymentTransaction(){}
/// <summary>
/// سازنده کلاس PaymentTransaction با دریافت اطلاعات تراکنش.
/// </summary>
/// <param name="contractingPartyId">شناسه طرف قرارداد</param>
/// <param name="amount">مبلغ تراکنش</param>
/// <param name="contractingPartyName"></param>
/// <param name="callBackUrl"></param>
/// <param name="contractingPartyName">نام طرف قرارداد</param>
/// <param name="callBackUrl">آدرس بازگشت</param>
/// <param name="transactionSource">منبع تراکنش</param>
/// <param name="sourceId">شناسه منبع</param>
public PaymentTransaction(long contractingPartyId,
double amount,
string contractingPartyName,string callBackUrl)
string contractingPartyName,
string callBackUrl,
PaymentTransactionSource transactionSource,
long sourceId)
{
ContractingPartyId = contractingPartyId;
Status = PaymentTransactionStatus.Pending;
Amount = amount;
ContractingPartyName = contractingPartyName;
CallBackUrl = callBackUrl;
Source = transactionSource;
SourceId = sourceId;
}
/// <summary>
@@ -67,6 +75,16 @@ public class PaymentTransaction:EntityBase
/// </summary>
public string TransactionId { get; private set; }
/// <summary>
/// نوع منبع تراکنش
/// </summary>
public PaymentTransactionSource Source { get; private set; }
/// <summary>
/// شناسه منبع (مثال: 2145 برای چک، 874156 برای بیمه)
/// </summary>
public long SourceId { get; private set; }
public string CallBackUrl { get; private set; }
public void SetPaid(string cardNumber,string bankName)

View File

@@ -238,6 +238,9 @@ public interface IInstitutionContractApplication
#endregion
Task<(double amount, long contractingPartyId, long institutionContractId)> GetFirstPaymentAmount(Guid id);
Task<OperationResult> CompletePendingVerify(long institutionContractId);
}
public class InsitutionContractAmendmentPaymentRequest

View File

@@ -17,4 +17,7 @@ public class CreatePaymentTransaction
/// مسیر برگشت پس از پرداخت
/// </summary>
public string CallBackUrl { get; set; }
public PaymentTransactionSource Source { get; set; }
public long SourceId { get; set; }
}

View File

@@ -101,5 +101,15 @@ public class PaymentTransactionDetailsViewModel
/// </summary>
public string TransactionId { get; set; }
/// <summary>
/// نوع منبع تراکنش
/// </summary>
public PaymentTransactionSource Source { get; set; }
/// <summary>
/// شناسه منبع (مثال: 2145 برای چک، 874156 برای بیمه)
/// </summary>
public string SourceId { get; set; }
public string CallBackUrl { get; set; }
}

View File

@@ -0,0 +1,17 @@
namespace CompanyManagment.App.Contracts.PaymentTransaction;
/// <summary>
/// انواع منابع تراکنش پرداخت
/// </summary>
public enum PaymentTransactionSource
{
/// <summary>
/// سایر
/// </summary>
Other,
/// <summary>
/// قرارداد
/// </summary>
InstitutionContract
}

View File

@@ -1,21 +1,22 @@
namespace CompanyManagment.App.Contracts.PaymentTransaction;
/// <summary>
/// وضعیت تراکنش درگاه پرداخت
/// وضعیت‌های مختلف تراکنش پرداخت
/// </summary>
public enum PaymentTransactionStatus
{
/// <summary>
/// تراکنش در انتظار انجام است.
/// در انتظار پرداخت
/// </summary>
Pending,
Pending = 1,
/// <summary>
/// تراکنش با شکست مواجه شد.
/// پرداخت موفق
/// </summary>
Failed,
Success = 2,
/// <summary>
/// تراکنش با موفقیت انجام شد.
/// پرداخت ناموفق
/// </summary>
Success,
}
Failed = 3
}

View File

@@ -2,14 +2,17 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Policy;
using System.Threading;
using System.Threading.Tasks;
using _0_Framework.Application;
using _0_Framework.Application.Enums;
using _0_Framework.Application.PaymentGateway;
using _0_Framework.Application.Sms;
using _0_Framework.Application.UID;
using _0_Framework.Exceptions;
using AccountManagement.Application.Contracts.Account;
using Azure.Core;
using Company.Domain.ContarctingPartyAgg;
using Company.Domain.EmployeeAgg;
using Company.Domain.empolyerAgg;
@@ -23,6 +26,7 @@ using Company.Domain.WorkshopAgg;
using CompanyManagment.App.Contracts.FinancialStatment;
using CompanyManagment.App.Contracts.InstitutionContract;
using CompanyManagment.App.Contracts.InstitutionContractContactinfo;
using CompanyManagment.App.Contracts.PaymentTransaction;
using CompanyManagment.App.Contracts.PersonalContractingParty;
using CompanyManagment.App.Contracts.Workshop;
using CompanyManagment.EFCore.Migrations;
@@ -1249,32 +1253,6 @@ public class InstitutionContractApplication : IInstitutionContractApplication
if (institutionContract.VerifyCode != code)
return op.Failed("کد وارد شده صحیح نمی باشد");
var transaction = await _institutionContractRepository.BeginTransactionAsync();
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);
}
await transaction.CommitAsync();
await _institutionContractRepository.SaveChangesAsync();
return op.Succcedded();
}
@@ -1345,6 +1323,46 @@ public class InstitutionContractApplication : IInstitutionContractApplication
return _institutionContractRepository.GetAmendmentPaymentDetails(request);
}
public async Task<(double amount,long contractingPartyId,long institutionContractId)> GetFirstPaymentAmount(Guid id)
{
var balanceAmount = await _institutionContractRepository.GetFirstPaymentAmount(id);
return balanceAmount;
}
public async Task<OperationResult> CompletePendingVerify(long institutionContractId)
{
var institutionContract = _institutionContractRepository.GetIncludeContacts(institutionContractId);
var transaction = await _institutionContractRepository.BeginTransactionAsync();
var phone = institutionContract.ContactInfoList.FirstOrDefault(x =>
x.SendSms && x.Position == "طرف قرارداد" && x.PhoneType == "شماره همراه");
var contractingParty = _contractingPartyRepository.Get(institutionContract.ContractingPartyId);
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 transaction.CommitAsync();
await _institutionContractRepository.SaveChangesAsync();
return new OperationResult().Succcedded();
}
private async Task<OperationResult<PersonalContractingParty>> CreateLegalContractingPartyEntity(
CreateInstitutionContractLegalPartyRequest request, long representativeId, string address, string city,

View File

@@ -50,7 +50,9 @@ public class PaymentTransactionApplication : IPaymentTransactionApplication
command.ContractingPartyId,
command.Amount,
contractingPartyName,
command.CallBackUrl);
command.CallBackUrl,
command.Source,
command.SourceId);
await _paymentTransactionRepository.CreateAsync(entity);
await _paymentTransactionRepository.SaveChangesAsync();

View File

@@ -17,6 +17,7 @@ public class PaymentTransactionMapping:IEntityTypeConfiguration<PaymentTransacti
builder.Property(x => x.Status).HasConversion<string>().HasMaxLength(35);
builder.Property(x => x.ContractingPartyName).HasMaxLength(255);
builder.Property(x => x.CallBackUrl).HasMaxLength(500);
builder.Property(x => x.Source).HasConversion<string>().HasMaxLength(35);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,52 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CompanyManagment.EFCore.Migrations
{
/// <inheritdoc />
public partial class addsourcetopaymenttransaction : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Source",
table: "PaymentTransactions",
type: "nvarchar(35)",
maxLength: 35,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<long>(
name: "SourceId",
table: "PaymentTransactions",
type: "bigint",
nullable: false,
defaultValue: 0L);
migrationBuilder.AddColumn<long>(
name: "LawId",
table: "InstitutionContractAmendments",
type: "bigint",
nullable: false,
defaultValue: 0L);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Source",
table: "PaymentTransactions");
migrationBuilder.DropColumn(
name: "SourceId",
table: "PaymentTransactions");
migrationBuilder.DropColumn(
name: "LawId",
table: "InstitutionContractAmendments");
}
}
}

View File

@@ -3209,6 +3209,9 @@ namespace CompanyManagment.EFCore.Migrations
b.Property<long>("InstitutionContractId")
.HasColumnType("bigint");
b.Property<long>("LawId")
.HasColumnType("bigint");
b.Property<DateTime>("VerificationCreation")
.HasColumnType("datetime2");
@@ -4903,6 +4906,14 @@ namespace CompanyManagment.EFCore.Migrations
b.Property<DateTime>("CreationDate")
.HasColumnType("datetime2");
b.Property<string>("Source")
.IsRequired()
.HasMaxLength(35)
.HasColumnType("nvarchar(35)");
b.Property<long>("SourceId")
.HasColumnType("bigint");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(35)

View File

@@ -2441,6 +2441,36 @@ public class InstitutionContractRepository : RepositoryBase<long, InstitutionCon
}
public async Task<(double amount,long contractingPartyId,long institutionContractId)> GetFirstPaymentAmount(Guid id)
{
var institutionContract = await _context.InstitutionContractSet
.Include(institutionContract => institutionContract.Installments)
.FirstOrDefaultAsync(x => x.PublicId == id);
long contractingPartyId = institutionContract.ContractingPartyId;
long institutionContractId = institutionContract.id;
if (institutionContract == null)
throw new NotFoundException("قرارداد مالی یافت نشد");
if (!institutionContract.IsInstallment)
return (institutionContract.TotalAmount,contractingPartyId,institutionContractId);
var institutionContractInstallment = institutionContract.Installments.FirstOrDefault();
if (institutionContractInstallment == null)
throw new NotFoundException("قسطی برای این قرارداد یافت نشد");
return (institutionContract.TotalAmount,contractingPartyId,institutionContractId);
}
public InstitutionContract GetIncludeContacts(long institutionContractId)
{
var institutionContract = _context.InstitutionContractSet
.Include(x => x.ContactInfoList)
.FirstOrDefault(x => x.id == institutionContractId);
if (institutionContract == null)
throw new NotFoundException("قرارداد مالی یافت نشد");
return institutionContract;
}
private InstitutionContractExtensionPaymentResponse CalculateInPersonPayment(
InstitutionContractExtensionPlanDetail selectedPlan, double baseAmount, double tenPercent,

View File

@@ -2,6 +2,7 @@ using System.Collections.Concurrent;
using System.Transactions;
using _0_Framework.Application;
using _0_Framework.Application.Enums;
using _0_Framework.Application.PaymentGateway;
using _0_Framework.Application.Sms;
using _0_Framework.Exceptions;
using AccountManagement.Application.Contracts.Account;
@@ -11,6 +12,7 @@ using CompanyManagment.App.Contracts.Employer;
using CompanyManagment.App.Contracts.InstitutionContract;
using CompanyManagment.App.Contracts.InstitutionContractContactinfo;
using CompanyManagment.App.Contracts.InstitutionPlan;
using CompanyManagment.App.Contracts.PaymentTransaction;
using CompanyManagment.App.Contracts.PersonalContractingParty;
using CompanyManagment.App.Contracts.Representative;
using CompanyManagment.App.Contracts.TemporaryClientRegistration;
@@ -19,6 +21,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.Extensions.Options;
using ServiceHost.Areas.Client.Pages.Company.PaymentToEmployee;
using ServiceHost.BaseControllers;
@@ -37,6 +40,9 @@ public class institutionContractController : AdminBaseController
private readonly IWorkshopApplication _workshopApplication;
private readonly ITemporaryClientRegistrationApplication _temporaryClientRegistration;
private readonly ITemporaryClientRegistrationApplication _clientRegistrationApplication;
private readonly IPaymentGateway _paymentGateway;
private readonly IPaymentTransactionApplication _paymentTransactionApplication;
private static readonly ConcurrentDictionary<Guid, SemaphoreSlim> _locks
= new ConcurrentDictionary<Guid, SemaphoreSlim>();
@@ -45,7 +51,9 @@ public class institutionContractController : AdminBaseController
public institutionContractController(IInstitutionContractApplication institutionContractApplication,
IPersonalContractingPartyApp contractingPartyApplication, IContactInfoApplication contactInfoApplication,
IAccountApplication accountApplication, IEmployerApplication employerApplication,
IWorkshopApplication workshopApplication, ITemporaryClientRegistrationApplication temporaryClientRegistration, ITemporaryClientRegistrationApplication clientRegistrationApplication)
IWorkshopApplication workshopApplication, ITemporaryClientRegistrationApplication temporaryClientRegistration,
ITemporaryClientRegistrationApplication clientRegistrationApplication, IHttpClientFactory httpClientFactory,
IOptions<AppSettingConfiguration> appSetting, IPaymentTransactionApplication paymentTransactionApplication)
{
_institutionContractApplication = institutionContractApplication;
_contractingPartyApplication = contractingPartyApplication;
@@ -55,6 +63,8 @@ public class institutionContractController : AdminBaseController
_workshopApplication = workshopApplication;
_temporaryClientRegistration = temporaryClientRegistration;
_clientRegistrationApplication = clientRegistrationApplication;
_paymentTransactionApplication = paymentTransactionApplication;
_paymentGateway = new AqayePardakhtPaymentGateway(httpClientFactory, appSetting);
}
/// <summary>
@@ -468,6 +478,57 @@ public class institutionContractController : AdminBaseController
return res;
}
[HttpPost("/api/institutionContract/pay")]
public async Task<ActionResult<OperationResult>> PayContract([FromForm]institutionContractPayContractRequest request)
{
var id = request.Id;
var baseUrl = request.BaseUrl;
var op = new OperationResult();
var amount = await _institutionContractApplication.GetFirstPaymentAmount(id);
var callbackUrl = Url.Action(
action: "InstitutionContractCallBack",
controller: "General", // نام کنترلر بدون کلمه‌ی "Controller"
values: null,
protocol: Request.Scheme); // http یا https
var transactionCommand = new CreatePaymentTransaction()
{
Amount = amount.amount,
ContractingPartyId = amount.contractingPartyId,
CallBackUrl = baseUrl,
Source = PaymentTransactionSource.InstitutionContract,
SourceId = amount.institutionContractId
};
var transaction = await _paymentTransactionApplication.Create(transactionCommand);
if (!transaction.IsSuccedded)
{
return op.Failed(transaction.Message);
}
var command = new CreatePaymentGatewayRequest()
{
CallBackUrl = callbackUrl,
Amount = amount.amount/10,
InvoiceId = transaction.SendId.ToString(),
};
var gatewayResponse = await _paymentGateway.Create(command);
if (gatewayResponse.IsSuccess)
{
_ = await _paymentTransactionApplication.SetTransactionId(transaction.SendId, gatewayResponse.TransactionId);
return Redirect(_paymentGateway.GetStartPayUrl(gatewayResponse.TransactionId));
}
if (gatewayResponse.ErrorCode.HasValue)
{
return op.Failed($"خطا در ایجاد درگاه پرداخت: {gatewayResponse.ErrorCode.Value}");
}
return op.Failed("خطا در ایجاد درگاه پرداخت");
}
[HttpPost("/api/institutionContract/Verification/{id:guid}/send-otp")]
[AllowAnonymous]
public async Task<ActionResult<OperationResult<OtpResultViewModel>>> SendVerifyOtp(Guid id)
@@ -529,6 +590,11 @@ public class institutionContractController : AdminBaseController
}
}
public class institutionContractPayContractRequest
{
public Guid Id { get; set; }
public string BaseUrl { get; set; }
}
public class VerifyOtpRequest
{

View File

@@ -72,7 +72,9 @@ public class FinancialController : ClientBaseController
{
Amount = balanceAmount.Amount,
ContractingPartyId = balanceAmount.ContractingPartyId,
CallBackUrl = request.BaseUrl
CallBackUrl = request.BaseUrl,
Source = PaymentTransactionSource.Other,
SourceId = 0
};
var transaction = await _paymentTransactionApplication.Create(transactionCommand);

View File

@@ -9,6 +9,7 @@ using _0_Framework.Application.PaymentGateway;
using Microsoft.Extensions.Options;
using CompanyManagment.App.Contracts.FinancialStatment;
using CompanyManagment.App.Contracts.FinancilTransaction;
using CompanyManagment.App.Contracts.InstitutionContract;
namespace ServiceHost.Controllers;
@@ -18,12 +19,14 @@ public class GeneralController : GeneralBaseController
private readonly IPaymentTransactionApplication _paymentTransactionApplication;
private readonly IPaymentGateway _paymentGateway;
private readonly IFinancialStatmentApplication _financialStatmentApplication;
private readonly IInstitutionContractApplication _institutionContractApplication;
public GeneralController(IPaymentTransactionApplication paymentTransactionApplication,IHttpClientFactory clientFactory, IFinancialStatmentApplication financialStatmentApplication, IOptions<AppSettingConfiguration> appSetting)
public GeneralController(IPaymentTransactionApplication paymentTransactionApplication,IHttpClientFactory clientFactory, IFinancialStatmentApplication financialStatmentApplication, IOptions<AppSettingConfiguration> appSetting, IInstitutionContractApplication institutionContractApplication)
{
_paymentTransactionApplication = paymentTransactionApplication;
_paymentGateway = new AqayePardakhtPaymentGateway(clientFactory, appSetting);
_financialStatmentApplication = financialStatmentApplication;
_institutionContractApplication = institutionContractApplication;
}
/// <summary>
@@ -112,6 +115,93 @@ public class GeneralController : GeneralBaseController
// در غیر این صورت تراکنش ناموفق است
return await HandleFailedTransaction(transaction, paymentTransactionId);
}
[HttpPost("/api/institution-contract/callback")]
public async Task<IActionResult> InstitutionContractCallBack(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("این تراکنش قبلا پرداخت شده است");
}
long institutionContractId = 0;
if (transaction.Source != PaymentTransactionSource.InstitutionContract
&& !long.TryParse(transaction.SourceId, out institutionContractId))
{
return BadRequest("اطلاعات تراکنش نامعتبر است");
}
// اگر شماره کارت یا شماره پیگیری خالی باشد، تراکنش ناموفق است
if (string.IsNullOrWhiteSpace(cardnumber) || string.IsNullOrWhiteSpace(tracking_number))
{
return await HandleFailedTransaction(transaction, paymentTransactionId);
}
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, paymentTransactionId);
}
var setSuccessResult = _paymentTransactionApplication.SetSuccess(paymentTransactionId, cardnumber, bank);
if (!setSuccessResult.IsSuccedded)
{
return new JsonResult(setSuccessResult);
}
//_institutionContractApplication.;
var institutionContract = await _institutionContractApplication
.CompletePendingVerify(institutionContractId);
if (!institutionContract.IsSuccedded)
{
return await HandleFailedTransaction(transaction, paymentTransactionId);
}
return Redirect(BuildCallbackUrl(transaction.CallBackUrl, true, transaction.Id));
}
// در غیر این صورت تراکنش ناموفق است
return await HandleFailedTransaction(transaction, paymentTransactionId);
}
private async Task<IActionResult> HandleFailedTransaction(PaymentTransactionDetailsViewModel transaction, long transactionId)
{