first commit
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
using Fitness_core_BE.Exceptions;
|
||||
using Fitness_core_BE.Helpers;
|
||||
using Fitness_core_BE.Infra.UoW;
|
||||
using Fitness_core_BE.Interfaces;
|
||||
using Fitness_core_BE.Interfaces.Services;
|
||||
using Fitness_core_BE.DTOs.PaymentsEnablement;
|
||||
using Fitness_core_BE.DTOs.PaymentGateways;
|
||||
using Fitness_core_BE.Models;
|
||||
using Fitness_core_BE.Models.Enums;
|
||||
|
||||
namespace Fitness_core_BE.Services
|
||||
{
|
||||
public class PaymentsEnablementService : IPaymentsEnablementService
|
||||
{
|
||||
private readonly IPaymentsEnablementRepository _paymentsRepository;
|
||||
private readonly IClientRepository _clientRepository;
|
||||
private readonly IPaymentServiceClient _paymentServiceClient;
|
||||
private readonly UserRoleHelper _userRoleHelper;
|
||||
private readonly IUnitOfWork _uow;
|
||||
|
||||
public PaymentsEnablementService(
|
||||
IPaymentsEnablementRepository paymentsRepository,
|
||||
IClientRepository clientRepository,
|
||||
IPaymentServiceClient paymentServiceClient,
|
||||
UserRoleHelper userRoleHelper,
|
||||
IUnitOfWork uow)
|
||||
{
|
||||
_paymentsRepository = paymentsRepository;
|
||||
_clientRepository = clientRepository;
|
||||
_paymentServiceClient = paymentServiceClient;
|
||||
_userRoleHelper = userRoleHelper;
|
||||
_uow = uow;
|
||||
}
|
||||
|
||||
public Task<List<PaymentTrackOption>> GetTrackOptionsAsync()
|
||||
{
|
||||
return Task.FromResult(new List<PaymentTrackOption>
|
||||
{
|
||||
new(PaymentTrack.OwnGateway.ToString(), new { description = "Use your own PayHere/Stripe merchant account.", feePercentage = 0 }),
|
||||
new(PaymentTrack.HexaFitness.ToString(), new { description = "Use HexaFitness's payment gateway; requires KYC before first payout.", feePercentage = 2.5 })
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<PaymentTrackSelectionResult> SelectTrackAsync(HttpContext httpContext, SelectPaymentTrackRequest request)
|
||||
{
|
||||
var userId = DataExtractor.GetUserIdFromContext(httpContext);
|
||||
var clientId = await _userRoleHelper.GetClientId(userId);
|
||||
|
||||
if (!Enum.TryParse<PaymentTrack>(request.Track, true, out var track))
|
||||
throw new BadRequestException($"Unknown payment track '{request.Track}'.");
|
||||
|
||||
var client = await _clientRepository.GetClientEntityByIdAsync(clientId)
|
||||
?? throw new NotFoundException("Client not found.");
|
||||
|
||||
client.PaymentTrack = track;
|
||||
await _uow.SaveChangesAsync();
|
||||
|
||||
return new PaymentTrackSelectionResult(track.ToString(), track == PaymentTrack.HexaFitness);
|
||||
}
|
||||
|
||||
public async Task<List<GatewayDefinitionDto>> GetAvailableGatewaysAsync(
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
return (await _paymentServiceClient.ListGatewaysAsync(ct))
|
||||
.Where(gateway => gateway.IsEnabled)
|
||||
.OrderByDescending(gateway => gateway.IsDefault)
|
||||
.ThenBy(gateway => gateway.DisplayName)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public async Task<ClientPayoutKycDto> SubmitKycAsync(
|
||||
HttpContext httpContext,
|
||||
SubmitGatewayOnboardingRequest request,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var userId = DataExtractor.GetUserIdFromContext(httpContext);
|
||||
var clientId = await _userRoleHelper.GetClientId(userId);
|
||||
|
||||
var gateway = (await GetAvailableGatewaysAsync(ct))
|
||||
.FirstOrDefault(item => item.Id == request.GatewayId)
|
||||
?? throw new BadRequestException("Select an available payment gateway.");
|
||||
|
||||
var client = await _clientRepository.GetClientEntityByIdAsync(clientId)
|
||||
?? throw new NotFoundException("Client not found.");
|
||||
|
||||
var current = await _paymentsRepository.GetKycByClientIdAsync(clientId, ct);
|
||||
if (current?.KycStatus == KycStatus.Pending)
|
||||
throw new BadRequestException("A gateway KYC request is already pending review.");
|
||||
if (current?.KycStatus == KycStatus.Approved)
|
||||
throw new BadRequestException("Gateway KYC is already approved for this client.");
|
||||
|
||||
await _uow.BeginAsync();
|
||||
try
|
||||
{
|
||||
// Catalog gateways are platform-managed, so this follows the
|
||||
// HexaFitness payment track rather than client-owned credentials.
|
||||
client.PaymentTrack = PaymentTrack.HexaFitness;
|
||||
|
||||
var kyc = new ClientPayoutKyc
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
FKClientId = clientId,
|
||||
SelectedGatewayId = gateway.Id,
|
||||
SelectedGatewayName = gateway.DisplayName,
|
||||
KycStatus = KycStatus.Pending,
|
||||
SubmittedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
await _paymentsRepository.CreateKycAsync(kyc, ct);
|
||||
await _uow.CommitAsync();
|
||||
|
||||
return ToDto(kyc, client);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await _uow.RollbackAsync();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<GatewayOnboardingStatusDto> GetKycStatusAsync(HttpContext httpContext)
|
||||
{
|
||||
var userId = DataExtractor.GetUserIdFromContext(httpContext);
|
||||
var clientId = await _userRoleHelper.GetClientId(userId);
|
||||
var kyc = await _paymentsRepository.GetKycByClientIdAsync(clientId);
|
||||
return kyc is null
|
||||
? new GatewayOnboardingStatusDto("NotSubmitted", null, null, null)
|
||||
: new GatewayOnboardingStatusDto(
|
||||
kyc.KycStatus.ToString(),
|
||||
kyc.SelectedGatewayId,
|
||||
kyc.SelectedGatewayName,
|
||||
kyc.RejectionReason);
|
||||
}
|
||||
|
||||
public async Task<WalletDto> GetWalletAsync(HttpContext httpContext)
|
||||
{
|
||||
var userId = DataExtractor.GetUserIdFromContext(httpContext);
|
||||
var clientId = await _userRoleHelper.GetClientId(userId);
|
||||
var wallet = await _paymentsRepository.GetWalletByClientIdAsync(clientId)
|
||||
?? throw new NotFoundException("Wallet not found for this client.");
|
||||
|
||||
var ledger = await _paymentsRepository.GetLedgerAsync(wallet.Id);
|
||||
|
||||
return new WalletDto(
|
||||
wallet.Balance,
|
||||
wallet.Currency,
|
||||
ledger.Select(e => new WalletLedgerEntryDto(e.Id, e.EntryType.ToString(), e.Amount, e.Description, e.CreatedAt)).ToList(),
|
||||
wallet.LastPayoutDate);
|
||||
}
|
||||
|
||||
public async Task<PayoutScheduleDto> GetPayoutScheduleAsync(HttpContext httpContext)
|
||||
{
|
||||
var userId = DataExtractor.GetUserIdFromContext(httpContext);
|
||||
var clientId = await _userRoleHelper.GetClientId(userId);
|
||||
var wallet = await _paymentsRepository.GetWalletByClientIdAsync(clientId)
|
||||
?? throw new NotFoundException("Wallet not found for this client.");
|
||||
|
||||
var today = DateTime.UtcNow.Date;
|
||||
var daysUntilTuesday = ((int)DayOfWeek.Tuesday - (int)today.DayOfWeek + 7) % 7;
|
||||
daysUntilTuesday = daysUntilTuesday == 0 ? 7 : daysUntilTuesday;
|
||||
var nextPayoutDate = today.AddDays(daysUntilTuesday);
|
||||
|
||||
return new PayoutScheduleDto(nextPayoutDate, "Weekly (Tuesday)", wallet.Balance);
|
||||
}
|
||||
|
||||
|
||||
// NIC fields used to come from the multi-gym onboarding wizard's OwnerActivation record;
|
||||
// that flow no longer exists for a single already-real club (see PayoutKycReviewService's
|
||||
// ToDto for the same note) -- always null here now.
|
||||
private static ClientPayoutKycDto ToDto(
|
||||
ClientPayoutKyc kyc,
|
||||
Client client)
|
||||
=> new(
|
||||
kyc.Id,
|
||||
kyc.FKClientId,
|
||||
client.ClientName,
|
||||
kyc.SelectedGatewayId,
|
||||
kyc.SelectedGatewayName,
|
||||
kyc.KycStatus.ToString(),
|
||||
kyc.SubmittedAt,
|
||||
kyc.ReviewedAt,
|
||||
kyc.RejectionReason,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user