1
0
mirror of https://github.com/bitwarden/server.git synced 2025-02-23 03:01:23 +01:00

Plan And Price Updates (#859)

* Expanded the Plan model to make plan & product data a bit more dynamic 
* Created a Product enum to track versioned instances of the same plan
* Created and API call and Response model for getting plan & product data from the server
This commit is contained in:
Addison Beck 2020-08-11 14:19:56 -04:00 committed by GitHub
parent 61b11e398b
commit c8220fdfa6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 541 additions and 153 deletions

View File

@ -471,7 +471,7 @@ namespace Bit.Api.Controllers
return response;
}
}
[HttpGet("{id}/tax")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task<TaxInfoResponseModel> GetTaxInfo(string id)
@ -491,7 +491,7 @@ namespace Bit.Api.Controllers
var taxInfo = await _paymentService.GetTaxInfoAsync(organization);
return new TaxInfoResponseModel(taxInfo);
}
[HttpPut("{id}/tax")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task PutTaxInfo(string id, [FromBody]OrganizationTaxInfoUpdateRequestModel model)

View File

@ -0,0 +1,21 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Utilities;
using Bit.Core.Models.Api;
using System.Linq;
namespace Bit.Api.Controllers
{
[Route("plans")]
[Authorize("Web")]
public class PlansController : Controller
{
[HttpGet("")]
public ListResponseModel<PlanResponseModel> Get()
{
var data = StaticStore.Plans;
var responses = data.Select(plan => new PlanResponseModel(plan));
return new ListResponseModel<PlanResponseModel>(responses);
}
}
}

View File

@ -6,21 +6,27 @@ namespace Bit.Core.Enums
{
[Display(Name = "Free")]
Free = 0,
[Display(Name = "Families")]
FamiliesAnnually = 1,
[Display(Name = "Teams (Monthly)")]
TeamsMonthly = 2,
[Display(Name = "Teams (Annually)")]
TeamsAnnually = 3,
[Display(Name = "Enterprise (Monthly)")]
EnterpriseMonthly = 4,
[Display(Name = "Enterprise (Annually)")]
EnterpriseAnnually = 5,
[Display(Name = "Families 2019")]
FamiliesAnnually2019 = 1,
[Display(Name = "Teams (Monthly) 2019")]
TeamsMonthly2019 = 2,
[Display(Name = "Teams (Annually) 2019")]
TeamsAnnually2019 = 3,
[Display(Name = "Enterprise (Monthly) 2019")]
EnterpriseMonthly2019 = 4,
[Display(Name = "Enterprise (Annually) 2019")]
EnterpriseAnnually2019 = 5,
[Display(Name = "Custom")]
Custom = 6,
[Display(Name = "PLACEHOLDER")]
SsoPlaceholderMonthly = 10,
[Display(Name = "PLACEHOLDER")]
SsoPlaceholderAnnually = 11,
[Display(Name = "Families")]
FamiliesAnnually = 7,
[Display(Name = "Teams (Monthly)")]
TeamsMonthly = 8,
[Display(Name = "Teams (Annually)")]
TeamsAnnually = 9,
[Display(Name = "Enterprise (Monthly)")]
EnterpriseMonthly = 10,
[Display(Name = "Enterprise (Annually)")]
EnterpriseAnnually= 11,
}
}

View File

@ -0,0 +1,17 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Core.Enums
{
public enum ProductType : byte
{
[Display(Name = "Free")]
Free = 0,
[Display(Name = "Families")]
Families = 1,
[Display(Name = "Teams")]
Teams = 2,
[Display(Name = "Enterprise")]
Enterprise = 3,
}
}

View File

