mirror of
https://github.com/bitwarden/browser
synced 2025-12-10 13:23:34 +00:00
[PM-28034] Pre-Launch Payment Defect Solution (#17331)
* fix(billing): update to password manager to signal * fix(billing): take first value so the dialog doesn't show again * fix(billing): add families plan to request builder * fix(billing): feedback and type update * fix(billing): fix selectedplan call
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
of,
|
||||
shareReplay,
|
||||
switchMap,
|
||||
take,
|
||||
} from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
@@ -182,6 +183,7 @@ export class CloudHostedPremiumVNextComponent {
|
||||
|
||||
this.shouldShowUpgradeDialogOnInit$
|
||||
.pipe(
|
||||
take(1),
|
||||
switchMap((shouldShowUpgradeDialogOnInit) => {
|
||||
if (shouldShowUpgradeDialogOnInit) {
|
||||
return from(this.openUpgradeDialog("Premium"));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<form [formGroup]="formGroup" [bitSubmit]="submit">
|
||||
<bit-dialog dialogSize="large" [loading]="loading()">
|
||||
<span bitDialogTitle class="tw-font-medium">{{ upgradeToMessage }}</span>
|
||||
<span bitDialogTitle class="tw-font-medium">{{ upgradeToMessage() }}</span>
|
||||
<ng-container bitDialogContent>
|
||||
<section>
|
||||
@if (isFamiliesPlan) {
|
||||
@@ -50,10 +50,9 @@
|
||||
</section>
|
||||
|
||||
<section>
|
||||
@if (passwordManager) {
|
||||
<billing-cart-summary
|
||||
#cartSummaryComponent
|
||||
[passwordManager]="passwordManager"
|
||||
[passwordManager]="passwordManager()"
|
||||
[estimatedTax]="estimatedTax$ | async"
|
||||
></billing-cart-summary>
|
||||
@if (isFamiliesPlan) {
|
||||
@@ -61,7 +60,6 @@
|
||||
{{ "paymentChargedWithTrial" | i18n }}
|
||||
</p>
|
||||
}
|
||||
}
|
||||
</section>
|
||||
</ng-container>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
AfterViewInit,
|
||||
Component,
|
||||
computed,
|
||||
DestroyRef,
|
||||
input,
|
||||
OnInit,
|
||||
@@ -34,7 +35,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
||||
import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values";
|
||||
import { ButtonModule, DialogModule, ToastService } from "@bitwarden/components";
|
||||
import { LogService } from "@bitwarden/logging";
|
||||
import { CartSummaryComponent, LineItem } from "@bitwarden/pricing";
|
||||
import { CartSummaryComponent } from "@bitwarden/pricing";
|
||||
import { SharedModule } from "@bitwarden/web-vault/app/shared";
|
||||
|
||||
import {
|
||||
@@ -104,8 +105,6 @@ export class UpgradePaymentComponent implements OnInit, AfterViewInit {
|
||||
protected readonly account = input.required<Account>();
|
||||
protected goBack = output<void>();
|
||||
protected complete = output<UpgradePaymentResult>();
|
||||
protected selectedPlan: PlanDetails | null = null;
|
||||
protected hasEnoughAccountCredit$!: Observable<boolean>;
|
||||
|
||||
readonly paymentComponent = viewChild.required(EnterPaymentMethodComponent);
|
||||
readonly cartSummaryComponent = viewChild.required(CartSummaryComponent);
|
||||
@@ -116,15 +115,26 @@ export class UpgradePaymentComponent implements OnInit, AfterViewInit {
|
||||
billingAddress: EnterBillingAddressComponent.getFormGroup(),
|
||||
});
|
||||
|
||||
protected readonly selectedPlan = signal<PlanDetails | null>(null);
|
||||
protected readonly loading = signal(true);
|
||||
private pricingTiers$!: Observable<PersonalSubscriptionPricingTier[]>;
|
||||
|
||||
protected readonly upgradeToMessage = signal("");
|
||||
// Cart Summary data
|
||||
protected passwordManager!: LineItem;
|
||||
protected estimatedTax$!: Observable<number>;
|
||||
protected readonly passwordManager = computed(() => {
|
||||
if (!this.selectedPlan()) {
|
||||
return { name: "", cost: 0, quantity: 0, cadence: "year" as const };
|
||||
}
|
||||
|
||||
// Display data
|
||||
protected upgradeToMessage = "";
|
||||
return {
|
||||
name: this.isFamiliesPlan ? "familiesMembership" : "premiumMembership",
|
||||
cost: this.selectedPlan()!.details.passwordManager.annualPrice,
|
||||
quantity: 1,
|
||||
cadence: "year" as const,
|
||||
};
|
||||
});
|
||||
|
||||
protected hasEnoughAccountCredit$!: Observable<boolean>;
|
||||
private pricingTiers$!: Observable<PersonalSubscriptionPricingTier[]>;
|
||||
protected estimatedTax$!: Observable<number>;
|
||||
|
||||
constructor(
|
||||
private i18nService: I18nService,
|
||||
@@ -162,19 +172,13 @@ export class UpgradePaymentComponent implements OnInit, AfterViewInit {
|
||||
const planDetails = plans.find((plan) => plan.id === this.selectedPlanId());
|
||||
|
||||
if (planDetails) {
|
||||
this.selectedPlan = {
|
||||
this.selectedPlan.set({
|
||||
tier: this.selectedPlanId(),
|
||||
details: planDetails,
|
||||
};
|
||||
this.passwordManager = {
|
||||
name: this.isFamiliesPlan ? "familiesMembership" : "premiumMembership",
|
||||
cost: this.selectedPlan.details.passwordManager.annualPrice,
|
||||
quantity: 1,
|
||||
cadence: "year",
|
||||
};
|
||||
});
|
||||
|
||||
this.upgradeToMessage = this.i18nService.t(
|
||||
this.isFamiliesPlan ? "startFreeFamiliesTrial" : "upgradeToPremium",
|
||||
this.upgradeToMessage.set(
|
||||
this.i18nService.t(this.isFamiliesPlan ? "startFreeFamiliesTrial" : "upgradeToPremium"),
|
||||
);
|
||||
} else {
|
||||
this.complete.emit({ status: UpgradePaymentStatus.Closed, organizationId: null });
|
||||
@@ -228,7 +232,7 @@ export class UpgradePaymentComponent implements OnInit, AfterViewInit {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.selectedPlan) {
|
||||
if (!this.selectedPlan()) {
|
||||
throw new Error("No plan selected");
|
||||
}
|
||||
|
||||
@@ -260,7 +264,7 @@ export class UpgradePaymentComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
|
||||
private async processUpgrade(): Promise<UpgradePaymentResult> {
|
||||
if (!this.selectedPlan) {
|
||||
if (!this.selectedPlan()) {
|
||||
throw new Error("No plan selected");
|
||||
}
|
||||
|
||||
@@ -308,7 +312,7 @@ export class UpgradePaymentComponent implements OnInit, AfterViewInit {
|
||||
|
||||
const response = await this.upgradePaymentService.upgradeToFamilies(
|
||||
this.account(),
|
||||
this.selectedPlan!,
|
||||
this.selectedPlan()!,
|
||||
paymentMethod,
|
||||
paymentFormValues,
|
||||
);
|
||||
@@ -344,7 +348,7 @@ export class UpgradePaymentComponent implements OnInit, AfterViewInit {
|
||||
|
||||
// Create an observable for tax calculation
|
||||
private refreshSalesTax$(): Observable<number> {
|
||||
if (this.formGroup.invalid || !this.selectedPlan) {
|
||||
if (this.formGroup.invalid || !this.selectedPlan()) {
|
||||
return of(this.INITIAL_TAX_VALUE);
|
||||
}
|
||||
|
||||
@@ -353,7 +357,7 @@ export class UpgradePaymentComponent implements OnInit, AfterViewInit {
|
||||
return of(this.INITIAL_TAX_VALUE);
|
||||
}
|
||||
return from(
|
||||
this.upgradePaymentService.calculateEstimatedTax(this.selectedPlan, billingAddress),
|
||||
this.upgradePaymentService.calculateEstimatedTax(this.selectedPlan()!, billingAddress),
|
||||
).pipe(
|
||||
catchError((error: unknown) => {
|
||||
this.logService.error("Tax calculation failed:", error);
|
||||
|
||||
@@ -135,6 +135,7 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs
|
||||
case PlanType.Free:
|
||||
case PlanType.FamiliesAnnually:
|
||||
case PlanType.FamiliesAnnually2019:
|
||||
case PlanType.FamiliesAnnually2025:
|
||||
case PlanType.TeamsStarter2023:
|
||||
case PlanType.TeamsStarter:
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user