diff --git a/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade-payment/premium-org-upgrade-payment.component.html b/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade-payment/premium-org-upgrade-payment.component.html
index 1df2ca13178..388c9f13ea7 100644
--- a/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade-payment/premium-org-upgrade-payment.component.html
+++ b/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade-payment/premium-org-upgrade-payment.component.html
@@ -14,13 +14,12 @@
{{ "paymentMethod" | i18n }}
-
+
+
{{ "billingAddress" | i18n }}
{{ "upgrade" | i18n }}
diff --git a/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade-payment/premium-org-upgrade-payment.component.ts b/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade-payment/premium-org-upgrade-payment.component.ts
index bd5964ad81b..4b35b2ae2ae 100644
--- a/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade-payment/premium-org-upgrade-payment.component.ts
+++ b/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade-payment/premium-org-upgrade-payment.component.ts
@@ -23,9 +23,11 @@ import {
Observable,
from,
defer,
+ map,
+ tap,
} from "rxjs";
-import { Account } from "@bitwarden/common/auth/abstractions/account.service";
+import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { SubscriptionPricingServiceAbstraction } from "@bitwarden/common/billing/abstractions/subscription-pricing.service.abstraction";
import {
BusinessSubscriptionPricingTier,
@@ -41,11 +43,14 @@ import { LogService } from "@bitwarden/logging";
import { Cart, CartSummaryComponent } from "@bitwarden/pricing";
import { SharedModule } from "@bitwarden/web-vault/app/shared";
+import { SubscriberBillingClient } from "../../../clients/subscriber-billing.client";
import {
EnterBillingAddressComponent,
- EnterPaymentMethodComponent,
+ DisplayPaymentMethodComponent,
getBillingAddressFromForm,
} from "../../../payment/components";
+import { MaskedPaymentMethod } from "../../../payment/types";
+import { BitwardenSubscriber, mapAccountToSubscriber } from "../../../types";
import {
PremiumOrgUpgradeService,
@@ -75,7 +80,7 @@ export type PremiumOrgUpgradePaymentResult = {
SharedModule,
CartSummaryComponent,
ButtonModule,
- EnterPaymentMethodComponent,
+ DisplayPaymentMethodComponent,
EnterBillingAddressComponent,
],
providers: [PremiumOrgUpgradeService],
@@ -108,12 +113,10 @@ export class PremiumOrgUpgradePaymentComponent implements OnInit, AfterViewInit
protected goBack = output();
protected complete = output();
- readonly paymentComponent = viewChild.required(EnterPaymentMethodComponent);
readonly cartSummaryComponent = viewChild.required(CartSummaryComponent);
protected formGroup = new FormGroup({
organizationName: new FormControl("", [Validators.required]),
- paymentForm: EnterPaymentMethodComponent.getFormGroup(),
billingAddress: EnterBillingAddressComponent.getFormGroup(),
});
@@ -121,6 +124,10 @@ export class PremiumOrgUpgradePaymentComponent implements OnInit, AfterViewInit
protected readonly loading = signal(true);
protected readonly upgradeToMessage = signal("");
+ // Signals for payment method
+ protected readonly paymentMethod = signal(null);
+ protected readonly subscriber = signal(null);
+
protected readonly planMembershipMessage = computed(
() => this.PLAN_MEMBERSHIP_MESSAGES[this.selectedPlanId()] ?? "",
);
@@ -144,6 +151,15 @@ export class PremiumOrgUpgradePaymentComponent implements OnInit, AfterViewInit
initialValue: this.getEmptyInvoicePreview(),
});
+ private readonly i18nService = inject(I18nService);
+ private readonly subscriptionPricingService = inject(SubscriptionPricingServiceAbstraction);
+ private readonly toastService = inject(ToastService);
+ private readonly logService = inject(LogService);
+ private readonly destroyRef = inject(DestroyRef);
+ private readonly premiumOrgUpgradeService = inject(PremiumOrgUpgradeService);
+ private readonly subscriberBillingClient = inject(SubscriberBillingClient);
+ private readonly accountService = inject(AccountService);
+
// Cart Summary data
protected readonly cart = computed(() => {
if (!this.selectedPlan()) {
@@ -180,13 +196,6 @@ export class PremiumOrgUpgradePaymentComponent implements OnInit, AfterViewInit
};
});
- private readonly i18nService = inject(I18nService);
- private readonly subscriptionPricingService = inject(SubscriptionPricingServiceAbstraction);
- private readonly toastService = inject(ToastService);
- private readonly logService = inject(LogService);
- private readonly destroyRef = inject(DestroyRef);
- private readonly premiumOrgUpgradeService = inject(PremiumOrgUpgradeService);
-
async ngOnInit(): Promise {
// If the selected plan is Personal Premium, no upgrade is needed
if (this.selectedPlanId() == PersonalSubscriptionPricingTierIds.Premium) {
@@ -231,6 +240,22 @@ export class PremiumOrgUpgradePaymentComponent implements OnInit, AfterViewInit
}
});
+ this.accountService.activeAccount$
+ .pipe(
+ mapAccountToSubscriber,
+ switchMap((subscriber) =>
+ from(this.subscriberBillingClient.getPaymentMethod(subscriber)).pipe(
+ map((paymentMethod) => ({ subscriber, paymentMethod })),
+ ),
+ ),
+ tap(({ subscriber, paymentMethod }) => {
+ this.subscriber.set(subscriber);
+ this.paymentMethod.set(paymentMethod);
+ }),
+ takeUntilDestroyed(this.destroyRef),
+ )
+ .subscribe();
+
this.loading.set(false);
}
@@ -240,7 +265,7 @@ export class PremiumOrgUpgradePaymentComponent implements OnInit, AfterViewInit
}
protected submit = async (): Promise => {
- if (!this.isFormValid()) {
+ if (!this.formGroup.valid) {
this.formGroup.markAllAsTouched();
return;
}
@@ -265,10 +290,6 @@ export class PremiumOrgUpgradePaymentComponent implements OnInit, AfterViewInit
}
};
- protected isFormValid(): boolean {
- return this.formGroup.valid && this.paymentComponent().validate();
- }
-
private async processUpgrade(): Promise {
const billingAddress = getBillingAddressFromForm(this.formGroup.controls.billingAddress);
const organizationName = this.formGroup.value?.organizationName;
@@ -281,12 +302,6 @@ export class PremiumOrgUpgradePaymentComponent implements OnInit, AfterViewInit
throw new Error("Organization name is required");
}
- const paymentMethod = await this.paymentComponent().tokenize();
-
- if (!paymentMethod) {
- throw new Error("Payment method is required");
- }
-
const organizationId = await this.premiumOrgUpgradeService.upgradeToOrganization(
this.account(),
organizationName,