1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-11 05:53:42 +00:00

[PM-16921] Verify tax information is being passed correctly and validated.

This commit is contained in:
Jonas Hendrickx
2025-01-10 13:55:59 +01:00
parent c1e3836cc3
commit 889b5e4ee2
2 changed files with 38 additions and 13 deletions

View File

@@ -131,7 +131,12 @@
<bit-section>
<h3 bitTypography="h2">{{ "paymentInformation" | i18n }}</h3>
<app-payment-v2 [showBankAccount]="false"></app-payment-v2>
<app-tax-info (taxInformationChanged)="onTaxInformationChanged()"></app-tax-info>
<app-manage-tax-information
*ngIf="taxInformation"
[showTaxIdField]="false"
[startWith]="taxInformation"
(taxInformationChanged)="onTaxInformationChanged($event)"
/>
<div class="tw-mb-4">
<div class="tw-text-muted tw-text-sm tw-flex tw-flex-col">
<span>{{ "planPrice" | i18n }}: {{ subtotal | currency: "USD $" }}</span>

View File

@@ -1,35 +1,37 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Component, ViewChild } from "@angular/core";
import { Component, OnInit, ViewChild } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormControl, FormGroup, Validators } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
import { combineLatest, concatMap, from, Observable, of, switchMap } from "rxjs";
import { debounceTime } from "rxjs/operators";
import { ManageTaxInformationComponent } from "@bitwarden/angular/billing/components";
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";
import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction";
import { TaxInformation } from "@bitwarden/common/billing/models/domain";
import { PreviewIndividualInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-individual-invoice.request";
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 { 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 { SyncService } from "@bitwarden/common/platform/sync";
import { ToastService } from "@bitwarden/components";
import { PaymentV2Component } from "../../shared/payment/payment-v2.component";
import { TaxInfoComponent } from "../../shared/tax-info.component";
@Component({
templateUrl: "./premium-v2.component.html",
})
export class PremiumV2Component {
export class PremiumV2Component implements OnInit {
@ViewChild(PaymentV2Component) paymentComponent: PaymentV2Component;
@ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent;
@ViewChild(ManageTaxInformationComponent) taxInfoComponent: ManageTaxInformationComponent;
protected hasPremiumFromAnyOrganization$: Observable<boolean>;
@@ -53,6 +55,8 @@ export class PremiumV2Component {
protected readonly premiumPrice = 10;
protected readonly storageGBPrice = 4;
protected taxInformation: TaxInformation;
constructor(
private activatedRoute: ActivatedRoute,
private apiService: ApiService,
@@ -67,6 +71,7 @@ export class PremiumV2Component {
private tokenService: TokenService,
private taxService: TaxServiceAbstraction,
private accountService: AccountService,
private logService: LogService,
) {
this.isSelfHost = this.platformUtilsService.isSelfHost();
@@ -104,6 +109,15 @@ export class PremiumV2Component {
});
}
async ngOnInit(): Promise<void> {
try {
const taxInfo = await this.apiService.getTaxInfo();
this.taxInformation = TaxInformation.from(taxInfo) || new TaxInformation();
} catch (e) {
this.logService.error(e);
}
}
finalizeUpgrade = async () => {
await this.apiService.refreshIdentityToken();
await this.syncService.fullSync(true);
@@ -155,8 +169,7 @@ export class PremiumV2Component {
};
submitPayment = async (): Promise<void> => {
this.taxInfoComponent.taxFormGroup.markAllAsTouched();
if (this.taxInfoComponent.taxFormGroup.invalid) {
if (!this.taxInfoComponent.validate()) {
return;
}
@@ -166,8 +179,14 @@ export class PremiumV2Component {
formData.append("paymentMethodType", type.toString());
formData.append("paymentToken", token);
formData.append("additionalStorageGb", this.addOnFormGroup.value.additionalStorage.toString());
formData.append("country", this.taxInfoComponent.country);
formData.append("postalCode", this.taxInfoComponent.postalCode);
const taxInformation = this.taxInfoComponent.getTaxInformation();
formData.append("country", taxInformation.country);
formData.append("postalCode", taxInformation.postalCode);
formData.append("line1", taxInformation.line1);
formData.append("line2", taxInformation.line2);
formData.append("city", taxInformation.city);
formData.append("state", taxInformation.state);
await this.apiService.postPremium(formData);
await this.finalizeUpgrade();
@@ -195,7 +214,7 @@ export class PremiumV2Component {
}
private refreshSalesTax(): void {
if (!this.taxInfoComponent.country || !this.taxInfoComponent.postalCode) {
if (!this.taxInformation.country || !this.taxInformation.postalCode) {
return;
}
const request: PreviewIndividualInvoiceRequest = {
@@ -203,8 +222,8 @@ export class PremiumV2Component {
additionalStorage: this.addOnFormGroup.value.additionalStorage,
},
taxInformation: {
postalCode: this.taxInfoComponent.postalCode,
country: this.taxInfoComponent.country,
postalCode: this.taxInformation.postalCode,
country: this.taxInformation.country,
},
};
@@ -222,7 +241,8 @@ export class PremiumV2Component {
});
}
protected onTaxInformationChanged(): void {
protected onTaxInformationChanged(event: TaxInformation): void {
this.taxInformation = event;
this.refreshSalesTax();
}
}