1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 07:36:14 -05:00

[PM-21092] Set tax exemption to reverse charge for non-US business-use customers (#5812)

* Set automatic tax to enabled and tax exempt to reverse where applicable when ff is on

* Fix and add tests

* Run dotnet format

* Run dotnet format

* PM-21745: Resolve defect

* PM-21770: Resolve defect

* Run dotnet format'
This commit is contained in:
Alex Morask
2025-05-19 14:53:48 -04:00
committed by GitHub
parent a07cce26f3
commit 7b3e2a80f4
21 changed files with 846 additions and 601 deletions

View File

@ -3,14 +3,11 @@ using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.Billing.Caches;
using Bit.Core.Billing.Constants;
using Bit.Core.Billing.Models;
using Bit.Core.Billing.Services.Contracts;
using Bit.Core.Billing.Services.Implementations;
using Bit.Core.Billing.Tax.Models;
using Bit.Core.Billing.Tax.Services;
using Bit.Core.Enums;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Test.Billing.Tax.Services;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Braintree;
@ -195,7 +192,7 @@ public class SubscriberServiceTests
await stripeAdapter
.DidNotReceiveWithAnyArgs()
.SubscriptionCancelAsync(Arg.Any<string>(), Arg.Any<SubscriptionCancelOptions>()); ;
.SubscriptionCancelAsync(Arg.Any<string>(), Arg.Any<SubscriptionCancelOptions>());
}
#endregion
@ -1029,7 +1026,7 @@ public class SubscriberServiceTests
stripeAdapter
.PaymentMethodListAutoPagingAsync(Arg.Any<PaymentMethodListOptions>())
.Returns(GetPaymentMethodsAsync(new List<Stripe.PaymentMethod>()));
.Returns(GetPaymentMethodsAsync(new List<PaymentMethod>()));
await sutProvider.Sut.RemovePaymentSource(organization);
@ -1061,7 +1058,7 @@ public class SubscriberServiceTests
stripeAdapter
.PaymentMethodListAutoPagingAsync(Arg.Any<PaymentMethodListOptions>())
.Returns(GetPaymentMethodsAsync(new List<Stripe.PaymentMethod>
.Returns(GetPaymentMethodsAsync(new List<PaymentMethod>
{
new ()
{
@ -1086,8 +1083,8 @@ public class SubscriberServiceTests
.PaymentMethodDetachAsync(cardId);
}
private static async IAsyncEnumerable<Stripe.PaymentMethod> GetPaymentMethodsAsync(
IEnumerable<Stripe.PaymentMethod> paymentMethods)
private static async IAsyncEnumerable<PaymentMethod> GetPaymentMethodsAsync(
IEnumerable<PaymentMethod> paymentMethods)
{
foreach (var paymentMethod in paymentMethods)
{
@ -1598,14 +1595,22 @@ public class SubscriberServiceTests
City = "Example Town",
State = "NY"
},
TaxIds = new StripeList<TaxId> { Data = [new TaxId { Id = "tax_id_1", Type = "us_ein" }] }
TaxIds = new StripeList<TaxId> { Data = [new TaxId { Id = "tax_id_1", Type = "us_ein" }] },
Subscriptions = new StripeList<Subscription>
{
Data = [
new Subscription
{
Id = provider.GatewaySubscriptionId,
AutomaticTax = new SubscriptionAutomaticTax { Enabled = false }
}
]
}
});
var subscription = new Subscription { Items = new StripeList<SubscriptionItem>() };
sutProvider.GetDependency<IStripeAdapter>().SubscriptionGetAsync(Arg.Any<string>())
.Returns(subscription);
sutProvider.GetDependency<IAutomaticTaxFactory>().CreateAsync(Arg.Any<AutomaticTaxFactoryParameters>())
.Returns(new FakeAutomaticTaxStrategy(true));
await sutProvider.Sut.UpdateTaxInformation(provider, taxInformation);
@ -1623,6 +1628,98 @@ public class SubscriberServiceTests
await stripeAdapter.Received(1).TaxIdCreateAsync(provider.GatewayCustomerId, Arg.Is<TaxIdCreateOptions>(
options => options.Type == "us_ein" &&
options.Value == taxInformation.TaxId));
await stripeAdapter.Received(1).SubscriptionUpdateAsync(provider.GatewaySubscriptionId,
Arg.Is<SubscriptionUpdateOptions>(options => options.AutomaticTax.Enabled == true));
}
[Theory, BitAutoData]
public async Task UpdateTaxInformation_NonUser_ReverseCharge_MakesCorrectInvocations(
Provider provider,
SutProvider<SubscriberService> sutProvider)
{
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
var customer = new Customer { Id = provider.GatewayCustomerId, TaxIds = new StripeList<TaxId> { Data = [new TaxId { Id = "tax_id_1", Type = "us_ein" }] } };
stripeAdapter.CustomerGetAsync(provider.GatewayCustomerId, Arg.Is<CustomerGetOptions>(
options => options.Expand.Contains("tax_ids"))).Returns(customer);
var taxInformation = new TaxInformation(
"CA",
"12345",
"123456789",
"us_ein",
"123 Example St.",
null,
"Example Town",
"NY");
sutProvider.GetDependency<IStripeAdapter>()
.CustomerUpdateAsync(
Arg.Is<string>(p => p == provider.GatewayCustomerId),
Arg.Is<CustomerUpdateOptions>(options =>
options.Address.Country == "CA" &&
options.Address.PostalCode == "12345" &&
options.Address.Line1 == "123 Example St." &&
options.Address.Line2 == null &&
options.Address.City == "Example Town" &&
options.Address.State == "NY"))
.Returns(new Customer
{
Id = provider.GatewayCustomerId,
Address = new Address
{
Country = "CA",
PostalCode = "12345",
Line1 = "123 Example St.",
Line2 = null,
City = "Example Town",
State = "NY"
},
TaxIds = new StripeList<TaxId> { Data = [new TaxId { Id = "tax_id_1", Type = "us_ein" }] },
Subscriptions = new StripeList<Subscription>
{
Data = [
new Subscription
{
Id = provider.GatewaySubscriptionId,
CustomerId = provider.GatewayCustomerId,
AutomaticTax = new SubscriptionAutomaticTax { Enabled = false }
}
]
}
});
var subscription = new Subscription { Items = new StripeList<SubscriptionItem>() };
sutProvider.GetDependency<IStripeAdapter>().SubscriptionGetAsync(Arg.Any<string>())
.Returns(subscription);
sutProvider.GetDependency<IFeatureService>()
.IsEnabled(FeatureFlagKeys.PM21092_SetNonUSBusinessUseToReverseCharge).Returns(true);
await sutProvider.Sut.UpdateTaxInformation(provider, taxInformation);
await stripeAdapter.Received(1).CustomerUpdateAsync(provider.GatewayCustomerId, Arg.Is<CustomerUpdateOptions>(
options =>
options.Address.Country == taxInformation.Country &&
options.Address.PostalCode == taxInformation.PostalCode &&
options.Address.Line1 == taxInformation.Line1 &&
options.Address.Line2 == taxInformation.Line2 &&
options.Address.City == taxInformation.City &&
options.Address.State == taxInformation.State));
await stripeAdapter.Received(1).TaxIdDeleteAsync(provider.GatewayCustomerId, "tax_id_1");
await stripeAdapter.Received(1).TaxIdCreateAsync(provider.GatewayCustomerId, Arg.Is<TaxIdCreateOptions>(
options => options.Type == "us_ein" &&
options.Value == taxInformation.TaxId));
await stripeAdapter.Received(1).CustomerUpdateAsync(provider.GatewayCustomerId,
Arg.Is<CustomerUpdateOptions>(options => options.TaxExempt == StripeConstants.TaxExempt.Reverse));
await stripeAdapter.Received(1).SubscriptionUpdateAsync(provider.GatewaySubscriptionId,
Arg.Is<SubscriptionUpdateOptions>(options => options.AutomaticTax.Enabled == true));
}
#endregion