* 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-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
* [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>
* 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
* 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
* 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
* 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