1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-21 12:05:42 +01:00

Removed automatic tax feature flag (#4487)

This commit is contained in:
Conner Turnbull 2024-07-10 07:32:41 -04:00 committed by GitHub
parent ff8a436cd4
commit 9e78236a72
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 22 additions and 160 deletions

View File

@ -1,5 +1,4 @@
using Bit.Billing.Constants; using Bit.Billing.Constants;
using Bit.Core;
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing.Constants; using Bit.Core.Billing.Constants;
@ -9,7 +8,6 @@ using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Stripe; using Stripe;
using Event = Stripe.Event; using Event = Stripe.Event;
using TaxRate = Bit.Core.Entities.TaxRate;
namespace Bit.Billing.Services.Implementations; namespace Bit.Billing.Services.Implementations;
@ -19,38 +17,32 @@ public class UpcomingInvoiceHandler : IUpcomingInvoiceHandler
private readonly IStripeEventService _stripeEventService; private readonly IStripeEventService _stripeEventService;
private readonly IUserService _userService; private readonly IUserService _userService;
private readonly IStripeFacade _stripeFacade; private readonly IStripeFacade _stripeFacade;
private readonly IFeatureService _featureService;
private readonly IMailService _mailService; private readonly IMailService _mailService;
private readonly IProviderRepository _providerRepository; private readonly IProviderRepository _providerRepository;
private readonly IValidateSponsorshipCommand _validateSponsorshipCommand; private readonly IValidateSponsorshipCommand _validateSponsorshipCommand;
private readonly IOrganizationRepository _organizationRepository; private readonly IOrganizationRepository _organizationRepository;
private readonly IStripeEventUtilityService _stripeEventUtilityService; private readonly IStripeEventUtilityService _stripeEventUtilityService;
private readonly ITaxRateRepository _taxRateRepository;
public UpcomingInvoiceHandler( public UpcomingInvoiceHandler(
ILogger<StripeEventProcessor> logger, ILogger<StripeEventProcessor> logger,
IStripeEventService stripeEventService, IStripeEventService stripeEventService,
IUserService userService, IUserService userService,
IStripeFacade stripeFacade, IStripeFacade stripeFacade,
IFeatureService featureService,
IMailService mailService, IMailService mailService,
IProviderRepository providerRepository, IProviderRepository providerRepository,
IValidateSponsorshipCommand validateSponsorshipCommand, IValidateSponsorshipCommand validateSponsorshipCommand,
IOrganizationRepository organizationRepository, IOrganizationRepository organizationRepository,
IStripeEventUtilityService stripeEventUtilityService, IStripeEventUtilityService stripeEventUtilityService)
ITaxRateRepository taxRateRepository)
{ {
_logger = logger; _logger = logger;
_stripeEventService = stripeEventService; _stripeEventService = stripeEventService;
_userService = userService; _userService = userService;
_stripeFacade = stripeFacade; _stripeFacade = stripeFacade;
_featureService = featureService;
_mailService = mailService; _mailService = mailService;
_providerRepository = providerRepository; _providerRepository = providerRepository;
_validateSponsorshipCommand = validateSponsorshipCommand; _validateSponsorshipCommand = validateSponsorshipCommand;
_organizationRepository = organizationRepository; _organizationRepository = organizationRepository;
_stripeEventUtilityService = stripeEventUtilityService; _stripeEventUtilityService = stripeEventUtilityService;
_taxRateRepository = taxRateRepository;
} }
/// <summary> /// <summary>
@ -75,27 +67,7 @@ public class UpcomingInvoiceHandler : IUpcomingInvoiceHandler
$"Received null Subscription from Stripe for ID '{invoice.SubscriptionId}' while processing Event with ID '{parsedEvent.Id}'"); $"Received null Subscription from Stripe for ID '{invoice.SubscriptionId}' while processing Event with ID '{parsedEvent.Id}'");
} }
var pm5766AutomaticTaxIsEnabled = _featureService.IsEnabled(FeatureFlagKeys.PM5766AutomaticTax); var updatedSubscription = await TryEnableAutomaticTaxAsync(subscription);
if (pm5766AutomaticTaxIsEnabled)
{
var customerGetOptions = new CustomerGetOptions();
customerGetOptions.AddExpand("tax");
var customer = await _stripeFacade.GetCustomer(subscription.CustomerId, customerGetOptions);
if (!subscription.AutomaticTax.Enabled &&
customer.Tax?.AutomaticTax == StripeConstants.AutomaticTaxStatus.Supported)
{
subscription = await _stripeFacade.UpdateSubscription(subscription.Id,
new SubscriptionUpdateOptions
{
DefaultTaxRates = [],
AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true }
});
}
}
var updatedSubscription = pm5766AutomaticTaxIsEnabled
? subscription
: await VerifyCorrectTaxRateForChargeAsync(invoice, subscription);
var (organizationId, userId, providerId) = _stripeEventUtilityService.GetIdsFromMetadata(updatedSubscription.Metadata); var (organizationId, userId, providerId) = _stripeEventUtilityService.GetIdsFromMetadata(updatedSubscription.Metadata);
@ -176,39 +148,24 @@ public class UpcomingInvoiceHandler : IUpcomingInvoiceHandler
} }
} }
private async Task<Stripe.Subscription> VerifyCorrectTaxRateForChargeAsync(Invoice invoice, Stripe.Subscription subscription) private async Task<Subscription> TryEnableAutomaticTaxAsync(Subscription subscription)
{ {
if (string.IsNullOrWhiteSpace(invoice?.CustomerAddress?.Country) || var customerGetOptions = new CustomerGetOptions { Expand = ["tax"] };
string.IsNullOrWhiteSpace(invoice?.CustomerAddress?.PostalCode)) var customer = await _stripeFacade.GetCustomer(subscription.CustomerId, customerGetOptions);
if (subscription.AutomaticTax.Enabled ||
customer.Tax?.AutomaticTax != StripeConstants.AutomaticTaxStatus.Supported)
{ {
return subscription; return subscription;
} }
var localBitwardenTaxRates = await _taxRateRepository.GetByLocationAsync( var subscriptionUpdateOptions = new SubscriptionUpdateOptions
new TaxRate()
{
Country = invoice.CustomerAddress.Country,
PostalCode = invoice.CustomerAddress.PostalCode
}
);
if (!localBitwardenTaxRates.Any())
{ {
return subscription; DefaultTaxRates = [],
} AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true }
};
var stripeTaxRate = await _stripeFacade.GetTaxRate(localBitwardenTaxRates.First().Id); return await _stripeFacade.UpdateSubscription(subscription.Id, subscriptionUpdateOptions);
if (stripeTaxRate == null || subscription.DefaultTaxRates.Any(x => x == stripeTaxRate))
{
return subscription;
}
subscription.DefaultTaxRates = [stripeTaxRate];
var subscriptionOptions = new SubscriptionUpdateOptions { DefaultTaxRates = [stripeTaxRate.Id] };
subscription = await _stripeFacade.UpdateSubscription(subscription.Id, subscriptionOptions);
return subscription;
} }
private static bool OrgPlanForInvoiceNotifications(Organization org) => StaticStore.GetPlan(org.PlanType).IsAnnual; private static bool OrgPlanForInvoiceNotifications(Organization org) => StaticStore.GetPlan(org.PlanType).IsAnnual;

