1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-03 17:12:49 -05:00

[AC-1904] Implement endpoint to retrieve Provider subscription (#3921)

* Refactor Core.Billing prior to adding new logic

* Add ProviderBillingQueries.GetSubscriptionData

* Add ProviderBillingController.GetSubscriptionAsync
This commit is contained in:
Alex Morask
2024-03-28 08:46:12 -04:00
committed by GitHub
parent 46dba15194
commit ffd988eeda
31 changed files with 786 additions and 238 deletions

View File

@ -66,7 +66,7 @@ public class OrganizationsController : Controller
private readonly IAddSecretsManagerSubscriptionCommand _addSecretsManagerSubscriptionCommand;
private readonly IPushNotificationService _pushNotificationService;
private readonly ICancelSubscriptionCommand _cancelSubscriptionCommand;
private readonly IGetSubscriptionQuery _getSubscriptionQuery;
private readonly ISubscriberQueries _subscriberQueries;
private readonly IReferenceEventService _referenceEventService;
private readonly IOrganizationEnableCollectionEnhancementsCommand _organizationEnableCollectionEnhancementsCommand;
@ -93,7 +93,7 @@ public class OrganizationsController : Controller
IAddSecretsManagerSubscriptionCommand addSecretsManagerSubscriptionCommand,
IPushNotificationService pushNotificationService,
ICancelSubscriptionCommand cancelSubscriptionCommand,
IGetSubscriptionQuery getSubscriptionQuery,
ISubscriberQueries subscriberQueries,
IReferenceEventService referenceEventService,
IOrganizationEnableCollectionEnhancementsCommand organizationEnableCollectionEnhancementsCommand)
{
@ -119,7 +119,7 @@ public class OrganizationsController : Controller
_addSecretsManagerSubscriptionCommand = addSecretsManagerSubscriptionCommand;
_pushNotificationService = pushNotificationService;
_cancelSubscriptionCommand = cancelSubscriptionCommand;
_getSubscriptionQuery = getSubscriptionQuery;
_subscriberQueries = subscriberQueries;
_referenceEventService = referenceEventService;
_organizationEnableCollectionEnhancementsCommand = organizationEnableCollectionEnhancementsCommand;
}
@ -479,7 +479,7 @@ public class OrganizationsController : Controller
throw new NotFoundException();
}
var subscription = await _getSubscriptionQuery.GetSubscription(organization);
var subscription = await _subscriberQueries.GetSubscriptionOrThrow(organization);
await _cancelSubscriptionCommand.CancelSubscription(subscription,
new OffboardingSurveyResponse

View File

@ -69,7 +69,7 @@ public class AccountsController : Controller
private readonly IRotateUserKeyCommand _rotateUserKeyCommand;
private readonly IFeatureService _featureService;
private readonly ICancelSubscriptionCommand _cancelSubscriptionCommand;
private readonly IGetSubscriptionQuery _getSubscriptionQuery;
private readonly ISubscriberQueries _subscriberQueries;
private readonly IReferenceEventService _referenceEventService;
private readonly ICurrentContext _currentContext;
@ -104,7 +104,7 @@ public class AccountsController : Controller
IRotateUserKeyCommand rotateUserKeyCommand,
IFeatureService featureService,
ICancelSubscriptionCommand cancelSubscriptionCommand,
IGetSubscriptionQuery getSubscriptionQuery,
ISubscriberQueries subscriberQueries,
IReferenceEventService referenceEventService,
ICurrentContext currentContext,
IRotationValidator<IEnumerable<CipherWithIdRequestModel>, IEnumerable<Cipher>> cipherValidator,
@ -133,7 +133,7 @@ public class AccountsController : Controller
_rotateUserKeyCommand = rotateUserKeyCommand;
_featureService = featureService;
_cancelSubscriptionCommand = cancelSubscriptionCommand;
_getSubscriptionQuery = getSubscriptionQuery;
_subscriberQueries = subscriberQueries;
_referenceEventService = referenceEventService;
_currentContext = currentContext;
_cipherValidator = cipherValidator;
@ -831,7 +831,7 @@ public class AccountsController : Controller
throw new UnauthorizedAccessException();
}
var subscription = await _getSubscriptionQuery.GetSubscription(user);
var subscription = await _subscriberQueries.GetSubscriptionOrThrow(user);
await _cancelSubscriptionCommand.CancelSubscription(subscription,
new OffboardingSurveyResponse

View File

@ -0,0 +1,44 @@
using Bit.Api.Billing.Models;
using Bit.Core;
using Bit.Core.Billing.Queries;
using Bit.Core.Context;
using Bit.Core.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Billing.Controllers;
[Route("providers/{providerId:guid}/billing")]
[Authorize("Application")]
public class ProviderBillingController(
ICurrentContext currentContext,
IFeatureService featureService,
IProviderBillingQueries providerBillingQueries) : Controller
{
[HttpGet("subscription")]
public async Task<IResult> GetSubscriptionAsync([FromRoute] Guid providerId)
{
if (!featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling))
{
return TypedResults.NotFound();
}
if (!currentContext.ProviderProviderAdmin(providerId))
{
return TypedResults.Unauthorized();
}
var subscriptionData = await providerBillingQueries.GetSubscriptionData(providerId);
if (subscriptionData == null)
{
return TypedResults.NotFound();
}
var (providerPlans, subscription) = subscriptionData;
var providerSubscriptionDTO = ProviderSubscriptionDTO.From(providerPlans, subscription);
return TypedResults.Ok(providerSubscriptionDTO);
}
}

View File

@ -0,0 +1,47 @@
using Bit.Core.Billing.Models;
using Bit.Core.Utilities;
using Stripe;
namespace Bit.Api.Billing.Models;
public record ProviderSubscriptionDTO(
string Status,
DateTime CurrentPeriodEndDate,
decimal? DiscountPercentage,
IEnumerable<ProviderPlanDTO> Plans)
{
private const string _annualCadence = "Annual";
private const string _monthlyCadence = "Monthly";
public static ProviderSubscriptionDTO From(
IEnumerable<ConfiguredProviderPlan> providerPlans,
Subscription subscription)
{
var providerPlansDTO = providerPlans
.Select(providerPlan =>
{
var plan = StaticStore.GetPlan(providerPlan.PlanType);
var cost = (providerPlan.SeatMinimum + providerPlan.PurchasedSeats) * plan.PasswordManager.SeatPrice;
var cadence = plan.IsAnnual ? _annualCadence : _monthlyCadence;
return new ProviderPlanDTO(
plan.Name,
providerPlan.SeatMinimum,
providerPlan.PurchasedSeats,
cost,
cadence);
});
return new ProviderSubscriptionDTO(
subscription.Status,
subscription.CurrentPeriodEnd,
subscription.Customer?.Discount?.Coupon?.PercentOff,
providerPlansDTO);
}
}
public record ProviderPlanDTO(
string PlanName,
int SeatMinimum,
int PurchasedSeats,
decimal Cost,
string Cadence);