-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[PM-28423] Add latest_invoice expansion / logging to SubscriptionCancellationJob
#6603
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,16 +1,17 @@ | ||||||||||||
| ๏ปฟ// FIXME: Update this file to be null safe and then delete the line below | ||||||||||||
| #nullable disable | ||||||||||||
|
|
||||||||||||
| using Bit.Billing.Services; | ||||||||||||
| ๏ปฟusing Bit.Billing.Services; | ||||||||||||
| using Bit.Core.Billing.Constants; | ||||||||||||
| using Bit.Core.Repositories; | ||||||||||||
| using Quartz; | ||||||||||||
| using Stripe; | ||||||||||||
|
|
||||||||||||
| namespace Bit.Billing.Jobs; | ||||||||||||
|
|
||||||||||||
| using static StripeConstants; | ||||||||||||
|
|
||||||||||||
| public class SubscriptionCancellationJob( | ||||||||||||
| IStripeFacade stripeFacade, | ||||||||||||
| IOrganizationRepository organizationRepository) | ||||||||||||
| IOrganizationRepository organizationRepository, | ||||||||||||
| ILogger<SubscriptionCancellationJob> logger) | ||||||||||||
| : IJob | ||||||||||||
| { | ||||||||||||
| public async Task Execute(IJobExecutionContext context) | ||||||||||||
|
|
@@ -21,20 +22,31 @@ public async Task Execute(IJobExecutionContext context) | |||||||||||
| var organization = await organizationRepository.GetByIdAsync(organizationId); | ||||||||||||
| if (organization == null || organization.Enabled) | ||||||||||||
| { | ||||||||||||
| logger.LogWarning("{Job} skipped for subscription ({SubscriptionID}) because organization is either null or enabled", nameof(SubscriptionCancellationJob), subscriptionId); | ||||||||||||
| // Organization was deleted or re-enabled by CS, skip cancellation | ||||||||||||
| return; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| var subscription = await stripeFacade.GetSubscription(subscriptionId); | ||||||||||||
| if (subscription?.Status != "unpaid" || | ||||||||||||
| subscription.LatestInvoice?.BillingReason is not ("subscription_cycle" or "subscription_create")) | ||||||||||||
| var subscription = await stripeFacade.GetSubscription(subscriptionId, new SubscriptionGetOptions | ||||||||||||
| { | ||||||||||||
| Expand = ["latest_invoice"] | ||||||||||||
| }); | ||||||||||||
|
|
||||||||||||
| if (subscription is not | ||||||||||||
| { | ||||||||||||
| Status: SubscriptionStatus.Unpaid, | ||||||||||||
| LatestInvoice.BillingReason: "subscription_cycle" or "subscription_create" | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. โ Potential NullReferenceException: The pattern
Suggested change
This nested pattern ensures
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||
| }) | ||||||||||||
| { | ||||||||||||
| logger.LogWarning("{Job} skipped for subscription ({SubscriptionID}) because subscription is not unpaid or does not have a cancellable billing reason", nameof(SubscriptionCancellationJob), subscriptionId); | ||||||||||||
| return; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // Cancel the subscription | ||||||||||||
| await stripeFacade.CancelSubscription(subscriptionId, new SubscriptionCancelOptions()); | ||||||||||||
|
|
||||||||||||
| logger.LogInformation("{Job} cancelled subscription ({SubscriptionID})", nameof(SubscriptionCancellationJob), subscriptionId); | ||||||||||||
|
|
||||||||||||
| // Void any open invoices | ||||||||||||
| var options = new InvoiceListOptions | ||||||||||||
| { | ||||||||||||
|
|
@@ -46,6 +58,7 @@ public async Task Execute(IJobExecutionContext context) | |||||||||||
| foreach (var invoice in invoices) | ||||||||||||
| { | ||||||||||||
| await stripeFacade.VoidInvoice(invoice.Id); | ||||||||||||
| logger.LogInformation("{Job} voided invoice ({InvoiceID}) for subscription ({SubscriptionID})", nameof(SubscriptionCancellationJob), invoice.Id, subscriptionId); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| while (invoices.HasMore) | ||||||||||||
|
|
@@ -55,6 +68,7 @@ public async Task Execute(IJobExecutionContext context) | |||||||||||
| foreach (var invoice in invoices) | ||||||||||||
| { | ||||||||||||
| await stripeFacade.VoidInvoice(invoice.Id); | ||||||||||||
| logger.LogInformation("{Job} voided invoice ({InvoiceID}) for subscription ({SubscriptionID})", nameof(SubscriptionCancellationJob), invoice.Id, subscriptionId); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
๐ญ Consistency question: This uses
SubscriptionStatus.Unpaid(Stripe SDK enum), but other services in the codebase useStripeSubscriptionStatus.UnpaidorStripeConstants.SubscriptionStatus.Unpaid(string constants). Should this align with the existing pattern?For reference, see
SubscriptionUpdatedHandler.cs:91which usesStripeSubscriptionStatus.Unpaid.