1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-01 16:12:49 -05:00

[AC-2568] Added invoices and transaction history endpoints. Added cursor paging for each (#4692)

* Added invoices and transaction history endpoints. Added cursor paging for each

* Removed try/catch since it's handled by middleware. Updated condition to use pattern matching

* Added unit tests for PaymentHistoryService

* Removed organizationId from account billing controller endpoints
This commit is contained in:
Conner Turnbull
2024-09-09 09:38:58 -04:00
committed by GitHub
parent ebf8bc0b85
commit 46ac2a9b3b
16 changed files with 385 additions and 34 deletions

View File

@ -38,6 +38,7 @@ public class BillingHistoryInfo
{
public BillingInvoice(Invoice inv)
{
Id = inv.Id;
Date = inv.Created;
Url = inv.HostedInvoiceUrl;
PdfUrl = inv.InvoicePdf;
@ -46,6 +47,7 @@ public class BillingHistoryInfo
Amount = inv.Total / 100M;
}
public string Id { get; set; }
public decimal Amount { get; set; }
public DateTime? Date { get; set; }
public string Url { get; set; }

View File

@ -0,0 +1,17 @@
using Bit.Core.Billing.Models;
using Bit.Core.Entities;
namespace Bit.Core.Billing.Services;
public interface IPaymentHistoryService
{
Task<IEnumerable<BillingHistoryInfo.BillingInvoice>> GetInvoiceHistoryAsync(
ISubscriber subscriber,
int pageSize = 5,
string startAfter = null);
Task<IEnumerable<BillingHistoryInfo.BillingTransaction>> GetTransactionHistoryAsync(
ISubscriber subscriber,
int pageSize = 5,
DateTime? startAfter = null);
}

View File

@ -0,0 +1,53 @@
using Bit.Core.AdminConsole.Entities;
using Bit.Core.Billing.Models;
using Bit.Core.Entities;
using Bit.Core.Models.BitStripe;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Microsoft.Extensions.Logging;
namespace Bit.Core.Billing.Services.Implementations;
public class PaymentHistoryService(
IStripeAdapter stripeAdapter,
ITransactionRepository transactionRepository,
ILogger<PaymentHistoryService> logger) : IPaymentHistoryService
{
public async Task<IEnumerable<BillingHistoryInfo.BillingInvoice>> GetInvoiceHistoryAsync(
ISubscriber subscriber,
int pageSize = 5,
string startAfter = null)
{
if (subscriber is not { GatewayCustomerId: not null, GatewaySubscriptionId: not null })
{
return null;
}
var invoices = await stripeAdapter.InvoiceListAsync(new StripeInvoiceListOptions
{
Customer = subscriber.GatewayCustomerId,
Subscription = subscriber.GatewaySubscriptionId,
Limit = pageSize,
StartingAfter = startAfter
});
return invoices.Select(invoice => new BillingHistoryInfo.BillingInvoice(invoice));
}
public async Task<IEnumerable<BillingHistoryInfo.BillingTransaction>> GetTransactionHistoryAsync(
ISubscriber subscriber,
int pageSize = 5,
DateTime? startAfter = null)
{
var transactions = subscriber switch
{
User => await transactionRepository.GetManyByUserIdAsync(subscriber.Id, pageSize, startAfter),
Organization => await transactionRepository.GetManyByOrganizationIdAsync(subscriber.Id, pageSize, startAfter),
_ => null
};
return transactions?.OrderByDescending(i => i.CreationDate)
.Select(t => new BillingHistoryInfo.BillingTransaction(t));
}
}

View File

@ -7,8 +7,8 @@ namespace Bit.Core.Repositories;
public interface ITransactionRepository : IRepository<Transaction, Guid>
{
Task<ICollection<Transaction>> GetManyByUserIdAsync(Guid userId, int? limit = null);
Task<ICollection<Transaction>> GetManyByOrganizationIdAsync(Guid organizationId, int? limit = null);
Task<ICollection<Transaction>> GetManyByProviderIdAsync(Guid providerId, int? limit = null);
Task<ICollection<Transaction>> GetManyByUserIdAsync(Guid userId, int? limit = null, DateTime? startAfter = null);
Task<ICollection<Transaction>> GetManyByOrganizationIdAsync(Guid organizationId, int? limit = null, DateTime? startAfter = null);
Task<ICollection<Transaction>> GetManyByProviderIdAsync(Guid providerId, int? limit = null, DateTime? startAfter = null);
Task<Transaction?> GetByGatewayIdAsync(GatewayType gatewayType, string gatewayId);
}