Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PM-16937] Resolved Circular Dependency issues for Billing #12861

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { Location } from "@angular/common";
import { Component, OnDestroy } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormGroup } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
import { from, lastValueFrom, switchMap } from "rxjs";

Expand All @@ -17,9 +18,10 @@ import { PaymentSourceResponse } from "@bitwarden/common/billing/models/response
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SyncService } from "@bitwarden/common/platform/sync";
import { DialogService, ToastService } from "@bitwarden/components";
import { DialogService } from "@bitwarden/components";

import { FreeTrial } from "../../../core/types/free-trial";
import { BillingNotificationService } from "../../services/billing-notification.service";
import { TrialFlowService } from "../../services/trial-flow.service";
import {
AddCreditDialogResult,
Expand All @@ -42,6 +44,7 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
protected freeTrialData: FreeTrial;
organization: Organization;
organizationSubscriptionResponse: OrganizationSubscriptionResponse;
protected verifyBankForm: FormGroup;

loading = true;

Expand All @@ -56,11 +59,11 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
private router: Router,
private toastService: ToastService,
private location: Location,
private trialFlowService: TrialFlowService,
private organizationService: OrganizationService,
protected syncService: SyncService,
private notificationService: BillingNotificationService,
) {
this.activatedRoute.params
.pipe(
Expand Down Expand Up @@ -182,12 +185,12 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
};

protected verifyBankAccount = async (request: VerifyBankAccountRequest): Promise<void> => {
await this.billingApiService.verifyOrganizationBankAccount(this.organizationId, request);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("verifiedBankAccount"),
});
try {
await this.billingApiService.verifyOrganizationBankAccount(this.organizationId, request);
this.notificationService.showSuccess("verifiedBankAccount");
} catch (error) {
this.notificationService.handleError(error);
}
};

protected get accountCreditHeaderText(): string {
Expand Down Expand Up @@ -221,4 +224,19 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
const key = this.paymentSource == null ? "addPaymentMethod" : "changePaymentMethod";
return this.i18nService.t(key);
}

verifyBank = async () => {
if (this.loading || !this.organization) {
return;
}

try {
const descriptorCode = `${this.verifyBankForm.value.amount1}${this.verifyBankForm.value.amount2}`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โŒ We don't use micro-deposits anymore. The verify-bank-account.component handles this. If it's causing a circular dependency, it can presumably just be moved to web.

const request = new VerifyBankAccountRequest(descriptorCode);
await this.verifyBankAccount(request);
await this.load();
} catch (error) {
this.notificationService.handleError(error);
}
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Injectable } from "@angular/core";

import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { ToastService } from "@bitwarden/components";

@Injectable({
providedIn: "root",
})
export class BillingNotificationService {
constructor(
private i18nService: I18nService,
private toastService: ToastService,
) {}

handleError(error: unknown, customMessage?: string) {
const message = this.getErrorMessage(error, customMessage);
this.toastService.showToast({
variant: "error",
title: null,
message,
});
throw error; // Re-throw to allow caller to handle if needed
}

showSuccess(messageKey: string) {
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t(messageKey),
});
}

private getErrorMessage(error: unknown, customMessage?: string): string {
if (error instanceof ErrorResponse) {
return error.getSingleMessage();
}
return this.i18nService.t(customMessage ?? "errorOccurred");
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
import { Component, forwardRef, Inject, OnInit, ViewChild } from "@angular/core";

Expand All @@ -14,8 +12,9 @@
import { UpdatePaymentMethodRequest } from "@bitwarden/common/billing/models/request/update-payment-method.request";
import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { DialogService, ToastService } from "@bitwarden/components";
import { DialogService } from "@bitwarden/components";

import { BillingNotificationService } from "../../services/billing-notification.service";
import { PaymentV2Component } from "../payment/payment-v2.component";

export interface AdjustPaymentDialogV2Params {
Expand Down Expand Up @@ -54,7 +53,7 @@
@Inject(DIALOG_DATA) protected dialogParams: AdjustPaymentDialogV2Params,
private dialogRef: DialogRef<AdjustPaymentDialogV2ResultType>,
private i18nService: I18nService,
private toastService: ToastService,
private notificationService: BillingNotificationService,
) {
const key = this.dialogParams.initialPaymentMethod ? "changePaymentMethod" : "addPaymentMethod";
this.dialogHeader = this.i18nService.t(key);
Expand Down Expand Up @@ -109,30 +108,25 @@
await this.updateOrganizationPaymentMethod();
}

this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("updatedPaymentMethod"),
});

this.notificationService.showSuccess("updatedPaymentMethod");
this.dialogRef.close(AdjustPaymentDialogV2ResultType.Submitted);
} catch (error) {

Check failure on line 113 in apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog-v2.component.ts

View workflow job for this annotation

GitHub Actions / Lint

'error' is defined but never used
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t(error.message) || error.message,
});
// Error is already handled by the methods called above
}
};

