Implement billing functionality and enhance role management
- Introduced billing capabilities, allowing users to request payments for subscription periods (3/6/12 months) with admin approval via Telegram. - Updated role management to include a `BillingEnabled` property, preventing billing for admin roles. - Enhanced the `CreateRoleCommand` and `UpdateRoleCommand` to accept billing parameters, ensuring proper handling during role creation and updates. - Added new endpoints for billing management and integrated billing checks into VPN config creation to enforce payment requirements. - Updated related services, models, and tests to support the new billing features, ensuring comprehensive coverage and functionality. - Enhanced documentation to reflect the new billing processes and role management changes.
This commit is contained in:
+64
@@ -0,0 +1,64 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using PnvPanel.Application.Auth;
|
||||
using PnvPanel.Application.Common.Interfaces;
|
||||
using PnvPanel.Application.Common.Messaging;
|
||||
using PnvPanel.Application.Common.Models;
|
||||
using PnvPanel.Domain.Billing;
|
||||
|
||||
namespace PnvPanel.Application.Billing.CreatePaymentRequest;
|
||||
|
||||
public sealed class CreatePaymentRequestCommandHandler(
|
||||
IAppDbContext dbContext,
|
||||
IIdentityService identityService,
|
||||
ICurrentUser currentUser
|
||||
) : ICommandHandler<CreatePaymentRequestCommand, Result<PaymentRequestDto>>
|
||||
{
|
||||
public async Task<Result<PaymentRequestDto>> Handle(
|
||||
CreatePaymentRequestCommand command,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
if (currentUser.UserId is not { } userId)
|
||||
return Result.Failure<PaymentRequestDto>(AuthErrors.Unauthorized);
|
||||
|
||||
var profile = await identityService.GetProfileAsync(userId, cancellationToken);
|
||||
if (profile is null)
|
||||
return Result.Failure<PaymentRequestDto>(AuthErrors.Unauthorized);
|
||||
|
||||
if (!profile.BillingEnabled)
|
||||
return Result.Failure<PaymentRequestDto>(BillingErrors.NotEnabled);
|
||||
|
||||
if (profile.MaxConfigs == RoleQuota.Unlimited)
|
||||
return Result.Failure<PaymentRequestDto>(BillingErrors.UnlimitedRoleNotSupported);
|
||||
|
||||
var hasActiveRequest = await dbContext.PaymentRequests.AnyAsync(
|
||||
r =>
|
||||
r.UserId == userId
|
||||
&& (
|
||||
r.Status == PaymentRequestStatus.AwaitingPayment
|
||||
|| r.Status == PaymentRequestStatus.AwaitingConfirmation
|
||||
),
|
||||
cancellationToken
|
||||
);
|
||||
if (hasActiveRequest)
|
||||
return Result.Failure<PaymentRequestDto>(BillingErrors.ActiveRequestExists);
|
||||
|
||||
var pricing = await dbContext.PricingSettings.AsNoTracking().FirstOrDefaultAsync(cancellationToken);
|
||||
var ratePerMonth = command.Period switch
|
||||
{
|
||||
PaymentPeriod.Quarter => pricing?.PricePerConfigPerQuarter,
|
||||
PaymentPeriod.HalfYear => pricing?.PricePerConfigPerHalfYear,
|
||||
PaymentPeriod.Year => pricing?.PricePerConfigPerYear,
|
||||
_ => null,
|
||||
};
|
||||
if (ratePerMonth is not { } rate)
|
||||
return Result.Failure<PaymentRequestDto>(BillingErrors.PricingNotConfigured);
|
||||
|
||||
var amount = rate * profile.MaxConfigs * command.Period.ToMonths();
|
||||
|
||||
var request = PaymentRequest.Create(userId, command.Period, amount);
|
||||
dbContext.PaymentRequests.Add(request);
|
||||
|
||||
return Result.Success(PaymentRequestDto.FromDomain(request));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user