From 7ef5cfe86933ab7267a12a158520d2fbb171116c Mon Sep 17 00:00:00 2001 From: Alex Morask Date: Thu, 23 Jan 2025 09:12:14 -0500 Subject: [PATCH 01/16] Remove FF from trial-billing-step.component --- .../trial-billing-step.component.html | 10 +---- .../trial-billing-step.component.ts | 44 ++++--------------- 2 files changed, 10 insertions(+), 44 deletions(-) diff --git a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html index 2f983944b70..36b818db666 100644 --- a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html +++ b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html @@ -49,15 +49,7 @@

{{ "billingPlanLabel" | i18n }

{{ "paymentType" | i18n }}

- - +
diff --git a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts index 213f051f75e..ba1889dd07d 100644 --- a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts +++ b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts @@ -13,13 +13,11 @@ import { } from "@bitwarden/common/billing/abstractions/organization-billing.service"; import { PaymentMethodType, PlanType, ProductTierType } from "@bitwarden/common/billing/enums"; import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { ToastService } from "@bitwarden/components"; -import { BillingSharedModule, PaymentComponent, TaxInfoComponent } from "../../shared"; +import { BillingSharedModule, TaxInfoComponent } from "../../shared"; import { PaymentV2Component } from "../../shared/payment/payment-v2.component"; export type TrialOrganizationType = Exclude; @@ -52,7 +50,6 @@ export enum SubscriptionProduct { standalone: true, }) export class TrialBillingStepComponent implements OnInit { - @ViewChild(PaymentComponent) paymentComponent: PaymentComponent; @ViewChild(PaymentV2Component) paymentV2Component: PaymentV2Component; @ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent; @Input() organizationInfo: OrganizationInfo; @@ -74,11 +71,8 @@ export class TrialBillingStepComponent implements OnInit { annualPlan?: PlanResponse; monthlyPlan?: PlanResponse; - deprecateStripeSourcesAPI: boolean; - constructor( private apiService: ApiService, - private configService: ConfigService, private i18nService: I18nService, private formBuilder: FormBuilder, private messagingService: MessagingService, @@ -87,9 +81,6 @@ export class TrialBillingStepComponent implements OnInit { ) {} async ngOnInit(): Promise { - this.deprecateStripeSourcesAPI = await this.configService.getFeatureFlag( - FeatureFlag.AC2476_DeprecateStripeSourcesAPI, - ); const plans = await this.apiService.getPlans(); this.applicablePlans = plans.data.filter(this.isApplicable); this.annualPlan = this.findPlanFor(SubscriptionCadence.Annual); @@ -124,23 +115,12 @@ export class TrialBillingStepComponent implements OnInit { } protected changedCountry() { - if (this.deprecateStripeSourcesAPI) { - this.paymentV2Component.showBankAccount = this.taxInfoComponent.country === "US"; - if ( - !this.paymentV2Component.showBankAccount && - this.paymentV2Component.selected === PaymentMethodType.BankAccount - ) { - this.paymentV2Component.select(PaymentMethodType.Card); - } - } else { - this.paymentComponent.hideBank = this.taxInfoComponent.taxFormGroup.value.country !== "US"; - if ( - this.paymentComponent.hideBank && - this.paymentComponent.method === PaymentMethodType.BankAccount - ) { - this.paymentComponent.method = PaymentMethodType.Card; - this.paymentComponent.changeMethod(); - } + this.paymentV2Component.showBankAccount = this.taxInfoComponent.country === "US"; + if ( + !this.paymentV2Component.showBankAccount && + this.paymentV2Component.selected === PaymentMethodType.BankAccount + ) { + this.paymentV2Component.select(PaymentMethodType.Card); } } @@ -162,14 +142,8 @@ export class TrialBillingStepComponent implements OnInit { private async createOrganization(): Promise { const planResponse = this.findPlanFor(this.formGroup.value.cadence); - let paymentMethod: [string, PaymentMethodType]; - - if (this.deprecateStripeSourcesAPI) { - const { type, token } = await this.paymentV2Component.tokenize(); - paymentMethod = [token, type]; - } else { - paymentMethod = await this.paymentComponent.createPaymentToken(); - } + const { type, token } = await this.paymentV2Component.tokenize(); + const paymentMethod: [string, PaymentMethodType] = [token, type]; const organization: OrganizationInformation = { name: this.organizationInfo.name, From b3fc5d5076655a653a01c09a21c031018f430c04 Mon Sep 17 00:00:00 2001 From: Alex Morask Date: Thu, 23 Jan 2025 09:14:04 -0500 Subject: [PATCH 02/16] Remove FF from user-subscription.component --- .../individual/user-subscription.component.ts | 46 ++++--------------- 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/apps/web/src/app/billing/individual/user-subscription.component.ts b/apps/web/src/app/billing/individual/user-subscription.component.ts index 97b4725e6d7..5fa5646add6 100644 --- a/apps/web/src/app/billing/individual/user-subscription.component.ts +++ b/apps/web/src/app/billing/individual/user-subscription.component.ts @@ -8,8 +8,6 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { SubscriptionResponse } from "@bitwarden/common/billing/models/response/subscription.response"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -21,10 +19,6 @@ import { AdjustStorageDialogV2Component, AdjustStorageDialogV2ResultType, } from "../shared/adjust-storage-dialog/adjust-storage-dialog-v2.component"; -import { - AdjustStorageDialogResult, - openAdjustStorageDialog, -} from "../shared/adjust-storage-dialog/adjust-storage-dialog.component"; import { OffboardingSurveyDialogResultType, openOffboardingSurvey, @@ -45,10 +39,6 @@ export class UserSubscriptionComponent implements OnInit { cancelPromise: Promise; reinstatePromise: Promise; - protected deprecateStripeSourcesAPI$ = this.configService.getFeatureFlag$( - FeatureFlag.AC2476_DeprecateStripeSourcesAPI, - ); - constructor( private apiService: ApiService, private platformUtilsService: PlatformUtilsService, @@ -60,7 +50,6 @@ export class UserSubscriptionComponent implements OnInit { private environmentService: EnvironmentService, private billingAccountProfileStateService: BillingAccountProfileStateService, private toastService: ToastService, - private configService: ConfigService, private accountService: AccountService, ) { this.selfHosted = this.platformUtilsService.isSelfHost(); @@ -166,33 +155,18 @@ export class UserSubscriptionComponent implements OnInit { }; adjustStorage = async (add: boolean) => { - const deprecateStripeSourcesAPI = await firstValueFrom(this.deprecateStripeSourcesAPI$); - - if (deprecateStripeSourcesAPI) { - const dialogRef = AdjustStorageDialogV2Component.open(this.dialogService, { - data: { - price: 4, - cadence: "year", - type: add ? "Add" : "Remove", - }, - }); + const dialogRef = AdjustStorageDialogV2Component.open(this.dialogService, { + data: { + price: 4, + cadence: "year", + type: add ? "Add" : "Remove", + }, + }); - const result = await lastValueFrom(dialogRef.closed); + const result = await lastValueFrom(dialogRef.closed); - if (result === AdjustStorageDialogV2ResultType.Submitted) { - await this.load(); - } - } else { - const dialogRef = openAdjustStorageDialog(this.dialogService, { - data: { - storageGbPrice: 4, - add: add, - }, - }); - const result = await lastValueFrom(dialogRef.closed); - if (result === AdjustStorageDialogResult.Adjusted) { - await this.load(); - } + if (result === AdjustStorageDialogV2ResultType.Submitted) { + await this.load(); } }; From a62d83a322aa194bfe9639428f2ed7e754c900f7 Mon Sep 17 00:00:00 2001 From: Alex Morask Date: Thu, 23 Jan 2025 09:15:18 -0500 Subject: [PATCH 03/16] Remove FF from individual-billing-routing.module --- .../individual-billing-routing.module.ts | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/apps/web/src/app/billing/individual/individual-billing-routing.module.ts b/apps/web/src/app/billing/individual/individual-billing-routing.module.ts index 585d9b418c1..6bbdf719a16 100644 --- a/apps/web/src/app/billing/individual/individual-billing-routing.module.ts +++ b/apps/web/src/app/billing/individual/individual-billing-routing.module.ts @@ -1,14 +1,10 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; -import { featureFlaggedRoute } from "@bitwarden/angular/platform/utils/feature-flagged-route"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; - import { PaymentMethodComponent } from "../shared"; import { BillingHistoryViewComponent } from "./billing-history-view.component"; import { PremiumV2Component } from "./premium/premium-v2.component"; -import { PremiumComponent } from "./premium/premium.component"; import { SubscriptionComponent } from "./subscription.component"; import { UserSubscriptionComponent } from "./user-subscription.component"; @@ -24,15 +20,11 @@ const routes: Routes = [ component: UserSubscriptionComponent, data: { titleId: "premiumMembership" }, }, - ...featureFlaggedRoute({ - defaultComponent: PremiumComponent, - flaggedComponent: PremiumV2Component, - featureFlag: FeatureFlag.AC2476_DeprecateStripeSourcesAPI, - routeOptions: { - path: "premium", - data: { titleId: "goPremium" }, - }, - }), + { + path: "premium", + component: PremiumV2Component, + data: { titleId: "goPremium" }, + }, { path: "payment-method", component: PaymentMethodComponent, From 14fb8ba4abf55df344717e22bb7cb7c4eb3ad4e3 Mon Sep 17 00:00:00 2001 From: Alex Morask Date: Thu, 23 Jan 2025 11:12:47 -0500 Subject: [PATCH 04/16] Remove FF from organization-billing.service --- .../src/services/jslib-services.module.ts | 1 - .../organization-billing.service.ts | 5 +---- .../services/organization-billing.service.ts | 22 +++---------------- 3 files changed, 4 insertions(+), 24 deletions(-) diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 766f0a2d411..bebeb1604d2 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -1234,7 +1234,6 @@ const safeProviders: SafeProvider[] = [ deps: [ ApiServiceAbstraction, BillingApiServiceAbstraction, - ConfigService, KeyService, EncryptService, I18nServiceAbstraction, diff --git a/libs/common/src/billing/abstractions/organization-billing.service.ts b/libs/common/src/billing/abstractions/organization-billing.service.ts index 1e68488ac98..69309014fac 100644 --- a/libs/common/src/billing/abstractions/organization-billing.service.ts +++ b/libs/common/src/billing/abstractions/organization-billing.service.ts @@ -4,7 +4,6 @@ import { OrganizationResponse } from "../../admin-console/models/response/organization.response"; import { InitiationPath } from "../../models/request/reference-event.request"; import { PaymentMethodType, PlanType } from "../enums"; -import { BillingSourceResponse } from "../models/response/billing.response"; import { PaymentSourceResponse } from "../models/response/payment-source.response"; export type OrganizationInformation = { @@ -46,9 +45,7 @@ export type SubscriptionInformation = { }; export abstract class OrganizationBillingServiceAbstraction { - getPaymentSource: ( - organizationId: string, - ) => Promise; + getPaymentSource: (organizationId: string) => Promise; purchaseSubscription: (subscription: SubscriptionInformation) => Promise; diff --git a/libs/common/src/billing/services/organization-billing.service.ts b/libs/common/src/billing/services/organization-billing.service.ts index da1a1666ff0..e61b092d677 100644 --- a/libs/common/src/billing/services/organization-billing.service.ts +++ b/libs/common/src/billing/services/organization-billing.service.ts @@ -7,8 +7,6 @@ import { OrganizationApiServiceAbstraction as OrganizationApiService } from "../ import { OrganizationCreateRequest } from "../../admin-console/models/request/organization-create.request"; import { OrganizationKeysRequest } from "../../admin-console/models/request/organization-keys.request"; import { OrganizationResponse } from "../../admin-console/models/response/organization.response"; -import { FeatureFlag } from "../../enums/feature-flag.enum"; -import { ConfigService } from "../../platform/abstractions/config/config.service"; import { EncryptService } from "../../platform/abstractions/encrypt.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { EncString } from "../../platform/models/domain/enc-string"; @@ -24,7 +22,6 @@ import { } from "../abstractions"; import { PlanType } from "../enums"; import { OrganizationNoPaymentMethodCreateRequest } from "../models/request/organization-no-payment-method-create-request"; -import { BillingSourceResponse } from "../models/response/billing.response"; import { PaymentSourceResponse } from "../models/response/payment-source.response"; interface OrganizationKeys { @@ -38,7 +35,6 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs constructor( private apiService: ApiService, private billingApiService: BillingApiServiceAbstraction, - private configService: ConfigService, private keyService: KeyService, private encryptService: EncryptService, private i18nService: I18nService, @@ -46,21 +42,9 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs private syncService: SyncService, ) {} - async getPaymentSource( - organizationId: string, - ): Promise { - const deprecateStripeSourcesAPI = await this.configService.getFeatureFlag( - FeatureFlag.AC2476_DeprecateStripeSourcesAPI, - ); - - if (deprecateStripeSourcesAPI) { - const paymentMethod = - await this.billingApiService.getOrganizationPaymentMethod(organizationId); - return paymentMethod.paymentSource; - } else { - const billing = await this.organizationApiService.getBilling(organizationId); - return billing.paymentSource; - } + async getPaymentSource(organizationId: string): Promise { + const paymentMethod = await this.billingApiService.getOrganizationPaymentMethod(organizationId); + return paymentMethod.paymentSource; } async purchaseSubscription(subscription: SubscriptionInformation): Promise { From f1333b3e1a2aae1468fb4d1cb0b65a51364755d6 Mon Sep 17 00:00:00 2001 From: Alex Morask Date: Thu, 23 Jan 2025 11:14:48 -0500 Subject: [PATCH 05/16] Remove FF from organization-subscription-cloud.component --- ...ganization-subscription-cloud.component.ts | 52 +++++-------------- 1 file changed, 13 insertions(+), 39 deletions(-) diff --git a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts index 7f81a1fe230..6b9dadb0cc3 100644 --- a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts +++ b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts @@ -28,10 +28,6 @@ import { AdjustStorageDialogV2Component, AdjustStorageDialogV2ResultType, } from "../shared/adjust-storage-dialog/adjust-storage-dialog-v2.component"; -import { - AdjustStorageDialogResult, - openAdjustStorageDialog, -} from "../shared/adjust-storage-dialog/adjust-storage-dialog.component"; import { OffboardingSurveyDialogResultType, openOffboardingSurvey, @@ -55,7 +51,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy organizationId: string; userOrg: Organization; showChangePlan = false; - showDownloadLicense = false; hasBillingSyncToken: boolean; showAdjustSecretsManager = false; showSecretsManagerSubscribe = false; @@ -70,10 +65,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy protected readonly subscriptionHiddenIcon = SubscriptionHiddenIcon; protected readonly teamsStarter = ProductTierType.TeamsStarter; - protected deprecateStripeSourcesAPI$ = this.configService.getFeatureFlag$( - FeatureFlag.AC2476_DeprecateStripeSourcesAPI, - ); - private destroy$ = new Subject(); constructor( @@ -426,36 +417,19 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy adjustStorage = (add: boolean) => { return async () => { - const deprecateStripeSourcesAPI = await firstValueFrom(this.deprecateStripeSourcesAPI$); - - if (deprecateStripeSourcesAPI) { - const dialogRef = AdjustStorageDialogV2Component.open(this.dialogService, { - data: { - price: this.storageGbPrice, - cadence: this.billingInterval, - type: add ? "Add" : "Remove", - organizationId: this.organizationId, - }, - }); - - const result = await lastValueFrom(dialogRef.closed); - - if (result === AdjustStorageDialogV2ResultType.Submitted) { - await this.load(); - } - } else { - const dialogRef = openAdjustStorageDialog(this.dialogService, { - data: { - storageGbPrice: this.storageGbPrice, - add: add, - organizationId: this.organizationId, - interval: this.billingInterval, - }, - }); - const result = await lastValueFrom(dialogRef.closed); - if (result === AdjustStorageDialogResult.Adjusted) { - await this.load(); - } + const dialogRef = AdjustStorageDialogV2Component.open(this.dialogService, { + data: { + price: this.storageGbPrice, + cadence: this.billingInterval, + type: add ? "Add" : "Remove", + organizationId: this.organizationId, + }, + }); + + const result = await lastValueFrom(dialogRef.closed); + + if (result === AdjustStorageDialogV2ResultType.Submitted) { + await this.load(); } }; }; From 708f2e7548ef0d35221da4aa75c4190865fed095 Mon Sep 17 00:00:00 2001 From: Alex Morask Date: Thu, 23 Jan 2025 11:16:30 -0500 Subject: [PATCH 06/16] Remove FF from organization-billing-routing.mdoule --- .../organization-billing-routing.module.ts | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/apps/web/src/app/billing/organizations/organization-billing-routing.module.ts b/apps/web/src/app/billing/organizations/organization-billing-routing.module.ts index 3d4c8dd3870..1bfb9fc4912 100644 --- a/apps/web/src/app/billing/organizations/organization-billing-routing.module.ts +++ b/apps/web/src/app/billing/organizations/organization-billing-routing.module.ts @@ -1,14 +1,11 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; -import { featureFlaggedRoute } from "@bitwarden/angular/platform/utils/feature-flagged-route"; import { canAccessBillingTab } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { organizationPermissionsGuard } from "../../admin-console/organizations/guards/org-permissions.guard"; import { organizationIsUnmanaged } from "../../billing/guards/organization-is-unmanaged.guard"; import { WebPlatformUtilsService } from "../../core/web-platform-utils.service"; -import { PaymentMethodComponent } from "../shared"; import { OrgBillingHistoryViewComponent } from "./organization-billing-history-view.component"; import { OrganizationSubscriptionCloudComponent } from "./organization-subscription-cloud.component"; @@ -28,21 +25,17 @@ const routes: Routes = [ : OrganizationSubscriptionCloudComponent, data: { titleId: "subscription" }, }, - ...featureFlaggedRoute({ - defaultComponent: PaymentMethodComponent, - flaggedComponent: OrganizationPaymentMethodComponent, - featureFlag: FeatureFlag.AC2476_DeprecateStripeSourcesAPI, - routeOptions: { - path: "payment-method", - canActivate: [ - organizationPermissionsGuard((org) => org.canEditPaymentMethods), - organizationIsUnmanaged, - ], - data: { - titleId: "paymentMethod", - }, + { + path: "payment-method", + component: OrganizationPaymentMethodComponent, + canActivate: [ + organizationPermissionsGuard((org) => org.canEditPaymentMethods), + organizationIsUnmanaged, + ], + data: { + titleId: "paymentMethod", }, - }), + }, { path: "history", component: OrgBillingHistoryViewComponent, From c495f98039cdecc7f6b85bee4cc51043a061955a Mon Sep 17 00:00:00 2001 From: Alex Morask Date: Thu, 23 Jan 2025 11:19:00 -0500 Subject: [PATCH 07/16] Remove FF from organization-plans.component --- .../organization-plans.component.html | 8 +- .../organization-plans.component.ts | 73 +++++-------------- 2 files changed, 18 insertions(+), 63 deletions(-) diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.html b/apps/web/src/app/billing/organizations/organization-plans.component.html index d37f95e3aa2..a87d79f327c 100644 --- a/apps/web/src/app/billing/organizations/organization-plans.component.html +++ b/apps/web/src/app/billing/organizations/organization-plans.component.html @@ -433,13 +433,7 @@

{{ paymentDesc }}

- - + a?.id)), @@ -580,23 +572,12 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { } protected changedCountry(): void { - if (this.deprecateStripeSourcesAPI) { - this.paymentV2Component.showBankAccount = this.taxInformation?.country === "US"; - if ( - !this.paymentV2Component.showBankAccount && - this.paymentV2Component.selected === PaymentMethodType.BankAccount - ) { - this.paymentV2Component.select(PaymentMethodType.Card); - } - } else { - this.paymentComponent.hideBank = this.taxInformation?.country !== "US"; - if ( - this.paymentComponent.hideBank && - this.paymentComponent.method === PaymentMethodType.BankAccount - ) { - this.paymentComponent.method = PaymentMethodType.Card; - this.paymentComponent.changeMethod(); - } + this.paymentV2Component.showBankAccount = this.taxInformation?.country === "US"; + if ( + !this.paymentV2Component.showBankAccount && + this.paymentV2Component.selected === PaymentMethodType.BankAccount + ) { + this.paymentV2Component.select(PaymentMethodType.Card); } } @@ -751,25 +732,15 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { this.buildSecretsManagerRequest(request); if (this.upgradeRequiresPaymentMethod) { - if (this.deprecateStripeSourcesAPI) { - const updatePaymentMethodRequest = new UpdatePaymentMethodRequest(); - updatePaymentMethodRequest.paymentSource = await this.paymentV2Component.tokenize(); - updatePaymentMethodRequest.taxInformation = ExpandedTaxInfoUpdateRequest.From( - this.taxInformation, - ); - await this.billingApiService.updateOrganizationPaymentMethod( - this.organizationId, - updatePaymentMethodRequest, - ); - } else { - const [paymentToken, paymentMethodType] = await this.paymentComponent.createPaymentToken(); - const paymentRequest = new PaymentRequest(); - paymentRequest.paymentToken = paymentToken; - paymentRequest.paymentMethodType = paymentMethodType; - paymentRequest.country = this.taxInformation?.country; - paymentRequest.postalCode = this.taxInformation?.postalCode; - await this.organizationApiService.updatePayment(this.organizationId, paymentRequest); - } + const updatePaymentMethodRequest = new UpdatePaymentMethodRequest(); + updatePaymentMethodRequest.paymentSource = await this.paymentV2Component.tokenize(); + updatePaymentMethodRequest.taxInformation = ExpandedTaxInfoUpdateRequest.From( + this.taxInformation, + ); + await this.billingApiService.updateOrganizationPaymentMethod( + this.organizationId, + updatePaymentMethodRequest, + ); } // Backfill pub/priv key if necessary @@ -779,10 +750,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { request.keys = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString); } - const result = await this.organizationApiService.upgrade(this.organizationId, request); - if (!result.success && result.paymentIntentClientSecret != null) { - await this.paymentComponent.handleStripeCardPayment(result.paymentIntentClientSecret, null); - } + await this.organizationApiService.upgrade(this.organizationId, request); return this.organizationId; } @@ -803,14 +771,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { if (this.selectedPlan.type === PlanType.Free) { request.planType = PlanType.Free; } else { - let type: PaymentMethodType; - let token: string; - - if (this.deprecateStripeSourcesAPI) { - ({ type, token } = await this.paymentV2Component.tokenize()); - } else { - [token, type] = await this.paymentComponent.createPaymentToken(); - } + const { type, token } = await this.paymentV2Component.tokenize(); request.paymentToken = token; request.paymentMethodType = type; From 069b12469dd4eef958f40e6e918182114c25d2ec Mon Sep 17 00:00:00 2001 From: Alex Morask Date: Thu, 23 Jan 2025 11:24:03 -0500 Subject: [PATCH 08/16] Remove FF from change-plan-dialog.component --- .../change-plan-dialog.component.html | 13 +- .../change-plan-dialog.component.ts | 153 +++++------------- 2 files changed, 43 insertions(+), 123 deletions(-) diff --git a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html index 96679ea1753..71882f17214 100644 --- a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html +++ b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html @@ -346,25 +346,20 @@

{{ "paymentMethod" | i18n }}

" > - {{ - deprecateStripeSourcesAPI - ? paymentSource?.description - : billing?.paymentSource?.description - }} + {{ paymentSource?.description }} {{ "changePaymentMethod" | i18n }}

- - + -
+

{{ "total" | i18n }}: @@ -962,7 +957,7 @@

{{ "paymentMethod" | i18n }}

-
+

; - deprecateStripeSourcesAPI: boolean; isSubscriptionCanceled: boolean = false; private destroy$ = new Subject(); @@ -210,7 +203,6 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { private messagingService: MessagingService, private formBuilder: FormBuilder, private organizationApiService: OrganizationApiServiceAbstraction, - private configService: ConfigService, private billingApiService: BillingApiServiceAbstraction, private taxService: TaxServiceAbstraction, private accountService: AccountService, @@ -218,10 +210,6 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { ) {} async ngOnInit(): Promise { - this.deprecateStripeSourcesAPI = await this.configService.getFeatureFlag( - FeatureFlag.AC2476_DeprecateStripeSourcesAPI, - ); - if (this.dialogParams.organizationId) { this.currentPlanName = this.resolvePlanName(this.dialogParams.productTierType); this.sub = @@ -239,14 +227,10 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { .organizations$(userId) .pipe(getOrganizationById(this.organizationId)), ); - if (this.deprecateStripeSourcesAPI) { - const { accountCredit, paymentSource } = - await this.billingApiService.getOrganizationPaymentMethod(this.organizationId); - this.accountCredit = accountCredit; - this.paymentSource = paymentSource; - } else { - this.billing = await this.organizationApiService.getBilling(this.organizationId); - } + const { accountCredit, paymentSource } = + await this.billingApiService.getOrganizationPaymentMethod(this.organizationId); + this.accountCredit = accountCredit; + this.paymentSource = paymentSource; } if (!this.selfHosted) { @@ -333,16 +317,8 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { return this.selectableProducts.find((product) => product.productTier === productTier); } - secretsManagerTrialDiscount() { - return this.sub?.customerDiscount?.appliesTo?.includes("sm-standalone") - ? this.discountPercentage - : this.discountPercentageFromSub + this.discountPercentage; - } - isPaymentSourceEmpty() { - return this.deprecateStripeSourcesAPI - ? this.paymentSource === null || this.paymentSource === undefined - : this.billing?.paymentSource === null || this.billing?.paymentSource === undefined; + return this.paymentSource === null || this.paymentSource === undefined; } isSecretsManagerTrial(): boolean { @@ -486,9 +462,7 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { get upgradeRequiresPaymentMethod() { const isFreeTier = this.organization?.productTierType === ProductTierType.Free; const shouldHideFree = !this.showFree; - const hasNoPaymentSource = this.deprecateStripeSourcesAPI - ? !this.paymentSource - : !this.billing?.paymentSource; + const hasNoPaymentSource = !this.paymentSource; return isFreeTier && shouldHideFree && hasNoPaymentSource; } @@ -721,25 +695,13 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { } changedCountry() { - if (this.deprecateStripeSourcesAPI && this.paymentV2Component) { - this.paymentV2Component.showBankAccount = this.taxInformation.country === "US"; + this.paymentV2Component.showBankAccount = this.taxInformation.country === "US"; - if ( - !this.paymentV2Component.showBankAccount && - this.paymentV2Component.selected === PaymentMethodType.BankAccount - ) { - this.paymentV2Component.select(PaymentMethodType.Card); - } - } else if (this.paymentComponent && this.taxInformation) { - this.paymentComponent!.hideBank = this.taxInformation.country !== "US"; - // Bank Account payments are only available for US customers - if ( - this.paymentComponent.hideBank && - this.paymentComponent.method === PaymentMethodType.BankAccount - ) { - this.paymentComponent.method = PaymentMethodType.Card; - this.paymentComponent.changeMethod(); - } + if ( + !this.paymentV2Component.showBankAccount && + this.paymentV2Component.selected === PaymentMethodType.BankAccount + ) { + this.paymentV2Component.select(PaymentMethodType.Card); } } @@ -821,14 +783,8 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { plan.secretsManagerSeats = org.smSeats; } - let paymentMethod: [string, PaymentMethodType]; - - if (this.deprecateStripeSourcesAPI) { - const { type, token } = await this.paymentV2Component.tokenize(); - paymentMethod = [token, type]; - } else { - paymentMethod = await this.paymentComponent.createPaymentToken(); - } + const { type, token } = await this.paymentV2Component.tokenize(); + const paymentMethod: [string, PaymentMethodType] = [token, type]; const payment: PaymentInformation = { paymentMethod, @@ -864,27 +820,17 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { this.buildSecretsManagerRequest(request); if (this.upgradeRequiresPaymentMethod || this.showPayment || this.isPaymentSourceEmpty()) { - if (this.deprecateStripeSourcesAPI) { - const tokenizedPaymentSource = await this.paymentV2Component.tokenize(); - const updatePaymentMethodRequest = new UpdatePaymentMethodRequest(); - updatePaymentMethodRequest.paymentSource = tokenizedPaymentSource; - updatePaymentMethodRequest.taxInformation = ExpandedTaxInfoUpdateRequest.From( - this.taxInformation, - ); - - await this.billingApiService.updateOrganizationPaymentMethod( - this.organizationId, - updatePaymentMethodRequest, - ); - } else { - const tokenResult = await this.paymentComponent.createPaymentToken(); - const paymentRequest = new PaymentRequest(); - paymentRequest.paymentToken = tokenResult[0]; - paymentRequest.paymentMethodType = tokenResult[1]; - paymentRequest.country = this.taxInformation.country; - paymentRequest.postalCode = this.taxInformation.postalCode; - await this.organizationApiService.updatePayment(this.organizationId, paymentRequest); - } + const tokenizedPaymentSource = await this.paymentV2Component.tokenize(); + const updatePaymentMethodRequest = new UpdatePaymentMethodRequest(); + updatePaymentMethodRequest.paymentSource = tokenizedPaymentSource; + updatePaymentMethodRequest.taxInformation = ExpandedTaxInfoUpdateRequest.From( + this.taxInformation, + ); + + await this.billingApiService.updateOrganizationPaymentMethod( + this.organizationId, + updatePaymentMethodRequest, + ); } // Backfill pub/priv key if necessary @@ -894,10 +840,7 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { request.keys = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString); } - const result = await this.organizationApiService.upgrade(this.organizationId, request); - if (!result.success && result.paymentIntentClientSecret != null) { - await this.paymentComponent.handleStripeCardPayment(result.paymentIntentClientSecret, null); - } + await this.organizationApiService.upgrade(this.organizationId, request); return this.organizationId; } @@ -994,38 +937,20 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { } get paymentSourceClasses() { - if (this.deprecateStripeSourcesAPI) { - if (this.paymentSource == null) { - return []; - } - switch (this.paymentSource.type) { - case PaymentMethodType.Card: - return ["bwi-credit-card"]; - case PaymentMethodType.BankAccount: - return ["bwi-bank"]; - case PaymentMethodType.Check: - return ["bwi-money"]; - case PaymentMethodType.PayPal: - return ["bwi-paypal text-primary"]; - default: - return []; - } - } else { - if (this.billing.paymentSource == null) { + if (this.paymentSource == null) { + return []; + } + switch (this.paymentSource.type) { + case PaymentMethodType.Card: + return ["bwi-credit-card"]; + case PaymentMethodType.BankAccount: + return ["bwi-bank"]; + case PaymentMethodType.Check: + return ["bwi-money"]; + case PaymentMethodType.PayPal: + return ["bwi-paypal text-primary"]; + default: return []; - } - switch (this.billing.paymentSource.type) { - case PaymentMethodType.Card: - return ["bwi-credit-card"]; - case PaymentMethodType.BankAccount: - return ["bwi-bank"]; - case PaymentMethodType.Check: - return ["bwi-money"]; - case PaymentMethodType.PayPal: - return ["bwi-paypal text-primary"]; - default: - return []; - } } } From 4eb0a361aba575e1832ad82c33f666395ce9e2ce Mon Sep 17 00:00:00 2001 From: Alex Morask Date: Thu, 23 Jan 2025 11:25:31 -0500 Subject: [PATCH 09/16] Remove FF --- libs/common/src/enums/feature-flag.enum.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index b321199d5e5..2d345c0d6da 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -34,7 +34,6 @@ export enum FeatureFlag { UnauthenticatedExtensionUIRefresh = "unauth-ui-refresh", SSHKeyVaultItem = "ssh-key-vault-item", SSHAgent = "ssh-agent", - AC2476_DeprecateStripeSourcesAPI = "AC-2476-deprecate-stripe-sources-api", CipherKeyEncryption = "cipher-key-encryption", PM11901_RefactorSelfHostingLicenseUploader = "PM-11901-refactor-self-hosting-license-uploader", CriticalApps = "pm-14466-risk-insights-critical-application", @@ -91,7 +90,6 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.UnauthenticatedExtensionUIRefresh]: FALSE, [FeatureFlag.SSHKeyVaultItem]: FALSE, [FeatureFlag.SSHAgent]: FALSE, - [FeatureFlag.AC2476_DeprecateStripeSourcesAPI]: FALSE, [FeatureFlag.CipherKeyEncryption]: FALSE, [FeatureFlag.PM11901_RefactorSelfHostingLicenseUploader]: FALSE, [FeatureFlag.CriticalApps]: FALSE, From ebd55310990da0150fd78d3de32e0f3fcd6453c5 Mon Sep 17 00:00:00 2001 From: Alex Morask Date: Thu, 23 Jan 2025 11:37:56 -0500 Subject: [PATCH 10/16] Remove legacy payment.component --- .../trial-initiation.module.ts | 3 +- apps/web/src/app/billing/index.ts | 2 +- .../individual/individual-billing.module.ts | 2 - .../individual/premium/premium.component.html | 144 -------- .../individual/premium/premium.component.ts | 218 ------------ .../organization-plans.component.html | 3 - .../adjust-payment-dialog.component.html | 30 -- .../adjust-payment-dialog.component.ts | 142 -------- .../adjust-storage-dialog.component.html | 35 -- .../adjust-storage-dialog.component.ts | 132 ------- .../billing/shared/billing-shared.module.ts | 8 - apps/web/src/app/billing/shared/index.ts | 1 - .../shared/payment-method.component.ts | 14 +- .../shared/payment/payment.component.html | 115 ------ .../shared/payment/payment.component.ts | 330 ------------------ 15 files changed, 10 insertions(+), 1169 deletions(-) delete mode 100644 apps/web/src/app/billing/individual/premium/premium.component.html delete mode 100644 apps/web/src/app/billing/individual/premium/premium.component.ts delete mode 100644 apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.html delete mode 100644 apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.ts delete mode 100644 apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.html delete mode 100644 apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.ts delete mode 100644 apps/web/src/app/billing/shared/payment/payment.component.html delete mode 100644 apps/web/src/app/billing/shared/payment/payment.component.ts diff --git a/apps/web/src/app/auth/trial-initiation/trial-initiation.module.ts b/apps/web/src/app/auth/trial-initiation/trial-initiation.module.ts index 464c00c4a3a..d49621222f6 100644 --- a/apps/web/src/app/auth/trial-initiation/trial-initiation.module.ts +++ b/apps/web/src/app/auth/trial-initiation/trial-initiation.module.ts @@ -10,7 +10,7 @@ import { RegisterFormModule } from "../../auth/register-form/register-form.modul import { SecretsManagerTrialFreeStepperComponent } from "../../auth/trial-initiation/secrets-manager/secrets-manager-trial-free-stepper.component"; import { SecretsManagerTrialPaidStepperComponent } from "../../auth/trial-initiation/secrets-manager/secrets-manager-trial-paid-stepper.component"; import { SecretsManagerTrialComponent } from "../../auth/trial-initiation/secrets-manager/secrets-manager-trial.component"; -import { PaymentComponent, TaxInfoComponent } from "../../billing"; +import { TaxInfoComponent } from "../../billing"; import { TrialBillingStepComponent } from "../../billing/accounts/trial-initiation/trial-billing-step.component"; import { EnvironmentSelectorModule } from "../../components/environment-selector/environment-selector.module"; import { SharedModule } from "../../shared"; @@ -51,7 +51,6 @@ import { VerticalStepperModule } from "./vertical-stepper/vertical-stepper.modul RegisterFormModule, OrganizationCreateModule, EnvironmentSelectorModule, - PaymentComponent, TaxInfoComponent, TrialBillingStepComponent, InputPasswordComponent, diff --git a/apps/web/src/app/billing/index.ts b/apps/web/src/app/billing/index.ts index b59ab33e54c..217f1e05be9 100644 --- a/apps/web/src/app/billing/index.ts +++ b/apps/web/src/app/billing/index.ts @@ -1,2 +1,2 @@ export { OrganizationPlansComponent } from "./organizations"; -export { PaymentComponent, TaxInfoComponent } from "./shared"; +export { TaxInfoComponent } from "./shared"; diff --git a/apps/web/src/app/billing/individual/individual-billing.module.ts b/apps/web/src/app/billing/individual/individual-billing.module.ts index 0dbbc8c6837..2caf24d6db2 100644 --- a/apps/web/src/app/billing/individual/individual-billing.module.ts +++ b/apps/web/src/app/billing/individual/individual-billing.module.ts @@ -6,7 +6,6 @@ import { BillingSharedModule } from "../shared"; import { BillingHistoryViewComponent } from "./billing-history-view.component"; import { IndividualBillingRoutingModule } from "./individual-billing-routing.module"; import { PremiumV2Component } from "./premium/premium-v2.component"; -import { PremiumComponent } from "./premium/premium.component"; import { SubscriptionComponent } from "./subscription.component"; import { UserSubscriptionComponent } from "./user-subscription.component"; @@ -16,7 +15,6 @@ import { UserSubscriptionComponent } from "./user-subscription.component"; SubscriptionComponent, BillingHistoryViewComponent, UserSubscriptionComponent, - PremiumComponent, PremiumV2Component, ], }) diff --git a/apps/web/src/app/billing/individual/premium/premium.component.html b/apps/web/src/app/billing/individual/premium/premium.component.html deleted file mode 100644 index 12b6932d0f5..00000000000 --- a/apps/web/src/app/billing/individual/premium/premium.component.html +++ /dev/null @@ -1,144 +0,0 @@ - -

{{ "goPremium" | i18n }}

- - {{ "alreadyPremiumFromOrg" | i18n }} - - -

{{ "premiumUpgradeUnlockFeatures" | i18n }}

-
    -
  • - - {{ "premiumSignUpStorage" | i18n }} -
  • -
  • - - {{ "premiumSignUpTwoStepOptions" | i18n }} -
  • -
  • - - {{ "premiumSignUpEmergency" | i18n }} -
  • -
  • - - {{ "premiumSignUpReports" | i18n }} -
  • -
  • - - {{ "premiumSignUpTotp" | i18n }} -
  • -
  • - - {{ "premiumSignUpSupport" | i18n }} -
  • -
  • - - {{ "premiumSignUpFuture" | i18n }} -
  • -
-

- {{ - "premiumPriceWithFamilyPlan" | i18n: (premiumPrice | currency: "$") : familyPlanMaxUserCount - }} - {{ "bitwardenFamiliesPlan" | i18n }} -

- - {{ "purchasePremium" | i18n }} - -
- - -

{{ "uploadLicenseFilePremium" | i18n }}

-
- - {{ "licenseFile" | i18n }} -
- - {{ this.licenseFile ? this.licenseFile.name : ("noFileChosen" | i18n) }} -
- - {{ "licenseFileDesc" | i18n: "bitwarden_premium_license.json" }} -
- -
-
-
- -

{{ "addons" | i18n }}

-
- - {{ "additionalStorageGb" | i18n }} - - {{ - "additionalStorageIntervalDesc" - | i18n: "1 GB" : (storageGbPrice | currency: "$") : ("year" | i18n) - }} - -
-
- -

{{ "summary" | i18n }}

- {{ "premiumMembership" | i18n }}: {{ premiumPrice | currency: "$" }}
- {{ "additionalStorageGb" | i18n }}: {{ additionalStorage || 0 }} GB × - {{ storageGbPrice | currency: "$" }} = - {{ additionalStorageTotal | currency: "$" }} -
-
- -

{{ "paymentInformation" | i18n }}

- - -
-
- {{ "planPrice" | i18n }}: {{ subtotal | currency: "USD $" }} -
- - {{ "estimatedTax" | i18n }}: {{ taxCharges | currency: "USD $" }} - -
-
-

- {{ "total" | i18n }}: {{ total | currency: "USD $" }}/{{ "year" | i18n }} -

-
-

{{ "paymentChargedAnnually" | i18n }}

- -
-
diff --git a/apps/web/src/app/billing/individual/premium/premium.component.ts b/apps/web/src/app/billing/individual/premium/premium.component.ts deleted file mode 100644 index f96f573cd4d..00000000000 --- a/apps/web/src/app/billing/individual/premium/premium.component.ts +++ /dev/null @@ -1,218 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, OnInit, ViewChild } from "@angular/core"; -import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; -import { FormControl, FormGroup, Validators } from "@angular/forms"; -import { Router } from "@angular/router"; -import { firstValueFrom, Observable, switchMap } from "rxjs"; -import { debounceTime } from "rxjs/operators"; - -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; -import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; -import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; -import { PreviewIndividualInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-individual-invoice.request"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; -import { ToastService } from "@bitwarden/components"; - -import { PaymentComponent, TaxInfoComponent } from "../../shared"; - -@Component({ - templateUrl: "premium.component.html", -}) -export class PremiumComponent implements OnInit { - @ViewChild(PaymentComponent) paymentComponent: PaymentComponent; - @ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent; - - canAccessPremium$: Observable; - selfHosted = false; - premiumPrice = 10; - familyPlanMaxUserCount = 6; - storageGbPrice = 4; - cloudWebVaultUrl: string; - licenseFile: File = null; - - formPromise: Promise; - protected licenseForm = new FormGroup({ - file: new FormControl(null, [Validators.required]), - }); - protected addonForm = new FormGroup({ - additionalStorage: new FormControl(0, [Validators.max(99), Validators.min(0)]), - }); - - private estimatedTax: number = 0; - - constructor( - private apiService: ApiService, - private i18nService: I18nService, - private platformUtilsService: PlatformUtilsService, - private tokenService: TokenService, - private router: Router, - private messagingService: MessagingService, - private syncService: SyncService, - private environmentService: EnvironmentService, - private billingAccountProfileStateService: BillingAccountProfileStateService, - private toastService: ToastService, - private taxService: TaxServiceAbstraction, - private accountService: AccountService, - ) { - this.selfHosted = platformUtilsService.isSelfHost(); - this.canAccessPremium$ = this.accountService.activeAccount$.pipe( - switchMap((account) => - this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id), - ), - ); - - this.addonForm.controls.additionalStorage.valueChanges - .pipe(debounceTime(1000), takeUntilDestroyed()) - .subscribe(() => { - this.refreshSalesTax(); - }); - } - protected setSelectedFile(event: Event) { - const fileInputEl = event.target; - const file: File = fileInputEl.files.length > 0 ? fileInputEl.files[0] : null; - this.licenseFile = file; - } - async ngOnInit() { - this.cloudWebVaultUrl = await firstValueFrom(this.environmentService.cloudWebVaultUrl$); - const account = await firstValueFrom(this.accountService.activeAccount$); - if ( - await firstValueFrom(this.billingAccountProfileStateService.hasPremiumPersonally$(account.id)) - ) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["/settings/subscription/user-subscription"]); - return; - } - } - submit = async () => { - if (this.taxInfoComponent) { - if (!this.taxInfoComponent?.taxFormGroup.valid) { - this.taxInfoComponent.taxFormGroup.markAllAsTouched(); - return; - } - } - this.licenseForm.markAllAsTouched(); - this.addonForm.markAllAsTouched(); - if (this.selfHosted) { - if (this.licenseFile == null) { - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: this.i18nService.t("selectFile"), - }); - return; - } - } - - if (this.selfHosted) { - // eslint-disable-next-line @typescript-eslint/no-misused-promises - if (!this.tokenService.getEmailVerified()) { - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: this.i18nService.t("verifyEmailFirst"), - }); - return; - } - - const fd = new FormData(); - fd.append("license", this.licenseFile); - await this.apiService.postAccountLicense(fd).then(() => { - return this.finalizePremium(); - }); - } else { - await this.paymentComponent - .createPaymentToken() - .then((result) => { - const fd = new FormData(); - fd.append("paymentMethodType", result[1].toString()); - if (result[0] != null) { - fd.append("paymentToken", result[0]); - } - fd.append("additionalStorageGb", (this.additionalStorage || 0).toString()); - fd.append("country", this.taxInfoComponent?.taxFormGroup?.value.country); - fd.append("postalCode", this.taxInfoComponent?.taxFormGroup?.value.postalCode); - return this.apiService.postPremium(fd); - }) - .then((paymentResponse) => { - if (!paymentResponse.success && paymentResponse.paymentIntentClientSecret != null) { - return this.paymentComponent.handleStripeCardPayment( - paymentResponse.paymentIntentClientSecret, - () => this.finalizePremium(), - ); - } else { - return this.finalizePremium(); - } - }); - } - }; - - async finalizePremium() { - await this.apiService.refreshIdentityToken(); - await this.syncService.fullSync(true); - this.toastService.showToast({ - variant: "success", - title: null, - message: this.i18nService.t("premiumUpdated"), - }); - await this.router.navigate(["/settings/subscription/user-subscription"]); - } - - get additionalStorage(): number { - return this.addonForm.get("additionalStorage").value; - } - get additionalStorageTotal(): number { - return this.storageGbPrice * Math.abs(this.additionalStorage || 0); - } - - get subtotal(): number { - return this.premiumPrice + this.additionalStorageTotal; - } - - get taxCharges(): number { - return this.estimatedTax; - } - - get total(): number { - return this.subtotal + this.taxCharges || 0; - } - - private refreshSalesTax(): void { - if (!this.taxInfoComponent.country || !this.taxInfoComponent.postalCode) { - return; - } - const request: PreviewIndividualInvoiceRequest = { - passwordManager: { - additionalStorage: this.addonForm.value.additionalStorage, - }, - taxInformation: { - postalCode: this.taxInfoComponent.postalCode, - country: this.taxInfoComponent.country, - }, - }; - - this.taxService - .previewIndividualInvoice(request) - .then((invoice) => { - this.estimatedTax = invoice.taxAmount; - }) - .catch((error) => { - this.toastService.showToast({ - title: "", - variant: "error", - message: this.i18nService.t(error.message), - }); - }); - } - - protected onTaxInformationChanged(): void { - this.refreshSalesTax(); - } -} diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.html b/apps/web/src/app/billing/organizations/organization-plans.component.html index a87d79f327c..61849ddff39 100644 --- a/apps/web/src/app/billing/organizations/organization-plans.component.html +++ b/apps/web/src/app/billing/organizations/organization-plans.component.html @@ -459,9 +459,6 @@

}}

- - - diff --git a/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.html b/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.html deleted file mode 100644 index de607314354..00000000000 --- a/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.html +++ /dev/null @@ -1,30 +0,0 @@ -
- - - - - - - - - - -
diff --git a/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.ts b/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.ts deleted file mode 100644 index bbae5099afa..00000000000 --- a/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.ts +++ /dev/null @@ -1,142 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog"; -import { Component, Inject, OnInit, ViewChild } from "@angular/core"; -import { FormGroup } from "@angular/forms"; - -import { ManageTaxInformationComponent } from "@bitwarden/angular/billing/components"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; -import { PaymentMethodType } from "@bitwarden/common/billing/enums"; -import { TaxInformation } from "@bitwarden/common/billing/models/domain"; -import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request"; -import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { DialogService, ToastService } from "@bitwarden/components"; - -import { PaymentComponent } from "../payment/payment.component"; - -export interface AdjustPaymentDialogData { - organizationId: string; - currentType: PaymentMethodType; -} - -export enum AdjustPaymentDialogResult { - Adjusted = "adjusted", - Cancelled = "cancelled", -} - -@Component({ - templateUrl: "adjust-payment-dialog.component.html", -}) -export class AdjustPaymentDialogComponent implements OnInit { - @ViewChild(PaymentComponent, { static: true }) paymentComponent: PaymentComponent; - @ViewChild(ManageTaxInformationComponent) taxInfoComponent: ManageTaxInformationComponent; - - organizationId: string; - currentType: PaymentMethodType; - paymentMethodType = PaymentMethodType; - - protected DialogResult = AdjustPaymentDialogResult; - protected formGroup = new FormGroup({}); - - protected taxInformation: TaxInformation; - - constructor( - private dialogRef: DialogRef, - @Inject(DIALOG_DATA) protected data: AdjustPaymentDialogData, - private apiService: ApiService, - private i18nService: I18nService, - private organizationApiService: OrganizationApiServiceAbstraction, - private configService: ConfigService, - private toastService: ToastService, - ) { - this.organizationId = data.organizationId; - this.currentType = data.currentType; - } - - ngOnInit(): void { - if (this.organizationId) { - this.organizationApiService - .getTaxInfo(this.organizationId) - .then((response: TaxInfoResponse) => { - this.taxInformation = TaxInformation.from(response); - }) - .catch(() => { - this.taxInformation = new TaxInformation(); - }); - } else { - this.apiService - .getTaxInfo() - .then((response: TaxInfoResponse) => { - this.taxInformation = TaxInformation.from(response); - }) - .catch(() => { - this.taxInformation = new TaxInformation(); - }); - } - } - - submit = async () => { - if (!this.taxInfoComponent?.validate()) { - return; - } - - const request = new PaymentRequest(); - const response = this.paymentComponent.createPaymentToken().then((result) => { - request.paymentToken = result[0]; - request.paymentMethodType = result[1]; - request.postalCode = this.taxInformation?.postalCode; - request.country = this.taxInformation?.country; - request.taxId = this.taxInformation?.taxId; - if (this.organizationId == null) { - return this.apiService.postAccountPayment(request); - } else { - request.taxId = this.taxInformation?.taxId; - request.state = this.taxInformation?.state; - request.line1 = this.taxInformation?.line1; - request.line2 = this.taxInformation?.line2; - request.city = this.taxInformation?.city; - request.state = this.taxInformation?.state; - return this.organizationApiService.updatePayment(this.organizationId, request); - } - }); - await response; - this.toastService.showToast({ - variant: "success", - title: null, - message: this.i18nService.t("updatedPaymentMethod"), - }); - this.dialogRef.close(AdjustPaymentDialogResult.Adjusted); - }; - - taxInformationChanged(event: TaxInformation) { - this.taxInformation = event; - if (event.country === "US") { - this.paymentComponent.hideBank = !this.organizationId; - } else { - this.paymentComponent.hideBank = true; - if (this.paymentComponent.method === PaymentMethodType.BankAccount) { - this.paymentComponent.method = PaymentMethodType.Card; - this.paymentComponent.changeMethod(); - } - } - } - - protected get showTaxIdField(): boolean { - return !!this.organizationId; - } -} - -/** - * Strongly typed helper to open a AdjustPaymentDialog - * @param dialogService Instance of the dialog service that will be used to open the dialog - * @param config Configuration for the dialog - */ -export function openAdjustPaymentDialog( - dialogService: DialogService, - config: DialogConfig, -) { - return dialogService.open(AdjustPaymentDialogComponent, config); -} diff --git a/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.html b/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.html deleted file mode 100644 index a597a3ae5ea..00000000000 --- a/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.html +++ /dev/null @@ -1,35 +0,0 @@ -
- - -

{{ (add ? "storageAddNote" : "storageRemoveNote") | i18n }}

-
- - {{ (add ? "gbStorageAdd" : "gbStorageRemove") | i18n }} - - - {{ "total" | i18n }}: - {{ formGroup.get("storageAdjustment").value || 0 }} GB × - {{ storageGbPrice | currency: "$" }} = {{ adjustedStorageTotal | currency: "$" }} /{{ - interval | i18n - }} - - -
-
- - - - -
-
- diff --git a/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.ts b/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.ts deleted file mode 100644 index f69f9e3eaad..00000000000 --- a/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.ts +++ /dev/null @@ -1,132 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog"; -import { Component, Inject, ViewChild } from "@angular/core"; -import { FormControl, FormGroup, Validators } from "@angular/forms"; -import { ActivatedRoute, Router } from "@angular/router"; - -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; -import { PaymentResponse } from "@bitwarden/common/billing/models/response/payment.response"; -import { StorageRequest } from "@bitwarden/common/models/request/storage.request"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { DialogService, ToastService } from "@bitwarden/components"; - -import { PaymentComponent } from "../payment/payment.component"; - -export interface AdjustStorageDialogData { - storageGbPrice: number; - add: boolean; - organizationId?: string; - interval?: string; -} - -export enum AdjustStorageDialogResult { - Adjusted = "adjusted", - Cancelled = "cancelled", -} - -@Component({ - templateUrl: "adjust-storage-dialog.component.html", -}) -export class AdjustStorageDialogComponent { - storageGbPrice: number; - add: boolean; - organizationId: string; - interval: string; - - @ViewChild(PaymentComponent, { static: true }) paymentComponent: PaymentComponent; - - protected DialogResult = AdjustStorageDialogResult; - protected formGroup = new FormGroup({ - storageAdjustment: new FormControl(0, [ - Validators.required, - Validators.min(0), - Validators.max(99), - ]), - }); - - constructor( - private dialogRef: DialogRef, - @Inject(DIALOG_DATA) protected data: AdjustStorageDialogData, - private apiService: ApiService, - private i18nService: I18nService, - private platformUtilsService: PlatformUtilsService, - private router: Router, - private activatedRoute: ActivatedRoute, - private logService: LogService, - private organizationApiService: OrganizationApiServiceAbstraction, - private toastService: ToastService, - ) { - this.storageGbPrice = data.storageGbPrice; - this.add = data.add; - this.organizationId = data.organizationId; - this.interval = data.interval || "year"; - } - - submit = async () => { - const request = new StorageRequest(); - request.storageGbAdjustment = this.formGroup.value.storageAdjustment; - if (!this.add) { - request.storageGbAdjustment *= -1; - } - - let paymentFailed = false; - const action = async () => { - let response: Promise; - if (this.organizationId == null) { - response = this.apiService.postAccountStorage(request); - } else { - response = this.organizationApiService.updateStorage(this.organizationId, request); - } - const result = await response; - if (result != null && result.paymentIntentClientSecret != null) { - try { - await this.paymentComponent.handleStripeCardPayment( - result.paymentIntentClientSecret, - null, - ); - } catch { - paymentFailed = true; - } - } - }; - await action(); - this.dialogRef.close(AdjustStorageDialogResult.Adjusted); - if (paymentFailed) { - this.toastService.showToast({ - variant: "warning", - title: null, - message: this.i18nService.t("couldNotChargeCardPayInvoice"), - timeout: 10000, - }); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["../billing"], { relativeTo: this.activatedRoute }); - } else { - this.toastService.showToast({ - variant: "success", - title: null, - message: this.i18nService.t("adjustedStorage", request.storageGbAdjustment.toString()), - }); - } - }; - - get adjustedStorageTotal(): number { - return this.storageGbPrice * this.formGroup.value.storageAdjustment; - } -} - -/** - * Strongly typed helper to open an AdjustStorageDialog - * @param dialogService Instance of the dialog service that will be used to open the dialog - * @param config Configuration for the dialog - */ -export function openAdjustStorageDialog( - dialogService: DialogService, - config: DialogConfig, -) { - return dialogService.open(AdjustStorageDialogComponent, config); -} diff --git a/apps/web/src/app/billing/shared/billing-shared.module.ts b/apps/web/src/app/billing/shared/billing-shared.module.ts index b9c235943ad..33828385f17 100644 --- a/apps/web/src/app/billing/shared/billing-shared.module.ts +++ b/apps/web/src/app/billing/shared/billing-shared.module.ts @@ -7,13 +7,10 @@ import { SharedModule } from "../../shared"; import { AddCreditDialogComponent } from "./add-credit-dialog.component"; import { AdjustPaymentDialogV2Component } from "./adjust-payment-dialog/adjust-payment-dialog-v2.component"; -import { AdjustPaymentDialogComponent } from "./adjust-payment-dialog/adjust-payment-dialog.component"; import { AdjustStorageDialogV2Component } from "./adjust-storage-dialog/adjust-storage-dialog-v2.component"; -import { AdjustStorageDialogComponent } from "./adjust-storage-dialog/adjust-storage-dialog.component"; import { BillingHistoryComponent } from "./billing-history.component"; import { OffboardingSurveyComponent } from "./offboarding-survey.component"; import { PaymentV2Component } from "./payment/payment-v2.component"; -import { PaymentComponent } from "./payment/payment.component"; import { PaymentMethodComponent } from "./payment-method.component"; import { IndividualSelfHostingLicenseUploaderComponent } from "./self-hosting-license-uploader/individual-self-hosting-license-uploader.component"; import { OrganizationSelfHostingLicenseUploaderComponent } from "./self-hosting-license-uploader/organization-self-hosting-license-uploader.component"; @@ -26,7 +23,6 @@ import { VerifyBankAccountComponent } from "./verify-bank-account/verify-bank-ac @NgModule({ imports: [ SharedModule, - PaymentComponent, TaxInfoComponent, HeaderModule, BannerModule, @@ -35,8 +31,6 @@ import { VerifyBankAccountComponent } from "./verify-bank-account/verify-bank-ac ], declarations: [ AddCreditDialogComponent, - AdjustPaymentDialogComponent, - AdjustStorageDialogComponent, BillingHistoryComponent, PaymentMethodComponent, SecretsManagerSubscribeComponent, @@ -50,9 +44,7 @@ import { VerifyBankAccountComponent } from "./verify-bank-account/verify-bank-ac ], exports: [ SharedModule, - PaymentComponent, TaxInfoComponent, - AdjustStorageDialogComponent, BillingHistoryComponent, SecretsManagerSubscribeComponent, UpdateLicenseComponent, diff --git a/apps/web/src/app/billing/shared/index.ts b/apps/web/src/app/billing/shared/index.ts index 69a4b93bec8..54ab5bc0a2a 100644 --- a/apps/web/src/app/billing/shared/index.ts +++ b/apps/web/src/app/billing/shared/index.ts @@ -1,5 +1,4 @@ export * from "./billing-shared.module"; export * from "./payment-method.component"; -export * from "./payment/payment.component"; export * from "./sm-subscribe.component"; export * from "./tax-info.component"; diff --git a/apps/web/src/app/billing/shared/payment-method.component.ts b/apps/web/src/app/billing/shared/payment-method.component.ts index 4a53e503e4e..7e738e03cbe 100644 --- a/apps/web/src/app/billing/shared/payment-method.component.ts +++ b/apps/web/src/app/billing/shared/payment-method.component.ts @@ -29,9 +29,9 @@ import { TrialFlowService } from "../services/trial-flow.service"; import { AddCreditDialogResult, openAddCreditDialog } from "./add-credit-dialog.component"; import { - AdjustPaymentDialogResult, - openAdjustPaymentDialog, -} from "./adjust-payment-dialog/adjust-payment-dialog.component"; + AdjustPaymentDialogV2Component, + AdjustPaymentDialogV2ResultType, +} from "./adjust-payment-dialog/adjust-payment-dialog-v2.component"; @Component({ templateUrl: "payment-method.component.html", @@ -170,14 +170,16 @@ export class PaymentMethodComponent implements OnInit, OnDestroy { }; changePayment = async () => { - const dialogRef = openAdjustPaymentDialog(this.dialogService, { + const dialogRef = AdjustPaymentDialogV2Component.open(this.dialogService, { data: { organizationId: this.organizationId, - currentType: this.paymentSource !== null ? this.paymentSource.type : null, + initialPaymentMethod: this.paymentSource !== null ? this.paymentSource.type : null, }, }); + const result = await lastValueFrom(dialogRef.closed); - if (result === AdjustPaymentDialogResult.Adjusted) { + + if (result === AdjustPaymentDialogV2ResultType.Submitted) { this.location.replaceState(this.location.path(), "", {}); if (this.launchPaymentModalAutomatically && !this.organization.enabled) { await this.syncService.fullSync(true); diff --git a/apps/web/src/app/billing/shared/payment/payment.component.html b/apps/web/src/app/billing/shared/payment/payment.component.html deleted file mode 100644 index d4853713579..00000000000 --- a/apps/web/src/app/billing/shared/payment/payment.component.html +++ /dev/null @@ -1,115 +0,0 @@ -
-
- - - - - {{ "creditCard" | i18n }} - - - - - {{ "bankAccount" | i18n }} - - - PayPal - - - - - {{ "accountCredit" | i18n }} - - -
- -
-
- {{ - "number" | i18n - }} -
-
-
- Visa, MasterCard, Discover, AmEx, JCB, Diners Club, UnionPay -
-
- {{ - "expiration" | i18n - }} -
-
-
- - {{ "securityCodeSlashCVV" | i18n }} - - - - -
-
-
-
- - - {{ "verifyBankAccountInitialDesc" | i18n }} {{ "verifyBankAccountFailureWarning" | i18n }} - -
- - {{ "routingNumber" | i18n }} - - - - {{ "accountNumber" | i18n }} - - - - {{ "accountHolderName" | i18n }} - - - - - {{ "bankAccountType" | i18n }} - - - - - - -
-
- -
-
- {{ "paypalClickSubmit" | i18n }} -
-
- - - {{ "makeSureEnoughCredit" | i18n }} - - -
diff --git a/apps/web/src/app/billing/shared/payment/payment.component.ts b/apps/web/src/app/billing/shared/payment/payment.component.ts deleted file mode 100644 index e067a5ee490..00000000000 --- a/apps/web/src/app/billing/shared/payment/payment.component.ts +++ /dev/null @@ -1,330 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, Input, OnDestroy, OnInit } from "@angular/core"; -import { FormControl, FormGroup, Validators } from "@angular/forms"; -import { Subject, takeUntil } from "rxjs"; - -import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { PaymentMethodType } from "@bitwarden/common/billing/enums"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; - -import { SharedModule } from "../../../shared"; - -import { PaymentLabelV2 } from "./payment-label-v2.component"; - -@Component({ - selector: "app-payment", - templateUrl: "payment.component.html", - standalone: true, - imports: [SharedModule, PaymentLabelV2], -}) -export class PaymentComponent implements OnInit, OnDestroy { - @Input() showMethods = true; - @Input() showOptions = true; - @Input() hideBank = false; - @Input() hidePaypal = false; - @Input() hideCredit = false; - @Input() trialFlow = false; - - @Input() - set method(value: PaymentMethodType) { - this._method = value; - this.paymentForm?.controls.method.setValue(value, { emitEvent: false }); - } - - get method(): PaymentMethodType { - return this._method; - } - private _method: PaymentMethodType = PaymentMethodType.Card; - - private destroy$ = new Subject(); - protected paymentForm = new FormGroup({ - method: new FormControl(this.method), - bank: new FormGroup({ - routing_number: new FormControl(null, [Validators.required]), - account_number: new FormControl(null, [Validators.required]), - account_holder_name: new FormControl(null, [Validators.required]), - account_holder_type: new FormControl("", [Validators.required]), - currency: new FormControl("USD"), - country: new FormControl("US"), - }), - }); - paymentMethodType = PaymentMethodType; - - private btScript: HTMLScriptElement; - private btInstance: any = null; - private stripeScript: HTMLScriptElement; - private stripe: any = null; - private stripeElements: any = null; - private stripeCardNumberElement: any = null; - private stripeCardExpiryElement: any = null; - private stripeCardCvcElement: any = null; - private StripeElementStyle: any; - private StripeElementClasses: any; - - constructor( - private apiService: ApiService, - private logService: LogService, - private themingService: AbstractThemingService, - private configService: ConfigService, - ) { - this.stripeScript = window.document.createElement("script"); - this.stripeScript.src = "https://js.stripe.com/v3/?advancedFraudSignals=false"; - this.stripeScript.async = true; - this.stripeScript.onload = async () => { - this.stripe = (window as any).Stripe(process.env.STRIPE_KEY); - this.stripeElements = this.stripe.elements(); - await this.setStripeElement(); - }; - this.btScript = window.document.createElement("script"); - this.btScript.src = `scripts/dropin.js?cache=${process.env.CACHE_TAG}`; - this.btScript.async = true; - this.StripeElementStyle = { - base: { - color: null, - fontFamily: - '"DM Sans", "Helvetica Neue", Helvetica, Arial, sans-serif, ' + - '"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', - fontSize: "16px", - fontSmoothing: "antialiased", - "::placeholder": { - color: null, - }, - }, - invalid: { - color: null, - }, - }; - this.StripeElementClasses = { - focus: "is-focused", - empty: "is-empty", - invalid: "is-invalid", - }; - } - async ngOnInit() { - if (!this.showOptions) { - this.hidePaypal = this.method !== PaymentMethodType.PayPal; - this.hideBank = this.method !== PaymentMethodType.BankAccount; - this.hideCredit = this.method !== PaymentMethodType.Credit; - } - this.subscribeToTheme(); - window.document.head.appendChild(this.stripeScript); - if (!this.hidePaypal) { - window.document.head.appendChild(this.btScript); - } - this.paymentForm - .get("method") - .valueChanges.pipe(takeUntil(this.destroy$)) - .subscribe((v) => { - this.method = v; - this.changeMethod(); - }); - } - - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - window.document.head.removeChild(this.stripeScript); - window.setTimeout(() => { - Array.from(window.document.querySelectorAll("iframe")).forEach((el) => { - if (el.src != null && el.src.indexOf("stripe") > -1) { - try { - window.document.body.removeChild(el); - } catch (e) { - this.logService.error(e); - } - } - }); - }, 500); - if (!this.hidePaypal) { - window.document.head.removeChild(this.btScript); - window.setTimeout(() => { - Array.from(window.document.head.querySelectorAll("script")).forEach((el) => { - if (el.src != null && el.src.indexOf("paypal") > -1) { - try { - window.document.head.removeChild(el); - } catch (e) { - this.logService.error(e); - } - } - }); - const btStylesheet = window.document.head.querySelector("#braintree-dropin-stylesheet"); - if (btStylesheet != null) { - try { - window.document.head.removeChild(btStylesheet); - } catch (e) { - this.logService.error(e); - } - } - }, 500); - } - } - - changeMethod() { - this.btInstance = null; - if (this.method === PaymentMethodType.PayPal) { - window.setTimeout(() => { - (window as any).braintree.dropin.create( - { - authorization: process.env.BRAINTREE_KEY, - container: "#bt-dropin-container", - paymentOptionPriority: ["paypal"], - paypal: { - flow: "vault", - buttonStyle: { - label: "pay", - size: "medium", - shape: "pill", - color: "blue", - tagline: "false", - }, - }, - }, - (createErr: any, instance: any) => { - if (createErr != null) { - // eslint-disable-next-line - console.error(createErr); - return; - } - this.btInstance = instance; - }, - ); - }, 250); - } else { - void this.setStripeElement(); - } - } - - createPaymentToken(): Promise<[string, PaymentMethodType]> { - return new Promise((resolve, reject) => { - if (this.method === PaymentMethodType.Credit) { - resolve([null, this.method]); - } else if (this.method === PaymentMethodType.PayPal) { - this.btInstance - .requestPaymentMethod() - .then((payload: any) => { - resolve([payload.nonce, this.method]); - }) - .catch((err: any) => { - reject(err.message); - }); - } else if ( - this.method === PaymentMethodType.Card || - this.method === PaymentMethodType.BankAccount - ) { - if (this.method === PaymentMethodType.Card) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.apiService - .postSetupPayment() - .then((clientSecret) => - this.stripe.handleCardSetup(clientSecret, this.stripeCardNumberElement), - ) - .then((result: any) => { - if (result.error) { - reject(result.error.message); - } else if (result.setupIntent && result.setupIntent.status === "succeeded") { - resolve([result.setupIntent.payment_method, this.method]); - } else { - reject(); - } - }); - } else { - this.stripe - .createToken("bank_account", this.paymentForm.get("bank").value) - .then((result: any) => { - if (result.error) { - reject(result.error.message); - } else if (result.token && result.token.id != null) { - resolve([result.token.id, this.method]); - } else { - reject(); - } - }); - } - } - }); - } - - handleStripeCardPayment(clientSecret: string, successCallback: () => Promise): Promise { - return new Promise((resolve, reject) => { - if (this.showMethods && this.stripeCardNumberElement == null) { - reject(); - return; - } - const handleCardPayment = () => - this.showMethods - ? this.stripe.handleCardSetup(clientSecret, this.stripeCardNumberElement) - : this.stripe.handleCardSetup(clientSecret); - return handleCardPayment().then(async (result: any) => { - if (result.error) { - reject(result.error.message); - } else if (result.paymentIntent && result.paymentIntent.status === "succeeded") { - if (successCallback != null) { - await successCallback(); - } - resolve(); - } else { - reject(); - } - }); - }); - } - - private async setStripeElement() { - const extensionRefreshFlag = await this.configService.getFeatureFlag( - FeatureFlag.ExtensionRefresh, - ); - - // Apply unique styles for extension refresh - if (extensionRefreshFlag) { - this.StripeElementStyle.base.fontWeight = "500"; - this.StripeElementClasses.base = "v2"; - } - - window.setTimeout(() => { - if (this.showMethods && this.method === PaymentMethodType.Card) { - if (this.stripeCardNumberElement == null) { - this.stripeCardNumberElement = this.stripeElements.create("cardNumber", { - style: this.StripeElementStyle, - classes: this.StripeElementClasses, - placeholder: "", - }); - } - if (this.stripeCardExpiryElement == null) { - this.stripeCardExpiryElement = this.stripeElements.create("cardExpiry", { - style: this.StripeElementStyle, - classes: this.StripeElementClasses, - }); - } - if (this.stripeCardCvcElement == null) { - this.stripeCardCvcElement = this.stripeElements.create("cardCvc", { - style: this.StripeElementStyle, - classes: this.StripeElementClasses, - placeholder: "", - }); - } - this.stripeCardNumberElement.mount("#stripe-card-number-element"); - this.stripeCardExpiryElement.mount("#stripe-card-expiry-element"); - this.stripeCardCvcElement.mount("#stripe-card-cvc-element"); - } - }, 50); - } - - private subscribeToTheme() { - this.themingService.theme$.pipe(takeUntil(this.destroy$)).subscribe(() => { - const style = getComputedStyle(document.documentElement); - this.StripeElementStyle.base.color = `rgb(${style.getPropertyValue("--color-text-main")})`; - this.StripeElementStyle.base["::placeholder"].color = `rgb(${style.getPropertyValue( - "--color-text-muted", - )})`; - this.StripeElementStyle.invalid.color = `rgb(${style.getPropertyValue("--color-text-main")})`; - this.StripeElementStyle.invalid.borderColor = `rgb(${style.getPropertyValue( - "--color-danger-600", - )})`; - }); - } -} From 076e2048d422d9faf15f96a617428f8f4b5e255d Mon Sep 17 00:00:00 2001 From: Alex Morask Date: Thu, 23 Jan 2025 11:42:18 -0500 Subject: [PATCH 11/16] Rename V2: adjust-payment-dialog.component --- .../organization-payment-method.component.ts | 14 +++++------ ...l => adjust-payment-dialog.component.html} | 0 ....ts => adjust-payment-dialog.component.ts} | 23 ++++++++----------- .../billing/shared/billing-shared.module.ts | 4 ++-- .../shared/payment-method.component.ts | 10 ++++---- 5 files changed, 24 insertions(+), 27 deletions(-) rename apps/web/src/app/billing/shared/adjust-payment-dialog/{adjust-payment-dialog-v2.component.html => adjust-payment-dialog.component.html} (100%) rename apps/web/src/app/billing/shared/adjust-payment-dialog/{adjust-payment-dialog-v2.component.ts => adjust-payment-dialog.component.ts} (90%) diff --git a/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.ts b/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.ts index a5b18d9edbf..3fb2121b036 100644 --- a/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.ts +++ b/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.ts @@ -30,9 +30,9 @@ import { openAddCreditDialog, } from "../../shared/add-credit-dialog.component"; import { - AdjustPaymentDialogV2Component, - AdjustPaymentDialogV2ResultType, -} from "../../shared/adjust-payment-dialog/adjust-payment-dialog-v2.component"; + AdjustPaymentDialogComponent, + AdjustPaymentDialogResultType, +} from "../../shared/adjust-payment-dialog/adjust-payment-dialog.component"; @Component({ templateUrl: "./organization-payment-method.component.html", @@ -159,7 +159,7 @@ export class OrganizationPaymentMethodComponent implements OnDestroy { }; protected updatePaymentMethod = async (): Promise => { - const dialogRef = AdjustPaymentDialogV2Component.open(this.dialogService, { + const dialogRef = AdjustPaymentDialogComponent.open(this.dialogService, { data: { initialPaymentMethod: this.paymentSource?.type, organizationId: this.organizationId, @@ -169,13 +169,13 @@ export class OrganizationPaymentMethodComponent implements OnDestroy { const result = await lastValueFrom(dialogRef.closed); - if (result === AdjustPaymentDialogV2ResultType.Submitted) { + if (result === AdjustPaymentDialogResultType.Submitted) { await this.load(); } }; changePayment = async () => { - const dialogRef = AdjustPaymentDialogV2Component.open(this.dialogService, { + const dialogRef = AdjustPaymentDialogComponent.open(this.dialogService, { data: { initialPaymentMethod: this.paymentSource?.type, organizationId: this.organizationId, @@ -183,7 +183,7 @@ export class OrganizationPaymentMethodComponent implements OnDestroy { }, }); const result = await lastValueFrom(dialogRef.closed); - if (result === AdjustPaymentDialogV2ResultType.Submitted) { + if (result === AdjustPaymentDialogResultType.Submitted) { this.location.replaceState(this.location.path(), "", {}); if (this.launchPaymentModalAutomatically && !this.organization.enabled) { await this.syncService.fullSync(true); diff --git a/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog-v2.component.html b/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.html similarity index 100% rename from apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog-v2.component.html rename to apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.html diff --git a/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog-v2.component.ts b/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.ts similarity index 90% rename from apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog-v2.component.ts rename to apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.ts index e7c29591f0a..5044aaac5ff 100644 --- a/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog-v2.component.ts +++ b/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.ts @@ -18,27 +18,27 @@ import { DialogService, ToastService } from "@bitwarden/components"; import { PaymentV2Component } from "../payment/payment-v2.component"; -export interface AdjustPaymentDialogV2Params { +export interface AdjustPaymentDialogParams { initialPaymentMethod?: PaymentMethodType; organizationId?: string; productTier?: ProductTierType; } -export enum AdjustPaymentDialogV2ResultType { +export enum AdjustPaymentDialogResultType { Closed = "closed", Submitted = "submitted", } @Component({ - templateUrl: "./adjust-payment-dialog-v2.component.html", + templateUrl: "./adjust-payment-dialog.component.html", }) -export class AdjustPaymentDialogV2Component implements OnInit { +export class AdjustPaymentDialogComponent implements OnInit { @ViewChild(PaymentV2Component) paymentComponent: PaymentV2Component; @ViewChild(forwardRef(() => ManageTaxInformationComponent)) taxInfoComponent: ManageTaxInformationComponent; protected readonly PaymentMethodType = PaymentMethodType; - protected readonly ResultType = AdjustPaymentDialogV2ResultType; + protected readonly ResultType = AdjustPaymentDialogResultType; protected dialogHeader: string; protected initialPaymentMethod: PaymentMethodType; @@ -51,8 +51,8 @@ export class AdjustPaymentDialogV2Component implements OnInit { private apiService: ApiService, private billingApiService: BillingApiServiceAbstraction, private organizationApiService: OrganizationApiServiceAbstraction, - @Inject(DIALOG_DATA) protected dialogParams: AdjustPaymentDialogV2Params, - private dialogRef: DialogRef, + @Inject(DIALOG_DATA) protected dialogParams: AdjustPaymentDialogParams, + private dialogRef: DialogRef, private i18nService: I18nService, private toastService: ToastService, ) { @@ -116,7 +116,7 @@ export class AdjustPaymentDialogV2Component implements OnInit { message: this.i18nService.t("updatedPaymentMethod"), }); - this.dialogRef.close(AdjustPaymentDialogV2ResultType.Submitted); + this.dialogRef.close(AdjustPaymentDialogResultType.Submitted); } catch (error) { const msg = typeof error == "object" ? error.message : error; this.toastService.showToast({ @@ -170,10 +170,7 @@ export class AdjustPaymentDialogV2Component implements OnInit { static open = ( dialogService: DialogService, - dialogConfig: DialogConfig, + dialogConfig: DialogConfig, ) => - dialogService.open( - AdjustPaymentDialogV2Component, - dialogConfig, - ); + dialogService.open(AdjustPaymentDialogComponent, dialogConfig); } diff --git a/apps/web/src/app/billing/shared/billing-shared.module.ts b/apps/web/src/app/billing/shared/billing-shared.module.ts index 33828385f17..4f30eef3bdf 100644 --- a/apps/web/src/app/billing/shared/billing-shared.module.ts +++ b/apps/web/src/app/billing/shared/billing-shared.module.ts @@ -6,7 +6,7 @@ import { HeaderModule } from "../../layouts/header/header.module"; import { SharedModule } from "../../shared"; import { AddCreditDialogComponent } from "./add-credit-dialog.component"; -import { AdjustPaymentDialogV2Component } from "./adjust-payment-dialog/adjust-payment-dialog-v2.component"; +import { AdjustPaymentDialogComponent } from "./adjust-payment-dialog/adjust-payment-dialog.component"; import { AdjustStorageDialogV2Component } from "./adjust-storage-dialog/adjust-storage-dialog-v2.component"; import { BillingHistoryComponent } from "./billing-history.component"; import { OffboardingSurveyComponent } from "./offboarding-survey.component"; @@ -37,7 +37,7 @@ import { VerifyBankAccountComponent } from "./verify-bank-account/verify-bank-ac UpdateLicenseComponent, UpdateLicenseDialogComponent, OffboardingSurveyComponent, - AdjustPaymentDialogV2Component, + AdjustPaymentDialogComponent, AdjustStorageDialogV2Component, IndividualSelfHostingLicenseUploaderComponent, OrganizationSelfHostingLicenseUploaderComponent, diff --git a/apps/web/src/app/billing/shared/payment-method.component.ts b/apps/web/src/app/billing/shared/payment-method.component.ts index 7e738e03cbe..c5ec942f8b7 100644 --- a/apps/web/src/app/billing/shared/payment-method.component.ts +++ b/apps/web/src/app/billing/shared/payment-method.component.ts @@ -29,9 +29,9 @@ import { TrialFlowService } from "../services/trial-flow.service"; import { AddCreditDialogResult, openAddCreditDialog } from "./add-credit-dialog.component"; import { - AdjustPaymentDialogV2Component, - AdjustPaymentDialogV2ResultType, -} from "./adjust-payment-dialog/adjust-payment-dialog-v2.component"; + AdjustPaymentDialogComponent, + AdjustPaymentDialogResultType, +} from "./adjust-payment-dialog/adjust-payment-dialog.component"; @Component({ templateUrl: "payment-method.component.html", @@ -170,7 +170,7 @@ export class PaymentMethodComponent implements OnInit, OnDestroy { }; changePayment = async () => { - const dialogRef = AdjustPaymentDialogV2Component.open(this.dialogService, { + const dialogRef = AdjustPaymentDialogComponent.open(this.dialogService, { data: { organizationId: this.organizationId, initialPaymentMethod: this.paymentSource !== null ? this.paymentSource.type : null, @@ -179,7 +179,7 @@ export class PaymentMethodComponent implements OnInit, OnDestroy { const result = await lastValueFrom(dialogRef.closed); - if (result === AdjustPaymentDialogV2ResultType.Submitted) { + if (result === AdjustPaymentDialogResultType.Submitted) { this.location.replaceState(this.location.path(), "", {}); if (this.launchPaymentModalAutomatically && !this.organization.enabled) { await this.syncService.fullSync(true); From 03961ea06697907ec33c5f2ad6da131159e6affe Mon Sep 17 00:00:00 2001 From: Alex Morask Date: Thu, 23 Jan 2025 11:43:44 -0500 Subject: [PATCH 12/16] Rename V2: adjust-storage-dialog.component --- .../individual/user-subscription.component.ts | 10 ++++----- ...ganization-subscription-cloud.component.ts | 10 ++++----- ...l => adjust-storage-dialog.component.html} | 0 ....ts => adjust-storage-dialog.component.ts} | 21 ++++++++----------- .../billing/shared/billing-shared.module.ts | 4 ++-- 5 files changed, 21 insertions(+), 24 deletions(-) rename apps/web/src/app/billing/shared/adjust-storage-dialog/{adjust-storage-dialog-v2.component.html => adjust-storage-dialog.component.html} (100%) rename apps/web/src/app/billing/shared/adjust-storage-dialog/{adjust-storage-dialog-v2.component.ts => adjust-storage-dialog.component.ts} (85%) diff --git a/apps/web/src/app/billing/individual/user-subscription.component.ts b/apps/web/src/app/billing/individual/user-subscription.component.ts index 5fa5646add6..38f4436fb47 100644 --- a/apps/web/src/app/billing/individual/user-subscription.component.ts +++ b/apps/web/src/app/billing/individual/user-subscription.component.ts @@ -16,9 +16,9 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { DialogService, ToastService } from "@bitwarden/components"; import { - AdjustStorageDialogV2Component, - AdjustStorageDialogV2ResultType, -} from "../shared/adjust-storage-dialog/adjust-storage-dialog-v2.component"; + AdjustStorageDialogComponent, + AdjustStorageDialogResultType, +} from "../shared/adjust-storage-dialog/adjust-storage-dialog.component"; import { OffboardingSurveyDialogResultType, openOffboardingSurvey, @@ -155,7 +155,7 @@ export class UserSubscriptionComponent implements OnInit { }; adjustStorage = async (add: boolean) => { - const dialogRef = AdjustStorageDialogV2Component.open(this.dialogService, { + const dialogRef = AdjustStorageDialogComponent.open(this.dialogService, { data: { price: 4, cadence: "year", @@ -165,7 +165,7 @@ export class UserSubscriptionComponent implements OnInit { const result = await lastValueFrom(dialogRef.closed); - if (result === AdjustStorageDialogV2ResultType.Submitted) { + if (result === AdjustStorageDialogResultType.Submitted) { await this.load(); } }; diff --git a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts index 6b9dadb0cc3..003f816ac30 100644 --- a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts +++ b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts @@ -25,9 +25,9 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { DialogService, ToastService } from "@bitwarden/components"; import { - AdjustStorageDialogV2Component, - AdjustStorageDialogV2ResultType, -} from "../shared/adjust-storage-dialog/adjust-storage-dialog-v2.component"; + AdjustStorageDialogComponent, + AdjustStorageDialogResultType, +} from "../shared/adjust-storage-dialog/adjust-storage-dialog.component"; import { OffboardingSurveyDialogResultType, openOffboardingSurvey, @@ -417,7 +417,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy adjustStorage = (add: boolean) => { return async () => { - const dialogRef = AdjustStorageDialogV2Component.open(this.dialogService, { + const dialogRef = AdjustStorageDialogComponent.open(this.dialogService, { data: { price: this.storageGbPrice, cadence: this.billingInterval, @@ -428,7 +428,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy const result = await lastValueFrom(dialogRef.closed); - if (result === AdjustStorageDialogV2ResultType.Submitted) { + if (result === AdjustStorageDialogResultType.Submitted) { await this.load(); } }; diff --git a/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog-v2.component.html b/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.html similarity index 100% rename from apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog-v2.component.html rename to apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.html diff --git a/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog-v2.component.ts b/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.ts similarity index 85% rename from apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog-v2.component.ts rename to apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.ts index ba7619729bf..4362e36f857 100644 --- a/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog-v2.component.ts +++ b/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.ts @@ -10,22 +10,22 @@ import { StorageRequest } from "@bitwarden/common/models/request/storage.request import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { DialogService, ToastService } from "@bitwarden/components"; -export interface AdjustStorageDialogV2Params { +export interface AdjustStorageDialogParams { price: number; cadence: "month" | "year"; type: "Add" | "Remove"; organizationId?: string; } -export enum AdjustStorageDialogV2ResultType { +export enum AdjustStorageDialogResultType { Submitted = "submitted", Closed = "closed", } @Component({ - templateUrl: "./adjust-storage-dialog-v2.component.html", + templateUrl: "./adjust-storage-dialog.component.html", }) -export class AdjustStorageDialogV2Component { +export class AdjustStorageDialogComponent { protected formGroup = new FormGroup({ storage: new FormControl(0, [ Validators.required, @@ -42,12 +42,12 @@ export class AdjustStorageDialogV2Component { protected body: string; protected storageFieldLabel: string; - protected ResultType = AdjustStorageDialogV2ResultType; + protected ResultType = AdjustStorageDialogResultType; constructor( private apiService: ApiService, - @Inject(DIALOG_DATA) protected dialogParams: AdjustStorageDialogV2Params, - private dialogRef: DialogRef, + @Inject(DIALOG_DATA) protected dialogParams: AdjustStorageDialogParams, + private dialogRef: DialogRef, private i18nService: I18nService, private organizationApiService: OrganizationApiServiceAbstraction, private toastService: ToastService, @@ -97,10 +97,7 @@ export class AdjustStorageDialogV2Component { static open = ( dialogService: DialogService, - dialogConfig: DialogConfig, + dialogConfig: DialogConfig, ) => - dialogService.open( - AdjustStorageDialogV2Component, - dialogConfig, - ); + dialogService.open(AdjustStorageDialogComponent, dialogConfig); } diff --git a/apps/web/src/app/billing/shared/billing-shared.module.ts b/apps/web/src/app/billing/shared/billing-shared.module.ts index 4f30eef3bdf..60a113e5ee6 100644 --- a/apps/web/src/app/billing/shared/billing-shared.module.ts +++ b/apps/web/src/app/billing/shared/billing-shared.module.ts @@ -7,7 +7,7 @@ import { SharedModule } from "../../shared"; import { AddCreditDialogComponent } from "./add-credit-dialog.component"; import { AdjustPaymentDialogComponent } from "./adjust-payment-dialog/adjust-payment-dialog.component"; -import { AdjustStorageDialogV2Component } from "./adjust-storage-dialog/adjust-storage-dialog-v2.component"; +import { AdjustStorageDialogComponent } from "./adjust-storage-dialog/adjust-storage-dialog.component"; import { BillingHistoryComponent } from "./billing-history.component"; import { OffboardingSurveyComponent } from "./offboarding-survey.component"; import { PaymentV2Component } from "./payment/payment-v2.component"; @@ -38,7 +38,7 @@ import { VerifyBankAccountComponent } from "./verify-bank-account/verify-bank-ac UpdateLicenseDialogComponent, OffboardingSurveyComponent, AdjustPaymentDialogComponent, - AdjustStorageDialogV2Component, + AdjustStorageDialogComponent, IndividualSelfHostingLicenseUploaderComponent, OrganizationSelfHostingLicenseUploaderComponent, ], From 29455adc656a62851bc91e44d9c32b07081db058 Mon Sep 17 00:00:00 2001 From: Alex Morask Date: Thu, 23 Jan 2025 11:47:09 -0500 Subject: [PATCH 13/16] Rename V2: payment-label.component --- ...2.component.html => payment-label.component.html} | 0 ...el-v2.component.ts => payment-label.component.ts} | 6 +++--- .../billing/shared/payment/payment-v2.component.html | 12 ++++++------ .../billing/shared/payment/payment-v2.component.ts | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) rename apps/web/src/app/billing/shared/payment/{payment-label-v2.component.html => payment-label.component.html} (100%) rename apps/web/src/app/billing/shared/payment/{payment-label-v2.component.ts => payment-label.component.ts} (89%) diff --git a/apps/web/src/app/billing/shared/payment/payment-label-v2.component.html b/apps/web/src/app/billing/shared/payment/payment-label.component.html similarity index 100% rename from apps/web/src/app/billing/shared/payment/payment-label-v2.component.html rename to apps/web/src/app/billing/shared/payment/payment-label.component.html diff --git a/apps/web/src/app/billing/shared/payment/payment-label-v2.component.ts b/apps/web/src/app/billing/shared/payment/payment-label.component.ts similarity index 89% rename from apps/web/src/app/billing/shared/payment/payment-label-v2.component.ts rename to apps/web/src/app/billing/shared/payment/payment-label.component.ts index f4d0f097766..179011e1144 100644 --- a/apps/web/src/app/billing/shared/payment/payment-label-v2.component.ts +++ b/apps/web/src/app/billing/shared/payment/payment-label.component.ts @@ -15,12 +15,12 @@ import { SharedModule } from "../../../shared"; * the `ExtensionRefresh` flag is set. */ @Component({ - selector: "app-payment-label-v2", - templateUrl: "./payment-label-v2.component.html", + selector: "app-payment-label", + templateUrl: "./payment-label.component.html", standalone: true, imports: [FormFieldModule, SharedModule], }) -export class PaymentLabelV2 implements OnInit { +export class PaymentLabelComponent implements OnInit { /** `id` of the associated input */ @Input({ required: true }) for: string; /** Displays required text on the label */ diff --git a/apps/web/src/app/billing/shared/payment/payment-v2.component.html b/apps/web/src/app/billing/shared/payment/payment-v2.component.html index 9804e6bc86f..b7499ddd3ec 100644 --- a/apps/web/src/app/billing/shared/payment/payment-v2.component.html +++ b/apps/web/src/app/billing/shared/payment/payment-v2.component.html @@ -43,9 +43,9 @@
- + {{ "number" | i18n }} - +
@@ -56,13 +56,13 @@ />
- + {{ "expiration" | i18n }} - +
- + {{ "securityCodeSlashCVV" | i18n }} - +
diff --git a/apps/web/src/app/billing/shared/payment/payment-v2.component.ts b/apps/web/src/app/billing/shared/payment/payment-v2.component.ts index f65a5743c35..5a534b57004 100644 --- a/apps/web/src/app/billing/shared/payment/payment-v2.component.ts +++ b/apps/web/src/app/billing/shared/payment/payment-v2.component.ts @@ -12,7 +12,7 @@ import { TokenizedPaymentSourceRequest } from "@bitwarden/common/billing/models/ import { SharedModule } from "../../../shared"; import { BillingServicesModule, BraintreeService, StripeService } from "../../services"; -import { PaymentLabelV2 } from "./payment-label-v2.component"; +import { PaymentLabelComponent } from "./payment-label.component"; /** * Render a form that allows the user to enter their payment method, tokenize it against one of our payment providers and, @@ -24,7 +24,7 @@ import { PaymentLabelV2 } from "./payment-label-v2.component"; selector: "app-payment-v2", templateUrl: "./payment-v2.component.html", standalone: true, - imports: [BillingServicesModule, SharedModule, PaymentLabelV2], + imports: [BillingServicesModule, SharedModule, PaymentLabelComponent], }) export class PaymentV2Component implements OnInit, OnDestroy { /** Show account credit as a payment option. */ From acf1d23594748cae353ee3a20ccec196f4f23c39 Mon Sep 17 00:00:00 2001 From: Alex Morask Date: Thu, 23 Jan 2025 11:50:34 -0500 Subject: [PATCH 14/16] Rename V2: payment.component --- .../trial-billing-step.component.html | 2 +- .../trial-billing-step.component.ts | 14 +++++++------- .../individual/premium/premium-v2.component.html | 2 +- .../individual/premium/premium-v2.component.ts | 4 ++-- .../change-plan-dialog.component.html | 2 +- .../change-plan-dialog.component.ts | 16 ++++++++-------- .../organization-plans.component.html | 2 +- .../organization-plans.component.ts | 16 ++++++++-------- .../adjust-payment-dialog.component.html | 4 ++-- .../adjust-payment-dialog.component.ts | 4 ++-- .../app/billing/shared/billing-shared.module.ts | 6 +++--- ...-v2.component.html => payment.component.html} | 0 ...ment-v2.component.ts => payment.component.ts} | 8 +++----- 13 files changed, 39 insertions(+), 41 deletions(-) rename apps/web/src/app/billing/shared/payment/{payment-v2.component.html => payment.component.html} (100%) rename apps/web/src/app/billing/shared/payment/{payment-v2.component.ts => payment.component.ts} (96%) diff --git a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html index 36b818db666..5bcde6a697a 100644 --- a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html +++ b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html @@ -49,7 +49,7 @@

{{ "billingPlanLabel" | i18n }

{{ "paymentType" | i18n }}

- +
diff --git a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts index ba1889dd07d..1c24bb9fd80 100644 --- a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts +++ b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts @@ -18,7 +18,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { ToastService } from "@bitwarden/components"; import { BillingSharedModule, TaxInfoComponent } from "../../shared"; -import { PaymentV2Component } from "../../shared/payment/payment-v2.component"; +import { PaymentComponent } from "../../shared/payment/payment.component"; export type TrialOrganizationType = Exclude; @@ -50,7 +50,7 @@ export enum SubscriptionProduct { standalone: true, }) export class TrialBillingStepComponent implements OnInit { - @ViewChild(PaymentV2Component) paymentV2Component: PaymentV2Component; + @ViewChild(PaymentComponent) paymentComponent: PaymentComponent; @ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent; @Input() organizationInfo: OrganizationInfo; @Input() subscriptionProduct: SubscriptionProduct = SubscriptionProduct.PasswordManager; @@ -115,12 +115,12 @@ export class TrialBillingStepComponent implements OnInit { } protected changedCountry() { - this.paymentV2Component.showBankAccount = this.taxInfoComponent.country === "US"; + this.paymentComponent.showBankAccount = this.taxInfoComponent.country === "US"; if ( - !this.paymentV2Component.showBankAccount && - this.paymentV2Component.selected === PaymentMethodType.BankAccount + !this.paymentComponent.showBankAccount && + this.paymentComponent.selected === PaymentMethodType.BankAccount ) { - this.paymentV2Component.select(PaymentMethodType.Card); + this.paymentComponent.select(PaymentMethodType.Card); } } @@ -142,7 +142,7 @@ export class TrialBillingStepComponent implements OnInit { private async createOrganization(): Promise { const planResponse = this.findPlanFor(this.formGroup.value.cadence); - const { type, token } = await this.paymentV2Component.tokenize(); + const { type, token } = await this.paymentComponent.tokenize(); const paymentMethod: [string, PaymentMethodType] = [token, type]; const organization: OrganizationInformation = { diff --git a/apps/web/src/app/billing/individual/premium/premium-v2.component.html b/apps/web/src/app/billing/individual/premium/premium-v2.component.html index 7adc93fd962..acf24ed2a34 100644 --- a/apps/web/src/app/billing/individual/premium/premium-v2.component.html +++ b/apps/web/src/app/billing/individual/premium/premium-v2.component.html @@ -130,7 +130,7 @@

{{ "summary" | i18n }}

{{ "paymentInformation" | i18n }}

- +
diff --git a/apps/web/src/app/billing/individual/premium/premium-v2.component.ts b/apps/web/src/app/billing/individual/premium/premium-v2.component.ts index 11b55f92b40..b3b9bc89859 100644 --- a/apps/web/src/app/billing/individual/premium/premium-v2.component.ts +++ b/apps/web/src/app/billing/individual/premium/premium-v2.component.ts @@ -21,14 +21,14 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { SyncService } from "@bitwarden/common/platform/sync"; import { ToastService } from "@bitwarden/components"; -import { PaymentV2Component } from "../../shared/payment/payment-v2.component"; +import { PaymentComponent } from "../../shared/payment/payment.component"; import { TaxInfoComponent } from "../../shared/tax-info.component"; @Component({ templateUrl: "./premium-v2.component.html", }) export class PremiumV2Component { - @ViewChild(PaymentV2Component) paymentComponent: PaymentV2Component; + @ViewChild(PaymentComponent) paymentComponent: PaymentComponent; @ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent; protected hasPremiumFromAnyOrganization$: Observable; diff --git a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html index 71882f17214..b5471a90fd5 100644 --- a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html +++ b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html @@ -353,7 +353,7 @@

{{ "paymentMethod" | i18n }}

- +

{{ paymentDesc }}

- + - + >
ManageTaxInformationComponent)) taxInfoComponent: ManageTaxInformationComponent; diff --git a/apps/web/src/app/billing/shared/billing-shared.module.ts b/apps/web/src/app/billing/shared/billing-shared.module.ts index 60a113e5ee6..9a69755b209 100644 --- a/apps/web/src/app/billing/shared/billing-shared.module.ts +++ b/apps/web/src/app/billing/shared/billing-shared.module.ts @@ -10,7 +10,7 @@ import { AdjustPaymentDialogComponent } from "./adjust-payment-dialog/adjust-pay import { AdjustStorageDialogComponent } from "./adjust-storage-dialog/adjust-storage-dialog.component"; import { BillingHistoryComponent } from "./billing-history.component"; import { OffboardingSurveyComponent } from "./offboarding-survey.component"; -import { PaymentV2Component } from "./payment/payment-v2.component"; +import { PaymentComponent } from "./payment/payment.component"; import { PaymentMethodComponent } from "./payment-method.component"; import { IndividualSelfHostingLicenseUploaderComponent } from "./self-hosting-license-uploader/individual-self-hosting-license-uploader.component"; import { OrganizationSelfHostingLicenseUploaderComponent } from "./self-hosting-license-uploader/organization-self-hosting-license-uploader.component"; @@ -26,7 +26,7 @@ import { VerifyBankAccountComponent } from "./verify-bank-account/verify-bank-ac TaxInfoComponent, HeaderModule, BannerModule, - PaymentV2Component, + PaymentComponent, VerifyBankAccountComponent, ], declarations: [ @@ -51,7 +51,7 @@ import { VerifyBankAccountComponent } from "./verify-bank-account/verify-bank-ac UpdateLicenseDialogComponent, OffboardingSurveyComponent, VerifyBankAccountComponent, - PaymentV2Component, + PaymentComponent, IndividualSelfHostingLicenseUploaderComponent, OrganizationSelfHostingLicenseUploaderComponent, ], diff --git a/apps/web/src/app/billing/shared/payment/payment-v2.component.html b/apps/web/src/app/billing/shared/payment/payment.component.html similarity index 100% rename from apps/web/src/app/billing/shared/payment/payment-v2.component.html rename to apps/web/src/app/billing/shared/payment/payment.component.html diff --git a/apps/web/src/app/billing/shared/payment/payment-v2.component.ts b/apps/web/src/app/billing/shared/payment/payment.component.ts similarity index 96% rename from apps/web/src/app/billing/shared/payment/payment-v2.component.ts rename to apps/web/src/app/billing/shared/payment/payment.component.ts index 5a534b57004..c11dfddb6cc 100644 --- a/apps/web/src/app/billing/shared/payment/payment-v2.component.ts +++ b/apps/web/src/app/billing/shared/payment/payment.component.ts @@ -17,16 +17,14 @@ import { PaymentLabelComponent } from "./payment-label.component"; /** * Render a form that allows the user to enter their payment method, tokenize it against one of our payment providers and, * optionally, submit it using the {@link onSubmit} function if it is provided. - * - * This component is meant to replace the existing {@link PaymentComponent} which is using the deprecated Stripe Sources API. */ @Component({ - selector: "app-payment-v2", - templateUrl: "./payment-v2.component.html", + selector: "app-payment", + templateUrl: "./payment.component.html", standalone: true, imports: [BillingServicesModule, SharedModule, PaymentLabelComponent], }) -export class PaymentV2Component implements OnInit, OnDestroy { +export class PaymentComponent implements OnInit, OnDestroy { /** Show account credit as a payment option. */ @Input() showAccountCredit: boolean = true; /** Show bank account as a payment option. */ From 0638232bbb191a961d8eda73f42c23a97490ad46 Mon Sep 17 00:00:00 2001 From: Alex Morask Date: Thu, 23 Jan 2025 11:54:12 -0500 Subject: [PATCH 15/16] Rename V2: premium.component --- .../billing/individual/individual-billing-routing.module.ts | 4 ++-- .../src/app/billing/individual/individual-billing.module.ts | 4 ++-- .../{premium-v2.component.html => premium.component.html} | 0 .../premium/{premium-v2.component.ts => premium.component.ts} | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) rename apps/web/src/app/billing/individual/premium/{premium-v2.component.html => premium.component.html} (100%) rename apps/web/src/app/billing/individual/premium/{premium-v2.component.ts => premium.component.ts} (99%) diff --git a/apps/web/src/app/billing/individual/individual-billing-routing.module.ts b/apps/web/src/app/billing/individual/individual-billing-routing.module.ts index 6bbdf719a16..bb1ada0b719 100644 --- a/apps/web/src/app/billing/individual/individual-billing-routing.module.ts +++ b/apps/web/src/app/billing/individual/individual-billing-routing.module.ts @@ -4,7 +4,7 @@ import { RouterModule, Routes } from "@angular/router"; import { PaymentMethodComponent } from "../shared"; import { BillingHistoryViewComponent } from "./billing-history-view.component"; -import { PremiumV2Component } from "./premium/premium-v2.component"; +import { PremiumComponent } from "./premium/premium.component"; import { SubscriptionComponent } from "./subscription.component"; import { UserSubscriptionComponent } from "./user-subscription.component"; @@ -22,7 +22,7 @@ const routes: Routes = [ }, { path: "premium", - component: PremiumV2Component, + component: PremiumComponent, data: { titleId: "goPremium" }, }, { diff --git a/apps/web/src/app/billing/individual/individual-billing.module.ts b/apps/web/src/app/billing/individual/individual-billing.module.ts index 2caf24d6db2..ad75da00c99 100644 --- a/apps/web/src/app/billing/individual/individual-billing.module.ts +++ b/apps/web/src/app/billing/individual/individual-billing.module.ts @@ -5,7 +5,7 @@ import { BillingSharedModule } from "../shared"; import { BillingHistoryViewComponent } from "./billing-history-view.component"; import { IndividualBillingRoutingModule } from "./individual-billing-routing.module"; -import { PremiumV2Component } from "./premium/premium-v2.component"; +import { PremiumComponent } from "./premium/premium.component"; import { SubscriptionComponent } from "./subscription.component"; import { UserSubscriptionComponent } from "./user-subscription.component"; @@ -15,7 +15,7 @@ import { UserSubscriptionComponent } from "./user-subscription.component"; SubscriptionComponent, BillingHistoryViewComponent, UserSubscriptionComponent, - PremiumV2Component, + PremiumComponent, ], }) export class IndividualBillingModule {} diff --git a/apps/web/src/app/billing/individual/premium/premium-v2.component.html b/apps/web/src/app/billing/individual/premium/premium.component.html similarity index 100% rename from apps/web/src/app/billing/individual/premium/premium-v2.component.html rename to apps/web/src/app/billing/individual/premium/premium.component.html diff --git a/apps/web/src/app/billing/individual/premium/premium-v2.component.ts b/apps/web/src/app/billing/individual/premium/premium.component.ts similarity index 99% rename from apps/web/src/app/billing/individual/premium/premium-v2.component.ts rename to apps/web/src/app/billing/individual/premium/premium.component.ts index b3b9bc89859..ec19eb02594 100644 --- a/apps/web/src/app/billing/individual/premium/premium-v2.component.ts +++ b/apps/web/src/app/billing/individual/premium/premium.component.ts @@ -25,9 +25,9 @@ import { PaymentComponent } from "../../shared/payment/payment.component"; import { TaxInfoComponent } from "../../shared/tax-info.component"; @Component({ - templateUrl: "./premium-v2.component.html", + templateUrl: "./premium.component.html", }) -export class PremiumV2Component { +export class PremiumComponent { @ViewChild(PaymentComponent) paymentComponent: PaymentComponent; @ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent; From aa743dd27d0346ef2f441e3ac1501f34b88a87eb Mon Sep 17 00:00:00 2001 From: Alex Morask Date: Fri, 24 Jan 2025 10:41:23 -0500 Subject: [PATCH 16/16] Patrick's feedback --- .../adjust-storage-dialog.component.html | 2 +- .../app/billing/shared/payment/payment.component.html | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.html b/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.html index 7b74379acb6..832356477c4 100644 --- a/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.html +++ b/apps/web/src/app/billing/shared/adjust-storage-dialog/adjust-storage-dialog.component.html @@ -2,7 +2,7 @@

{{ body }}

-
+
{{ storageFieldLabel }} diff --git a/apps/web/src/app/billing/shared/payment/payment.component.html b/apps/web/src/app/billing/shared/payment/payment.component.html index b7499ddd3ec..af261155171 100644 --- a/apps/web/src/app/billing/shared/payment/payment.component.html +++ b/apps/web/src/app/billing/shared/payment/payment.component.html @@ -120,13 +120,10 @@ {{ "bankAccountType" | i18n }} - + + -