mirror of
https://github.com/bitwarden/server.git
synced 2025-07-03 09:02:48 -05:00
WIP
This commit is contained in:
@ -47,6 +47,8 @@ public static class StripeConstants
|
||||
public static class MetadataKeys
|
||||
{
|
||||
public const string OrganizationId = "organizationId";
|
||||
public const string ProviderId = "providerId";
|
||||
public const string UserId = "userId";
|
||||
}
|
||||
|
||||
public static class PaymentBehavior
|
||||
|
12
src/Core/Billing/Extensions/SubscriptionExtensions.cs
Normal file
12
src/Core/Billing/Extensions/SubscriptionExtensions.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Bit.Core.Billing.Constants;
|
||||
using Stripe;
|
||||
|
||||
namespace Bit.Core.Billing.Extensions;
|
||||
|
||||
public static class SubscriptionExtensions
|
||||
{
|
||||
public static bool IsOrganization(this Subscription subscription)
|
||||
{
|
||||
return subscription.Metadata.ContainsKey(StripeConstants.MetadataKeys.OrganizationId);
|
||||
}
|
||||
}
|
10
src/Core/Billing/Services/IAutomaticTaxStrategy.cs
Normal file
10
src/Core/Billing/Services/IAutomaticTaxStrategy.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using Stripe;
|
||||
|
||||
namespace Bit.Core.Billing.Services;
|
||||
|
||||
public interface IAutomaticTaxStrategy
|
||||
{
|
||||
Task<SubscriptionUpdateOptions> GetUpdateOptionsAsync(Subscription subscription);
|
||||
Task SetCreateOptionsAsync(SubscriptionCreateOptions options, Customer customer = null);
|
||||
Task SetUpdateOptionsAsync(SubscriptionUpdateOptions options, Subscription subscription);
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
namespace Bit.Core.Billing.Services;
|
||||
|
||||
public interface IIndividualAutomaticTaxStrategy : IAutomaticTaxStrategy;
|
@ -0,0 +1,3 @@
|
||||
namespace Bit.Core.Billing.Services;
|
||||
|
||||
public interface IOrganizationAutomaticTaxStrategy : IAutomaticTaxStrategy;
|
@ -0,0 +1,45 @@
|
||||
using Stripe;
|
||||
|
||||
namespace Bit.Core.Billing.Services.Implementations.AutomaticTax;
|
||||
|
||||
public class IndividualAutomaticTaxStrategy : IIndividualAutomaticTaxStrategy
|
||||
{
|
||||
public Task SetCreateOptionsAsync(SubscriptionCreateOptions options, Customer customer = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
options.AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true };
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task SetUpdateOptionsAsync(SubscriptionUpdateOptions options, Subscription subscription)
|
||||
{
|
||||
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
|
||||
if (subscription.AutomaticTax.Enabled)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
options.AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true };
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<SubscriptionUpdateOptions> GetUpdateOptionsAsync(Subscription subscription)
|
||||
{
|
||||
if (subscription.AutomaticTax.Enabled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var options = new SubscriptionUpdateOptions
|
||||
{
|
||||
AutomaticTax = new SubscriptionAutomaticTaxOptions
|
||||
{
|
||||
Enabled = true
|
||||
}
|
||||
};
|
||||
|
||||
return Task.FromResult(options);
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Billing.Extensions;
|
||||
using Bit.Core.Billing.Pricing;
|
||||
using Stripe;
|
||||
|
||||
namespace Bit.Core.Billing.Services.Implementations.AutomaticTax;
|
||||
|
||||
public class OrganizationAutomaticTaxStrategy(
|
||||
IPricingClient pricingClient) : IOrganizationAutomaticTaxStrategy
|
||||
{
|
||||
public async Task<SubscriptionUpdateOptions> GetUpdateOptionsAsync(Subscription subscription)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(subscription);
|
||||
|
||||
var isEnabled = await IsEnabledAsync(subscription);
|
||||
if (!isEnabled.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var options = new SubscriptionUpdateOptions
|
||||
{
|
||||
AutomaticTax = new SubscriptionAutomaticTaxOptions
|
||||
{
|
||||
Enabled = isEnabled.Value
|
||||
}
|
||||
};
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public async Task SetCreateOptionsAsync(SubscriptionCreateOptions options, Customer customer = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
ArgumentNullException.ThrowIfNull(customer);
|
||||
|
||||
options.AutomaticTax = new SubscriptionAutomaticTaxOptions
|
||||
{
|
||||
Enabled = await IsEnabledAsync(options, customer)
|
||||
};
|
||||
}
|
||||
|
||||
public async Task SetUpdateOptionsAsync(SubscriptionUpdateOptions options, Subscription subscription)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(subscription);
|
||||
|
||||
if (subscription.AutomaticTax.Enabled == options.AutomaticTax?.Enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var isEnabled = await IsEnabledAsync(subscription);
|
||||
if (!isEnabled.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
options.AutomaticTax = new SubscriptionAutomaticTaxOptions
|
||||
{
|
||||
Enabled = isEnabled.Value
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<bool?> IsEnabledAsync(Subscription subscription)
|
||||
{
|
||||
if (subscription.AutomaticTax.Enabled ||
|
||||
!subscription.Customer.HasBillingLocation() ||
|
||||
await IsNonTaxableNonUsBusinessUseSubscriptionAsync(subscription))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return !await IsNonTaxableNonUsBusinessUseSubscriptionAsync(subscription);
|
||||
}
|
||||
|
||||
private async Task<bool> IsNonTaxableNonUsBusinessUseSubscriptionAsync(Subscription subscription)
|
||||
{
|
||||
var familyPriceIds = (await Task.WhenAll(
|
||||
pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually2019),
|
||||
pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually)))
|
||||
.Select(plan => plan.PasswordManager.StripePlanId);
|
||||
|
||||
return subscription.Customer.Address.Country != "US" &&
|
||||
subscription.IsOrganization() &&
|
||||
!subscription.Items.Select(item => item.Price.Id).Intersect(familyPriceIds).Any() &&
|
||||
!subscription.Customer.TaxIds.Any();
|
||||
}
|
||||
|
||||
private async Task<bool?> IsEnabledAsync(SubscriptionCreateOptions options, Customer customer)
|
||||
{
|
||||
if (!customer.HasBillingLocation() ||
|
||||
await IsNonTaxableNonUsBusinessUseSubscriptionAsync(options, customer))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return !await IsNonTaxableNonUsBusinessUseSubscriptionAsync(options, customer);
|
||||
}
|
||||
|
||||
private async Task<bool> IsNonTaxableNonUsBusinessUseSubscriptionAsync(SubscriptionCreateOptions options, Customer customer)
|
||||
{
|
||||
var familyPriceIds = (await Task.WhenAll(
|
||||
pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually2019),
|
||||
pricingClient.GetPlanOrThrow(PlanType.FamiliesAnnually)))
|
||||
.Select(plan => plan.PasswordManager.StripePlanId);
|
||||
|
||||
return customer.Address.Country != "US" &&
|
||||
!options.Items.Select(item => item.Price).Intersect(familyPriceIds).Any() &&
|
||||
!customer.TaxIds.Any();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user