diff --git a/libs/common/src/billing/services/subscription-pricing.service.spec.ts b/libs/common/src/billing/services/subscription-pricing.service.spec.ts index 33bfcebeb58..e96c0d4b74c 100644 --- a/libs/common/src/billing/services/subscription-pricing.service.spec.ts +++ b/libs/common/src/billing/services/subscription-pricing.service.spec.ts @@ -231,6 +231,7 @@ describe("DefaultSubscriptionPricingService", () => { }, storage: { price: 4, + provided: 1, }, } as PremiumPlanResponse; @@ -350,7 +351,7 @@ describe("DefaultSubscriptionPricingService", () => { billingApiService.getPlans.mockResolvedValue(mockPlansResponse); billingApiService.getPremiumPlan.mockResolvedValue(mockPremiumPlanResponse); - configService.getFeatureFlag$.mockReturnValue(of(false)); // Default to false (use hardcoded value) + configService.getFeatureFlag$.mockReturnValue(of(false)); setupEnvironmentService(environmentService); service = new DefaultSubscriptionPricingService( @@ -915,7 +916,7 @@ describe("DefaultSubscriptionPricingService", () => { const testError = new Error("Premium plan API error"); errorBillingApiService.getPlans.mockResolvedValue(mockPlansResponse); errorBillingApiService.getPremiumPlan.mockRejectedValue(testError); - errorConfigService.getFeatureFlag$.mockReturnValue(of(true)); // Enable feature flag to use premium plan API + errorConfigService.getFeatureFlag$.mockReturnValue(of(false)); setupEnvironmentService(errorEnvironmentService); const errorService = new DefaultSubscriptionPricingService( @@ -959,71 +960,16 @@ describe("DefaultSubscriptionPricingService", () => { expect(getPlansResponse).toHaveBeenCalledTimes(1); }); - it("should share premium plan API response between multiple subscriptions when feature flag is enabled", () => { - // Create a new mock to avoid conflicts with beforeEach setup - const newBillingApiService = mock(); - const newConfigService = mock(); - const newEnvironmentService = mock(); - - newBillingApiService.getPlans.mockResolvedValue(mockPlansResponse); - newBillingApiService.getPremiumPlan.mockResolvedValue(mockPremiumPlanResponse); - newConfigService.getFeatureFlag$.mockReturnValue(of(true)); - setupEnvironmentService(newEnvironmentService); - - const getPremiumPlanSpy = jest.spyOn(newBillingApiService, "getPremiumPlan"); - - // Create a new service instance with the feature flag enabled - const newService = new DefaultSubscriptionPricingService( - newBillingApiService, - newConfigService, - i18nService, - logService, - newEnvironmentService, - ); + it("should share premium plan API response between multiple subscriptions", () => { + const getPremiumPlanSpy = jest.spyOn(billingApiService, "getPremiumPlan"); // Subscribe to the premium pricing tier multiple times - newService.getPersonalSubscriptionPricingTiers$().subscribe(); - newService.getPersonalSubscriptionPricingTiers$().subscribe(); + service.getPersonalSubscriptionPricingTiers$().subscribe(); + service.getPersonalSubscriptionPricingTiers$().subscribe(); // API should only be called once due to shareReplay on premiumPlanResponse$ expect(getPremiumPlanSpy).toHaveBeenCalledTimes(1); }); - - it("should use hardcoded premium price when feature flag is disabled", (done) => { - // Create a new mock to test from scratch - const newBillingApiService = mock(); - const newConfigService = mock(); - const newEnvironmentService = mock(); - - newBillingApiService.getPlans.mockResolvedValue(mockPlansResponse); - newBillingApiService.getPremiumPlan.mockResolvedValue({ - seat: { price: 999 }, // Different price to verify hardcoded value is used - storage: { price: 999 }, - } as PremiumPlanResponse); - newConfigService.getFeatureFlag$.mockReturnValue(of(false)); - setupEnvironmentService(newEnvironmentService); - - // Create a new service instance with the feature flag disabled - const newService = new DefaultSubscriptionPricingService( - newBillingApiService, - newConfigService, - i18nService, - logService, - newEnvironmentService, - ); - - // Subscribe with feature flag disabled - newService.getPersonalSubscriptionPricingTiers$().subscribe((tiers) => { - const premiumTier = tiers.find( - (tier) => tier.id === PersonalSubscriptionPricingTierIds.Premium, - ); - - // Should use hardcoded value of 10, not the API response value of 999 - expect(premiumTier!.passwordManager.annualPrice).toBe(10); - expect(premiumTier!.passwordManager.annualPricePerAdditionalStorageGB).toBe(4); - done(); - }); - }); }); describe("Self-hosted environment behavior", () => { @@ -1035,7 +981,7 @@ describe("DefaultSubscriptionPricingService", () => { const getPlansSpy = jest.spyOn(selfHostedBillingApiService, "getPlans"); const getPremiumPlanSpy = jest.spyOn(selfHostedBillingApiService, "getPremiumPlan"); - selfHostedConfigService.getFeatureFlag$.mockReturnValue(of(true)); + selfHostedConfigService.getFeatureFlag$.mockReturnValue(of(false)); setupEnvironmentService(selfHostedEnvironmentService, Region.SelfHosted); const selfHostedService = new DefaultSubscriptionPricingService( @@ -1061,7 +1007,7 @@ describe("DefaultSubscriptionPricingService", () => { const selfHostedConfigService = mock(); const selfHostedEnvironmentService = mock(); - selfHostedConfigService.getFeatureFlag$.mockReturnValue(of(true)); + selfHostedConfigService.getFeatureFlag$.mockReturnValue(of(false)); setupEnvironmentService(selfHostedEnvironmentService, Region.SelfHosted); const selfHostedService = new DefaultSubscriptionPricingService( diff --git a/libs/common/src/billing/services/subscription-pricing.service.ts b/libs/common/src/billing/services/subscription-pricing.service.ts index 9ba76d348d0..73f3dc1bc77 100644 --- a/libs/common/src/billing/services/subscription-pricing.service.ts +++ b/libs/common/src/billing/services/subscription-pricing.service.ts @@ -33,16 +33,6 @@ import { } from "../types/subscription-pricing-tier"; export class DefaultSubscriptionPricingService implements SubscriptionPricingServiceAbstraction { - /** - * Fallback premium pricing used when the feature flag is disabled. - * These values represent the legacy pricing model and will not reflect - * server-side price changes. They are retained for backward compatibility - * during the feature flag rollout period. - */ - private static readonly FALLBACK_PREMIUM_SEAT_PRICE = 10; - private static readonly FALLBACK_PREMIUM_STORAGE_PRICE = 4; - private static readonly FALLBACK_PREMIUM_PROVIDED_STORAGE_GB = 1; - constructor( private billingApiService: BillingApiServiceAbstraction, private configService: ConfigService, @@ -123,45 +113,27 @@ export class DefaultSubscriptionPricingService implements SubscriptionPricingSer shareReplay({ bufferSize: 1, refCount: false }), ); - private premium$: Observable = this.configService - .getFeatureFlag$(FeatureFlag.PM26793_FetchPremiumPriceFromPricingService) - .pipe( - take(1), // Lock behavior at first subscription to prevent switching data sources mid-stream - switchMap((fetchPremiumFromPricingService) => - fetchPremiumFromPricingService - ? this.premiumPlanResponse$.pipe( - map((premiumPlan) => ({ - seat: premiumPlan.seat?.price, - storage: premiumPlan.storage?.price, - provided: premiumPlan.storage?.provided, - })), - ) - : of({ - seat: DefaultSubscriptionPricingService.FALLBACK_PREMIUM_SEAT_PRICE, - storage: DefaultSubscriptionPricingService.FALLBACK_PREMIUM_STORAGE_PRICE, - provided: DefaultSubscriptionPricingService.FALLBACK_PREMIUM_PROVIDED_STORAGE_GB, - }), - ), - map((premiumPrices) => ({ - id: PersonalSubscriptionPricingTierIds.Premium, - name: this.i18nService.t("premium"), - description: this.i18nService.t("advancedOnlineSecurity"), - availableCadences: [SubscriptionCadenceIds.Annually], - passwordManager: { - type: "standalone", - annualPrice: premiumPrices.seat, - annualPricePerAdditionalStorageGB: premiumPrices.storage, - providedStorageGB: premiumPrices.provided, - features: [ - this.featureTranslations.builtInAuthenticator(), - this.featureTranslations.secureFileStorage(), - this.featureTranslations.emergencyAccess(), - this.featureTranslations.breachMonitoring(), - this.featureTranslations.andMoreFeatures(), - ], - }, - })), - ); + private premium$: Observable = this.premiumPlanResponse$.pipe( + map((premiumPlan) => ({ + id: PersonalSubscriptionPricingTierIds.Premium, + name: this.i18nService.t("premium"), + description: this.i18nService.t("advancedOnlineSecurity"), + availableCadences: [SubscriptionCadenceIds.Annually], + passwordManager: { + type: "standalone", + annualPrice: premiumPlan.seat?.price, + annualPricePerAdditionalStorageGB: premiumPlan.storage?.price, + providedStorageGB: premiumPlan.storage?.provided, + features: [ + this.featureTranslations.builtInAuthenticator(), + this.featureTranslations.secureFileStorage(), + this.featureTranslations.emergencyAccess(), + this.featureTranslations.breachMonitoring(), + this.featureTranslations.andMoreFeatures(), + ], + }, + })), + ); private families$: Observable = this.organizationPlansResponse$.pipe( diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 5160e6aa542..b7fad43ebbf 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -31,7 +31,6 @@ export enum FeatureFlag { /* Billing */ TrialPaymentOptional = "PM-8163-trial-payment", PM24032_NewNavigationPremiumUpgradeButton = "pm-24032-new-navigation-premium-upgrade-button", - PM26793_FetchPremiumPriceFromPricingService = "pm-26793-fetch-premium-price-from-pricing-service", PM23713_PremiumBadgeOpensNewPremiumUpgradeDialog = "pm-23713-premium-badge-opens-new-premium-upgrade-dialog", PM26462_Milestone_3 = "pm-26462-milestone-3", PM23341_Milestone_2 = "pm-23341-milestone-2", @@ -146,7 +145,6 @@ export const DefaultFeatureFlagValue = { /* Billing */ [FeatureFlag.TrialPaymentOptional]: FALSE, [FeatureFlag.PM24032_NewNavigationPremiumUpgradeButton]: FALSE, - [FeatureFlag.PM26793_FetchPremiumPriceFromPricingService]: FALSE, [FeatureFlag.PM23713_PremiumBadgeOpensNewPremiumUpgradeDialog]: FALSE, [FeatureFlag.PM26462_Milestone_3]: FALSE, [FeatureFlag.PM23341_Milestone_2]: FALSE,