1
0
mirror of https://github.com/bitwarden/server synced 2025-12-28 06:03:29 +00:00

[PM-7452] Handle PayPal for premium users (#4835)

* Add PremiumUserSale

* Add PremiumUserBillingService

* Integrate into UserService behind FF

* Update invoice.created handler to bill newly created PayPal customers

* Run dotnet format
This commit is contained in:
Alex Morask
2024-10-01 09:12:08 -04:00
committed by GitHub
parent 84f7cd262c
commit 594b2a274d
11 changed files with 451 additions and 55 deletions

View File

@@ -2,34 +2,63 @@
namespace Bit.Billing.Services.Implementations;
public class InvoiceCreatedHandler : IInvoiceCreatedHandler
public class InvoiceCreatedHandler(
ILogger<InvoiceCreatedHandler> logger,
IStripeEventService stripeEventService,
IStripeEventUtilityService stripeEventUtilityService,
IProviderEventService providerEventService)
: IInvoiceCreatedHandler
{
private readonly IStripeEventService _stripeEventService;
private readonly IStripeEventUtilityService _stripeEventUtilityService;
private readonly IProviderEventService _providerEventService;
public InvoiceCreatedHandler(
IStripeEventService stripeEventService,
IStripeEventUtilityService stripeEventUtilityService,
IProviderEventService providerEventService)
{
_stripeEventService = stripeEventService;
_stripeEventUtilityService = stripeEventUtilityService;
_providerEventService = providerEventService;
}
/// <summary>
/// Handles the <see cref="HandledStripeWebhook.InvoiceCreated"/> event type from Stripe.
/// <para>
/// This handler processes the `invoice.created` event in <see href="https://docs.stripe.com/api/events/types#event_types-invoice.created">Stripe</see>. It has
/// two primary responsibilities.
/// </para>
/// <para>
/// 1. Checks to see if the newly created invoice belongs to a PayPal customer. If it does, and the invoice is ready to be paid, it will attempt to pay the invoice
/// with Braintree and then let Stripe know the invoice can be marked as paid.
/// </para>
/// <para>
/// 2. If the invoice is for a provider, it records a point-in-time snapshot of the invoice broken down by the provider's client organizations. This is later used in
/// the provider invoice export.
/// </para>
/// </summary>
/// <param name="parsedEvent"></param>
public async Task HandleAsync(Event parsedEvent)
{
var invoice = await _stripeEventService.GetInvoice(parsedEvent, true);
if (_stripeEventUtilityService.ShouldAttemptToPayInvoice(invoice))
try
{
await _stripeEventUtilityService.AttemptToPayInvoiceAsync(invoice);
var invoice = await stripeEventService.GetInvoice(parsedEvent, true, ["customer"]);
var usingPayPal = invoice.Customer?.Metadata.ContainsKey("btCustomerId") ?? false;
if (usingPayPal && invoice is
{
AmountDue: > 0,
Paid: false,
CollectionMethod: "charge_automatically",
BillingReason:
"subscription_create" or
"subscription_cycle" or
"automatic_pending_invoice_item_invoice",
SubscriptionId: not null and not ""
})
{
await stripeEventUtilityService.AttemptToPayInvoiceAsync(invoice);
}
}
catch (Exception exception)
{
logger.LogError(exception, "Failed to attempt paying for invoice while handling 'invoice.created' event ({EventID})", parsedEvent.Id);
}
await _providerEventService.TryRecordInvoiceLineItems(parsedEvent);
try
{
await providerEventService.TryRecordInvoiceLineItems(parsedEvent);
}
catch (Exception exception)
{
logger.LogError(exception, "Failed to record provider invoice line items while handling 'invoice.created' event ({EventID})", parsedEvent.Id);
}
}
}