mirror of
https://github.com/bitwarden/server.git
synced 2025-04-19 03:58:13 -05:00
[AC-2942] Sync seat minimum updates from Admin to Stripe (#4580)
* Sync stripe when seat minimums are updated from admin portal * Add unit tests * Run dotnet format
This commit is contained in:
parent
02fe8777ce
commit
2157df9ac8
@ -449,6 +449,119 @@ public class ProviderBillingService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task UpdateSeatMinimums(
|
||||||
|
Provider provider,
|
||||||
|
int enterpriseSeatMinimum,
|
||||||
|
int teamsSeatMinimum)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(provider);
|
||||||
|
|
||||||
|
if (enterpriseSeatMinimum < 0 || teamsSeatMinimum < 0)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Provider seat minimums must be at least 0.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var subscription = await stripeAdapter.SubscriptionGetAsync(provider.GatewaySubscriptionId);
|
||||||
|
|
||||||
|
var subscriptionItemOptionsList = new List<SubscriptionItemOptions>();
|
||||||
|
|
||||||
|
var providerPlans = await providerPlanRepository.GetByProviderId(provider.Id);
|
||||||
|
|
||||||
|
var enterpriseProviderPlan =
|
||||||
|
providerPlans.Single(providerPlan => providerPlan.PlanType == PlanType.EnterpriseMonthly);
|
||||||
|
|
||||||
|
if (enterpriseProviderPlan.SeatMinimum != enterpriseSeatMinimum)
|
||||||
|
{
|
||||||
|
var enterprisePriceId = StaticStore.GetPlan(PlanType.EnterpriseMonthly).PasswordManager
|
||||||
|
.StripeProviderPortalSeatPlanId;
|
||||||
|
|
||||||
|
var enterpriseSubscriptionItem = subscription.Items.First(item => item.Price.Id == enterprisePriceId);
|
||||||
|
|
||||||
|
if (enterpriseProviderPlan.PurchasedSeats == 0)
|
||||||
|
{
|
||||||
|
subscriptionItemOptionsList.Add(new SubscriptionItemOptions
|
||||||
|
{
|
||||||
|
Id = enterpriseSubscriptionItem.Id,
|
||||||
|
Price = enterprisePriceId,
|
||||||
|
Quantity = enterpriseSeatMinimum
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var totalEnterpriseSeats = enterpriseProviderPlan.SeatMinimum + enterpriseProviderPlan.PurchasedSeats;
|
||||||
|
|
||||||
|
if (enterpriseSeatMinimum <= totalEnterpriseSeats)
|
||||||
|
{
|
||||||
|
enterpriseProviderPlan.PurchasedSeats = totalEnterpriseSeats - enterpriseSeatMinimum;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
enterpriseProviderPlan.PurchasedSeats = 0;
|
||||||
|
subscriptionItemOptionsList.Add(new SubscriptionItemOptions
|
||||||
|
{
|
||||||
|
Id = enterpriseSubscriptionItem.Id,
|
||||||
|
Price = enterprisePriceId,
|
||||||
|
Quantity = enterpriseSeatMinimum
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enterpriseProviderPlan.SeatMinimum = enterpriseSeatMinimum;
|
||||||
|
|
||||||
|
await providerPlanRepository.ReplaceAsync(enterpriseProviderPlan);
|
||||||
|
}
|
||||||
|
|
||||||
|
var teamsProviderPlan =
|
||||||
|
providerPlans.Single(providerPlan => providerPlan.PlanType == PlanType.TeamsMonthly);
|
||||||
|
|
||||||
|
if (teamsProviderPlan.SeatMinimum != teamsSeatMinimum)
|
||||||
|
{
|
||||||
|
var teamsPriceId = StaticStore.GetPlan(PlanType.TeamsMonthly).PasswordManager
|
||||||
|
.StripeProviderPortalSeatPlanId;
|
||||||
|
|
||||||
|
var teamsSubscriptionItem = subscription.Items.First(item => item.Price.Id == teamsPriceId);
|
||||||
|
|
||||||
|
if (teamsProviderPlan.PurchasedSeats == 0)
|
||||||
|
{
|
||||||
|
subscriptionItemOptionsList.Add(new SubscriptionItemOptions
|
||||||
|
{
|
||||||
|
Id = teamsSubscriptionItem.Id,
|
||||||
|
Price = teamsPriceId,
|
||||||
|
Quantity = teamsSeatMinimum
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var totalTeamsSeats = teamsProviderPlan.SeatMinimum + teamsProviderPlan.PurchasedSeats;
|
||||||
|
|
||||||
|
if (teamsSeatMinimum <= totalTeamsSeats)
|
||||||
|
{
|
||||||
|
teamsProviderPlan.PurchasedSeats = totalTeamsSeats - teamsSeatMinimum;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
teamsProviderPlan.PurchasedSeats = 0;
|
||||||
|
subscriptionItemOptionsList.Add(new SubscriptionItemOptions
|
||||||
|
{
|
||||||
|
Id = teamsSubscriptionItem.Id,
|
||||||
|
Price = teamsPriceId,
|
||||||
|
Quantity = teamsSeatMinimum
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
teamsProviderPlan.SeatMinimum = teamsSeatMinimum;
|
||||||
|
|
||||||
|
await providerPlanRepository.ReplaceAsync(teamsProviderPlan);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subscriptionItemOptionsList.Count > 0)
|
||||||
|
{
|
||||||
|
await stripeAdapter.SubscriptionUpdateAsync(provider.GatewaySubscriptionId,
|
||||||
|
new SubscriptionUpdateOptions { Items = subscriptionItemOptionsList });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Func<int, int, Task> CurrySeatScalingUpdate(
|
private Func<int, int, Task> CurrySeatScalingUpdate(
|
||||||
Provider provider,
|
Provider provider,
|
||||||
ProviderPlan providerPlan,
|
ProviderPlan providerPlan,
|
||||||
|
@ -16,6 +16,7 @@ using Bit.Core.Billing.Services;
|
|||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
@ -1009,4 +1010,259 @@ public class ProviderBillingServiceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region UpdateSeatMinimums
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task UpdateSeatMinimums_NullProvider_ThrowsArgumentNullException(
|
||||||
|
SutProvider<ProviderBillingService> sutProvider) =>
|
||||||
|
await Assert.ThrowsAsync<ArgumentNullException>(() => sutProvider.Sut.UpdateSeatMinimums(null, 0, 0));
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task UpdateSeatMinimums_NegativeSeatMinimum_ThrowsBadRequestException(
|
||||||
|
Provider provider,
|
||||||
|
SutProvider<ProviderBillingService> sutProvider) =>
|
||||||
|
await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSeatMinimums(provider, -10, 100));
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task UpdateSeatMinimums_NoPurchasedSeats_SyncsStripeWithNewSeatMinimum(
|
||||||
|
Provider provider,
|
||||||
|
SutProvider<ProviderBillingService> sutProvider)
|
||||||
|
{
|
||||||
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
|
var providerPlanRepository = sutProvider.GetDependency<IProviderPlanRepository>();
|
||||||
|
|
||||||
|
const string enterpriseLineItemId = "enterprise_line_item_id";
|
||||||
|
const string teamsLineItemId = "teams_line_item_id";
|
||||||
|
|
||||||
|
var enterprisePriceId = StaticStore.GetPlan(PlanType.EnterpriseMonthly).PasswordManager.StripeProviderPortalSeatPlanId;
|
||||||
|
var teamsPriceId = StaticStore.GetPlan(PlanType.TeamsMonthly).PasswordManager.StripeProviderPortalSeatPlanId;
|
||||||
|
|
||||||
|
var subscription = new Subscription
|
||||||
|
{
|
||||||
|
Items = new StripeList<SubscriptionItem>
|
||||||
|
{
|
||||||
|
Data =
|
||||||
|
[
|
||||||
|
new SubscriptionItem
|
||||||
|
{
|
||||||
|
Id = enterpriseLineItemId,
|
||||||
|
Price = new Price { Id = enterprisePriceId }
|
||||||
|
},
|
||||||
|
new SubscriptionItem
|
||||||
|
{
|
||||||
|
Id = teamsLineItemId,
|
||||||
|
Price = new Price { Id = teamsPriceId }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
stripeAdapter.SubscriptionGetAsync(provider.GatewaySubscriptionId).Returns(subscription);
|
||||||
|
|
||||||
|
var providerPlans = new List<ProviderPlan>
|
||||||
|
{
|
||||||
|
new() { PlanType = PlanType.EnterpriseMonthly, SeatMinimum = 50, PurchasedSeats = 0 },
|
||||||
|
new() { PlanType = PlanType.TeamsMonthly, SeatMinimum = 30, PurchasedSeats = 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
providerPlanRepository.GetByProviderId(provider.Id).Returns(providerPlans);
|
||||||
|
|
||||||
|
await sutProvider.Sut.UpdateSeatMinimums(provider, 70, 50);
|
||||||
|
|
||||||
|
await providerPlanRepository.Received(1).ReplaceAsync(Arg.Is<ProviderPlan>(
|
||||||
|
providerPlan => providerPlan.PlanType == PlanType.EnterpriseMonthly && providerPlan.SeatMinimum == 70));
|
||||||
|
|
||||||
|
await providerPlanRepository.Received(1).ReplaceAsync(Arg.Is<ProviderPlan>(
|
||||||
|
providerPlan => providerPlan.PlanType == PlanType.TeamsMonthly && providerPlan.SeatMinimum == 50));
|
||||||
|
|
||||||
|
await stripeAdapter.Received(1).SubscriptionUpdateAsync(provider.GatewaySubscriptionId,
|
||||||
|
Arg.Is<SubscriptionUpdateOptions>(
|
||||||
|
options =>
|
||||||
|
options.Items.Count == 2 &&
|
||||||
|
options.Items.ElementAt(0).Id == enterpriseLineItemId &&
|
||||||
|
options.Items.ElementAt(0).Quantity == 70 &&
|
||||||
|
options.Items.ElementAt(1).Id == teamsLineItemId &&
|
||||||
|
options.Items.ElementAt(1).Quantity == 50));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task UpdateSeatMinimums_PurchasedSeats_NewMinimumLessThanTotal_UpdatesPurchasedSeats(
|
||||||
|
Provider provider,
|
||||||
|
SutProvider<ProviderBillingService> sutProvider)
|
||||||
|
{
|
||||||
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
|
var providerPlanRepository = sutProvider.GetDependency<IProviderPlanRepository>();
|
||||||
|
|
||||||
|
const string enterpriseLineItemId = "enterprise_line_item_id";
|
||||||
|
const string teamsLineItemId = "teams_line_item_id";
|
||||||
|
|
||||||
|
var enterprisePriceId = StaticStore.GetPlan(PlanType.EnterpriseMonthly).PasswordManager.StripeProviderPortalSeatPlanId;
|
||||||
|
var teamsPriceId = StaticStore.GetPlan(PlanType.TeamsMonthly).PasswordManager.StripeProviderPortalSeatPlanId;
|
||||||
|
|
||||||
|
var subscription = new Subscription
|
||||||
|
{
|
||||||
|
Items = new StripeList<SubscriptionItem>
|
||||||
|
{
|
||||||
|
Data =
|
||||||
|
[
|
||||||
|
new SubscriptionItem
|
||||||
|
{
|
||||||
|
Id = enterpriseLineItemId,
|
||||||
|
Price = new Price { Id = enterprisePriceId }
|
||||||
|
},
|
||||||
|
new SubscriptionItem
|
||||||
|
{
|
||||||
|
Id = teamsLineItemId,
|
||||||
|
Price = new Price { Id = teamsPriceId }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
stripeAdapter.SubscriptionGetAsync(provider.GatewaySubscriptionId).Returns(subscription);
|
||||||
|
|
||||||
|
var providerPlans = new List<ProviderPlan>
|
||||||
|
{
|
||||||
|
new() { PlanType = PlanType.EnterpriseMonthly, SeatMinimum = 50, PurchasedSeats = 20 },
|
||||||
|
new() { PlanType = PlanType.TeamsMonthly, SeatMinimum = 50, PurchasedSeats = 20 }
|
||||||
|
};
|
||||||
|
|
||||||
|
providerPlanRepository.GetByProviderId(provider.Id).Returns(providerPlans);
|
||||||
|
|
||||||
|
await sutProvider.Sut.UpdateSeatMinimums(provider, 60, 60);
|
||||||
|
|
||||||
|
await providerPlanRepository.Received(1).ReplaceAsync(Arg.Is<ProviderPlan>(
|
||||||
|
providerPlan => providerPlan.PlanType == PlanType.EnterpriseMonthly && providerPlan.SeatMinimum == 60 && providerPlan.PurchasedSeats == 10));
|
||||||
|
|
||||||
|
await providerPlanRepository.Received(1).ReplaceAsync(Arg.Is<ProviderPlan>(
|
||||||
|
providerPlan => providerPlan.PlanType == PlanType.TeamsMonthly && providerPlan.SeatMinimum == 60 && providerPlan.PurchasedSeats == 10));
|
||||||
|
|
||||||
|
await stripeAdapter.DidNotReceiveWithAnyArgs()
|
||||||
|
.SubscriptionUpdateAsync(Arg.Any<string>(), Arg.Any<SubscriptionUpdateOptions>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task UpdateSeatMinimums_PurchasedSeats_NewMinimumGreaterThanTotal_ClearsPurchasedSeats_SyncsStripeWithNewSeatMinimum(
|
||||||
|
Provider provider,
|
||||||
|
SutProvider<ProviderBillingService> sutProvider)
|
||||||
|
{
|
||||||
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
|
var providerPlanRepository = sutProvider.GetDependency<IProviderPlanRepository>();
|
||||||
|
|
||||||
|
const string enterpriseLineItemId = "enterprise_line_item_id";
|
||||||
|
const string teamsLineItemId = "teams_line_item_id";
|
||||||
|
|
||||||
|
var enterprisePriceId = StaticStore.GetPlan(PlanType.EnterpriseMonthly).PasswordManager.StripeProviderPortalSeatPlanId;
|
||||||
|
var teamsPriceId = StaticStore.GetPlan(PlanType.TeamsMonthly).PasswordManager.StripeProviderPortalSeatPlanId;
|
||||||
|
|
||||||
|
var subscription = new Subscription
|
||||||
|
{
|
||||||
|
Items = new StripeList<SubscriptionItem>
|
||||||
|
{
|
||||||
|
Data =
|
||||||
|
[
|
||||||
|
new SubscriptionItem
|
||||||
|
{
|
||||||
|
Id = enterpriseLineItemId,
|
||||||
|
Price = new Price { Id = enterprisePriceId }
|
||||||
|
},
|
||||||
|
new SubscriptionItem
|
||||||
|
{
|
||||||
|
Id = teamsLineItemId,
|
||||||
|
Price = new Price { Id = teamsPriceId }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
stripeAdapter.SubscriptionGetAsync(provider.GatewaySubscriptionId).Returns(subscription);
|
||||||
|
|
||||||
|
var providerPlans = new List<ProviderPlan>
|
||||||
|
{
|
||||||
|
new() { PlanType = PlanType.EnterpriseMonthly, SeatMinimum = 50, PurchasedSeats = 20 },
|
||||||
|
new() { PlanType = PlanType.TeamsMonthly, SeatMinimum = 50, PurchasedSeats = 20 }
|
||||||
|
};
|
||||||
|
|
||||||
|
providerPlanRepository.GetByProviderId(provider.Id).Returns(providerPlans);
|
||||||
|
|
||||||
|
await sutProvider.Sut.UpdateSeatMinimums(provider, 80, 80);
|
||||||
|
|
||||||
|
await providerPlanRepository.Received(1).ReplaceAsync(Arg.Is<ProviderPlan>(
|
||||||
|
providerPlan => providerPlan.PlanType == PlanType.EnterpriseMonthly && providerPlan.SeatMinimum == 80 && providerPlan.PurchasedSeats == 0));
|
||||||
|
|
||||||
|
await providerPlanRepository.Received(1).ReplaceAsync(Arg.Is<ProviderPlan>(
|
||||||
|
providerPlan => providerPlan.PlanType == PlanType.TeamsMonthly && providerPlan.SeatMinimum == 80 && providerPlan.PurchasedSeats == 0));
|
||||||
|
|
||||||
|
await stripeAdapter.Received(1).SubscriptionUpdateAsync(provider.GatewaySubscriptionId,
|
||||||
|
Arg.Is<SubscriptionUpdateOptions>(
|
||||||
|
options =>
|
||||||
|
options.Items.Count == 2 &&
|
||||||
|
options.Items.ElementAt(0).Id == enterpriseLineItemId &&
|
||||||
|
options.Items.ElementAt(0).Quantity == 80 &&
|
||||||
|
options.Items.ElementAt(1).Id == teamsLineItemId &&
|
||||||
|
options.Items.ElementAt(1).Quantity == 80));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task UpdateSeatMinimums_SinglePlanTypeUpdate_Succeeds(
|
||||||
|
Provider provider,
|
||||||
|
SutProvider<ProviderBillingService> sutProvider)
|
||||||
|
{
|
||||||
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
|
var providerPlanRepository = sutProvider.GetDependency<IProviderPlanRepository>();
|
||||||
|
|
||||||
|
const string enterpriseLineItemId = "enterprise_line_item_id";
|
||||||
|
const string teamsLineItemId = "teams_line_item_id";
|
||||||
|
|
||||||
|
var enterprisePriceId = StaticStore.GetPlan(PlanType.EnterpriseMonthly).PasswordManager.StripeProviderPortalSeatPlanId;
|
||||||
|
var teamsPriceId = StaticStore.GetPlan(PlanType.TeamsMonthly).PasswordManager.StripeProviderPortalSeatPlanId;
|
||||||
|
|
||||||
|
var subscription = new Subscription
|
||||||
|
{
|
||||||
|
Items = new StripeList<SubscriptionItem>
|
||||||
|
{
|
||||||
|
Data =
|
||||||
|
[
|
||||||
|
new SubscriptionItem
|
||||||
|
{
|
||||||
|
Id = enterpriseLineItemId,
|
||||||
|
Price = new Price { Id = enterprisePriceId }
|
||||||
|
},
|
||||||
|
new SubscriptionItem
|
||||||
|
{
|
||||||
|
Id = teamsLineItemId,
|
||||||
|
Price = new Price { Id = teamsPriceId }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
stripeAdapter.SubscriptionGetAsync(provider.GatewaySubscriptionId).Returns(subscription);
|
||||||
|
|
||||||
|
var providerPlans = new List<ProviderPlan>
|
||||||
|
{
|
||||||
|
new() { PlanType = PlanType.EnterpriseMonthly, SeatMinimum = 50, PurchasedSeats = 0 },
|
||||||
|
new() { PlanType = PlanType.TeamsMonthly, SeatMinimum = 30, PurchasedSeats = 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
providerPlanRepository.GetByProviderId(provider.Id).Returns(providerPlans);
|
||||||
|
|
||||||
|
await sutProvider.Sut.UpdateSeatMinimums(provider, 70, 30);
|
||||||
|
|
||||||
|
await providerPlanRepository.Received(1).ReplaceAsync(Arg.Is<ProviderPlan>(
|
||||||
|
providerPlan => providerPlan.PlanType == PlanType.EnterpriseMonthly && providerPlan.SeatMinimum == 70));
|
||||||
|
|
||||||
|
await providerPlanRepository.DidNotReceive().ReplaceAsync(Arg.Is<ProviderPlan>(
|
||||||
|
providerPlan => providerPlan.PlanType == PlanType.TeamsMonthly));
|
||||||
|
|
||||||
|
await stripeAdapter.Received(1).SubscriptionUpdateAsync(provider.GatewaySubscriptionId,
|
||||||
|
Arg.Is<SubscriptionUpdateOptions>(
|
||||||
|
options =>
|
||||||
|
options.Items.Count == 1 &&
|
||||||
|
options.Items.ElementAt(0).Id == enterpriseLineItemId &&
|
||||||
|
options.Items.ElementAt(0).Quantity == 70));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ using Bit.Core.Billing.Entities;
|
|||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
using Bit.Core.Billing.Extensions;
|
using Bit.Core.Billing.Extensions;
|
||||||
using Bit.Core.Billing.Repositories;
|
using Bit.Core.Billing.Repositories;
|
||||||
|
using Bit.Core.Billing.Services;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
@ -40,6 +41,7 @@ public class ProvidersController : Controller
|
|||||||
private readonly ICreateProviderCommand _createProviderCommand;
|
private readonly ICreateProviderCommand _createProviderCommand;
|
||||||
private readonly IFeatureService _featureService;
|
private readonly IFeatureService _featureService;
|
||||||
private readonly IProviderPlanRepository _providerPlanRepository;
|
private readonly IProviderPlanRepository _providerPlanRepository;
|
||||||
|
private readonly IProviderBillingService _providerBillingService;
|
||||||
private readonly string _stripeUrl;
|
private readonly string _stripeUrl;
|
||||||
private readonly string _braintreeMerchantUrl;
|
private readonly string _braintreeMerchantUrl;
|
||||||
private readonly string _braintreeMerchantId;
|
private readonly string _braintreeMerchantId;
|
||||||
@ -57,6 +59,7 @@ public class ProvidersController : Controller
|
|||||||
ICreateProviderCommand createProviderCommand,
|
ICreateProviderCommand createProviderCommand,
|
||||||
IFeatureService featureService,
|
IFeatureService featureService,
|
||||||
IProviderPlanRepository providerPlanRepository,
|
IProviderPlanRepository providerPlanRepository,
|
||||||
|
IProviderBillingService providerBillingService,
|
||||||
IWebHostEnvironment webHostEnvironment)
|
IWebHostEnvironment webHostEnvironment)
|
||||||
{
|
{
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
@ -71,6 +74,7 @@ public class ProvidersController : Controller
|
|||||||
_createProviderCommand = createProviderCommand;
|
_createProviderCommand = createProviderCommand;
|
||||||
_featureService = featureService;
|
_featureService = featureService;
|
||||||
_providerPlanRepository = providerPlanRepository;
|
_providerPlanRepository = providerPlanRepository;
|
||||||
|
_providerBillingService = providerBillingService;
|
||||||
_stripeUrl = webHostEnvironment.GetStripeUrl();
|
_stripeUrl = webHostEnvironment.GetStripeUrl();
|
||||||
_braintreeMerchantUrl = webHostEnvironment.GetBraintreeMerchantUrl();
|
_braintreeMerchantUrl = webHostEnvironment.GetBraintreeMerchantUrl();
|
||||||
_braintreeMerchantId = globalSettings.Braintree.MerchantId;
|
_braintreeMerchantId = globalSettings.Braintree.MerchantId;
|
||||||
@ -223,19 +227,10 @@ public class ProvidersController : Controller
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (var providerPlan in providerPlans)
|
await _providerBillingService.UpdateSeatMinimums(
|
||||||
{
|
provider,
|
||||||
if (providerPlan.PlanType == PlanType.EnterpriseMonthly)
|
model.EnterpriseMonthlySeatMinimum,
|
||||||
{
|
model.TeamsMonthlySeatMinimum);
|
||||||
providerPlan.SeatMinimum = model.EnterpriseMonthlySeatMinimum;
|
|
||||||
}
|
|
||||||
else if (providerPlan.PlanType == PlanType.TeamsMonthly)
|
|
||||||
{
|
|
||||||
providerPlan.SeatMinimum = model.TeamsMonthlySeatMinimum;
|
|
||||||
}
|
|
||||||
|
|
||||||
await _providerPlanRepository.ReplaceAsync(providerPlan);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return RedirectToAction("Edit", new { id });
|
return RedirectToAction("Edit", new { id });
|
||||||
|
@ -88,4 +88,9 @@ public interface IProviderBillingService
|
|||||||
/// <remarks>This method requires the <paramref name="provider"/> to already have a linked Stripe <see cref="Stripe.Customer"/> via its <see cref="Provider.GatewayCustomerId"/> field.</remarks>
|
/// <remarks>This method requires the <paramref name="provider"/> to already have a linked Stripe <see cref="Stripe.Customer"/> via its <see cref="Provider.GatewayCustomerId"/> field.</remarks>
|
||||||
Task<Subscription> SetupSubscription(
|
Task<Subscription> SetupSubscription(
|
||||||
Provider provider);
|
Provider provider);
|
||||||
|
|
||||||
|
Task UpdateSeatMinimums(
|
||||||
|
Provider provider,
|
||||||
|
int enterpriseSeatMinimum,
|
||||||
|
int teamsSeatMinimum);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user