using Bit.Core.Billing.Constants; using Stripe; using Event = Stripe.Event; namespace Bit.Billing.Services.Implementations; public class PaymentFailedHandler : IPaymentFailedHandler { private readonly IStripeEventService _stripeEventService; private readonly IStripeFacade _stripeFacade; private readonly IStripeEventUtilityService _stripeEventUtilityService; public PaymentFailedHandler( IStripeEventService stripeEventService, IStripeFacade stripeFacade, IStripeEventUtilityService stripeEventUtilityService) { _stripeEventService = stripeEventService; _stripeFacade = stripeFacade; _stripeEventUtilityService = stripeEventUtilityService; } /// /// Handles the event type from Stripe. /// /// public async Task HandleAsync(Event parsedEvent) { var invoice = await _stripeEventService.GetInvoice(parsedEvent, true); if (invoice.Status == StripeConstants.InvoiceStatus.Paid || invoice.AttemptCount <= 1 || !ShouldAttemptToPayInvoice(invoice)) { return; } if (invoice.Parent?.SubscriptionDetails != null) { var subscription = await _stripeFacade.GetSubscription(invoice.Parent.SubscriptionDetails.SubscriptionId); // attempt count 4 = 11 days after initial failure if (invoice.AttemptCount <= 3 || !subscription.Items.Any(i => i.Price.Id is IStripeEventUtilityService.PremiumPlanId or IStripeEventUtilityService.PremiumPlanIdAppStore)) { await _stripeEventUtilityService.AttemptToPayInvoiceAsync(invoice); } } } private static bool ShouldAttemptToPayInvoice(Invoice invoice) => invoice is { AmountDue: > 0, Status: not StripeConstants.InvoiceStatus.Paid, CollectionMethod: "charge_automatically", BillingReason: "subscription_cycle" or "automatic_pending_invoice_item_invoice", Parent.SubscriptionDetails: not null }; }