1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-02 16:42:50 -05:00

[PM-19180] Calculate sales tax correctly for sponsored plans (#5611)

* [PM-19180] Calculate sales tax correctly for sponsored plans

* Cannot divide by zero if total amount excluding tax is zero.

* Unit tests for families & families for enterprise

---------

Co-authored-by: Conner Turnbull <133619638+cturnbull-bitwarden@users.noreply.github.com>
This commit is contained in:
Jonas Hendrickx
2025-04-17 17:33:16 +02:00
committed by GitHub
parent 60e7db7dbb
commit bd90c34af2
3 changed files with 263 additions and 30 deletions

View File

@ -1,5 +1,6 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Billing.Enums;
using Bit.Core.Enums;
namespace Bit.Core.Billing.Models.Api.Requests.Organizations;
@ -20,6 +21,8 @@ public class OrganizationPasswordManagerRequestModel
{
public PlanType Plan { get; set; }
public PlanSponsorshipType? SponsoredPlan { get; set; }
[Range(0, int.MaxValue)]
public int Seats { get; set; }

View File

@ -1265,7 +1265,7 @@ public class StripePaymentService : IPaymentService
{
var invoice = await _stripeAdapter.InvoiceCreatePreviewAsync(options);
var effectiveTaxRate = invoice.Tax != null && invoice.TotalExcludingTax != null
var effectiveTaxRate = invoice.Tax != null && invoice.TotalExcludingTax != null && invoice.TotalExcludingTax.Value != 0
? invoice.Tax.Value.ToMajor() / invoice.TotalExcludingTax.Value.ToMajor()
: 0M;
@ -1300,6 +1300,7 @@ public class StripePaymentService : IPaymentService
string gatewaySubscriptionId)
{
var plan = await _pricingClient.GetPlanOrThrow(parameters.PasswordManager.Plan);
var isSponsored = parameters.PasswordManager.SponsoredPlan.HasValue;
var options = new InvoiceCreatePreviewOptions
{
@ -1325,45 +1326,47 @@ public class StripePaymentService : IPaymentService
},
};
if (plan.PasswordManager.HasAdditionalSeatsOption)
if (isSponsored)
{
var sponsoredPlan = Utilities.StaticStore.GetSponsoredPlan(parameters.PasswordManager.SponsoredPlan.Value);
options.SubscriptionDetails.Items.Add(
new()
{
Quantity = parameters.PasswordManager.Seats,
Plan = plan.PasswordManager.StripeSeatPlanId
}
new() { Quantity = 1, Plan = sponsoredPlan.StripePlanId }
);
}
else
{
options.SubscriptionDetails.Items.Add(
new()
{
Quantity = 1,
Plan = plan.PasswordManager.StripePlanId
}
);
}
if (plan.SupportsSecretsManager)
{
if (plan.SecretsManager.HasAdditionalSeatsOption)
if (plan.PasswordManager.HasAdditionalSeatsOption)
{
options.SubscriptionDetails.Items.Add(new()
{
Quantity = parameters.SecretsManager?.Seats ?? 0,
Plan = plan.SecretsManager.StripeSeatPlanId
});
options.SubscriptionDetails.Items.Add(
new() { Quantity = parameters.PasswordManager.Seats, Plan = plan.PasswordManager.StripeSeatPlanId }
);
}
else
{
options.SubscriptionDetails.Items.Add(
new() { Quantity = 1, Plan = plan.PasswordManager.StripePlanId }
);
}
if (plan.SecretsManager.HasAdditionalServiceAccountOption)
if (plan.SupportsSecretsManager)
{
options.SubscriptionDetails.Items.Add(new()
if (plan.SecretsManager.HasAdditionalSeatsOption)
{
Quantity = parameters.SecretsManager?.AdditionalMachineAccounts ?? 0,
Plan = plan.SecretsManager.StripeServiceAccountPlanId
});
options.SubscriptionDetails.Items.Add(new()
{
Quantity = parameters.SecretsManager?.Seats ?? 0,
Plan = plan.SecretsManager.StripeSeatPlanId
});
}
if (plan.SecretsManager.HasAdditionalServiceAccountOption)
{
options.SubscriptionDetails.Items.Add(new()
{
Quantity = parameters.SecretsManager?.AdditionalMachineAccounts ?? 0,
Plan = plan.SecretsManager.StripeServiceAccountPlanId
});
}
}
}
@ -1420,7 +1423,7 @@ public class StripePaymentService : IPaymentService
{
var invoice = await _stripeAdapter.InvoiceCreatePreviewAsync(options);
var effectiveTaxRate = invoice.Tax != null && invoice.TotalExcludingTax != null
var effectiveTaxRate = invoice.Tax != null && invoice.TotalExcludingTax != null && invoice.TotalExcludingTax.Value != 0
? invoice.Tax.Value.ToMajor() / invoice.TotalExcludingTax.Value.ToMajor()
: 0M;