[PM-37084] Business Aware Schedule Recovery and Cancellation#7686
[PM-37084] Business Aware Schedule Recovery and Cancellation#7686sbrown-livefront wants to merge 35 commits into
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #7686 +/- ##
==========================================
- Coverage 64.90% 60.48% -4.42%
==========================================
Files 2141 2142 +1
Lines 94654 94742 +88
Branches 8456 8469 +13
==========================================
- Hits 61435 57307 -4128
- Misses 31118 35424 +4306
+ Partials 2101 2011 -90 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
🤖 Bitwarden Claude Code ReviewOverall Assessment: APPROVE This PR unifies price-increase scheduling under a new Code Review DetailsNo new findings. All previously surfaced concerns (Stripe expansion gaps on the unified dispatch in |
amorask-bitwarden
left a comment
There was a problem hiding this comment.
Looks great - just left a question on a refactoring opp we have here. Let me know if you want to discuss further.
| var subscription = await stripeAdapter.GetSubscriptionAsync( | ||
| subscriber.GatewaySubscriptionId, | ||
| new SubscriptionGetOptions { Expand = ["discounts"] }); | ||
| new SubscriptionGetOptions { Expand = ["discounts", "customer", "customer.discount"] }); |
There was a problem hiding this comment.
⛏️ Expanding customer.discount necessarily expands customer.
| public async Task HandleAsync(Event parsedEvent) | ||
| { | ||
| var subscription = await _stripeEventService.GetSubscription(parsedEvent, true, ["customer", "discounts", "latest_invoice", "test_clock"]); | ||
| var subscription = await _stripeEventService.GetSubscription(parsedEvent, true, ["customer", "customer.discount", "discounts", "latest_invoice", "test_clock"]); |
There was a problem hiding this comment.
⛏ Same thing here.
| /// <param name="subscription"> The subscription to schedule a price increase for. </param> | ||
| /// <param name="organizationId"> The ID of the organization associated with the subscription. </param> | ||
| /// <returns> True if the schedule was dispatched successfully, false otherwise. </returns> | ||
| private async Task<bool> DispatchOrganizationScheduleAsync(Subscription subscription, Guid organizationId) |
There was a problem hiding this comment.
I think we have a refactoring opportunity here that I overlooked in the original implementation. This is almost the same exact logic that's in the UpcomingInvoiceHandler.ScheduleBusinessPlanPriceMigration. It seems like we're going to need the same logic in multiple areas, but I'm not positive they're supposed to behave exactly the same way between the original scheduling and a reinstatement. For example, some of the validation checks such as assignment.ScheduledAt is not null might not translate. Wanna give this a whirl or discuss further?
|



🎟️ Tracking
https://bitwarden.atlassian.net/browse/PM-37084
📔 Objective
Introduces refactorings to the billing and subscription scheduling logic, particularly around price increase scheduling and business plan migrations. The changes add a more robust and unified scheduling pathway, improve feature flag handling, and enhance testability and maintainability.
Key changes include:
Unified Price Increase Scheduling
Introduced a new
ScheduleForSubscriptionmethod inIPriceIncreaseSchedulerand its implementation, which intelligently dispatches scheduling logic based on subscriber type and organization eligibility. This centralizes and simplifies price increase scheduling for both personal and business plans.Added a new
OrganizationPriceIncreaseOptionsrecord to allow fine-grained control over scheduling guards, such as skipping if already scheduled.Refactored business plan price migration scheduling to use the new unified scheduling pathway, reducing code duplication and improving maintainability.
Improved Metadata and Feature Flag Handling
CancellingUserIdtoMetadataKeysand updated cancellation logic to consistently use this key, improving traceability of who initiated cancellations.Stripe Integration and Testability
customer,customer.discount) to ensure all necessary data is available for downstream logic.Code Cleanup and Refactoring
📸 Screenshots
Screen.Recording.2026-05-21.at.2.42.23.PM.mov