mirror of
https://github.com/bitwarden/server
synced 2026-01-28 15:23:38 +00:00
* feat(get-subscription): Add EnumMemberJsonConverter * feat(get-subscription): Add BitwardenDiscount model * feat(get-subscription): Add Cart model * feat(get-subscription): Add Storage model * feat(get-subscription): Add BitwardenSubscription model * feat(get-subscription): Add DiscountExtensions * feat(get-subscription): Add error code to StripeConstants * feat(get-subscription): Add GetBitwardenSubscriptionQuery * feat(get-subscription): Expose GET /account/billing/vnext/subscription * feat(reinstate-subscription): Add ReinstateSubscriptionCommand * feat(reinstate-subscription): Expose POST /account/billing/vnext/subscription/reinstate * feat(pay-with-paypal-immediately): Add SubscriberId union * feat(pay-with-paypal-immediately): Add BraintreeService with PayInvoice method * feat(pay-with-paypal-immediately): Pay PayPal invoice immediately when starting premium subscription * feat(pay-with-paypal-immediately): Pay invoice with Braintree on invoice.created for subscription cycles only * fix(update-storage): Always invoice for premium storage update * fix(update-storage): Move endpoint to subscription path * docs: Note FF removal POIs * (format): Run dotnet format
66 lines
2.6 KiB
C#
66 lines
2.6 KiB
C#
using Bit.Core.Billing.Constants;
|
|
using Bit.Core.Services;
|
|
using Event = Stripe.Event;
|
|
|
|
namespace Bit.Billing.Services.Implementations;
|
|
|
|
public class InvoiceCreatedHandler(
|
|
IBraintreeService braintreeService,
|
|
ILogger<InvoiceCreatedHandler> logger,
|
|
IStripeEventService stripeEventService,
|
|
IProviderEventService providerEventService)
|
|
: IInvoiceCreatedHandler
|
|
{
|
|
|
|
/// <summary>
|
|
/// <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>
|
|
public async Task HandleAsync(Event parsedEvent)
|
|
{
|
|
try
|
|
{
|
|
var invoice = await stripeEventService.GetInvoice(parsedEvent, true, ["customer", "parent.subscription_details.subscription"]);
|
|
|
|
var usingPayPal = invoice.Customer.Metadata.ContainsKey("btCustomerId");
|
|
|
|
if (usingPayPal && invoice is
|
|
{
|
|
AmountDue: > 0,
|
|
Status: not StripeConstants.InvoiceStatus.Paid,
|
|
CollectionMethod: "charge_automatically",
|
|
BillingReason:
|
|
"subscription_cycle" or
|
|
"automatic_pending_invoice_item_invoice",
|
|
Parent.SubscriptionDetails.Subscription: not null
|
|
})
|
|
{
|
|
await braintreeService.PayInvoice(invoice.Parent.SubscriptionDetails.Subscription, invoice);
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
logger.LogError(exception, "Failed to attempt paying for invoice while handling 'invoice.created' event ({EventID})", parsedEvent.Id);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|