Commit Graph

23 Commits

Author SHA1 Message Date
cyprain-okeke
d07a941896 [PM-37092] feat: Add business plan migration handler to SubscriptionUpdatedHandler (#7707)
* 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
2026-05-27 10:51:44 +00:00
Stephon Brown
ee07462d28 [PM-37084] Business Aware Schedule Recovery and Cancellation (#7686)
* 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
2026-05-26 17:16:18 -04:00
Alex Morask
466bf01148 [PM-37064] feat: Add ScheduleBusinessPriceIncrease scheduler entry point (#7665)
* [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
2026-05-19 14:14:30 -05:00
Alex Morask
316f7cdc6c [PM-36613] Void open invoices for unpaid subscriptions (#7589)
* fix(billing): void open invoices when subscription is deleted

* refactor(billing): gate invoice voiding on subscription deletion

* feat(billing): clear pending unpaid cancellation on subscriber re-enable

* feat(billing): schedule unpaid cancellation on subscriber disable

* fix(billing): respect subscription test clock when scheduling cancellation
2026-05-12 21:29:57 +00:00
Kyle Denney
ce029bea4f [PM-31781] skip unpaid automations for exempt orgs (#7480)
* [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>
2026-05-12 14:56:09 -05:00
Alex Morask
d25553e534 fix(billing): stop 500-retry loop on incomplete_expired subscription transitions (#7525) 2026-05-07 10:44:26 -05:00
Alex Morask
54c3e4a695 feat(billing): add IPriceIncreaseScheduler for deferred price migration scheduling (#7305) 2026-03-26 15:00:12 -05:00
Conner Turnbull
89f6e432b5 [PM-33896] Update Families organization on schedule transition (#7300) 2026-03-25 15:53:07 +00:00
cyprain-okeke
768de5f1e2 [PM-32477]PremiumStatusChanged Push Notification (#7198)
* 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
2026-03-19 15:51:36 +01:00
cyprain-okeke
84521a67c8 [PM-30908]Correct Premium subscription status handling (#6877)
* 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
2026-02-13 18:56:26 +01:00
Alex Morask
81e3044b91 [PM-31140] Consolidate unpaid subscription handling (#6918)
* Consolidate unpaid subscription handling

* Move enable/disable operations to SubscriberService

* Revert "Move enable/disable operations to SubscriberService"

This reverts commit fe0bc1516b.
2026-02-10 11:24:00 -06:00
Todd Martin
a27eda7df6 chore(flags): [PM-30613] Remove unused mjml-based-email-templates feature flag
* Removed old method.

* Removed IFeatureService.

* Fixed test.

* Renamed the v2 endpoint.

* Removed old Handlebars templates.

* Renamed v2 templates to remove v2 suffix.
2026-02-02 11:36:39 -05:00
Stephon Brown
8d30fbcc8a Billing/pm 30882/defect pm coupon removed on upgrade (#6863)
* fix(billing): update coupon check logic

* tests(billing): update tests and add plan check test
2026-01-16 18:13:57 -05:00
cyprain-okeke
5ac8536855 [PM-28662] Fix Individual Premium automatically disabled due to duplicate subscription leftover from failed payment (#6663)
* Fix the Bug

* Address the hardcode issue

* Fix the tailing test

* resolve the lint issue
2025-12-12 13:19:09 -06:00
Alex Morask
71be3865ea [PM-24558] Remove FF: pm-21821-provider-portal-takeover (#6613)
* Remove FF: pm-21821-provider-portal-takeover

* Run dotnet format
2025-12-02 10:16:37 -06:00
Alex Morask
f595818ede [PM-24549] Remove feature flag: use-pricing-service (#6567)
* 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
2025-11-19 09:53:30 -06:00
Alex Morask
9c51c9971b [PM-21638] Stripe .NET v48 (#6202)
* 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>
2025-10-21 14:07:55 -05:00
Alex Morask
3dd5accb56 [PM-24964] Stripe-hosted bank account verification (#6263)
* Implement bank account hosted URL verification with webhook handling notification

* Fix tests

* Run dotnet format

* Remove unused VerifyBankAccount operation

* Stephon's feedback

* Removing unused test

* TEMP: Add logging for deployment check

* Run dotnet format

* fix test

* Revert "fix test"

This reverts commit b8743ab3b5.

* Revert "Run dotnet format"

This reverts commit 5c861b0b72.

* Revert "TEMP: Add logging for deployment check"

This reverts commit 0a88acd6a1.

* Resolve GetPaymentMethodQuery order of operations
2025-09-09 12:22:42 -05:00
Alex Morask
c503ecbefc [PM-21827] Implement mechanism to suspend currently unpaid providers (#6119)
* Manually suspend provider and set cancel_at when we receive 'suspend_provider' metadata update

* Run dotnet format'
2025-07-24 11:50:09 -05:00
Stephon Brown
76d1a2e875 [PM-23287] Enable Provider When Subscription Is Paid (#6113)
* test : add tests for provider update

* feat: add provider update logic and dependencies

* fix: remove duplicate dependencies

* refactor: updated switch logic for helper method

* test: add feature flag to tests

* feat: add feature flag for changes
2025-07-24 11:46:16 -04:00
Alex Morask
829c3ed1d7 [PM-21821] Provider portal takeover states (#6109)
* 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
2025-07-23 08:25:37 -05:00
Jonas Hendrickx
f8e89f1747 [PM-18170] Remove PM-15814-alert-owners-of-reseller-managed-orgs (#5412) 2025-04-09 07:53:43 +02:00
cyprain-okeke
e53e701097 Initial commit (#5612) 2025-04-07 11:32:38 +01:00