1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 06:13:38 +00:00

[PM-26947] Fix Billing Address Defect (#16872)

* fix(billing): Add Billing Address component

* fix(billing): Update tax refresh logic and swap billing address component

* fix(billing): Fix headers

* fix(billing): Do not show bank payment option for premium upgrade
This commit is contained in:
Stephon Brown
2025-10-16 12:08:19 -04:00
committed by GitHub
parent cd08a71a0b
commit 9b2fbdba1c
2 changed files with 42 additions and 22 deletions

View File

@@ -31,11 +31,19 @@
</div> </div>
} }
<div class="tw-pb-8 !tw-mx-0"> <div class="tw-pb-8 !tw-mx-0">
<h5 bitTypography="h5">{{ "paymentMethod" | i18n }}</h5>
<app-enter-payment-method <app-enter-payment-method
[showBankAccount]="isFamiliesPlan"
[group]="formGroup.controls.paymentForm" [group]="formGroup.controls.paymentForm"
[includeBillingAddress]="true" [includeBillingAddress]="false"
#paymentComponent #paymentComponent
></app-enter-payment-method> ></app-enter-payment-method>
<h5 bitTypography="h5" class="tw-pt-4 tw-pb-2">{{ "billingAddress" | i18n }}</h5>
<app-enter-billing-address
[group]="formGroup.controls.billingAddress"
[scenario]="{ type: 'checkout', supportsTaxId: false }"
>
</app-enter-billing-address>
</div> </div>
</section> </section>

View File

@@ -10,7 +10,7 @@ import {
} from "@angular/core"; } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormControl, FormGroup, Validators } from "@angular/forms"; import { FormControl, FormGroup, Validators } from "@angular/forms";
import { debounceTime, Observable } from "rxjs"; import { catchError, debounceTime, from, Observable, of, switchMap } from "rxjs";
import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -20,7 +20,10 @@ import { LogService } from "@bitwarden/logging";
import { CartSummaryComponent, LineItem } from "@bitwarden/pricing"; import { CartSummaryComponent, LineItem } from "@bitwarden/pricing";
import { SharedModule } from "@bitwarden/web-vault/app/shared"; import { SharedModule } from "@bitwarden/web-vault/app/shared";
import { EnterPaymentMethodComponent } from "../../../payment/components"; import {
EnterBillingAddressComponent,
EnterPaymentMethodComponent,
} from "../../../payment/components";
import { BillingServicesModule } from "../../../services"; import { BillingServicesModule } from "../../../services";
import { SubscriptionPricingService } from "../../../services/subscription-pricing.service"; import { SubscriptionPricingService } from "../../../services/subscription-pricing.service";
import { BitwardenSubscriber } from "../../../types"; import { BitwardenSubscriber } from "../../../types";
@@ -65,6 +68,7 @@ export type UpgradePaymentParams = {
CartSummaryComponent, CartSummaryComponent,
ButtonModule, ButtonModule,
EnterPaymentMethodComponent, EnterPaymentMethodComponent,
EnterBillingAddressComponent,
BillingServicesModule, BillingServicesModule,
], ],
providers: [UpgradePaymentService], providers: [UpgradePaymentService],
@@ -83,6 +87,7 @@ export class UpgradePaymentComponent implements OnInit, AfterViewInit {
protected formGroup = new FormGroup({ protected formGroup = new FormGroup({
organizationName: new FormControl<string>("", [Validators.required]), organizationName: new FormControl<string>("", [Validators.required]),
paymentForm: EnterPaymentMethodComponent.getFormGroup(), paymentForm: EnterPaymentMethodComponent.getFormGroup(),
billingAddress: EnterBillingAddressComponent.getFormGroup(),
}); });
protected loading = signal(true); protected loading = signal(true);
@@ -140,9 +145,16 @@ export class UpgradePaymentComponent implements OnInit, AfterViewInit {
} }
}); });
this.formGroup.valueChanges this.formGroup.controls.billingAddress.valueChanges
.pipe(debounceTime(1000), takeUntilDestroyed(this.destroyRef)) .pipe(
.subscribe(() => this.refreshSalesTax()); debounceTime(1000),
// Only proceed when form has required values
switchMap(() => this.refreshSalesTax$()),
takeUntilDestroyed(this.destroyRef),
)
.subscribe((tax) => {
this.estimatedTax = tax;
});
this.loading.set(false); this.loading.set(false);
} }
@@ -199,8 +211,8 @@ export class UpgradePaymentComponent implements OnInit, AfterViewInit {
private async processUpgrade(): Promise<UpgradePaymentResult> { private async processUpgrade(): Promise<UpgradePaymentResult> {
// Get common values // Get common values
const country = this.formGroup.value?.paymentForm?.billingAddress?.country; const country = this.formGroup.value?.billingAddress?.country;
const postalCode = this.formGroup.value?.paymentForm?.billingAddress?.postalCode; const postalCode = this.formGroup.value?.billingAddress?.postalCode;
if (!this.selectedPlan) { if (!this.selectedPlan) {
throw new Error("No plan selected"); throw new Error("No plan selected");
@@ -246,19 +258,20 @@ export class UpgradePaymentComponent implements OnInit, AfterViewInit {
} }
} }
private async refreshSalesTax(): Promise<void> { // Create an observable for tax calculation
private refreshSalesTax$(): Observable<number> {
const billingAddress = { const billingAddress = {
country: this.formGroup.value.paymentForm?.billingAddress?.country, country: this.formGroup.value?.billingAddress?.country,
postalCode: this.formGroup.value.paymentForm?.billingAddress?.postalCode, postalCode: this.formGroup.value?.billingAddress?.postalCode,
}; };
if (!this.selectedPlan || !billingAddress.country || !billingAddress.postalCode) { if (!this.selectedPlan || !billingAddress.country || !billingAddress.postalCode) {
this.estimatedTax = 0; return of(0);
return;
} }
this.upgradePaymentService // Convert Promise to Observable
.calculateEstimatedTax(this.selectedPlan, { return from(
this.upgradePaymentService.calculateEstimatedTax(this.selectedPlan, {
line1: null, line1: null,
line2: null, line2: null,
city: null, city: null,
@@ -266,17 +279,16 @@ export class UpgradePaymentComponent implements OnInit, AfterViewInit {
country: billingAddress.country, country: billingAddress.country,
postalCode: billingAddress.postalCode, postalCode: billingAddress.postalCode,
taxId: null, taxId: null,
}) }),
.then((tax) => { ).pipe(
this.estimatedTax = tax; catchError((error: unknown) => {
})
.catch((error: unknown) => {
this.logService.error("Tax calculation failed:", error); this.logService.error("Tax calculation failed:", error);
this.toastService.showToast({ this.toastService.showToast({
variant: "error", variant: "error",
message: this.i18nService.t("taxCalculationError"), message: this.i18nService.t("taxCalculationError"),
}); });
this.estimatedTax = 0; return of(0); // Return default value on error
}); }),
);
} }
} }