1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-28 13:15:12 +01:00

[PM-11525] Tax calculation shown to customers potentially incorrect

This commit is contained in:
Jonas Hendrickx 2024-10-10 09:53:50 +02:00
parent d4c486e189
commit 5ce7aaecc2
5 changed files with 82 additions and 0 deletions

View File

@ -0,0 +1,52 @@
using Bit.Api.Billing.Models.Requests;
using Bit.Api.Billing.Models.Responses;
using Bit.Core.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Billing.Controllers;
[Route("billing")]
[Authorize("Application")]
public class BillingController(
IStripeAdapter stripeAdapter) : Controller
{
[HttpPost]
[Route("calculate-tax")]
[AllowAnonymous]
public async Task<IResult> CalculateTaxAsync([FromBody] CalculateTaxRequestModel requestBody)
{
var options = new Stripe.Tax.CalculationCreateOptions
{
Currency = "usd",
CustomerDetails = new()
{
Address = new()
{
PostalCode = requestBody.BillingAddressPostalCode,
Country = requestBody.BillingAddressCountry
},
AddressSource = "billing"
},
LineItems = new()
{
new()
{
Amount = Convert.ToInt64(requestBody.Amount * 100),
Reference = "Subscription",
},
}
};
var taxCalculation = await stripeAdapter.CalculateTaxAsync(options);
var response = new CalculateTaxResponseModel
{
SalesTaxRate = taxCalculation.TaxBreakdown.Any()
? decimal.Parse(taxCalculation.TaxBreakdown.Single().TaxRateDetails.PercentageDecimal) / 100
: 0,
SalesTaxAmount = Convert.ToDecimal(taxCalculation.TaxAmountExclusive) / 100,
TaxableAmount = Convert.ToDecimal(requestBody.Amount),
TotalAmount = Convert.ToDecimal(taxCalculation.AmountTotal) / 100,
};
return TypedResults.Ok(response);
}
}

View File

@ -0,0 +1,10 @@
namespace Bit.Api.Billing.Models.Requests;
public class CalculateTaxRequestModel
{
public decimal Amount { get; set; }
public string BillingAddressPostalCode { get; set; }
public string BillingAddressCountry { get; set; }
}

View File

@ -0,0 +1,12 @@
namespace Bit.Api.Billing.Models.Responses;
public class CalculateTaxResponseModel
{
public decimal SalesTaxAmount { get; set; }
public decimal SalesTaxRate { get; set; }
public decimal TaxableAmount { get; set; }
public decimal TotalAmount { get; set; }
}

View File

@ -5,6 +5,7 @@ namespace Bit.Core.Services;
public interface IStripeAdapter
{
Task<Stripe.Tax.Calculation> CalculateTaxAsync(Stripe.Tax.CalculationCreateOptions options);
Task<Stripe.Customer> CustomerCreateAsync(Stripe.CustomerCreateOptions customerCreateOptions);
Task<Stripe.Customer> CustomerGetAsync(string id, Stripe.CustomerGetOptions options = null);
Task<Stripe.Customer> CustomerUpdateAsync(string id, Stripe.CustomerUpdateOptions options = null);

View File

@ -5,6 +5,7 @@ namespace Bit.Core.Services;
public class StripeAdapter : IStripeAdapter
{
private readonly Stripe.Tax.CalculationService _taxCalculationService;
private readonly Stripe.CustomerService _customerService;
private readonly Stripe.SubscriptionService _subscriptionService;
private readonly Stripe.InvoiceService _invoiceService;
@ -21,6 +22,7 @@ public class StripeAdapter : IStripeAdapter
public StripeAdapter()
{
_taxCalculationService = new Stripe.Tax.CalculationService();
_customerService = new Stripe.CustomerService();
_subscriptionService = new Stripe.SubscriptionService();
_invoiceService = new Stripe.InvoiceService();
@ -36,6 +38,11 @@ public class StripeAdapter : IStripeAdapter
_testClockService = new Stripe.TestHelpers.TestClockService();
}
public Task<Stripe.Tax.Calculation> CalculateTaxAsync(Stripe.Tax.CalculationCreateOptions options)
{
return _taxCalculationService.CreateAsync(options);
}
public Task<Stripe.Customer> CustomerCreateAsync(Stripe.CustomerCreateOptions options)
{
return _customerService.CreateAsync(options);