@ -1,8 +1,9 @@
using System;
using System.Linq;
using Bit.Core.Models.Table;
using System.Collections.Generic;
using Bit.Core.Models.Business;
using Bit.Core.Models.StaticStore;
using System.Linq;
using Bit.Core.Enums;
namespace Bit.Core.Models.Api
{
@ -25,7 +26,7 @@ namespace Bit.Core.Models.Api
BusinessCountry = organization.BusinessCountry;
BusinessTaxNumber = organization.BusinessTaxNumber;
BillingEmail = organization.BillingEmail;
Plan = organization.Plan;
Plan = new PlanResponseModel(Utilities.StaticStore.Plans.FirstOrDefault(plan => plan.Type == organization.PlanType));
PlanType = organization.PlanType;
Seats = organization.Seats;
MaxCollections = organization.MaxCollections;
@ -51,8 +52,8 @@ namespace Bit.Core.Models.Api
public string BusinessCountry { get; set; }
public string BusinessTaxNumber { get; set; }
public string BillingEmail { get; set; }
public string Plan { get; set; }
public Enums.PlanType PlanType { get; set; }
public PlanResponseModel Plan { get; set; }
public PlanType PlanType { get; set; }
public short? Seats { get; set; }
public short? MaxCollections { get; set; }
public short? MaxStorageGb { get; set; }

View File

@ -0,0 +1,100 @@
using System;
using Bit.Core.Enums;
using Bit.Core.Models.StaticStore;
namespace Bit.Core.Models.Api
{
public class PlanResponseModel : ResponseModel
{
public PlanResponseModel(Plan plan, string obj = "plan")
: base(obj)
{
if (plan == null)
{
throw new ArgumentNullException(nameof(plan));
}
Type = plan.Type;
Product = plan.Product;
Name = plan.Name;
IsAnnual = plan.IsAnnual;
NameLocalizationKey = plan.NameLocalizationKey;
DescriptionLocalizationKey = plan.DescriptionLocalizationKey;
CanBeUsedByBusiness = plan.CanBeUsedByBusiness;
BaseSeats = plan.BaseSeats;
BaseStorageGb = plan.BaseStorageGb;
MaxCollections = plan.MaxCollections;
MaxUsers = plan.MaxUsers;
HasAdditionalSeatsOption = plan.HasAdditionalSeatsOption;
HasAdditionalStorageOption = plan.HasAdditionalStorageOption;
MaxAdditionalSeats = plan.MaxAdditionalSeats;
MaxAdditionalStorage = plan.MaxAdditionalStorage;
HasPremiumAccessOption = plan.HasPremiumAccessOption;
TrialPeriodDays = plan.TrialPeriodDays;
HasSelfHost = plan.HasSelfHost;
HasPolicies = plan.HasPolicies;
HasGroups = plan.HasGroups;
HasDirectory = plan.HasDirectory;
HasEvents = plan.HasEvents;
HasTotp = plan.HasTotp;
Has2fa = plan.Has2fa;
HasSso = plan.HasSso;
UsersGetPremium = plan.UsersGetPremium;
UpgradeSortOrder = plan.UpgradeSortOrder;
DisplaySortOrder = plan.DisplaySortOrder;
LegacyYear = plan.LegacyYear;
Disabled = plan.Disabled;
StripePlanId = plan.StripePlanId;
StripeSeatPlanId = plan.StripeSeatPlanId;
StripeStoragePlanId = plan.StripeStoragePlanId;
BasePrice = plan.BasePrice;
SeatPrice = plan.SeatPrice;
AdditionalStoragePricePerGb = plan.AdditionalStoragePricePerGb;
PremiumAccessOptionPrice = plan.PremiumAccessOptionPrice;
}
public PlanType Type { get; set; }
public ProductType Product { get; set; }
public string Name { get; set; }
public bool IsAnnual { get; set; }
public string NameLocalizationKey { get; set; }
public string DescriptionLocalizationKey { get; set; }
public bool CanBeUsedByBusiness { get; set; }
public int BaseSeats { get; set; }
public short? BaseStorageGb { get; set; }
public short? MaxCollections { get; set; }
public short? MaxUsers { get; set; }
public bool HasAdditionalSeatsOption { get; set; }
public short? MaxAdditionalSeats { get; set; }
public bool HasAdditionalStorageOption { get; set; }
public short? MaxAdditionalStorage { get; set; }
public bool HasPremiumAccessOption { get; set; }
public int? TrialPeriodDays { get; set; }
public bool HasSelfHost { get; set; }
public bool HasPolicies { get; set; }
public bool HasGroups { get; set; }
public bool HasDirectory { get; set; }
public bool HasEvents { get; set; }
public bool HasTotp { get; set; }
public bool Has2fa { get; set; }
public bool HasApi { get; set; }
public bool HasSso { get; set; }
public bool UsersGetPremium { get; set; }
public int UpgradeSortOrder { get; set; }
public int DisplaySortOrder { get; set; }
public int? LegacyYear { get; set; }
public bool Disabled { get; set; }
public string StripePlanId { get; set; }
public string StripeSeatPlanId { get; set; }
public string StripeStoragePlanId { get; set; }
public string StripePremiumAccessPlanId { get; set; }
public decimal BasePrice { get; set; }
public decimal SeatPrice { get; set; }
public decimal AdditionalStoragePricePerGb { get; set; }
public decimal PremiumAccessOptionPrice { get; set; }
}
}

View File

@ -235,7 +235,7 @@ namespace Bit.Core.Models.Business
{
valid = organization.UsePolicies == UsePolicies;
}
if (valid && Version >= 7)
{
valid = organization.UseSso == UseSso;

View File

@ -4,32 +4,48 @@ namespace Bit.Core.Models.StaticStore
{
public class Plan
{
public PlanType Type { get; set; }
public ProductType Product { get; set; }
public string Name { get; set; }
public bool IsAnnual { get; set; }
public string NameLocalizationKey { get; set; }
public string DescriptionLocalizationKey { get; set; }
public bool CanBeUsedByBusiness { get; set; }
public int BaseSeats { get; set; }
public short? BaseStorageGb { get; set; }
public short? MaxCollections { get; set; }
public short? MaxUsers { get; set; }
public bool HasAdditionalSeatsOption { get; set; }
public short? MaxAdditionalSeats { get; set; }
public bool HasAdditionalStorageOption { get; set; }
public short? MaxAdditionalStorage { get; set; }
public bool HasPremiumAccessOption { get; set; }
public int? TrialPeriodDays { get; set; }
public bool HasSelfHost { get; set; }
public bool HasPolicies { get; set; }
public bool HasGroups { get; set; }
public bool HasDirectory { get; set; }
public bool HasEvents { get; set; }
public bool HasTotp { get; set; }
public bool Has2fa { get; set; }
public bool HasApi { get; set; }
public bool HasSso { get; set; }
public bool UsersGetPremium { get; set; }
public int UpgradeSortOrder { get; set; }
public int DisplaySortOrder { get; set; }
public int? LegacyYear { get; set; }
public bool Disabled { get; set; }
public string StripePlanId { get; set; }
public string StripeSeatPlanId { get; set; }
public string StripeStoragePlanId { get; set; }
public string StripePremiumAccessPlanId { get; set; }
public PlanType Type { get; set; }
public short BaseSeats { get; set; }
public bool CanBuyAdditionalSeats { get; set; }
public short? MaxAdditionalSeats { get; set; }
public bool CanBuyPremiumAccessAddon { get; set; }
public bool UseGroups { get; set; }
public bool UsePolicies { get; set; }
public bool UseSso { get; set; }
public bool UseDirectory { get; set; }
public bool UseEvents { get; set; }
public bool UseTotp { get; set; }
public bool Use2fa { get; set; }
public bool UseApi { get; set; }
public short? MaxStorageGb { get; set; }
public decimal BasePrice { get; set; }
public decimal SeatPrice { get; set; }
public short? MaxCollections { get; set; }
public int UpgradeSortOrder { get; set; }
public bool Disabled { get; set; }
public int? TrialPeriodDays { get; set; }
public bool SelfHost { get; set; }
public bool UsersGetPremium { get; set; }
public decimal AdditionalStoragePricePerGb { get; set; }
public decimal PremiumAccessOptionPrice { get; set; }
}
}

View File

@ -177,7 +177,7 @@ namespace Bit.Core.Services
ValidateOrganizationUpgradeParameters(newPlan, upgrade);
var newPlanSeats = (short)(newPlan.BaseSeats +
(newPlan.CanBuyAdditionalSeats ? upgrade.AdditionalSeats : 0));
(newPlan.HasAdditionalSeatsOption ? upgrade.AdditionalSeats : 0));
if (!organization.Seats.HasValue || organization.Seats.Value > newPlanSeats)
{
var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organization.Id);
@ -200,7 +200,7 @@ namespace Bit.Core.Services
}
}
if (!newPlan.UseGroups && organization.UseGroups)
if (!newPlan.HasGroups && organization.UseGroups)
{
var groups = await _groupRepository.GetManyByOrganizationIdAsync(organization.Id);
if (groups.Any())
@ -210,7 +210,7 @@ namespace Bit.Core.Services
}
}
if (!newPlan.UsePolicies && organization.UsePolicies)
if (!newPlan.HasPolicies && organization.UsePolicies)
{
var policies = await _policyRepository.GetManyByOrganizationIdAsync(organization.Id);
if (policies.Any(p => p.Enabled))
@ -219,8 +219,8 @@ namespace Bit.Core.Services
$"Disable your policies.");
}
}
if (!newPlan.UseSso && organization.UseSso)
if (!newPlan.HasSso && organization.UseSso)
{
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id);
if (ssoConfig != null && ssoConfig.Enabled)
@ -250,16 +250,25 @@ namespace Bit.Core.Services
organization.PlanType = newPlan.Type;
organization.Seats = (short)(newPlan.BaseSeats + upgrade.AdditionalSeats);
organization.MaxCollections = newPlan.MaxCollections;
organization.MaxStorageGb = !newPlan.MaxStorageGb.HasValue ?
(short?)null : (short)(newPlan.MaxStorageGb.Value + upgrade.AdditionalStorageGb);
organization.UseGroups = newPlan.UseGroups;
organization.UseDirectory = newPlan.UseDirectory;
organization.UseEvents = newPlan.UseEvents;
organization.UseTotp = newPlan.UseTotp;
organization.Use2fa = newPlan.Use2fa;
organization.UseApi = newPlan.UseApi;
organization.SelfHost = newPlan.SelfHost;
organization.UsePolicies = newPlan.UsePolicies;
organization.MaxStorageGb = !newPlan.BaseStorageGb.HasValue ?
(short?)null : (short)(newPlan.BaseStorageGb.Value + upgrade.AdditionalStorageGb);
organization.UseGroups = newPlan.HasGroups;
organization.UseDirectory = newPlan.HasDirectory;
organization.UseEvents = newPlan.HasEvents;
organization.UseTotp = newPlan.HasTotp;
organization.Use2fa = newPlan.Has2fa;
organization.UseApi = newPlan.HasApi;
organization.SelfHost = newPlan.HasSelfHost;
organization.UsePolicies = newPlan.HasPolicies;
organization.MaxStorageGb = !newPlan.MaxAdditionalStorage.HasValue ?
(short?)null : (short)(newPlan.MaxAdditionalStorage.Value + upgrade.AdditionalStorageGb);
organization.UseGroups = newPlan.HasGroups;
organization.UseDirectory = newPlan.HasDirectory;
organization.UseEvents = newPlan.HasEvents;
organization.UseTotp = newPlan.HasTotp;
organization.Use2fa = newPlan.Has2fa;
organization.UseApi = newPlan.HasApi;
organization.SelfHost = newPlan.HasSelfHost;
organization.UsersGetPremium = newPlan.UsersGetPremium || upgrade.PremiumAccessAddon;
organization.Plan = newPlan.Name;
organization.Enabled = success;
@ -293,7 +302,7 @@ namespace Bit.Core.Services
throw new BadRequestException("Existing plan not found.");
}
if (!plan.MaxStorageGb.HasValue)
if (!plan.HasAdditionalStorageOption)
{
throw new BadRequestException("Plan does not allow additional storage.");
}
@ -335,7 +344,7 @@ namespace Bit.Core.Services
throw new BadRequestException("Existing plan not found.");
}
if (!plan.CanBuyAdditionalSeats)
if (!plan.HasAdditionalSeatsOption)
{
throw new BadRequestException("Plan does not allow additional seats.");
}
@ -515,17 +524,17 @@ namespace Bit.Core.Services
PlanType = plan.Type,
Seats = (short)(plan.BaseSeats + signup.AdditionalSeats),
MaxCollections = plan.MaxCollections,
MaxStorageGb = !plan.MaxStorageGb.HasValue ?
(short?)null : (short)(plan.MaxStorageGb.Value + signup.AdditionalStorageGb),
UsePolicies = plan.UsePolicies,
UseSso = plan.UseSso,
UseGroups = plan.UseGroups,
UseEvents = plan.UseEvents,
UseDirectory = plan.UseDirectory,
UseTotp = plan.UseTotp,
Use2fa = plan.Use2fa,
UseApi = plan.UseApi,
SelfHost = plan.SelfHost,
MaxStorageGb = !plan.BaseStorageGb.HasValue ?
(short?)null : (short)(plan.BaseStorageGb.Value + signup.AdditionalStorageGb),
UsePolicies = plan.HasPolicies,
UseSso = plan.HasSso,
UseGroups = plan.HasGroups,
UseEvents = plan.HasEvents,
UseDirectory = plan.HasDirectory,
UseTotp = plan.HasTotp,
Use2fa = plan.Has2fa,
UseApi = plan.HasApi,
SelfHost = plan.HasSelfHost,
UsersGetPremium = plan.UsersGetPremium || signup.PremiumAccessAddon,
Plan = plan.Name,
Gateway = null,
@ -763,7 +772,7 @@ namespace Bit.Core.Services
$"policies. Your new license does not allow for the use of policies. Disable all policies.");
}
}
if (!license.UseSso && organization.UseSso)
{
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id);
@ -1522,7 +1531,7 @@ namespace Bit.Core.Services
private void ValidateOrganizationUpgradeParameters(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade)
{
if (!plan.MaxStorageGb.HasValue && upgrade.AdditionalStorageGb > 0)
if (!plan.HasAdditionalStorageOption && upgrade.AdditionalStorageGb > 0)
{
throw new BadRequestException("Plan does not allow additional storage.");
}
@ -1532,7 +1541,7 @@ namespace Bit.Core.Services
throw new BadRequestException("You can't subtract storage!");
}
if (!plan.CanBuyPremiumAccessAddon && upgrade.PremiumAccessAddon)
if (!plan.HasPremiumAccessOption && upgrade.PremiumAccessAddon)
{
throw new BadRequestException("This plan does not allow you to buy the premium access addon.");
}
@ -1547,12 +1556,12 @@ namespace Bit.Core.Services
throw new BadRequestException("You can't subtract seats!");
}
if (!plan.CanBuyAdditionalSeats && upgrade.AdditionalSeats > 0)
if (!plan.HasAdditionalSeatsOption && upgrade.AdditionalSeats > 0)
{
throw new BadRequestException("Plan does not allow additional users.");
}
if (plan.CanBuyAdditionalSeats && plan.MaxAdditionalSeats.HasValue &&
if (plan.HasAdditionalSeatsOption && plan.MaxAdditionalSeats.HasValue &&
upgrade.AdditionalSeats > plan.MaxAdditionalSeats.Value)
{
throw new BadRequestException($"Selected plan allows a maximum of " +

View File

@ -103,109 +103,327 @@ namespace Bit.Core.Utilities
new Plan
{
Type = PlanType.Free,
BaseSeats = 2,
CanBuyAdditionalSeats = false,
MaxCollections = 2,
Product = ProductType.Free,
Name = "Free",
UpgradeSortOrder = -1 // Always the lowest plan, cannot be upgraded to
NameLocalizationKey = "planNameFree",
DescriptionLocalizationKey = "planDescFree",
BaseSeats = 2,
MaxCollections = 2,
MaxUsers = 2,
UpgradeSortOrder = -1, // Always the lowest plan, cannot be upgraded to
DisplaySortOrder = -1
},
new Plan
{
Type = PlanType.FamiliesAnnually2019,
Product = ProductType.Families,
Name = "Families 2019",
IsAnnual = true,
NameLocalizationKey = "planNameFamilies",
DescriptionLocalizationKey = "planDescFamilies",
BaseSeats = 6,
BaseStorageGb = 1,
MaxUsers = 6,
HasAdditionalStorageOption = true,
HasPremiumAccessOption = true,
TrialPeriodDays = 7,
HasSelfHost = true,
HasTotp = true,
UpgradeSortOrder = 1,
DisplaySortOrder = 1,
LegacyYear = 2020,
StripePlanId = "personal-org-annually",
StripeStoragePlanId = "storage-gb-annually",
StripePremiumAccessPlanId = "personal-org-premium-access-annually",
BasePrice = 12,
AdditionalStoragePricePerGb = 4,
PremiumAccessOptionPrice = 40
},
new Plan
{
Type = PlanType.TeamsAnnually2019,
Product = ProductType.Teams,
Name = "Teams (Annually) 2019",
IsAnnual = true,
NameLocalizationKey = "planNameTeams",
DescriptionLocalizationKey = "planDescTeams",
CanBeUsedByBusiness = true,
BaseSeats = 6,
BaseStorageGb = 1,
HasAdditionalSeatsOption = true,
HasAdditionalStorageOption = true,
TrialPeriodDays = 7,
HasTotp = true,
UpgradeSortOrder = 2,
DisplaySortOrder = 2,
LegacyYear = 2020,
StripePlanId = "teams-org-annually",
StripeSeatPlanId = "teams-org-seat-annually",
StripeStoragePlanId = "storage-gb-annually",
BasePrice = 60,
SeatPrice = 24,
AdditionalStoragePricePerGb = 4
},
new Plan
{
Type = PlanType.TeamsMonthly2019,
Product = ProductType.Teams,
Name = "Teams (Monthly) 2019",
NameLocalizationKey = "planNameTeams",
DescriptionLocalizationKey = "planDescTeams",
CanBeUsedByBusiness = true,
BaseSeats = 6,
BaseStorageGb = 1,
HasAdditionalSeatsOption = true,
HasAdditionalStorageOption = true,
TrialPeriodDays = 7,
HasTotp = true,
UpgradeSortOrder = 2,
DisplaySortOrder = 2,
LegacyYear = 2020,
StripePlanId = "teams-org-monthly",
StripeSeatPlanId = "teams-org-seat-monthly",
StripeStoragePlanId = "storage-gb-monthly",
BasePrice = 8,
SeatPrice = 2.5M,
AdditionalStoragePricePerGb = 0.5M
},
new Plan
{
Type = PlanType.EnterpriseAnnually2019,
Name = "Enterprise (Annually) 2019",
IsAnnual = true,
Product = ProductType.Enterprise,
NameLocalizationKey = "planNameEnterprise",
DescriptionLocalizationKey = "planDescEnterprise",
CanBeUsedByBusiness = true,
BaseSeats = 0,
BaseStorageGb = 1,
HasAdditionalSeatsOption = true,
HasAdditionalStorageOption = true,
TrialPeriodDays = 7,
HasPolicies = true,
HasSelfHost = true,
HasGroups = true,
HasDirectory = true,
HasEvents = true,
HasTotp = true,
Has2fa = true,
HasApi = true,
UsersGetPremium = true,
UpgradeSortOrder = 3,
DisplaySortOrder = 3,
LegacyYear = 2020,
StripePlanId = null,
StripeSeatPlanId = "enterprise-org-seat-annually",
StripeStoragePlanId = "storage-gb-annually",
BasePrice = 0,
SeatPrice = 36,
AdditionalStoragePricePerGb = 4
},
new Plan
{
Type = PlanType.EnterpriseMonthly2019,
Product = ProductType.Enterprise,
Name = "Enterprise (Monthly) 2019",
NameLocalizationKey = "planNameEnterprise",
DescriptionLocalizationKey = "planDescEnterprise",
CanBeUsedByBusiness = true,
BaseSeats = 0,
BaseStorageGb = 1,
HasAdditionalSeatsOption = true,
HasAdditionalStorageOption = true,
TrialPeriodDays = 7,
HasPolicies = true,
HasGroups = true,
HasDirectory = true,
HasEvents = true,
HasTotp = true,
Has2fa = true,
HasApi = true,
HasSelfHost = true,
UsersGetPremium = true,
UpgradeSortOrder = 3,
DisplaySortOrder = 3,
LegacyYear = 2020,
StripePlanId = null,
StripeSeatPlanId = "enterprise-org-seat-monthly",
StripeStoragePlanId = "storage-gb-monthly",
BasePrice = 0,
SeatPrice = 4M,
AdditionalStoragePricePerGb = 0.5M
},
new Plan
{
Type = PlanType.FamiliesAnnually,
BaseSeats = 5,
BasePrice = 12,
CanBuyAdditionalSeats = false,
CanBuyPremiumAccessAddon = true,
Product = ProductType.Families,
Name = "Families",
IsAnnual = true,
NameLocalizationKey = "planNameFamilies",
DescriptionLocalizationKey = "planDescFamilies",
BaseSeats = 6,
BaseStorageGb = 1,
MaxUsers = 6,
TrialPeriodDays = 7,
HasSelfHost = true,
HasTotp = true,
UsersGetPremium = true,
UpgradeSortOrder = 1,
DisplaySortOrder = 1,
StripePlanId = "personal-org-annually",
StripeStoragePlanId = "storage-gb-annually",
StripePremiumAccessPlanId = "personal-org-premium-access-annually",
UpgradeSortOrder = 1,
TrialPeriodDays = 7,
UseTotp = true,
MaxStorageGb = 1,
SelfHost = true
},
new Plan
{
Type = PlanType.TeamsMonthly,
BaseSeats = 5,
BasePrice = 8,
SeatPrice = 2.5M,
CanBuyAdditionalSeats = true,
Name = "Teams (Monthly)",
StripePlanId = "teams-org-monthly",
StripeSeatPlanId = "teams-org-seat-monthly",
StripeStoragePlanId = "storage-gb-monthly",
UpgradeSortOrder = 2,
TrialPeriodDays = 7,
UseTotp = true,
MaxStorageGb = 1
BasePrice = 40,
AdditionalStoragePricePerGb = 4
},
new Plan
{
Type = PlanType.TeamsAnnually,
BaseSeats = 5,
BasePrice = 60,
SeatPrice = 24,
CanBuyAdditionalSeats = true,
Product = ProductType.Teams,
Name = "Teams (Annually)",
StripePlanId = "teams-org-annually",
StripeSeatPlanId = "teams-org-seat-annually",
StripeStoragePlanId = "storage-gb-annually",
UpgradeSortOrder = 2,
IsAnnual = true,
NameLocalizationKey = "planNameTeams",
DescriptionLocalizationKey = "planDescTeams",
CanBeUsedByBusiness = true,
BaseStorageGb = 1,
BaseSeats = 0,
HasAdditionalSeatsOption = true,
HasAdditionalStorageOption = true,
TrialPeriodDays = 7,
UseTotp = true,
MaxStorageGb = 1
HasTotp = true,
UsersGetPremium = true,
UpgradeSortOrder = 2,
DisplaySortOrder = 2,
StripeSeatPlanId = "2020-teams-org-seat-annually",
StripeStoragePlanId = "storage-gb-annually",
SeatPrice = 36,
AdditionalStoragePricePerGb = 4
},
new Plan
{
Type = PlanType.EnterpriseMonthly,
Type = PlanType.TeamsMonthly,
Product = ProductType.Teams,
Name = "Teams (Monthly)",
NameLocalizationKey = "planNameTeams",
DescriptionLocalizationKey = "planDescTeams",
CanBeUsedByBusiness = true,
BaseStorageGb = 1,
BaseSeats = 0,
BasePrice = 0,
SeatPrice = 4M,
CanBuyAdditionalSeats = true,
Name = "Enterprise (Monthly)",
StripePlanId = null,
StripeSeatPlanId = "enterprise-org-seat-monthly",
StripeStoragePlanId = "storage-gb-monthly",
UpgradeSortOrder = 3,
HasAdditionalSeatsOption = true,
HasAdditionalStorageOption = true,
TrialPeriodDays = 7,
UsePolicies = true,
UseGroups = true,
UseDirectory = true,
UseEvents = true,
UseTotp = true,
Use2fa = true,
UseApi = true,
MaxStorageGb = 1,
SelfHost = true,
UsersGetPremium = true
HasTotp = true,
UsersGetPremium = true,
UpgradeSortOrder = 2,
DisplaySortOrder = 2,
StripeSeatPlanId = "2020-teams-org-seat-monthly",
StripeStoragePlanId = "storage-gb-monthly",
SeatPrice = 4,
AdditionalStoragePricePerGb = 0.5M
},
new Plan
{
Type = PlanType.EnterpriseAnnually,
BaseSeats = 0,
BasePrice = 0,
SeatPrice = 36,
CanBuyAdditionalSeats = true,
Name = "Enterprise (Annually)",
StripePlanId = null,
StripeSeatPlanId = "enterprise-org-seat-annually",
StripeStoragePlanId = "storage-gb-annually",
UpgradeSortOrder = 3,
Product = ProductType.Enterprise,
IsAnnual = true,
NameLocalizationKey = "planNameEnterprise",
DescriptionLocalizationKey = "planDescEnterprise",
CanBeUsedByBusiness = true,
BaseSeats = 0,
BaseStorageGb = 1,
HasAdditionalSeatsOption = true,
HasAdditionalStorageOption = true,
TrialPeriodDays = 7,
UsePolicies = true,
UseGroups = true,
UseDirectory = true,
UseEvents = true,
UseTotp = true,
Use2fa = true,
UseApi = true,
MaxStorageGb = 1,
SelfHost = true,
UsersGetPremium = true
}
HasPolicies = true,
HasSelfHost = true,
HasGroups = true,
HasDirectory = true,
HasEvents = true,
HasTotp = true,
Has2fa = true,
HasApi = true,
HasSso = true,
UsersGetPremium = true,
UpgradeSortOrder = 3,
DisplaySortOrder = 3,
StripeSeatPlanId = "2020-enterprise-org-seat-annually",
StripeStoragePlanId = "storage-gb-annually",
BasePrice = 0,
SeatPrice = 60,
AdditionalStoragePricePerGb = 4
},
new Plan
{
Type = PlanType.EnterpriseMonthly,
Product = ProductType.Enterprise,
Name = "Enterprise (Monthly)",
NameLocalizationKey = "planNameEnterprise",
DescriptionLocalizationKey = "planDescEnterprise",
CanBeUsedByBusiness = true,
BaseSeats = 0,
BaseStorageGb = 1,
HasAdditionalSeatsOption = true,
HasAdditionalStorageOption = true,
TrialPeriodDays = 7,
HasPolicies = true,
HasGroups = true,
HasDirectory = true,
HasEvents = true,
HasTotp = true,
Has2fa = true,
HasApi = true,
HasSelfHost = true,
HasSso = true,
UsersGetPremium = true,
UpgradeSortOrder = 3,
DisplaySortOrder = 3,
StripeSeatPlanId = "2020-enterprise-org-seat-monthly",
StripeStoragePlanId = "storage-gb-monthly",
BasePrice = 0,
SeatPrice = 6,
AdditionalStoragePricePerGb = 0.5M
},
};
#endregion