mirror of
https://github.com/bitwarden/browser
synced 2025-12-17 00:33:44 +00:00
[AC-2959] ACH Direct Debit POC (#10746)
* (No Logic) Fix typo in billing-api-service.abstraction file name * (Cleanup) Remove payment method components and API methods from provider portal Product team decided not to have a payment method page in the provider portal for consolidated billing. This just removes all the unused components and API methods. * Add organization endpoints to support new payment method behavior * Add payment-v2.component This component existed in the libs folder because we used it for the provider portal, but since we've removed payment functionality from the provider portal, I moved it into web in this commit. * (No Logic) Move existing payment.component into new payment component folder * Add verify-bank-account.component This component existed in the libs folder because we used it for the provider portal, but since we've removed payment functionality from the provider portal, I moved it into web in this commit. * Add adjust-payment-dialog-v2.component * (No Logic) Move existing adjust-payment-dialog.component into new adjust-payment-dialog component folder * Add organization-payment-method.component * Add feature flag: AC-2476-deprecate-stripe-sources-api * Pivot organization payment method route on new feature flag * Fix broken test
This commit is contained in:
@@ -16,8 +16,6 @@ import {
|
||||
ManageClientsComponent,
|
||||
ManageClientSubscriptionDialogComponent,
|
||||
ProviderBillingHistoryComponent,
|
||||
ProviderPaymentMethodComponent,
|
||||
ProviderSelectPaymentMethodDialogComponent,
|
||||
ProviderSubscriptionComponent,
|
||||
ProviderSubscriptionStatusComponent,
|
||||
} from "../../billing/providers";
|
||||
@@ -80,8 +78,6 @@ import { SetupComponent } from "./setup/setup.component";
|
||||
ManageClientSubscriptionDialogComponent,
|
||||
ProviderBillingHistoryComponent,
|
||||
ProviderSubscriptionComponent,
|
||||
ProviderSelectPaymentMethodDialogComponent,
|
||||
ProviderPaymentMethodComponent,
|
||||
ProviderSubscriptionStatusComponent,
|
||||
],
|
||||
providers: [WebProviderService],
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Injectable } from "@angular/core";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
|
||||
import { ProviderAddOrganizationRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-add-organization.request";
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
||||
import { PlanType } from "@bitwarden/common/billing/enums";
|
||||
import { CreateClientOrganizationRequest } from "@bitwarden/common/billing/models/request/create-client-organization.request";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
|
||||
@@ -2,7 +2,7 @@ import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||
import { Component, Inject, OnInit } from "@angular/core";
|
||||
import { FormControl, FormGroup, Validators } from "@angular/forms";
|
||||
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
||||
import { PlanType } from "@bitwarden/common/billing/enums";
|
||||
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
|
||||
import { ProviderPlanResponse } from "@bitwarden/common/billing/models/response/provider-subscription-response";
|
||||
|
||||
@@ -2,7 +2,7 @@ import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||
import { Component, Inject } from "@angular/core";
|
||||
import { FormBuilder, Validators } from "@angular/forms";
|
||||
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
||||
import { UpdateClientOrganizationRequest } from "@bitwarden/common/billing/models/request/update-client-organization.request";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { DialogService, ToastService } from "@bitwarden/components";
|
||||
|
||||
@@ -9,7 +9,7 @@ import { ProviderService } from "@bitwarden/common/admin-console/abstractions/pr
|
||||
import { ProviderUserType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
|
||||
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
|
||||
import { BillingApiServiceAbstraction as BillingApiService } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
|
||||
import { BillingApiServiceAbstraction as BillingApiService } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
||||
import { hasConsolidatedBilling } from "@bitwarden/common/billing/abstractions/provider-billing.service.abstraction";
|
||||
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
export * from "./billing-history/provider-billing-history.component";
|
||||
export * from "./clients";
|
||||
export * from "./guards/has-consolidated-billing.guard";
|
||||
export * from "./payment-method/provider-select-payment-method-dialog.component";
|
||||
export * from "./payment-method/provider-payment-method.component";
|
||||
export * from "./subscription/provider-subscription.component";
|
||||
export * from "./subscription/provider-subscription-status.component";
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
<app-header></app-header>
|
||||
<ng-container *ngIf="loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||
</ng-container>
|
||||
<bit-container *ngIf="!loading">
|
||||
<!-- Account Credit -->
|
||||
<ng-container>
|
||||
<h2 bitTypography="h2">
|
||||
{{ "accountCredit" | i18n }}
|
||||
</h2>
|
||||
<p class="tw-text-lg tw-font-bold">{{ accountCredit | currency: "$" }}</p>
|
||||
<p bitTypography="body1">{{ "creditAppliedDesc" | i18n }}</p>
|
||||
<button type="button" bitButton buttonType="secondary" [bitAction]="addAccountCredit">
|
||||
{{ "addCredit" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
<!-- Payment Method -->
|
||||
<ng-container>
|
||||
<h2 class="spaced-header">{{ "paymentMethod" | i18n }}</h2>
|
||||
<p *ngIf="!hasPaymentMethod">{{ "noPaymentMethod" | i18n }}</p>
|
||||
<app-verify-bank-account
|
||||
[onSubmit]="verifyBankAccount"
|
||||
(verificationSubmitted)="onDataUpdated()"
|
||||
*ngIf="hasUnverifiedPaymentMethod"
|
||||
/>
|
||||
<ng-container *ngIf="hasPaymentMethod">
|
||||
<p>
|
||||
<i class="bwi bwi-fw" [ngClass]="paymentMethodClass"></i>
|
||||
{{ paymentMethodDescription }}
|
||||
</p>
|
||||
</ng-container>
|
||||
<button type="button" bitButton buttonType="secondary" [bitAction]="changePaymentMethod">
|
||||
{{ (hasPaymentMethod ? "changePaymentMethod" : "addPaymentMethod") | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
<!-- Tax Information -->
|
||||
<ng-container>
|
||||
<h2 class="spaced-header">{{ "taxInformation" | i18n }}</h2>
|
||||
<p>{{ "taxInformationDesc" | i18n }}</p>
|
||||
<app-manage-tax-information
|
||||
*ngIf="taxInformation"
|
||||
[startWith]="taxInformation"
|
||||
[onSubmit]="updateTaxInformation"
|
||||
(taxInformationUpdated)="onDataUpdated()"
|
||||
/>
|
||||
</ng-container>
|
||||
</bit-container>
|
||||
@@ -1,140 +0,0 @@
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { from, lastValueFrom, Subject, switchMap } from "rxjs";
|
||||
import { takeUntil } from "rxjs/operators";
|
||||
|
||||
import { openAddAccountCreditDialog } from "@bitwarden/angular/billing/components";
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
||||
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
||||
import { MaskedPaymentMethod, TaxInformation } from "@bitwarden/common/billing/models/domain";
|
||||
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
|
||||
import { VerifyBankAccountRequest } from "@bitwarden/common/billing/models/request/verify-bank-account.request";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { DialogService, ToastService } from "@bitwarden/components";
|
||||
|
||||
import {
|
||||
openProviderSelectPaymentMethodDialog,
|
||||
ProviderSelectPaymentMethodDialogResultType,
|
||||
} from "./provider-select-payment-method-dialog.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-provider-payment-method",
|
||||
templateUrl: "./provider-payment-method.component.html",
|
||||
})
|
||||
export class ProviderPaymentMethodComponent implements OnInit, OnDestroy {
|
||||
protected providerId: string;
|
||||
protected loading: boolean;
|
||||
|
||||
protected accountCredit: number;
|
||||
protected maskedPaymentMethod: MaskedPaymentMethod;
|
||||
protected taxInformation: TaxInformation;
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private billingApiService: BillingApiServiceAbstraction,
|
||||
private dialogService: DialogService,
|
||||
private i18nService: I18nService,
|
||||
private toastService: ToastService,
|
||||
) {}
|
||||
|
||||
addAccountCredit = () =>
|
||||
openAddAccountCreditDialog(this.dialogService, {
|
||||
data: {
|
||||
providerId: this.providerId,
|
||||
},
|
||||
});
|
||||
|
||||
changePaymentMethod = async () => {
|
||||
const dialogRef = openProviderSelectPaymentMethodDialog(this.dialogService, {
|
||||
data: {
|
||||
providerId: this.providerId,
|
||||
},
|
||||
});
|
||||
|
||||
const result = await lastValueFrom(dialogRef.closed);
|
||||
|
||||
if (result == ProviderSelectPaymentMethodDialogResultType.Submitted) {
|
||||
await this.load();
|
||||
}
|
||||
};
|
||||
|
||||
async load() {
|
||||
this.loading = true;
|
||||
const paymentInformation = await this.billingApiService.getProviderPaymentInformation(
|
||||
this.providerId,
|
||||
);
|
||||
this.accountCredit = paymentInformation.accountCredit;
|
||||
this.maskedPaymentMethod = MaskedPaymentMethod.from(paymentInformation.paymentMethod);
|
||||
this.taxInformation = TaxInformation.from(paymentInformation.taxInformation);
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
onDataUpdated = async () => await this.load();
|
||||
|
||||
updateTaxInformation = async (taxInformation: TaxInformation) => {
|
||||
const request = ExpandedTaxInfoUpdateRequest.From(taxInformation);
|
||||
await this.billingApiService.updateProviderTaxInformation(this.providerId, request);
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("updatedTaxInformation"),
|
||||
});
|
||||
};
|
||||
|
||||
verifyBankAccount = async (amount1: number, amount2: number) => {
|
||||
const request = new VerifyBankAccountRequest(amount1, amount2);
|
||||
await this.billingApiService.verifyProviderBankAccount(this.providerId, request);
|
||||
};
|
||||
|
||||
ngOnInit() {
|
||||
this.activatedRoute.params
|
||||
.pipe(
|
||||
switchMap(({ providerId }) => {
|
||||
this.providerId = providerId;
|
||||
return from(this.load());
|
||||
}),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
protected get hasPaymentMethod(): boolean {
|
||||
return !!this.maskedPaymentMethod;
|
||||
}
|
||||
|
||||
protected get hasUnverifiedPaymentMethod(): boolean {
|
||||
return !!this.maskedPaymentMethod && this.maskedPaymentMethod.needsVerification;
|
||||
}
|
||||
|
||||
protected get paymentMethodClass(): string[] {
|
||||
switch (this.maskedPaymentMethod.type) {
|
||||
case PaymentMethodType.Card:
|
||||
return ["bwi-credit-card"];
|
||||
case PaymentMethodType.BankAccount:
|
||||
return ["bwi-bank"];
|
||||
case PaymentMethodType.PayPal:
|
||||
return ["bwi-paypal tw-text-primary"];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
protected get paymentMethodDescription(): string {
|
||||
let description = this.maskedPaymentMethod.description;
|
||||
if (this.maskedPaymentMethod.type === PaymentMethodType.BankAccount) {
|
||||
if (this.hasUnverifiedPaymentMethod) {
|
||||
description += " - " + this.i18nService.t("unverified");
|
||||
} else {
|
||||
description += " - " + this.i18nService.t("verified");
|
||||
}
|
||||
}
|
||||
return description;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<form [formGroup]="formGroup" [bitSubmit]="submit">
|
||||
<bit-dialog dialogSize="large">
|
||||
<span bitDialogTitle class="tw-font-semibold">
|
||||
{{ "addPaymentMethod" | i18n }}
|
||||
</span>
|
||||
<ng-container bitDialogContent>
|
||||
<app-select-payment-method [showAccountCredit]="false" />
|
||||
</ng-container>
|
||||
<ng-container bitDialogFooter>
|
||||
<button bitButton bitFormButton buttonType="primary" type="submit">
|
||||
{{ "submit" | i18n }}
|
||||
</button>
|
||||
<button bitButton buttonType="secondary" type="button" [bitDialogClose]="ResultType.Closed">
|
||||
{{ "cancel" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</bit-dialog>
|
||||
</form>
|
||||
@@ -1,60 +0,0 @@
|
||||
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||
import { Component, EventEmitter, Inject, Output, ViewChild } from "@angular/core";
|
||||
import { FormGroup } from "@angular/forms";
|
||||
|
||||
import { SelectPaymentMethodComponent } from "@bitwarden/angular/billing/components";
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
||||
import { TokenizedPaymentMethodRequest } from "@bitwarden/common/billing/models/request/tokenized-payment-method.request";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { DialogService, ToastService } from "@bitwarden/components";
|
||||
|
||||
type ProviderSelectPaymentMethodDialogParams = {
|
||||
providerId: string;
|
||||
};
|
||||
|
||||
export enum ProviderSelectPaymentMethodDialogResultType {
|
||||
Closed = "closed",
|
||||
Submitted = "submitted",
|
||||
}
|
||||
|
||||
export const openProviderSelectPaymentMethodDialog = (
|
||||
dialogService: DialogService,
|
||||
dialogConfig: DialogConfig<ProviderSelectPaymentMethodDialogParams>,
|
||||
) =>
|
||||
dialogService.open<
|
||||
ProviderSelectPaymentMethodDialogResultType,
|
||||
ProviderSelectPaymentMethodDialogParams
|
||||
>(ProviderSelectPaymentMethodDialogComponent, dialogConfig);
|
||||
|
||||
@Component({
|
||||
templateUrl: "provider-select-payment-method-dialog.component.html",
|
||||
})
|
||||
export class ProviderSelectPaymentMethodDialogComponent {
|
||||
@ViewChild(SelectPaymentMethodComponent)
|
||||
selectPaymentMethodComponent: SelectPaymentMethodComponent;
|
||||
@Output() providerPaymentMethodUpdated = new EventEmitter();
|
||||
|
||||
protected readonly formGroup = new FormGroup({});
|
||||
protected readonly ResultType = ProviderSelectPaymentMethodDialogResultType;
|
||||
|
||||
constructor(
|
||||
private billingApiService: BillingApiServiceAbstraction,
|
||||
@Inject(DIALOG_DATA) private dialogParams: ProviderSelectPaymentMethodDialogParams,
|
||||
private dialogRef: DialogRef<ProviderSelectPaymentMethodDialogResultType>,
|
||||
private i18nService: I18nService,
|
||||
private toastService: ToastService,
|
||||
) {}
|
||||
|
||||
submit = async () => {
|
||||
const tokenizedPaymentMethod = await this.selectPaymentMethodComponent.tokenizePaymentMethod();
|
||||
const request = TokenizedPaymentMethodRequest.From(tokenizedPaymentMethod);
|
||||
await this.billingApiService.updateProviderPaymentMethod(this.dialogParams.providerId, request);
|
||||
this.providerPaymentMethodUpdated.emit();
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("updatedPaymentMethod"),
|
||||
});
|
||||
this.dialogRef.close(this.ResultType.Submitted);
|
||||
};
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { Subject, concatMap, takeUntil } from "rxjs";
|
||||
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
||||
import { TaxInformation } from "@bitwarden/common/billing/models/domain";
|
||||
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
|
||||
import {
|
||||
|
||||
Reference in New Issue
Block a user