mirror of
https://github.com/bitwarden/server.git
synced 2025-07-01 08:02:49 -05:00
[AC-2567] Billing Performance Improvements (#4143)
* Moved AccountsBilling controller to be owned by Billing * Added org billing history endpoint * Updated GetBillingInvoicesAsync to only retrieve paid, open, and uncollectible invoices, and added option to limit results * Removed invoices and transactions from GetBillingAsync * Limiting the number of invoices and transactions returned * Moved Billing models to Billing namespace * Split billing info and billing history objects * Removed billing method GetBillingBalanceAndSourceAsync * Removed unused using * Cleaned up BillingInfo a bit * Update migration scripts to use `CREATE OR ALTER` instead of checking for the `OBJECT_ID` * Applying limit to aggregated invoices after they return from Stripe
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Entities.Provider;
|
||||
using Bit.Core.Billing.Models;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Business;
|
||||
@ -43,8 +44,7 @@ public interface IPaymentService
|
||||
string paymentToken, TaxInfo taxInfo = null);
|
||||
Task<bool> CreditAccountAsync(ISubscriber subscriber, decimal creditAmount);
|
||||
Task<BillingInfo> GetBillingAsync(ISubscriber subscriber);
|
||||
Task<BillingInfo> GetBillingHistoryAsync(ISubscriber subscriber);
|
||||
Task<BillingInfo> GetBillingBalanceAndSourceAsync(ISubscriber subscriber);
|
||||
Task<BillingHistoryInfo> GetBillingHistoryAsync(ISubscriber subscriber);
|
||||
Task<SubscriptionInfo> GetSubscriptionAsync(ISubscriber subscriber);
|
||||
Task<TaxInfo> GetTaxInfoAsync(ISubscriber subscriber);
|
||||
Task SaveTaxInfoAsync(ISubscriber subscriber, TaxInfo taxInfo);
|
||||
|
@ -1,6 +1,7 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Entities.Provider;
|
||||
using Bit.Core.Billing.Constants;
|
||||
using Bit.Core.Billing.Models;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
@ -1555,20 +1556,6 @@ public class StripePaymentService : IPaymentService
|
||||
}
|
||||
|
||||
public async Task<BillingInfo> GetBillingAsync(ISubscriber subscriber)
|
||||
{
|
||||
var customer = await GetCustomerAsync(subscriber.GatewayCustomerId, GetCustomerPaymentOptions());
|
||||
var billingInfo = new BillingInfo
|
||||
{
|
||||
Balance = GetBillingBalance(customer),
|
||||
PaymentSource = await GetBillingPaymentSourceAsync(customer),
|
||||
Invoices = await GetBillingInvoicesAsync(customer),
|
||||
Transactions = await GetBillingTransactionsAsync(subscriber)
|
||||
};
|
||||
|
||||
return billingInfo;
|
||||
}
|
||||
|
||||
public async Task<BillingInfo> GetBillingBalanceAndSourceAsync(ISubscriber subscriber)
|
||||
{
|
||||
var customer = await GetCustomerAsync(subscriber.GatewayCustomerId, GetCustomerPaymentOptions());
|
||||
var billingInfo = new BillingInfo
|
||||
@ -1580,13 +1567,13 @@ public class StripePaymentService : IPaymentService
|
||||
return billingInfo;
|
||||
}
|
||||
|
||||
public async Task<BillingInfo> GetBillingHistoryAsync(ISubscriber subscriber)
|
||||
public async Task<BillingHistoryInfo> GetBillingHistoryAsync(ISubscriber subscriber)
|
||||
{
|
||||
var customer = await GetCustomerAsync(subscriber.GatewayCustomerId);
|
||||
var billingInfo = new BillingInfo
|
||||
var billingInfo = new BillingHistoryInfo
|
||||
{
|
||||
Transactions = await GetBillingTransactionsAsync(subscriber),
|
||||
Invoices = await GetBillingInvoicesAsync(customer)
|
||||
Transactions = await GetBillingTransactionsAsync(subscriber, 20),
|
||||
Invoices = await GetBillingInvoicesAsync(customer, 20)
|
||||
};
|
||||
|
||||
return billingInfo;
|
||||
@ -1936,44 +1923,66 @@ public class StripePaymentService : IPaymentService
|
||||
return customer;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<BillingInfo.BillingTransaction>> GetBillingTransactionsAsync(ISubscriber subscriber)
|
||||
private async Task<IEnumerable<BillingHistoryInfo.BillingTransaction>> GetBillingTransactionsAsync(ISubscriber subscriber, int? limit = null)
|
||||
{
|
||||
ICollection<Transaction> transactions = null;
|
||||
if (subscriber is User)
|
||||
var transactions = subscriber switch
|
||||
{
|
||||
transactions = await _transactionRepository.GetManyByUserIdAsync(subscriber.Id);
|
||||
}
|
||||
else if (subscriber is Organization)
|
||||
{
|
||||
transactions = await _transactionRepository.GetManyByOrganizationIdAsync(subscriber.Id);
|
||||
}
|
||||
User => await _transactionRepository.GetManyByUserIdAsync(subscriber.Id, limit),
|
||||
Organization => await _transactionRepository.GetManyByOrganizationIdAsync(subscriber.Id, limit),
|
||||
_ => null
|
||||
};
|
||||
|
||||
return transactions?.OrderByDescending(i => i.CreationDate)
|
||||
.Select(t => new BillingInfo.BillingTransaction(t));
|
||||
|
||||
.Select(t => new BillingHistoryInfo.BillingTransaction(t));
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<BillingInfo.BillingInvoice>> GetBillingInvoicesAsync(Customer customer)
|
||||
private async Task<IEnumerable<BillingHistoryInfo.BillingInvoice>> GetBillingInvoicesAsync(Customer customer,
|
||||
int? limit = null)
|
||||
{
|
||||
if (customer == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var options = new StripeInvoiceListOptions
|
||||
{
|
||||
Customer = customer.Id,
|
||||
SelectAll = true
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var invoices = await _stripeAdapter.InvoiceListAsync(options);
|
||||
var paidInvoicesTask = _stripeAdapter.InvoiceListAsync(new StripeInvoiceListOptions
|
||||
{
|
||||
Customer = customer.Id,
|
||||
SelectAll = !limit.HasValue,
|
||||
Limit = limit,
|
||||
Status = "paid"
|
||||
});
|
||||
var openInvoicesTask = _stripeAdapter.InvoiceListAsync(new StripeInvoiceListOptions
|
||||
{
|
||||
Customer = customer.Id,
|
||||
SelectAll = !limit.HasValue,
|
||||
Limit = limit,
|
||||
Status = "open"
|
||||
});
|
||||
var uncollectibleInvoicesTask = _stripeAdapter.InvoiceListAsync(new StripeInvoiceListOptions
|
||||
{
|
||||
Customer = customer.Id,
|
||||
SelectAll = !limit.HasValue,
|
||||
Limit = limit,
|
||||
Status = "uncollectible"
|
||||
});
|
||||
|
||||
return invoices
|
||||
.Where(invoice => invoice.Status != "void" && invoice.Status != "draft")
|
||||
var paidInvoices = await paidInvoicesTask;
|
||||
var openInvoices = await openInvoicesTask;
|
||||
var uncollectibleInvoices = await uncollectibleInvoicesTask;
|
||||
|
||||
var invoices = paidInvoices
|
||||
.Concat(openInvoices)
|
||||
.Concat(uncollectibleInvoices);
|
||||
|
||||
var result = invoices
|
||||
.OrderByDescending(invoice => invoice.Created)
|
||||
.Select(invoice => new BillingInfo.BillingInvoice(invoice));
|
||||
.Select(invoice => new BillingHistoryInfo.BillingInvoice(invoice));
|
||||
|
||||
return limit.HasValue
|
||||
? result.Take(limit.Value)
|
||||
: result;
|
||||
}
|
||||
catch (StripeException exception)
|
||||
{
|
||||
|
Reference in New Issue
Block a user