mirror of
https://github.com/bitwarden/server.git
synced 2025-04-05 05:00:19 -05:00
started charging sales tax on seat/storage upgrades and auto renewals (#1034)
* started charging sales tax on seat/storage upgrades and auto renewals * Code review fixes for auto-renewing subscriptions charging sales tax
This commit is contained in:
parent
7d3fb55b2d
commit
fee5c932db
@ -36,6 +36,7 @@ namespace Bit.Billing.Controllers
|
||||
private readonly ILogger<StripeController> _logger;
|
||||
private readonly Braintree.BraintreeGateway _btGateway;
|
||||
private readonly IReferenceEventService _referenceEventService;
|
||||
private readonly ITaxRateRepository _taxRateRepository;
|
||||
|
||||
public StripeController(
|
||||
GlobalSettings globalSettings,
|
||||
@ -48,7 +49,8 @@ namespace Bit.Billing.Controllers
|
||||
IAppleIapService appleIapService,
|
||||
IMailService mailService,
|
||||
IReferenceEventService referenceEventService,
|
||||
ILogger<StripeController> logger)
|
||||
ILogger<StripeController> logger,
|
||||
ITaxRateRepository taxRateRepository)
|
||||
{
|
||||
_billingSettings = billingSettings?.Value;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
@ -59,6 +61,7 @@ namespace Bit.Billing.Controllers
|
||||
_appleIapService = appleIapService;
|
||||
_mailService = mailService;
|
||||
_referenceEventService = referenceEventService;
|
||||
_taxRateRepository = taxRateRepository;
|
||||
_logger = logger;
|
||||
_btGateway = new Braintree.BraintreeGateway
|
||||
{
|
||||
@ -150,6 +153,8 @@ namespace Bit.Billing.Controllers
|
||||
throw new Exception("Invoice subscription is null. " + invoice.Id);
|
||||
}
|
||||
|
||||
subscription = await VerifyCorrectTaxRateForCharge(invoice, subscription);
|
||||
|
||||
string email = null;
|
||||
var ids = GetIdsFromMetaData(subscription.Metadata);
|
||||
// org
|
||||
@ -741,5 +746,31 @@ namespace Bit.Billing.Controllers
|
||||
}
|
||||
return subscription;
|
||||
}
|
||||
|
||||
private async Task<Subscription> VerifyCorrectTaxRateForCharge(Invoice invoice, Subscription subscription)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(invoice?.CustomerAddress?.Country) && !string.IsNullOrWhiteSpace(invoice?.CustomerAddress?.PostalCode))
|
||||
{
|
||||
var localBitwardenTaxRates = await _taxRateRepository.GetByLocationAsync(
|
||||
new Bit.Core.Models.Table.TaxRate()
|
||||
{
|
||||
Country = invoice.CustomerAddress.Country,
|
||||
PostalCode = invoice.CustomerAddress.PostalCode
|
||||
}
|
||||
);
|
||||
|
||||
if (localBitwardenTaxRates.Any())
|
||||
{
|
||||
var stripeTaxRate = await new TaxRateService().GetAsync(localBitwardenTaxRates.First().Id);
|
||||
if (stripeTaxRate != null && !subscription.DefaultTaxRates.Any(x => x == stripeTaxRate))
|
||||
{
|
||||
subscription.DefaultTaxRates = new List<Stripe.TaxRate> { stripeTaxRate };
|
||||
var subscriptionOptions = new SubscriptionUpdateOptions() { DefaultTaxRates = new List<string>() { stripeTaxRate.Id } };
|
||||
subscription = await new SubscriptionService().UpdateAsync(subscription.Id, subscriptionOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
return subscription;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ namespace Bit.Core.Services
|
||||
private readonly ISsoUserRepository _ssoUserRepository;
|
||||
private readonly IReferenceEventService _referenceEventService;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ITaxRateRepository _taxRateRepository;
|
||||
|
||||
public OrganizationService(
|
||||
IOrganizationRepository organizationRepository,
|
||||
@ -59,7 +60,8 @@ namespace Bit.Core.Services
|
||||
ISsoConfigRepository ssoConfigRepository,
|
||||
ISsoUserRepository ssoUserRepository,
|
||||
IReferenceEventService referenceEventService,
|
||||
GlobalSettings globalSettings)
|
||||
GlobalSettings globalSettings,
|
||||
ITaxRateRepository taxRateRepository)
|
||||
{
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
@ -81,6 +83,7 @@ namespace Bit.Core.Services
|
||||
_ssoUserRepository = ssoUserRepository;
|
||||
_referenceEventService = referenceEventService;
|
||||
_globalSettings = globalSettings;
|
||||
_taxRateRepository = taxRateRepository;
|
||||
}
|
||||
|
||||
public async Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken,
|
||||
@ -392,7 +395,7 @@ namespace Bit.Core.Services
|
||||
// Retain original collection method
|
||||
var collectionMethod = sub.CollectionMethod;
|
||||
|
||||
var subResponse = await subscriptionService.UpdateAsync(sub.Id, new SubscriptionUpdateOptions
|
||||
var subUpdateOptions = new SubscriptionUpdateOptions
|
||||
{
|
||||
Items = new List<SubscriptionItemOptions>
|
||||
{
|
||||
@ -408,7 +411,26 @@ namespace Bit.Core.Services
|
||||
DaysUntilDue = 1,
|
||||
CollectionMethod = "send_invoice",
|
||||
ProrationDate = prorationDate,
|
||||
});
|
||||
};
|
||||
|
||||
var customer = await new CustomerService().GetAsync(sub.CustomerId);
|
||||
var taxRates = await _taxRateRepository.GetByLocationAsync(
|
||||
new Bit.Core.Models.Table.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 = new List<string>(1)
|
||||
{
|
||||
taxRate.Id
|
||||
};
|
||||
}
|
||||
|
||||
var subResponse = await subscriptionService.UpdateAsync(sub.Id, subUpdateOptions);
|
||||
|
||||
string paymentIntentClientSecret = null;
|
||||
if (additionalSeats > 0)
|
||||
|
@ -684,7 +684,7 @@ namespace Bit.Core.Services
|
||||
// Retain original collection method
|
||||
var collectionMethod = sub.CollectionMethod;
|
||||
|
||||
var subResponse = await subscriptionService.UpdateAsync(sub.Id, new SubscriptionUpdateOptions
|
||||
var subUpdateOptions = new SubscriptionUpdateOptions
|
||||
{
|
||||
Items = new List<SubscriptionItemOptions>
|
||||
{
|
||||
@ -700,7 +700,26 @@ namespace Bit.Core.Services
|
||||
DaysUntilDue = 1,
|
||||
CollectionMethod = "send_invoice",
|
||||
ProrationDate = prorationDate,
|
||||
});
|
||||
};
|
||||
|
||||
var customer = await new CustomerService().GetAsync(sub.CustomerId);
|
||||
var taxRates = await _taxRateRepository.GetByLocationAsync(
|
||||
new Bit.Core.Models.Table.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 = new List<string>(1)
|
||||
{
|
||||
taxRate.Id
|
||||
};
|
||||
}
|
||||
|
||||
var subResponse = await subscriptionService.UpdateAsync(sub.Id, subUpdateOptions);
|
||||
|
||||
string paymentIntentClientSecret = null;
|
||||
if (additionalStorage > 0)
|
||||
|
@ -36,11 +36,12 @@ namespace Bit.Core.Test.Services
|
||||
var ssoUserRepo = Substitute.For<ISsoUserRepository>();
|
||||
var referenceEventService = Substitute.For<IReferenceEventService>();
|
||||
var globalSettings = Substitute.For<GlobalSettings>();
|
||||
var taxRateRepository = Substitute.For<ITaxRateRepository>();
|
||||
|
||||
var orgService = new OrganizationService(orgRepo, orgUserRepo, collectionRepo, userRepo,
|
||||
groupRepo, dataProtector, mailService, pushNotService, pushRegService, deviceRepo,
|
||||
licenseService, eventService, installationRepo, appCacheService, paymentService, policyRepo,
|
||||
ssoConfigRepo, ssoUserRepo, referenceEventService, globalSettings);
|
||||
ssoConfigRepo, ssoUserRepo, referenceEventService, globalSettings, taxRateRepository);
|
||||
|
||||
var id = Guid.NewGuid();
|
||||
var userId = Guid.NewGuid();
|
||||
@ -97,11 +98,12 @@ namespace Bit.Core.Test.Services
|
||||
var ssoUserRepo = Substitute.For<ISsoUserRepository>();
|
||||
var referenceEventService = Substitute.For<IReferenceEventService>();
|
||||
var globalSettings = Substitute.For<GlobalSettings>();
|
||||
var taxRateRepo = Substitute.For<ITaxRateRepository>();
|
||||
|
||||
var orgService = new OrganizationService(orgRepo, orgUserRepo, collectionRepo, userRepo,
|
||||
groupRepo, dataProtector, mailService, pushNotService, pushRegService, deviceRepo,
|
||||
licenseService, eventService, installationRepo, appCacheService, paymentService, policyRepo,
|
||||
ssoConfigRepo, ssoUserRepo, referenceEventService, globalSettings);
|
||||
ssoConfigRepo, ssoUserRepo, referenceEventService, globalSettings, taxRateRepo);
|
||||
|
||||
var id = Guid.NewGuid();
|
||||
var userId = Guid.NewGuid();
|
||||
|
@ -25,6 +25,7 @@ namespace Bit.Core.Test.Services
|
||||
_appleIapService = Substitute.For<IAppleIapService>();
|
||||
_globalSettings = new GlobalSettings();
|
||||
_logger = Substitute.For<ILogger<StripePaymentService>>();
|
||||
_taxRateRepository = Substitute.For<ITaxRateRepository>();
|
||||
|
||||
_sut = new StripePaymentService(
|
||||
_transactionRepository,
|
||||
|
Loading…
x
Reference in New Issue
Block a user