mirror of
https://github.com/bitwarden/server.git
synced 2025-07-04 01:22:50 -05:00
[PM-13999] Show estimated tax for taxable countries (#5077)
This commit is contained in:
@ -28,7 +28,8 @@ public class OrganizationBillingService(
|
||||
IOrganizationRepository organizationRepository,
|
||||
ISetupIntentCache setupIntentCache,
|
||||
IStripeAdapter stripeAdapter,
|
||||
ISubscriberService subscriberService) : IOrganizationBillingService
|
||||
ISubscriberService subscriberService,
|
||||
ITaxService taxService) : IOrganizationBillingService
|
||||
{
|
||||
public async Task Finalize(OrganizationSale sale)
|
||||
{
|
||||
@ -167,14 +168,38 @@ public class OrganizationBillingService(
|
||||
throw new BillingException();
|
||||
}
|
||||
|
||||
var (address, taxIdData) = customerSetup.TaxInformation.GetStripeOptions();
|
||||
|
||||
customerCreateOptions.Address = address;
|
||||
customerCreateOptions.Address = new AddressOptions
|
||||
{
|
||||
Line1 = customerSetup.TaxInformation.Line1,
|
||||
Line2 = customerSetup.TaxInformation.Line2,
|
||||
City = customerSetup.TaxInformation.City,
|
||||
PostalCode = customerSetup.TaxInformation.PostalCode,
|
||||
State = customerSetup.TaxInformation.State,
|
||||
Country = customerSetup.TaxInformation.Country,
|
||||
};
|
||||
customerCreateOptions.Tax = new CustomerTaxOptions
|
||||
{
|
||||
ValidateLocation = StripeConstants.ValidateTaxLocationTiming.Immediately
|
||||
};
|
||||
customerCreateOptions.TaxIdData = taxIdData;
|
||||
|
||||
if (!string.IsNullOrEmpty(customerSetup.TaxInformation.TaxId))
|
||||
{
|
||||
var taxIdType = taxService.GetStripeTaxCode(customerSetup.TaxInformation.Country,
|
||||
customerSetup.TaxInformation.TaxId);
|
||||
|
||||
if (taxIdType == null)
|
||||
{
|
||||
logger.LogWarning("Could not determine tax ID type for organization '{OrganizationID}' in country '{Country}' with tax ID '{TaxID}'.",
|
||||
organization.Id,
|
||||
customerSetup.TaxInformation.Country,
|
||||
customerSetup.TaxInformation.TaxId);
|
||||
}
|
||||
|
||||
customerCreateOptions.TaxIdData =
|
||||
[
|
||||
new() { Type = taxIdType, Value = customerSetup.TaxInformation.TaxId }
|
||||
];
|
||||
}
|
||||
|
||||
var (paymentMethodType, paymentMethodToken) = customerSetup.TokenizedPaymentSource;
|
||||
|
||||
|
@ -24,7 +24,8 @@ public class PremiumUserBillingService(
|
||||
ISetupIntentCache setupIntentCache,
|
||||
IStripeAdapter stripeAdapter,
|
||||
ISubscriberService subscriberService,
|
||||
IUserRepository userRepository) : IPremiumUserBillingService
|
||||
IUserRepository userRepository,
|
||||
ITaxService taxService) : IPremiumUserBillingService
|
||||
{
|
||||
public async Task Finalize(PremiumUserSale sale)
|
||||
{
|
||||
@ -82,13 +83,19 @@ public class PremiumUserBillingService(
|
||||
throw new BillingException();
|
||||
}
|
||||
|
||||
var (address, taxIdData) = customerSetup.TaxInformation.GetStripeOptions();
|
||||
|
||||
var subscriberName = user.SubscriberName();
|
||||
|
||||
var customerCreateOptions = new CustomerCreateOptions
|
||||
{
|
||||
Address = address,
|
||||
Address = new AddressOptions
|
||||
{
|
||||
Line1 = customerSetup.TaxInformation.Line1,
|
||||
Line2 = customerSetup.TaxInformation.Line2,
|
||||
City = customerSetup.TaxInformation.City,
|
||||
PostalCode = customerSetup.TaxInformation.PostalCode,
|
||||
State = customerSetup.TaxInformation.State,
|
||||
Country = customerSetup.TaxInformation.Country,
|
||||
},
|
||||
Description = user.Name,
|
||||
Email = user.Email,
|
||||
Expand = ["tax"],
|
||||
@ -113,10 +120,28 @@ public class PremiumUserBillingService(
|
||||
Tax = new CustomerTaxOptions
|
||||
{
|
||||
ValidateLocation = StripeConstants.ValidateTaxLocationTiming.Immediately
|
||||
},
|
||||
TaxIdData = taxIdData
|
||||
}
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(customerSetup.TaxInformation.TaxId))
|
||||
{
|
||||
var taxIdType = taxService.GetStripeTaxCode(customerSetup.TaxInformation.Country,
|
||||
customerSetup.TaxInformation.TaxId);
|
||||
|
||||
if (taxIdType == null)
|
||||
{
|
||||
logger.LogWarning("Could not infer tax ID type in country '{Country}' with tax ID '{TaxID}'.",
|
||||
customerSetup.TaxInformation.Country,
|
||||
customerSetup.TaxInformation.TaxId);
|
||||
throw new Exceptions.BadRequestException("billingTaxIdTypeInferenceError");
|
||||
}
|
||||
|
||||
customerCreateOptions.TaxIdData =
|
||||
[
|
||||
new() { Type = taxIdType, Value = customerSetup.TaxInformation.TaxId }
|
||||
];
|
||||
}
|
||||
|
||||
var (paymentMethodType, paymentMethodToken) = customerSetup.TokenizedPaymentSource;
|
||||
|
||||
var braintreeCustomerId = "";
|
||||
|
@ -23,7 +23,8 @@ public class SubscriberService(
|
||||
IGlobalSettings globalSettings,
|
||||
ILogger<SubscriberService> logger,
|
||||
ISetupIntentCache setupIntentCache,
|
||||
IStripeAdapter stripeAdapter) : ISubscriberService
|
||||
IStripeAdapter stripeAdapter,
|
||||
ITaxService taxService) : ISubscriberService
|
||||
{
|
||||
public async Task CancelSubscription(
|
||||
ISubscriber subscriber,
|
||||
@ -618,16 +619,47 @@ public class SubscriberService(
|
||||
await stripeAdapter.TaxIdDeleteAsync(customer.Id, taxId.Id);
|
||||
}
|
||||
|
||||
var taxIdType = taxInformation.GetTaxIdType();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(taxInformation.TaxId) &&
|
||||
!string.IsNullOrWhiteSpace(taxIdType))
|
||||
if (string.IsNullOrWhiteSpace(taxInformation.TaxId))
|
||||
{
|
||||
await stripeAdapter.TaxIdCreateAsync(customer.Id, new TaxIdCreateOptions
|
||||
return;
|
||||
}
|
||||
|
||||
var taxIdType = taxInformation.TaxIdType;
|
||||
if (string.IsNullOrWhiteSpace(taxIdType))
|
||||
{
|
||||
taxIdType = taxService.GetStripeTaxCode(taxInformation.Country,
|
||||
taxInformation.TaxId);
|
||||
|
||||
if (taxIdType == null)
|
||||
{
|
||||
Type = taxIdType,
|
||||
Value = taxInformation.TaxId,
|
||||
});
|
||||
logger.LogWarning("Could not infer tax ID type in country '{Country}' with tax ID '{TaxID}'.",
|
||||
taxInformation.Country,
|
||||
taxInformation.TaxId);
|
||||
throw new Exceptions.BadRequestException("billingTaxIdTypeInferenceError");
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await stripeAdapter.TaxIdCreateAsync(customer.Id,
|
||||
new TaxIdCreateOptions { Type = taxIdType, Value = taxInformation.TaxId });
|
||||
}
|
||||
catch (StripeException e)
|
||||
{
|
||||
switch (e.StripeError.Code)
|
||||
{
|
||||
case StripeConstants.ErrorCodes.TaxIdInvalid:
|
||||
logger.LogWarning("Invalid tax ID '{TaxID}' for country '{Country}'.",
|
||||
taxInformation.TaxId,
|
||||
taxInformation.Country);
|
||||
throw new Exceptions.BadRequestException("billingInvalidTaxIdError");
|
||||
default:
|
||||
logger.LogError(e, "Error creating tax ID '{TaxId}' in country '{Country}' for customer '{CustomerID}'.",
|
||||
taxInformation.TaxId,
|
||||
taxInformation.Country,
|
||||
customer.Id);
|
||||
throw new Exceptions.BadRequestException("billingTaxIdCreationError");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -770,6 +802,7 @@ public class SubscriberService(
|
||||
customer.Address.Country,
|
||||
customer.Address.PostalCode,
|
||||
customer.TaxIds?.FirstOrDefault()?.Value,
|
||||
customer.TaxIds?.FirstOrDefault()?.Type,
|
||||
customer.Address.Line1,
|
||||
customer.Address.Line2,
|
||||
customer.Address.City,
|
||||
|
Reference in New Issue
Block a user