diff --git a/src/Core/Models/Stripe/StripeInvoiceListOptions.cs b/src/Core/Models/Stripe/StripeInvoiceListOptions.cs new file mode 100644 index 0000000000..bcffc738da --- /dev/null +++ b/src/Core/Models/Stripe/StripeInvoiceListOptions.cs @@ -0,0 +1,27 @@ +using Stripe; + +namespace Bit.Core.Models.BitStripe; + +/// +/// A model derived from the Stripe class that includes a flag used to +/// retrieve all invoices from the Stripe API rather than a limited set. +/// +public class StripeInvoiceListOptions : InvoiceListOptions +{ + public bool SelectAll { get; set; } + + public InvoiceListOptions ToInvoiceListOptions() + { + var options = (InvoiceListOptions)this; + + if (!SelectAll) + { + return options; + } + + options.EndingBefore = null; + options.StartingAfter = null; + + return options; + } +} diff --git a/src/Core/Services/IStripeAdapter.cs b/src/Core/Services/IStripeAdapter.cs index 4f1cdd37eb..f79cc1200e 100644 --- a/src/Core/Services/IStripeAdapter.cs +++ b/src/Core/Services/IStripeAdapter.cs @@ -18,7 +18,7 @@ public interface IStripeAdapter Task InvoiceCreateAsync(Stripe.InvoiceCreateOptions options); Task InvoiceItemCreateAsync(Stripe.InvoiceItemCreateOptions options); Task InvoiceGetAsync(string id, Stripe.InvoiceGetOptions options); - Task> InvoiceListAsync(Stripe.InvoiceListOptions options); + Task> InvoiceListAsync(StripeInvoiceListOptions options); IEnumerable InvoiceItemListAsync(InvoiceItemListOptions options); Task InvoiceUpdateAsync(string id, Stripe.InvoiceUpdateOptions options); Task InvoiceFinalizeInvoiceAsync(string id, Stripe.InvoiceFinalizeOptions options); diff --git a/src/Core/Services/Implementations/StripeAdapter.cs b/src/Core/Services/Implementations/StripeAdapter.cs index 478d092fdc..28dd35034c 100644 --- a/src/Core/Services/Implementations/StripeAdapter.cs +++ b/src/Core/Services/Implementations/StripeAdapter.cs @@ -97,9 +97,23 @@ public class StripeAdapter : IStripeAdapter return _invoiceService.GetAsync(id, options); } - public Task> InvoiceListAsync(Stripe.InvoiceListOptions options) + public async Task> InvoiceListAsync(StripeInvoiceListOptions options) { - return _invoiceService.ListAsync(options); + if (!options.SelectAll) + { + return (await _invoiceService.ListAsync(options.ToInvoiceListOptions())).Data; + } + + options.Limit = 100; + + var invoices = new List(); + + await foreach (var invoice in _invoiceService.ListAutoPagingAsync(options.ToInvoiceListOptions())) + { + invoices.Add(invoice); + } + + return invoices; } public IEnumerable InvoiceItemListAsync(InvoiceItemListOptions options) diff --git a/src/Core/Services/Implementations/StripePaymentService.cs b/src/Core/Services/Implementations/StripePaymentService.cs index 214b2bff10..67de73a188 100644 --- a/src/Core/Services/Implementations/StripePaymentService.cs +++ b/src/Core/Services/Implementations/StripePaymentService.cs @@ -2,6 +2,7 @@ using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; +using Bit.Core.Models.BitStripe; using Bit.Core.Models.Business; using Bit.Core.Repositories; using Bit.Core.Settings; @@ -668,7 +669,7 @@ public class StripePaymentService : IPaymentService if (!stripePaymentMethod && subInvoiceMetadata.Any()) { - var invoices = await _stripeAdapter.InvoiceListAsync(new Stripe.InvoiceListOptions + var invoices = await _stripeAdapter.InvoiceListAsync(new StripeInvoiceListOptions { Subscription = subscription.Id }); @@ -2138,15 +2139,26 @@ public class StripePaymentService : IPaymentService return null; } - var invoices = await _stripeAdapter.InvoiceListAsync(new Stripe.InvoiceListOptions + var options = new StripeInvoiceListOptions { Customer = customer.Id, - Limit = 50 - }); + SelectAll = true + }; - return invoices.Data.Where(i => i.Status != "void" && i.Status != "draft") - .OrderByDescending(i => i.Created).Select(i => new BillingInfo.BillingInvoice(i)); + try + { + var invoices = await _stripeAdapter.InvoiceListAsync(options); + return invoices + .Where(invoice => invoice.Status != "void" && invoice.Status != "draft") + .OrderByDescending(invoice => invoice.Created) + .Select(invoice => new BillingInfo.BillingInvoice(invoice)); + } + catch (Stripe.StripeException exception) + { + _logger.LogError(exception, "An error occurred while listing Stripe invoices"); + throw new GatewayException("Failed to retrieve current invoices", exception); + } } // We are taking only first 30 characters of the SubscriberName because stripe provide