diff --git a/apps/web/src/app/organizations/organization-routing.module.ts b/apps/web/src/app/organizations/organization-routing.module.ts index 09184ec6b58..cb99f5b98d8 100644 --- a/apps/web/src/app/organizations/organization-routing.module.ts +++ b/apps/web/src/app/organizations/organization-routing.module.ts @@ -5,12 +5,12 @@ import { AuthGuard } from "@bitwarden/angular/guards/auth.guard"; import { Permissions } from "@bitwarden/common/enums/permissions"; import { OrganizationBillingTabComponent } from "../modules/organizations/billing/organization-billing-tab.component"; -import { OrganizationPaymentMethodComponent } from "../modules/organizations/billing/organization-payment-method.component"; import { OrganizationSubscriptionComponent } from "../modules/organizations/billing/organization-subscription.component"; import { ReportListComponent } from "../modules/organizations/reporting/report-list.component"; import { ReportingComponent } from "../modules/organizations/reporting/reporting.component"; import { OrganizationVaultModule } from "../modules/vault/modules/organization-vault/organization-vault.module"; import { BillingHistoryComponent } from "../settings/billing-history.component"; +import { PaymentMethodComponent } from "../settings/payment-method.component"; import { PermissionsGuard } from "./guards/permissions.guard"; import { OrganizationLayoutComponent } from "./layouts/organization-layout.component"; @@ -162,7 +162,7 @@ const routes: Routes = [ }, { path: "payment-method", - component: OrganizationPaymentMethodComponent, + component: PaymentMethodComponent, canActivate: [PermissionsGuard], data: { titleId: "paymentMethod", permissions: [Permissions.ManageBilling] }, }, diff --git a/apps/web/src/app/settings/payment-method.component.html b/apps/web/src/app/settings/payment-method.component.html index bae22bf4cf0..37d7c91f4a3 100644 --- a/apps/web/src/app/settings/payment-method.component.html +++ b/apps/web/src/app/settings/payment-method.component.html @@ -1,4 +1,4 @@ -
+

{{ "paymentMethod" | i18n }}

@@ -40,18 +40,63 @@

{{ "paymentMethod" | i18n }}

{{ "noPaymentMethod" | i18n }}

+ +

{{ "verifyBankAccountDesc" | i18n }} {{ "verifyBankAccountFailureWarning" | i18n }}

+
+ +
+
+
$0.
+
+ +
+ +
+
+
$0.
+
+ +
+ +
+

- + {{ "inAppPurchase" | i18n }} {{ paymentSource.description }}

@@ -66,4 +111,29 @@ *ngIf="showAdjustPayment" > + +

{{ "taxInformation" | i18n }}

+

{{ "taxInformationDesc" | i18n }}

+
+ + {{ "loading" | i18n }} +
+
+ + +
+
diff --git a/apps/web/src/app/settings/payment-method.component.ts b/apps/web/src/app/settings/payment-method.component.ts index 2cf9a179112..6cab4d2599e 100644 --- a/apps/web/src/app/settings/payment-method.component.ts +++ b/apps/web/src/app/settings/payment-method.component.ts @@ -1,37 +1,59 @@ -import { Component, OnInit } from "@angular/core"; -import { Router } from "@angular/router"; +import { Component, OnInit, ViewChild } from "@angular/core"; +import { ActivatedRoute, Router } from "@angular/router"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PaymentMethodType } from "@bitwarden/common/enums/paymentMethodType"; +import { VerifyBankRequest } from "@bitwarden/common/models/request/verifyBankRequest"; import { BillingPaymentResponse } from "@bitwarden/common/models/response/billingPaymentResponse"; +import { OrganizationResponse } from "@bitwarden/common/models/response/organizationResponse"; + +import { TaxInfoComponent } from "./tax-info.component"; @Component({ selector: "app-payment-method", templateUrl: "payment-method.component.html", }) export class PaymentMethodComponent implements OnInit { + @ViewChild(TaxInfoComponent) taxInfo: TaxInfoComponent; + loading = false; firstLoaded = false; showAdjustPayment = false; showAddCredit = false; billing: BillingPaymentResponse; + org: OrganizationResponse; paymentMethodType = PaymentMethodType; + organizationId: string; + verifyAmount1: number; + verifyAmount2: number; + + verifyBankPromise: Promise; + taxFormPromise: Promise; constructor( protected apiService: ApiService, protected i18nService: I18nService, protected platformUtilsService: PlatformUtilsService, - private router: Router + private router: Router, + private logService: LogService, + private route: ActivatedRoute ) {} async ngOnInit() { - if (this.platformUtilsService.isSelfHost()) { - this.router.navigate(["/settings/subscription"]); - } - await this.load(); - this.firstLoaded = true; + this.route.params.subscribe(async (params) => { + if (params.organizationId) { + this.organizationId = params.organizationId; + } else if (this.platformUtilsService.isSelfHost()) { + this.router.navigate(["/settings/subscription"]); + return; + } + + await this.load(); + this.firstLoaded = true; + }); } async load() { @@ -39,7 +61,16 @@ export class PaymentMethodComponent implements OnInit { return; } this.loading = true; - this.billing = await this.apiService.getUserBillingPayment(); + + if (this.forOrganization) { + const billingPromise = this.apiService.getOrganizationBilling(this.organizationId); + const orgPromise = this.apiService.getOrganization(this.organizationId); + + [this.billing, this.org] = await Promise.all([billingPromise, orgPromise]); + } else { + this.billing = await this.apiService.getUserBillingPayment(); + } + this.loading = false; } @@ -85,6 +116,37 @@ export class PaymentMethodComponent implements OnInit { } } + async verifyBank() { + if (this.loading || !this.forOrganization) { + return; + } + + try { + const request = new VerifyBankRequest(); + request.amount1 = this.verifyAmount1; + request.amount2 = this.verifyAmount2; + this.verifyBankPromise = this.apiService.postOrganizationVerifyBank( + this.organizationId, + request + ); + await this.verifyBankPromise; + this.platformUtilsService.showToast( + "success", + null, + this.i18nService.t("verifiedBankAccount") + ); + this.load(); + } catch (e) { + this.logService.error(e); + } + } + + async submitTaxInfo() { + this.taxFormPromise = this.taxInfo.submitTaxInfo(); + await this.taxFormPromise; + this.platformUtilsService.showToast("success", null, this.i18nService.t("taxInfoUpdated")); + } + get isCreditBalance() { return this.billing == null || this.billing.balance <= 0; } @@ -97,6 +159,36 @@ export class PaymentMethodComponent implements OnInit { return this.billing != null ? this.billing.paymentSource : null; } + get forOrganization() { + return this.organizationId != null; + } + + get headerClass() { + return this.forOrganization ? ["page-header"] : ["tabbed-header"]; + } + + get paymentSourceClasses() { + 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.AppleInApp: + return ["bwi-apple text-muted"]; + case PaymentMethodType.GoogleInApp: + return ["bwi-google text-muted"]; + case PaymentMethodType.PayPal: + return ["bwi-paypal text-primary"]; + default: + return []; + } + } + get paymentSourceInApp() { return ( this.paymentSource != null &&