private updateOrganizationPaymentMethod = async () => {
const paymentSource = await this.paymentComponent.tokenize();
try {
const paymentSource = await this.paymentComponent.tokenize();

const request = new UpdatePaymentMethodRequest();
request.paymentSource = paymentSource;
request.taxInformation = ExpandedTaxInfoUpdateRequest.From(this.taxInformation);
const request = new UpdatePaymentMethodRequest();
request.paymentSource = paymentSource;
request.taxInformation = ExpandedTaxInfoUpdateRequest.From(this.taxInformation);

await this.billingApiService.updateOrganizationPaymentMethod(this.organizationId, request);
await this.billingApiService.updateOrganizationPaymentMethod(this.organizationId, request);
} catch (error) {
this.notificationService.handleError(error);
}
};

protected get showTaxIdField(): boolean {
Expand All @@ -150,20 +144,24 @@
}

private updatePremiumUserPaymentMethod = async () => {
const { type, token } = await this.paymentComponent.tokenize();

const request = new PaymentRequest();
request.paymentMethodType = type;
request.paymentToken = token;
request.country = this.taxInformation.country;
request.postalCode = this.taxInformation.postalCode;
request.taxId = this.taxInformation.taxId;
request.state = this.taxInformation.state;
request.line1 = this.taxInformation.line1;
request.line2 = this.taxInformation.line2;
request.city = this.taxInformation.city;
request.state = this.taxInformation.state;
await this.apiService.postAccountPayment(request);
try {
const { type, token } = await this.paymentComponent.tokenize();

const request = new PaymentRequest();
request.paymentMethodType = type;
request.paymentToken = token;
request.country = this.taxInformation.country;
request.postalCode = this.taxInformation.postalCode;
request.taxId = this.taxInformation.taxId;
request.state = this.taxInformation.state;
request.line1 = this.taxInformation.line1;
request.line2 = this.taxInformation.line2;
request.city = this.taxInformation.city;
request.state = this.taxInformation.state;
await this.apiService.postAccountPayment(request);
} catch (error) {
this.notificationService.handleError(error);
}
};

static open = (
Expand Down
22 changes: 6 additions & 16 deletions libs/common/src/billing/services/billing-api.service.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { ToastService } from "@bitwarden/components";
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
import { UpdatePaymentMethodRequest } from "@bitwarden/common/billing/models/request/update-payment-method.request";
import { VerifyBankAccountRequest } from "@bitwarden/common/billing/models/request/verify-bank-account.request";
import { InvoicesResponse } from "@bitwarden/common/billing/models/response/invoices.response";
import { PaymentMethodResponse } from "@bitwarden/common/billing/models/response/payment-method.response";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";

import { ApiService } from "../../abstractions/api.service";
import { OrganizationCreateRequest } from "../../admin-console/models/request/organization-create.request";
import { ProviderOrganizationOrganizationDetailsResponse } from "../../admin-console/models/response/provider/provider-organization.response";
import { ErrorResponse } from "../../models/response/error.response";
import { ListResponse } from "../../models/response/list.response";
import { LogService } from "../../platform/abstractions/log.service";
import { BillingApiServiceAbstraction } from "../abstractions";
import { PaymentMethodType } from "../enums";
import { CreateClientOrganizationRequest } from "../models/request/create-client-organization.request";
import { ExpandedTaxInfoUpdateRequest } from "../models/request/expanded-tax-info-update.request";
import { SubscriptionCancellationRequest } from "../models/request/subscription-cancellation.request";
import { UpdateClientOrganizationRequest } from "../models/request/update-client-organization.request";
import { UpdatePaymentMethodRequest } from "../models/request/update-payment-method.request";
import { VerifyBankAccountRequest } from "../models/request/verify-bank-account.request";
import { InvoicesResponse } from "../models/response/invoices.response";
import { OrganizationBillingMetadataResponse } from "../models/response/organization-billing-metadata.response";
import { PaymentMethodResponse } from "../models/response/payment-method.response";
import { PlanResponse } from "../models/response/plan.response";
import { ProviderSubscriptionResponse } from "../models/response/provider-subscription-response";

export class BillingApiService implements BillingApiServiceAbstraction {
constructor(
private apiService: ApiService,
private logService: LogService,
private toastService: ToastService,
) {}

cancelOrganizationSubscription(
Expand Down Expand Up @@ -233,13 +230,6 @@ export class BillingApiService implements BillingApiServiceAbstraction {
return await request();
} catch (error) {
this.logService.error(error);
if (error instanceof ErrorResponse) {
this.toastService.showToast({
variant: "error",
title: null,
message: error.getSingleMessage(),
});
}
throw error;
}
}
Expand Down
1 change: 0 additions & 1 deletion libs/common/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"@bitwarden/auth/common": ["../auth/src/common"],
// TODO: Remove once circular dependencies in admin-console, auth and key-management are resolved
"@bitwarden/common/*": ["../common/src/*"],
"@bitwarden/components": ["../components/src"],
"@bitwarden/key-management": ["../key-management/src"],
"@bitwarden/platform": ["../platform/src"]
}
Expand Down
Loading