From 9e78236a72cea71372da82700040f68825249f29 Mon Sep 17 00:00:00 2001 From: Conner Turnbull <133619638+cturnbull-bitwarden@users.noreply.github.com> Date: Wed, 10 Jul 2024 07:32:41 -0400 Subject: [PATCH] Removed automatic tax feature flag (#4487) --- .../Implementations/UpcomingInvoiceHandler.cs | 69 +++-------- .../Implementations/OrganizationService.cs | 4 +- src/Core/Constants.cs | 1 - .../Implementations/StripePaymentService.cs | 107 ++---------------- .../Services/StripePaymentServiceTests.cs | 1 - 5 files changed, 22 insertions(+), 160 deletions(-) diff --git a/src/Billing/Services/Implementations/UpcomingInvoiceHandler.cs b/src/Billing/Services/Implementations/UpcomingInvoiceHandler.cs index 48b691097..6b54fd9af 100644 --- a/src/Billing/Services/Implementations/UpcomingInvoiceHandler.cs +++ b/src/Billing/Services/Implementations/UpcomingInvoiceHandler.cs @@ -1,5 +1,4 @@ using Bit.Billing.Constants; -using Bit.Core; using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Repositories; using Bit.Core.Billing.Constants; @@ -9,7 +8,6 @@ using Bit.Core.Services; using Bit.Core.Utilities; using Stripe; using Event = Stripe.Event; -using TaxRate = Bit.Core.Entities.TaxRate; namespace Bit.Billing.Services.Implementations; @@ -19,38 +17,32 @@ public class UpcomingInvoiceHandler : IUpcomingInvoiceHandler private readonly IStripeEventService _stripeEventService; private readonly IUserService _userService; private readonly IStripeFacade _stripeFacade; - private readonly IFeatureService _featureService; private readonly IMailService _mailService; private readonly IProviderRepository _providerRepository; private readonly IValidateSponsorshipCommand _validateSponsorshipCommand; private readonly IOrganizationRepository _organizationRepository; private readonly IStripeEventUtilityService _stripeEventUtilityService; - private readonly ITaxRateRepository _taxRateRepository; public UpcomingInvoiceHandler( ILogger logger, IStripeEventService stripeEventService, IUserService userService, IStripeFacade stripeFacade, - IFeatureService featureService, IMailService mailService, IProviderRepository providerRepository, IValidateSponsorshipCommand validateSponsorshipCommand, IOrganizationRepository organizationRepository, - IStripeEventUtilityService stripeEventUtilityService, - ITaxRateRepository taxRateRepository) + IStripeEventUtilityService stripeEventUtilityService) { _logger = logger; _stripeEventService = stripeEventService; _userService = userService; _stripeFacade = stripeFacade; - _featureService = featureService; _mailService = mailService; _providerRepository = providerRepository; _validateSponsorshipCommand = validateSponsorshipCommand; _organizationRepository = organizationRepository; _stripeEventUtilityService = stripeEventUtilityService; - _taxRateRepository = taxRateRepository; } /// @@ -75,27 +67,7 @@ public class UpcomingInvoiceHandler : IUpcomingInvoiceHandler $"Received null Subscription from Stripe for ID '{invoice.SubscriptionId}' while processing Event with ID '{parsedEvent.Id}'"); } - var pm5766AutomaticTaxIsEnabled = _featureService.IsEnabled(FeatureFlagKeys.PM5766AutomaticTax); - 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 updatedSubscription = await TryEnableAutomaticTaxAsync(subscription); var (organizationId, userId, providerId) = _stripeEventUtilityService.GetIdsFromMetadata(updatedSubscription.Metadata); @@ -176,39 +148,24 @@ public class UpcomingInvoiceHandler : IUpcomingInvoiceHandler } } - private async Task VerifyCorrectTaxRateForChargeAsync(Invoice invoice, Stripe.Subscription subscription) + private async Task TryEnableAutomaticTaxAsync(Subscription subscription) { - if (string.IsNullOrWhiteSpace(invoice?.CustomerAddress?.Country) || - string.IsNullOrWhiteSpace(invoice?.CustomerAddress?.PostalCode)) + var customerGetOptions = new CustomerGetOptions { Expand = ["tax"] }; + var customer = await _stripeFacade.GetCustomer(subscription.CustomerId, customerGetOptions); + + if (subscription.AutomaticTax.Enabled || + customer.Tax?.AutomaticTax != StripeConstants.AutomaticTaxStatus.Supported) { return subscription; } - var localBitwardenTaxRates = await _taxRateRepository.GetByLocationAsync( - new TaxRate() - { - Country = invoice.CustomerAddress.Country, - PostalCode = invoice.CustomerAddress.PostalCode - } - ); - - if (!localBitwardenTaxRates.Any()) + var subscriptionUpdateOptions = new SubscriptionUpdateOptions { - return subscription; - } + DefaultTaxRates = [], + AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true } + }; - var stripeTaxRate = await _stripeFacade.GetTaxRate(localBitwardenTaxRates.First().Id); - 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; + return await _stripeFacade.UpdateSubscription(subscription.Id, subscriptionUpdateOptions); } private static bool OrgPlanForInvoiceNotifications(Organization org) => StaticStore.GetPlan(org.PlanType).IsAnnual; diff --git a/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs b/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs index 5ada7324a..a155832d8 100644 --- a/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs +++ b/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs @@ -148,9 +148,7 @@ public class OrganizationService : IOrganizationService organization, paymentMethodType, paymentToken, - _featureService.IsEnabled(FeatureFlagKeys.PM5766AutomaticTax) - ? taxInfo - : null); + taxInfo); if (updated) { await ReplaceAndUpdateCacheAsync(organization); diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index 6ff37e52d..caecac64c 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -115,7 +115,6 @@ public static class FeatureFlagKeys public const string ItemShare = "item-share"; public const string KeyRotationImprovements = "key-rotation-improvements"; 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 ShowPaymentMethodWarningBanners = "show-payment-method-warning-banners"; public const string AC2101UpdateTrialInitiationEmail = "AC-2101-update-trial-initiation-email"; diff --git a/src/Core/Services/Implementations/StripePaymentService.cs b/src/Core/Services/Implementations/StripePaymentService.cs index 7d1776220..7e7c91a10 100644 --- a/src/Core/Services/Implementations/StripePaymentService.cs +++ b/src/Core/Services/Implementations/StripePaymentService.cs @@ -103,28 +103,6 @@ public class StripePaymentService : IPaymentService 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 , additionalSmSeats, additionalServiceAccount); @@ -180,7 +158,7 @@ public class StripePaymentService : IPaymentService subCreateOptions.AddExpand("latest_invoice.payment_intent"); subCreateOptions.Customer = customer.Id; - if (pm5766AutomaticTaxIsEnabled && CustomerHasTaxLocationVerified(customer)) + if (CustomerHasTaxLocationVerified(customer)) { subCreateOptions.AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true }; } @@ -273,31 +251,7 @@ public class StripePaymentService : IPaymentService throw new GatewayException("Could not find customer payment profile."); } - var pm5766AutomaticTaxIsEnabled = _featureService.IsEnabled(FeatureFlagKeys.PM5766AutomaticTax); - 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) && + if (!string.IsNullOrEmpty(upgrade.TaxInfo?.BillingAddressCountry) && !string.IsNullOrEmpty(upgrade.TaxInfo?.BillingAddressPostalCode)) { var addressOptions = new AddressOptions @@ -319,7 +273,7 @@ public class StripePaymentService : IPaymentService var subCreateOptions = new OrganizationUpgradeSubscriptionOptions(customer.Id, org, plan, upgrade); - if (pm5766AutomaticTaxIsEnabled && CustomerHasTaxLocationVerified(customer)) + if (CustomerHasTaxLocationVerified(customer)) { subCreateOptions.DefaultTaxRates = []; subCreateOptions.AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true }; @@ -533,26 +487,6 @@ public class StripePaymentService : IPaymentService 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) { subCreateOptions.Items.Add(new SubscriptionItemOptions @@ -562,7 +496,7 @@ public class StripePaymentService : IPaymentService }); } - if (pm5766AutomaticTaxIsEnabled && CustomerHasTaxLocationVerified(customer)) + if (CustomerHasTaxLocationVerified(customer)) { subCreateOptions.DefaultTaxRates = []; subCreateOptions.AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true }; @@ -605,8 +539,7 @@ public class StripePaymentService : IPaymentService SubscriptionItems = ToInvoiceSubscriptionItemOptions(subCreateOptions.Items) }); - if (_featureService.IsEnabled(FeatureFlagKeys.PM5766AutomaticTax) && - CustomerHasTaxLocationVerified(customer)) + if (CustomerHasTaxLocationVerified(customer)) { previewInvoice.AutomaticTax = new InvoiceAutomaticTax { Enabled = true }; } @@ -669,8 +602,7 @@ public class StripePaymentService : IPaymentService SubscriptionDefaultTaxRates = subCreateOptions.DefaultTaxRates, }; - var pm5766AutomaticTaxIsEnabled = _featureService.IsEnabled(FeatureFlagKeys.PM5766AutomaticTax); - if (pm5766AutomaticTaxIsEnabled && CustomerHasTaxLocationVerified(customer)) + if (CustomerHasTaxLocationVerified(customer)) { upcomingInvoiceOptions.AutomaticTax = new InvoiceAutomaticTaxOptions { Enabled = true }; upcomingInvoiceOptions.SubscriptionDefaultTaxRates = []; @@ -800,9 +732,7 @@ public class StripePaymentService : IPaymentService new SubscriptionPendingInvoiceItemIntervalOptions { Interval = "month" }; } - var pm5766AutomaticTaxIsEnabled = _featureService.IsEnabled(FeatureFlagKeys.PM5766AutomaticTax); - if (pm5766AutomaticTaxIsEnabled && - sub.AutomaticTax.Enabled != true && + if (sub.AutomaticTax.Enabled != true && CustomerHasTaxLocationVerified(sub.Customer)) { subUpdateOptions.DefaultTaxRates = []; @@ -815,26 +745,6 @@ public class StripePaymentService : IPaymentService 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; try { @@ -1502,8 +1412,7 @@ public class StripePaymentService : IPaymentService }); } - if (_featureService.IsEnabled(FeatureFlagKeys.PM5766AutomaticTax) && - !string.IsNullOrEmpty(subscriber.GatewaySubscriptionId) && + if (!string.IsNullOrEmpty(subscriber.GatewaySubscriptionId) && customer.Subscriptions.Any(sub => sub.Id == subscriber.GatewaySubscriptionId && !sub.AutomaticTax.Enabled) && diff --git a/test/Core.Test/Services/StripePaymentServiceTests.cs b/test/Core.Test/Services/StripePaymentServiceTests.cs index 4d20fd2c6..e15f07b11 100644 --- a/test/Core.Test/Services/StripePaymentServiceTests.cs +++ b/test/Core.Test/Services/StripePaymentServiceTests.cs @@ -725,7 +725,6 @@ public class StripePaymentServiceTests AmountDue = 0 }); stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription { }); - featureService.IsEnabled(FeatureFlagKeys.PM5766AutomaticTax).Returns(true); var upgrade = new OrganizationUpgrade() {