* feat(billing): add Organization.ChangePlan extension for structural plan shape
Pure helper that writes plan-derived structural columns (PlanType, Plan,
Use* capability flags, UsersGetPremium, MaxCollections) without touching
customer-purchase columns. Preserves the existing UseKeyConnector carve-out.
Behavior-preserving extraction of the field-copy block at lines 287-310
of UpgradeOrganizationPlanCommand.UpgradePlanAsync.
* refactor(billing): use Organization.ChangePlan in UpgradePlanAsync
Replaces the inline structural field-copy block with a call to the new
ChangePlan helper. Customer-purchase columns (Seats, MaxStorageGb,
Enabled, UseSecretsManager) and the PremiumAccessAddon override on
UsersGetPremium stay inline at the call site. Behavior-preserving.
* feat(billing): register Teams 2020 -> current migration paths
Appends Teams2020AnnualToCurrent (byte 3) and Teams2020MonthlyToCurrent
(byte 4) to MigrationPathId and the MigrationPaths registry. Required for
the business migration handler to resolve cohorts mapped to Teams source
plans; without these entries MigrationPaths.FromId returns null and the
handler no-ops on Teams orgs in Cohort A1. Snapshot tests updated.
* chore(billing): inject migration cohort repositories into SubscriptionUpdatedHandler
Adds IOrganizationPlanMigrationCohortRepository and
IOrganizationPlanMigrationCohortAssignmentRepository as constructor
dependencies in preparation for HandleScheduleTriggeredBusinessMigrationAsync.
No behavior change.
* feat(billing): scaffold business plan Phase-2 migration handler
Adds HandleScheduleTriggeredBusinessMigrationAsync as a sibling call in
HandleAsync's organization branch, gated on PM35215_BusinessPlanPriceMigration.
Initial implementation short-circuits when ScheduleId is null. Locks the
no-op behaviour with NoScheduleId + FeatureFlagOff tests. Full handler body
lands in subsequent commits.
* feat(billing): gate business migration handler on registered source price IDs
Builds the source-price allowlist from MigrationPaths.All, using the
seat-vs-non-seat pattern (HasNonSeatBasedPasswordManagerPlan). All four
Track A 2020 plans register automatically. Skips when the previous
subscription items don't include any registered 2020 source price.
* feat(billing): resolve cohort via assignment row for business migration handler
Reads assignment by organization id (DB is source of truth), then resolves
the cohort and migration path. Stripe subscription.Metadata['migration_cohort_id']
remains stamped by PriceIncreaseScheduler for dashboard attribution but is
not consulted by the handler. Skips with a warning when the assignment is
missing, the cohort is missing, or MigrationPathId references an unregistered
path. Idempotent: skips with info-level log when assignment.MigratedDate is
already set, before any further DB reads.
* feat(billing): defensive target-price sanity check for business migration
After resolving the target plan from cohort.MigrationPath.ToPlan, verifies
the current subscription items contain the target's PM price ID (seat-aware).
Skips with a warning on mismatch to protect against operator data errors or
off-path schedule transitions.
* feat(billing): apply plan shape and mark assignment migrated on Phase 2
Completes HandleScheduleTriggeredBusinessMigrationAsync: loads the org,
calls Organization.ChangePlan(targetPlan), persists via ReplaceAsync, and
sets assignment.MigratedDate + RevisionDate before persisting the
assignment. Happy-path coverage for all four Track A pairs (Teams +
Enterprise, monthly + annual). Teams tests assert the UseScim flip - the
load-bearing capability gain for Teams 2020 -> current.
* Add more unit test
* fix: add UTF-8 BOM to .cs files for editorconfig charset compliance
* Add exception handle
* Code refactoring
* Add more unit testing
* Resolve the pr comment
* feat(billing): introduce unified subscription price increase scheduler API
* feat(billing): implement unified subscription price increase scheduler logic
* refactor(billing): update subscription handlers to use unified scheduler
* feat(billing): extend price migration feature flag checks
* test(billing): add and update tests for unified price increase scheduler
* fix(billing): run dotnet format
* feat(billing): expand customer and customer.discount on subscription fetch
* refactor(ReinstateSubscriptionCommandTests): rename test method for broader scope
* feat(billing): expand customer.discount in update handler
* test(billing): update test name
* feat(billing): add test clock waiting mechanism for upcoming invoices
* feat(billing): introduce cancelling user ID metadata key
* feat(billing): store cancelling user ID on subscription cancellation
* feat(billing): clear cancelling user ID on subscription reinstatement
* test(billing): update subscriber service tests for cancelling user ID
* style(SubscriberService): use 'is not null' pattern matching
* feat(SubscriberService): add PM35215 migration cohort metadata handling
* feat(SubscriberService): extend price migration deferral to PM35215
* test(SubscriberService): add and update tests for PM35215 feature
* feat(billing): Introduce OrganizationPriceIncreaseOptions
* refactor(billing): Centralize price increase eligibility in scheduler
* refactor(billing): Delegate price increase validation from UpcomingInvoiceHandler
* feat(billing): Manage price increase schedules during subscription lifecycle events
* test(billing): Update UpcomingInvoiceHandlerTests for centralized validation
* test(billing): Add PriceIncreaseScheduler tests for SkipIfAlreadyScheduled option
* test(billing): Add SubscriberService tests for price increase schedule management
* fix(billing): run dotnet format
* fix(billing): remove redundant customer expansion
* fix(billing): expand discounts for customer and subscription
* refactor(billing): Rename method to clarify dispatching role for organization scheduling
* fix(billing): Prevent clearing migration cohort metadata on cancellation
* fix(billing): Fallback to standard email when price increase migration fails
* feat(billing): improve observability for missing migration path data
* refactor(billing): simplify business plan type identification
* [PM-37068] feat: Add business plan cohort branch to UpcomingInvoiceHandler
Wires the caller side of the Teams/Enterprise 2020 → current price
migration. AlignOrganizationSubscriptionConcernsAsync becomes a
ProductTier dispatcher; the new business branch loads the cohort
assignment, runs eligibility guards (assignment unscheduled, cohort
active, org PlanType matches cohort source), and invokes PM-37064's
ScheduleBusinessPriceIncrease. The renewal email is a placeholder until
PM-37070 lands.
Also expands subscriptions.data.customer on the customer load so
PM-37064 can preserve customer-level discounts into Phase 2.
Incidentally strips pre-existing #region markers in
UpcomingInvoiceHandlerTests.cs per the no-regions rule.
* Apply review feedback: gate FF in handler, split MigrationPath checks, silence scheduler churn-only warning
* Add cohort metadata to subscription on migration schedule
---------
Co-authored-by: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com>
* [PM-37064] feat: Add ScheduleBusinessPriceIncrease scheduler entry point
Extends PriceIncreaseScheduler (PM-32645) with a parallel entry point for
Teams/Enterprise 2020 → current plan migrations under epic PM-35215. The
business path consumes a cohort directly, preserves existing discounts,
optionally appends the cohort's proactive coupon, and stamps ScheduledDate
on the assignment row on success.
Existing Schedule renames to SchedulePersonalPriceIncrease across four
call sites; ResolvePhase2Async becomes private. Shared concerns extract
into ActiveScheduleExistsAsync and CreateAndConfigureScheduleAsync helpers.
ScheduleBusinessPriceIncrease has zero production callers — the
UpcomingInvoiceHandler dispatcher branch lands in a follow-up. The new
PM35215_BusinessPlanPriceMigration feature flag defaults off.
* Address PR review: prevent orphan schedules and fix Release flag gate
* feat(billing): add feature flag for automatic tax enforcement
* refactor(billing): remove unused SubscriptionUpdateOptionsExtensions
* refactor(billing): inject IFeatureService into billing services and commands
* feat(billing): conditionalize customer tax exemption logic with feature flag
* feat(billing): conditionally enable Stripe automatic tax in OrganizationBillingService
* test(billing): add unit tests for Stripe automatic tax feature flag
* fix(billing): Run dotnet format
* test(Premium): use class-level IFeatureService mock in UpgradePremiumToOrganizationCommandTests
* refactor(billing): consolidate customer return conditions for automatic tax
* refactor(billing): broaden postal code validation for organization creation
* refactor(billing): remove PM37597 feature flag for automatic tax logic
* Revert "refactor(billing): broaden postal code validation for organization creation"
This reverts commit cddbda838c.
* fix(test): remove outdated test
* [PM-31781] skip unpaid automations for exempt orgs
* removing check for subscription create since it's impossible to be unpaid with that billing reason
* pr feedback
* Add subscriber existence guard to SubscriptionUpdatedHandler
* conflict resolution fixup
* pr feedback fix: don't update stale entities
---------
Co-authored-by: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com>
* Remove pm-26462-milestone-3 flag from PricingClient
Simplify FamiliesAnnually2025 lookup to always return "families-2025".
Remove PreProcessFamiliesPreMigrationPlan deployment safeguard method
and its call sites. Remove unused IFeatureService constructor dependency.
* Remove pm-26462-milestone-3 flag from UpcomingInvoiceHandler.HandleForOrganizationAsync
Remove milestone3 variable and parameter from
AlignOrganizationSubscriptionConcernsAsync. Simplify guard clause
to only check plan type.
* Update PricingClientTests after pm-26462-milestone-3 removal
Remove IFeatureService from test setup. Delete tests for flag-disabled
safeguard behavior. Clean up remaining test names.
* Update UpcomingInvoiceHandlerTests after pm-26462-milestone-3 removal
Remove all feature flag mock setup lines for PM26462_Milestone_3.
Delete test methods that verified flag-disabled behavior.
* Merge GetPlan lookup key test regions into one
* [PM-34866] Fix EnableAutomaticTaxAsync to update schedule phases
* Use test clock frozen time for phase filtering
* Expand test_clock on customer subscription fetches
* Remove pm-23341-milestone-2 flag from UpcomingInvoiceHandler.HandleForUserAsync
Always call AlignPremiumUsersSubscriptionConcernsAsync instead of gating
it behind the feature flag.
* Remove pm-23341-milestone-2 flag from AccountsController and SubscriptionResponseModel
Remove includeMilestone2Discount parameter from both SubscriptionResponseModel
constructors. Rename ShouldIncludeMilestone2Discount to ShouldIncludeDiscount
and always evaluate the discount (coupon ID match + active check).
* Update AccountsControllerTests after pm-23341-milestone-2 removal
Remove feature flag mock setup lines and delete test methods that
verified flag-disabled behavior. Rename tests to remove flag references.
* Update SubscriptionResponseModelTests after pm-23341-milestone-2 removal
Remove includeMilestone2Discount parameter from all constructor calls.
Delete tests that verified flag-disabled behavior.
* Update UpcomingInvoiceHandlerTests after pm-23341-milestone-2 removal
Remove all feature flag mock setup lines for PM23341_Milestone_2.
* Remove IFeatureService from AccountsController constructor and tests
The constructor parameter became unused after removing the flag check.
* Add default ListPremiumPlans mock for UpcomingInvoiceHandler tests
After removing the m2 flag guard, AlignPremiumUsersSubscriptionConcernsAsync
is always called and needs ListPremiumPlans to return a non-null value.
Add a default empty list in the test constructor setup.
Make tax-related subscription updates schedule-aware during the ~15-day
window between invoice.upcoming and renewal. When a subscription schedule
is present and the feature flag is enabled, update default_settings.automatic_tax
on the schedule instead of the subscription directly.
Modified paths:
- UpcomingInvoiceHandler: AlignOrganizationTaxConcernsAsync,
AlignPremiumUsersTaxConcernsAsync, new shared EnableAutomaticTaxAsync helper
- UpdateBillingAddressCommand: EnableAutomaticTaxAsync, added IFeatureService
* changes for the premium push notification
* Fix the lint build
* implement the hub-helper
* Resolve the pr comments
* fix the lint error
* move PremiumStatusPushNotification to billing
* docs(billing): add design document for replacing SetupIntent cache
* docs(billing): add implementation plan for replacing SetupIntent cache
* feat(db): add gateway lookup stored procedures for Organization, Provider, and User
* feat(db): add gateway lookup indexes to Organization, Provider, and User table definitions
* chore(db): add SQL Server migration for gateway lookup indexes and stored procedures
* feat(repos): add gateway lookup methods to IOrganizationRepository and Dapper implementation
* feat(repos): add gateway lookup methods to IProviderRepository and Dapper implementation
* feat(repos): add gateway lookup methods to IUserRepository and Dapper implementation
* feat(repos): add EF OrganizationRepository gateway lookup methods and index configuration
* feat(repos): add EF ProviderRepository gateway lookup methods and index configuration
* feat(repos): add EF UserRepository gateway lookup methods and index configuration
* chore(db): add EF migrations for gateway lookup indexes
* refactor(billing): update SetupIntentSucceededHandler to use repository instead of cache
* refactor(billing): simplify StripeEventService by expanding customer on SetupIntent
* refactor(billing): query Stripe for SetupIntents by customer ID in GetPaymentMethodQuery
* refactor(billing): query Stripe for SetupIntents by customer ID in HasPaymentMethodQuery
* refactor(billing): update OrganizationBillingService to set customer on SetupIntent
* refactor(billing): update ProviderBillingService to set customer on SetupIntent and query by customer
* refactor(billing): update UpdatePaymentMethodCommand to set customer on SetupIntent
* refactor(billing): remove bank account support from CreatePremiumCloudHostedSubscriptionCommand
* refactor(billing): remove OrganizationBillingService.UpdatePaymentMethod dead code
* refactor(billing): remove ProviderBillingService.UpdatePaymentMethod
* refactor(billing): remove PremiumUserBillingService.UpdatePaymentMethod and UserService.ReplacePaymentMethodAsync
* refactor(billing): remove SubscriberService.UpdatePaymentSource and related dead code
* refactor(billing): update SubscriberService.GetPaymentSourceAsync to query Stripe by customer ID
Add Task 15a to plan - this was a missed requirement for updating
GetPaymentSourceAsync which still used the cache.
* refactor(billing): complete removal of PremiumUserBillingService.Finalize and UserService.SignUpPremiumAsync
* refactor(billing): remove ISetupIntentCache and SetupIntentDistributedCache
* chore: remove temporary planning documents
* chore: run dotnet format
* fix(billing): add MaxLength(50) to Provider gateway ID properties
* chore(db): add EF migrations for Provider gateway column lengths
* chore: run dotnet format
* chore: rename SQL migration for chronological order
* Implement the correct changes
* failing test has been removed
* Add unit testing and logs
* Resolve the pr comment on missed requirements
* fix the lint error
* resolve the build lint
* Fix the failing test
* Fix the failing test
* Add the IncompleteExpired status
* resolve the lint error
* Fix the build lint error
* Implement the IncompleteExpired flow
* move billing services+tests to billing namespaces
* reorganized methods in file and added comment headers
* renamed StripeAdapter methods for better clarity
* clean up redundant qualifiers
* Upgrade Stripe.net to v48.4.0
* Update PreviewTaxAmountCommand
* Remove unused UpcomingInvoiceOptionExtensions
* Added SubscriptionExtensions with GetCurrentPeriodEnd
* Update PremiumUserBillingService
* Update OrganizationBillingService
* Update GetOrganizationWarningsQuery
* Update BillingHistoryInfo
* Update SubscriptionInfo
* Remove unused Sql Billing folder
* Update StripeAdapter
* Update StripePaymentService
* Update InvoiceCreatedHandler
* Update PaymentFailedHandler
* Update PaymentSucceededHandler
* Update ProviderEventService
* Update StripeEventUtilityService
* Update SubscriptionDeletedHandler
* Update SubscriptionUpdatedHandler
* Update UpcomingInvoiceHandler
* Update ProviderSubscriptionResponse
* Remove unused Stripe Subscriptions Admin Tool
* Update RemoveOrganizationFromProviderCommand
* Update ProviderBillingService
* Update RemoveOrganizatinoFromProviderCommandTests
* Update PreviewTaxAmountCommandTests
* Update GetCloudOrganizationLicenseQueryTests
* Update GetOrganizationWarningsQueryTests
* Update StripePaymentServiceTests
* Update ProviderBillingControllerTests
* Update ProviderEventServiceTests
* Update SubscriptionDeletedHandlerTests
* Update SubscriptionUpdatedHandlerTests
* Resolve Billing test failures
I completely removed tests for the StripeEventService as they were using a system I setup a while back that read JSON files of the Stripe event structure. I did not anticipate how frequently these structures would change with each API version and the cost of trying to update these specific JSON files to test a very static data retrieval service far outweigh the benefit.
* Resolve Core test failures
* Run dotnet format
* Remove unused provider migration
* Fixed failing tests
* Run dotnet format
* Replace the old webhook secret key with new one (#6223)
* Fix compilation failures in additions
* Run dotnet format
* Bump Stripe API version
* Fix recent addition: CreatePremiumCloudHostedSubscriptionCommand
* Fix new code in main according to Stripe update
* Fix InvoiceExtensions
* Bump SDK version to match API Version
* cleanup
* fixing items missed after the merge
* use expression body for all simple returns
* forgot fixes, format, and pr feedback
* claude pr feedback
* pr feedback and cleanup
* more claude feedback
---------
Co-authored-by: Alex Morask <amorask@bitwarden.com>
Co-authored-by: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com>
* Remove feature flag and move StaticStore plans to MockPlans for tests
* Remove old plan models / move sponsored plans out of StaticStore
* Run dotnet format
* Add pricing URI to Development appsettings for local development and integration tests
* Updated Api Integration tests to get current plan type
* Run dotnet format
* Fix failing tests
* Re-organize UpcomingInvoiceHandler for readability
* Milestone 3 renewal
* Map premium access data from additonal data in pricing
* Feedback
* Fix test
* Upgrade Stripe.net to v48.4.0
* Update PreviewTaxAmountCommand
* Remove unused UpcomingInvoiceOptionExtensions
* Added SubscriptionExtensions with GetCurrentPeriodEnd
* Update PremiumUserBillingService
* Update OrganizationBillingService
* Update GetOrganizationWarningsQuery
* Update BillingHistoryInfo
* Update SubscriptionInfo
* Remove unused Sql Billing folder
* Update StripeAdapter
* Update StripePaymentService
* Update InvoiceCreatedHandler
* Update PaymentFailedHandler
* Update PaymentSucceededHandler
* Update ProviderEventService
* Update StripeEventUtilityService
* Update SubscriptionDeletedHandler
* Update SubscriptionUpdatedHandler
* Update UpcomingInvoiceHandler
* Update ProviderSubscriptionResponse
* Remove unused Stripe Subscriptions Admin Tool
* Update RemoveOrganizationFromProviderCommand
* Update ProviderBillingService
* Update RemoveOrganizatinoFromProviderCommandTests
* Update PreviewTaxAmountCommandTests
* Update GetCloudOrganizationLicenseQueryTests
* Update GetOrganizationWarningsQueryTests
* Update StripePaymentServiceTests
* Update ProviderBillingControllerTests
* Update ProviderEventServiceTests
* Update SubscriptionDeletedHandlerTests
* Update SubscriptionUpdatedHandlerTests
* Resolve Billing test failures
I completely removed tests for the StripeEventService as they were using a system I setup a while back that read JSON files of the Stripe event structure. I did not anticipate how frequently these structures would change with each API version and the cost of trying to update these specific JSON files to test a very static data retrieval service far outweigh the benefit.
* Resolve Core test failures
* Run dotnet format
* Remove unused provider migration
* Fixed failing tests
* Run dotnet format
* Replace the old webhook secret key with new one (#6223)
* Fix compilation failures in additions
* Run dotnet format
* Bump Stripe API version
* Fix recent addition: CreatePremiumCloudHostedSubscriptionCommand
* Fix new code in main according to Stripe update
* Fix InvoiceExtensions
* Bump SDK version to match API Version
* Fix provider invoice generation validation
* More QA fixes
* Fix tests
* QA defect resolutions
* QA defect resolutions
* Run dotnet format
* Fix tests
---------
Co-authored-by: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com>
* Add feature flag
* Disable provider and schedule cancellation when subscription goes unpaid
* Run dotnet format
* Only set provider subscription cancel_at when subscription is going from paid to unpaid
* Update tests
* Remove gRPC and convert PricingClient to HttpClient wrapper
* Add PlanType.GetProductTier extension
Many instances of StaticStore use are just to get the ProductTierType of a PlanType, but this can be derived from the PlanType itself without having to fetch the entire plan.
* Remove invocations of the StaticStore in non-Test code
* Deprecate StaticStore entry points
* Run dotnet format
* Matt's feedback
* Run dotnet format
* Rui's feedback
* Run dotnet format
* Replacements since approval
* Run dotnet format