View File

@ -148,9 +148,7 @@ public class OrganizationService : IOrganizationService
organization, organization,
paymentMethodType, paymentMethodType,
paymentToken, paymentToken,
_featureService.IsEnabled(FeatureFlagKeys.PM5766AutomaticTax) taxInfo);
? taxInfo
: null);
if (updated) if (updated)
{ {
await ReplaceAndUpdateCacheAsync(organization); await ReplaceAndUpdateCacheAsync(organization);

View File

@ -115,7 +115,6 @@ public static class FeatureFlagKeys
public const string ItemShare = "item-share"; public const string ItemShare = "item-share";
public const string KeyRotationImprovements = "key-rotation-improvements"; public const string KeyRotationImprovements = "key-rotation-improvements";
public const string DuoRedirect = "duo-redirect"; public const string DuoRedirect = "duo-redirect";
public const string PM5766AutomaticTax = "PM-5766-automatic-tax";
public const string PM5864DollarThreshold = "PM-5864-dollar-threshold"; public const string PM5864DollarThreshold = "PM-5864-dollar-threshold";
public const string ShowPaymentMethodWarningBanners = "show-payment-method-warning-banners"; public const string ShowPaymentMethodWarningBanners = "show-payment-method-warning-banners";
public const string AC2101UpdateTrialInitiationEmail = "AC-2101-update-trial-initiation-email"; public const string AC2101UpdateTrialInitiationEmail = "AC-2101-update-trial-initiation-email";

View File

@ -103,28 +103,6 @@ public class StripePaymentService : IPaymentService
throw new GatewayException("Payment method is not supported at this time."); throw new GatewayException("Payment method is not supported at this time.");
} }
var pm5766AutomaticTaxIsEnabled = _featureService.IsEnabled(FeatureFlagKeys.PM5766AutomaticTax);
if (!pm5766AutomaticTaxIsEnabled &&
taxInfo != null &&
!string.IsNullOrWhiteSpace(taxInfo.BillingAddressCountry) &&
!string.IsNullOrWhiteSpace(taxInfo.BillingAddressPostalCode))
{
var taxRateSearch = new TaxRate
{
Country = taxInfo.BillingAddressCountry,
PostalCode = taxInfo.BillingAddressPostalCode
};
var taxRates = await _taxRateRepository.GetByLocationAsync(taxRateSearch);
// should only be one tax rate per country/zip combo
var taxRate = taxRates.FirstOrDefault();
if (taxRate != null)
{
taxInfo.StripeTaxRateId = taxRate.Id;
}
}
var subCreateOptions = new OrganizationPurchaseSubscriptionOptions(org, plan, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon var subCreateOptions = new OrganizationPurchaseSubscriptionOptions(org, plan, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon
, additionalSmSeats, additionalServiceAccount); , additionalSmSeats, additionalServiceAccount);
@ -180,7 +158,7 @@ public class StripePaymentService : IPaymentService
subCreateOptions.AddExpand("latest_invoice.payment_intent"); subCreateOptions.AddExpand("latest_invoice.payment_intent");
subCreateOptions.Customer = customer.Id; subCreateOptions.Customer = customer.Id;
if (pm5766AutomaticTaxIsEnabled && CustomerHasTaxLocationVerified(customer)) if (CustomerHasTaxLocationVerified(customer))
{ {
subCreateOptions.AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true }; subCreateOptions.AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true };
} }
@ -273,31 +251,7 @@ public class StripePaymentService : IPaymentService
throw new GatewayException("Could not find customer payment profile."); throw new GatewayException("Could not find customer payment profile.");
} }
var pm5766AutomaticTaxIsEnabled = _featureService.IsEnabled(FeatureFlagKeys.PM5766AutomaticTax); if (!string.IsNullOrEmpty(upgrade.TaxInfo?.BillingAddressCountry) &&
var taxInfo = upgrade.TaxInfo;
if (!pm5766AutomaticTaxIsEnabled &&
taxInfo != null &&
!string.IsNullOrWhiteSpace(taxInfo.BillingAddressCountry) &&
!string.IsNullOrWhiteSpace(taxInfo.BillingAddressPostalCode))
{
var taxRateSearch = new TaxRate
{
Country = taxInfo.BillingAddressCountry,
PostalCode = taxInfo.BillingAddressPostalCode
};
var taxRates = await _taxRateRepository.GetByLocationAsync(taxRateSearch);
// should only be one tax rate per country/zip combo
var taxRate = taxRates.FirstOrDefault();
if (taxRate != null)
{
taxInfo.StripeTaxRateId = taxRate.Id;
}
}
if (pm5766AutomaticTaxIsEnabled &&
!string.IsNullOrEmpty(upgrade.TaxInfo?.BillingAddressCountry) &&
!string.IsNullOrEmpty(upgrade.TaxInfo?.BillingAddressPostalCode)) !string.IsNullOrEmpty(upgrade.TaxInfo?.BillingAddressPostalCode))
{ {
var addressOptions = new AddressOptions var addressOptions = new AddressOptions
@ -319,7 +273,7 @@ public class StripePaymentService : IPaymentService
var subCreateOptions = new OrganizationUpgradeSubscriptionOptions(customer.Id, org, plan, upgrade); var subCreateOptions = new OrganizationUpgradeSubscriptionOptions(customer.Id, org, plan, upgrade);
if (pm5766AutomaticTaxIsEnabled && CustomerHasTaxLocationVerified(customer)) if (CustomerHasTaxLocationVerified(customer))
{ {
subCreateOptions.DefaultTaxRates = []; subCreateOptions.DefaultTaxRates = [];
subCreateOptions.AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true }; subCreateOptions.AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true };
@ -533,26 +487,6 @@ public class StripePaymentService : IPaymentService
Quantity = 1 Quantity = 1
}); });
var pm5766AutomaticTaxIsEnabled = _featureService.IsEnabled(FeatureFlagKeys.PM5766AutomaticTax);
if (!pm5766AutomaticTaxIsEnabled &&
!string.IsNullOrWhiteSpace(taxInfo?.BillingAddressCountry) &&
!string.IsNullOrWhiteSpace(taxInfo?.BillingAddressPostalCode))
{
var taxRates = await _taxRateRepository.GetByLocationAsync(
new TaxRate
{
Country = taxInfo.BillingAddressCountry,
PostalCode = taxInfo.BillingAddressPostalCode
}
);
var taxRate = taxRates.FirstOrDefault();
if (taxRate != null)
{
subCreateOptions.DefaultTaxRates = [taxRate.Id];
}
}
if (additionalStorageGb > 0) if (additionalStorageGb > 0)
{ {
subCreateOptions.Items.Add(new SubscriptionItemOptions subCreateOptions.Items.Add(new SubscriptionItemOptions
@ -562,7 +496,7 @@ public class StripePaymentService : IPaymentService
}); });
} }
if (pm5766AutomaticTaxIsEnabled && CustomerHasTaxLocationVerified(customer)) if (CustomerHasTaxLocationVerified(customer))
{ {
subCreateOptions.DefaultTaxRates = []; subCreateOptions.DefaultTaxRates = [];
subCreateOptions.AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true }; subCreateOptions.AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true };
@ -605,8 +539,7 @@ public class StripePaymentService : IPaymentService
SubscriptionItems = ToInvoiceSubscriptionItemOptions(subCreateOptions.Items) SubscriptionItems = ToInvoiceSubscriptionItemOptions(subCreateOptions.Items)
}); });
if (_featureService.IsEnabled(FeatureFlagKeys.PM5766AutomaticTax) && if (CustomerHasTaxLocationVerified(customer))
CustomerHasTaxLocationVerified(customer))
{ {
previewInvoice.AutomaticTax = new InvoiceAutomaticTax { Enabled = true }; previewInvoice.AutomaticTax = new InvoiceAutomaticTax { Enabled = true };
} }
@ -669,8 +602,7 @@ public class StripePaymentService : IPaymentService
SubscriptionDefaultTaxRates = subCreateOptions.DefaultTaxRates, SubscriptionDefaultTaxRates = subCreateOptions.DefaultTaxRates,
}; };
var pm5766AutomaticTaxIsEnabled = _featureService.IsEnabled(FeatureFlagKeys.PM5766AutomaticTax); if (CustomerHasTaxLocationVerified(customer))
if (pm5766AutomaticTaxIsEnabled && CustomerHasTaxLocationVerified(customer))
{ {
upcomingInvoiceOptions.AutomaticTax = new InvoiceAutomaticTaxOptions { Enabled = true }; upcomingInvoiceOptions.AutomaticTax = new InvoiceAutomaticTaxOptions { Enabled = true };
upcomingInvoiceOptions.SubscriptionDefaultTaxRates = []; upcomingInvoiceOptions.SubscriptionDefaultTaxRates = [];
@ -800,9 +732,7 @@ public class StripePaymentService : IPaymentService
new SubscriptionPendingInvoiceItemIntervalOptions { Interval = "month" }; new SubscriptionPendingInvoiceItemIntervalOptions { Interval = "month" };
} }
var pm5766AutomaticTaxIsEnabled = _featureService.IsEnabled(FeatureFlagKeys.PM5766AutomaticTax); if (sub.AutomaticTax.Enabled != true &&
if (pm5766AutomaticTaxIsEnabled &&
sub.AutomaticTax.Enabled != true &&
CustomerHasTaxLocationVerified(sub.Customer)) CustomerHasTaxLocationVerified(sub.Customer))
{ {
subUpdateOptions.DefaultTaxRates = []; subUpdateOptions.DefaultTaxRates = [];
@ -815,26 +745,6 @@ public class StripePaymentService : IPaymentService
return null; return null;
} }
if (!pm5766AutomaticTaxIsEnabled)
{
var customer = await _stripeAdapter.CustomerGetAsync(sub.CustomerId);
if (!string.IsNullOrWhiteSpace(customer?.Address?.Country)
&& !string.IsNullOrWhiteSpace(customer?.Address?.PostalCode))
{
var taxRates = await _taxRateRepository.GetByLocationAsync(new TaxRate
{
Country = customer.Address.Country,
PostalCode = customer.Address.PostalCode
});
var taxRate = taxRates.FirstOrDefault();
if (taxRate != null && !sub.DefaultTaxRates.Any(x => x.Equals(taxRate.Id)))
{
subUpdateOptions.DefaultTaxRates = [taxRate.Id];
}
}
}
string paymentIntentClientSecret = null; string paymentIntentClientSecret = null;
try try
{ {
@ -1502,8 +1412,7 @@ public class StripePaymentService : IPaymentService
}); });
} }
if (_featureService.IsEnabled(FeatureFlagKeys.PM5766AutomaticTax) && if (!string.IsNullOrEmpty(subscriber.GatewaySubscriptionId) &&
!string.IsNullOrEmpty(subscriber.GatewaySubscriptionId) &&
customer.Subscriptions.Any(sub => customer.Subscriptions.Any(sub =>
sub.Id == subscriber.GatewaySubscriptionId && sub.Id == subscriber.GatewaySubscriptionId &&
!sub.AutomaticTax.Enabled) && !sub.AutomaticTax.Enabled) &&

View File

@ -725,7 +725,6 @@ public class StripePaymentServiceTests
AmountDue = 0 AmountDue = 0
}); });
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription { }); stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription { });
featureService.IsEnabled(FeatureFlagKeys.PM5766AutomaticTax).Returns(true);
var upgrade = new OrganizationUpgrade() var upgrade = new OrganizationUpgrade()
{ {