mirror of
https://github.com/bitwarden/server
synced 2025-12-26 05:03:18 +00:00
137 lines
4.8 KiB
C#
137 lines
4.8 KiB
C#
using Bit.Core.Billing.Commands;
|
|
using Bit.Core.Billing.Constants;
|
|
using Bit.Core.Billing.Enums;
|
|
using Bit.Core.Billing.Extensions;
|
|
using Bit.Core.Billing.Pricing;
|
|
using Bit.Core.Billing.Tax.Services;
|
|
using Bit.Core.Services;
|
|
using Microsoft.Extensions.Logging;
|
|
using Stripe;
|
|
|
|
namespace Bit.Core.Billing.Tax.Commands;
|
|
|
|
public interface IPreviewTaxAmountCommand
|
|
{
|
|
Task<BillingCommandResult<decimal>> Run(OrganizationTrialParameters parameters);
|
|
}
|
|
|
|
public class PreviewTaxAmountCommand(
|
|
ILogger<PreviewTaxAmountCommand> logger,
|
|
IPricingClient pricingClient,
|
|
IStripeAdapter stripeAdapter,
|
|
ITaxService taxService) : BaseBillingCommand<PreviewTaxAmountCommand>(logger), IPreviewTaxAmountCommand
|
|
{
|
|
protected override Conflict DefaultConflict
|
|
=> new("We had a problem calculating your tax obligation. Please contact support for assistance.");
|
|
|
|
public Task<BillingCommandResult<decimal>> Run(OrganizationTrialParameters parameters)
|
|
=> HandleAsync<decimal>(async () =>
|
|
{
|
|
var (planType, productType, taxInformation) = parameters;
|
|
|
|
var plan = await pricingClient.GetPlanOrThrow(planType);
|
|
|
|
var options = new InvoiceCreatePreviewOptions
|
|
{
|
|
Currency = "usd",
|
|
CustomerDetails = new InvoiceCustomerDetailsOptions
|
|
{
|
|
Address = new AddressOptions
|
|
{
|
|
Country = taxInformation.Country,
|
|
PostalCode = taxInformation.PostalCode
|
|
}
|
|
},
|
|
SubscriptionDetails = new InvoiceSubscriptionDetailsOptions
|
|
{
|
|
Items =
|
|
[
|
|
new InvoiceSubscriptionDetailsItemOptions
|
|
{
|
|
Price = plan.HasNonSeatBasedPasswordManagerPlan()
|
|
? plan.PasswordManager.StripePlanId
|
|
: plan.PasswordManager.StripeSeatPlanId,
|
|
Quantity = 1
|
|
}
|
|
]
|
|
}
|
|
};
|
|
|
|
if (productType == ProductType.SecretsManager)
|
|
{
|
|
options.SubscriptionDetails.Items.Add(new InvoiceSubscriptionDetailsItemOptions
|
|
{
|
|
Price = plan.SecretsManager.StripeSeatPlanId,
|
|
Quantity = 1
|
|
});
|
|
|
|
options.Coupon = StripeConstants.CouponIDs.SecretsManagerStandalone;
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(taxInformation.TaxId))
|
|
{
|
|
var taxIdType = taxService.GetStripeTaxCode(
|
|
taxInformation.Country,
|
|
taxInformation.TaxId);
|
|
|
|
if (string.IsNullOrEmpty(taxIdType))
|
|
{
|
|
return new BadRequest(
|
|
"We couldn't find a corresponding tax ID type for the tax ID you provided. Please try again or contact support for assistance.");
|
|
}
|
|
|
|
options.CustomerDetails.TaxIds =
|
|
[
|
|
new InvoiceCustomerDetailsTaxIdOptions { Type = taxIdType, Value = taxInformation.TaxId }
|
|
];
|
|
|
|
if (taxIdType == StripeConstants.TaxIdType.SpanishNIF)
|
|
{
|
|
options.CustomerDetails.TaxIds.Add(new InvoiceCustomerDetailsTaxIdOptions
|
|
{
|
|
Type = StripeConstants.TaxIdType.EUVAT,
|
|
Value = $"ES{parameters.TaxInformation.TaxId}"
|
|
});
|
|
}
|
|
}
|
|
|
|
options.AutomaticTax = new InvoiceAutomaticTaxOptions { Enabled = true };
|
|
if (parameters.PlanType.IsBusinessProductTierType() &&
|
|
parameters.TaxInformation.Country != Core.Constants.CountryAbbreviations.UnitedStates)
|
|
{
|
|
options.CustomerDetails.TaxExempt = StripeConstants.TaxExempt.Reverse;
|
|
}
|
|
|
|
var invoice = await stripeAdapter.InvoiceCreatePreviewAsync(options);
|
|
return Convert.ToDecimal(invoice.Tax) / 100;
|
|
});
|
|
}
|
|
|
|
#region Command Parameters
|
|
|
|
public record OrganizationTrialParameters
|
|
{
|
|
public required PlanType PlanType { get; set; }
|
|
public required ProductType ProductType { get; set; }
|
|
public required TaxInformationDTO TaxInformation { get; set; }
|
|
|
|
public void Deconstruct(
|
|
out PlanType planType,
|
|
out ProductType productType,
|
|
out TaxInformationDTO taxInformation)
|
|
{
|
|
planType = PlanType;
|
|
productType = ProductType;
|
|
taxInformation = TaxInformation;
|
|
}
|
|
|
|
public record TaxInformationDTO
|
|
{
|
|
public required string Country { get; set; }
|
|
public required string PostalCode { get; set; }
|
|
public string? TaxId { get; set; }
|
|
}
|
|
}
|
|
|
|
#endregion
|