[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:
parent
b0ffac04af
commit
a58642e370
|
@ -32,7 +32,7 @@ import {
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||||
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
|
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
|
||||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
|
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
||||||
import { isNotSelfUpgradable, ProductTierType } from "@bitwarden/common/billing/enums";
|
import { isNotSelfUpgradable, ProductTierType } from "@bitwarden/common/billing/enums";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
|
|
|
@ -37,7 +37,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
||||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||||
import { DialogService, ToastService } from "@bitwarden/components";
|
import { DialogService, ToastService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { PaymentComponent } from "../shared/payment.component";
|
import { PaymentComponent } from "../shared/payment/payment.component";
|
||||||
import { TaxInfoComponent } from "../shared/tax-info.component";
|
import { TaxInfoComponent } from "../shared/tax-info.component";
|
||||||
|
|
||||||
type ChangePlanDialogParams = {
|
type ChangePlanDialogParams = {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from "@angular/core";
|
||||||
import { RouterModule, Routes } from "@angular/router";
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
|
|
||||||
|
import { featureFlaggedRoute } from "@bitwarden/angular/platform/utils/feature-flagged-route";
|
||||||
import { canAccessBillingTab } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { canAccessBillingTab } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
|
|
||||||
import { organizationPermissionsGuard } from "../../admin-console/organizations/guards/org-permissions.guard";
|
import { organizationPermissionsGuard } from "../../admin-console/organizations/guards/org-permissions.guard";
|
||||||
import { organizationIsUnmanaged } from "../../billing/guards/organization-is-unmanaged.guard";
|
import { organizationIsUnmanaged } from "../../billing/guards/organization-is-unmanaged.guard";
|
||||||
|
@ -11,6 +13,7 @@ import { PaymentMethodComponent } from "../shared";
|
||||||
import { OrgBillingHistoryViewComponent } from "./organization-billing-history-view.component";
|
import { OrgBillingHistoryViewComponent } from "./organization-billing-history-view.component";
|
||||||
import { OrganizationSubscriptionCloudComponent } from "./organization-subscription-cloud.component";
|
import { OrganizationSubscriptionCloudComponent } from "./organization-subscription-cloud.component";
|
||||||
import { OrganizationSubscriptionSelfhostComponent } from "./organization-subscription-selfhost.component";
|
import { OrganizationSubscriptionSelfhostComponent } from "./organization-subscription-selfhost.component";
|
||||||
|
import { OrganizationPaymentMethodComponent } from "./payment-method/organization-payment-method.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
|
@ -25,17 +28,21 @@ const routes: Routes = [
|
||||||
: OrganizationSubscriptionCloudComponent,
|
: OrganizationSubscriptionCloudComponent,
|
||||||
data: { titleId: "subscription" },
|
data: { titleId: "subscription" },
|
||||||
},
|
},
|
||||||
{
|
...featureFlaggedRoute({
|
||||||
path: "payment-method",
|
defaultComponent: PaymentMethodComponent,
|
||||||
component: PaymentMethodComponent,
|
flaggedComponent: OrganizationPaymentMethodComponent,
|
||||||
canActivate: [
|
featureFlag: FeatureFlag.AC2476_DeprecateStripeSourcesAPI,
|
||||||
organizationPermissionsGuard((org) => org.canEditPaymentMethods),
|
routeOptions: {
|
||||||
organizationIsUnmanaged,
|
path: "payment-method",
|
||||||
],
|
canActivate: [
|
||||||
data: {
|
organizationPermissionsGuard((org) => org.canEditPaymentMethods),
|
||||||
titleId: "paymentMethod",
|
organizationIsUnmanaged,
|
||||||
|
],
|
||||||
|
data: {
|
||||||
|
titleId: "paymentMethod",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
{
|
{
|
||||||
path: "history",
|
path: "history",
|
||||||
component: OrgBillingHistoryViewComponent,
|
component: OrgBillingHistoryViewComponent,
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { OrganizationBillingRoutingModule } from "./organization-billing-routing
|
||||||
import { OrganizationPlansComponent } from "./organization-plans.component";
|
import { OrganizationPlansComponent } from "./organization-plans.component";
|
||||||
import { OrganizationSubscriptionCloudComponent } from "./organization-subscription-cloud.component";
|
import { OrganizationSubscriptionCloudComponent } from "./organization-subscription-cloud.component";
|
||||||
import { OrganizationSubscriptionSelfhostComponent } from "./organization-subscription-selfhost.component";
|
import { OrganizationSubscriptionSelfhostComponent } from "./organization-subscription-selfhost.component";
|
||||||
|
import { OrganizationPaymentMethodComponent } from "./payment-method/organization-payment-method.component";
|
||||||
import { SecretsManagerAdjustSubscriptionComponent } from "./sm-adjust-subscription.component";
|
import { SecretsManagerAdjustSubscriptionComponent } from "./sm-adjust-subscription.component";
|
||||||
import { SecretsManagerSubscribeStandaloneComponent } from "./sm-subscribe-standalone.component";
|
import { SecretsManagerSubscribeStandaloneComponent } from "./sm-subscribe-standalone.component";
|
||||||
import { SubscriptionHiddenComponent } from "./subscription-hidden.component";
|
import { SubscriptionHiddenComponent } from "./subscription-hidden.component";
|
||||||
|
@ -42,6 +43,7 @@ import { SubscriptionStatusComponent } from "./subscription-status.component";
|
||||||
SubscriptionHiddenComponent,
|
SubscriptionHiddenComponent,
|
||||||
SubscriptionStatusComponent,
|
SubscriptionStatusComponent,
|
||||||
ChangePlanDialogComponent,
|
ChangePlanDialogComponent,
|
||||||
|
OrganizationPaymentMethodComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class OrganizationBillingModule {}
|
export class OrganizationBillingModule {}
|
||||||
|
|
|
@ -41,7 +41,7 @@ import { ToastService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { OrganizationCreateModule } from "../../admin-console/organizations/create/organization-create.module";
|
import { OrganizationCreateModule } from "../../admin-console/organizations/create/organization-create.module";
|
||||||
import { BillingSharedModule, secretsManagerSubscribeFormFactory } from "../shared";
|
import { BillingSharedModule, secretsManagerSubscribeFormFactory } from "../shared";
|
||||||
import { PaymentComponent } from "../shared/payment.component";
|
import { PaymentComponent } from "../shared/payment/payment.component";
|
||||||
import { TaxInfoComponent } from "../shared/tax-info.component";
|
import { TaxInfoComponent } from "../shared/tax-info.component";
|
||||||
|
|
||||||
interface OnSuccessArgs {
|
interface OnSuccessArgs {
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
<app-header></app-header>
|
||||||
|
<bit-container>
|
||||||
|
<ng-container *ngIf="loading">
|
||||||
|
<i
|
||||||
|
class="bwi bwi-spinner bwi-spin tw-text-muted"
|
||||||
|
title="{{ 'loading' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="!loading">
|
||||||
|
<!-- Account Credit -->
|
||||||
|
<bit-section>
|
||||||
|
<h2 bitTypography="h2">
|
||||||
|
{{ accountCreditHeaderText }}
|
||||||
|
</h2>
|
||||||
|
<p class="tw-text-lg tw-font-bold">{{ Math.abs(accountCredit) | currency: "$" }}</p>
|
||||||
|
<p bitTypography="body1">{{ "creditAppliedDesc" | i18n }}</p>
|
||||||
|
<button type="button" bitButton buttonType="secondary" [bitAction]="addAccountCredit">
|
||||||
|
{{ "addCredit" | i18n }}
|
||||||
|
</button>
|
||||||
|
</bit-section>
|
||||||
|
<!-- Payment Method -->
|
||||||
|
<bit-section>
|
||||||
|
<h2 bitTypography="h2">{{ "paymentMethod" | i18n }}</h2>
|
||||||
|
<p *ngIf="!paymentSource" bitTypography="body1">{{ "noPaymentMethod" | i18n }}</p>
|
||||||
|
<ng-container *ngIf="paymentSource">
|
||||||
|
<app-verify-bank-account
|
||||||
|
*ngIf="paymentSource.needsVerification"
|
||||||
|
[onSubmit]="verifyBankAccount"
|
||||||
|
(submitted)="load()"
|
||||||
|
>
|
||||||
|
</app-verify-bank-account>
|
||||||
|
<p>
|
||||||
|
<i class="bwi bwi-fw" [ngClass]="paymentSourceClasses"></i>
|
||||||
|
{{ paymentSource.description }}
|
||||||
|
<span *ngIf="paymentSource.needsVerification">- {{ "unverified" | i18n }}</span>
|
||||||
|
</p>
|
||||||
|
</ng-container>
|
||||||
|
<button type="button" bitButton buttonType="secondary" [bitAction]="updatePaymentMethod">
|
||||||
|
{{ updatePaymentSourceButtonText }}
|
||||||
|
</button>
|
||||||
|
<p *ngIf="subscriptionIsUnpaid" bitTypography="body1">
|
||||||
|
{{ "paymentChargedWithUnpaidSubscription" | i18n }}
|
||||||
|
</p>
|
||||||
|
</bit-section>
|
||||||
|
<!-- Tax Information -->
|
||||||
|
<bit-section>
|
||||||
|
<h2 bitTypography="h2">{{ "taxInformation" | i18n }}</h2>
|
||||||
|
<p bitTypography="body1">{{ "taxInformationDesc" | i18n }}</p>
|
||||||
|
<app-tax-info></app-tax-info>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
bitButton
|
||||||
|
bitFormButton
|
||||||
|
buttonType="primary"
|
||||||
|
[bitAction]="updateTaxInformation"
|
||||||
|
>
|
||||||
|
{{ "save" | i18n }}
|
||||||
|
</button>
|
||||||
|
</bit-section>
|
||||||
|
</ng-container>
|
||||||
|
</bit-container>
|
|
@ -0,0 +1,169 @@
|
||||||
|
import { Component, ViewChild } from "@angular/core";
|
||||||
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
import { from, lastValueFrom, switchMap } from "rxjs";
|
||||||
|
|
||||||
|
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
||||||
|
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
||||||
|
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 { PaymentSourceResponse } from "@bitwarden/common/billing/models/response/payment-source.response";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
import { DialogService, ToastService } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import { TaxInfoComponent } from "../../shared";
|
||||||
|
import {
|
||||||
|
AddCreditDialogResult,
|
||||||
|
openAddCreditDialog,
|
||||||
|
} from "../../shared/add-credit-dialog.component";
|
||||||
|
import {
|
||||||
|
AdjustPaymentDialogV2Component,
|
||||||
|
AdjustPaymentDialogV2ResultType,
|
||||||
|
} from "../../shared/adjust-payment-dialog/adjust-payment-dialog-v2.component";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: "./organization-payment-method.component.html",
|
||||||
|
})
|
||||||
|
export class OrganizationPaymentMethodComponent {
|
||||||
|
@ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent;
|
||||||
|
|
||||||
|
organizationId: string;
|
||||||
|
accountCredit: number;
|
||||||
|
paymentSource?: PaymentSourceResponse;
|
||||||
|
subscriptionStatus?: string;
|
||||||
|
|
||||||
|
loading = true;
|
||||||
|
|
||||||
|
protected readonly Math = Math;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private activatedRoute: ActivatedRoute,
|
||||||
|
private billingApiService: BillingApiServiceAbstraction,
|
||||||
|
private dialogService: DialogService,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private platformUtilsService: PlatformUtilsService,
|
||||||
|
private router: Router,
|
||||||
|
private toastService: ToastService,
|
||||||
|
) {
|
||||||
|
this.activatedRoute.params
|
||||||
|
.pipe(
|
||||||
|
takeUntilDestroyed(),
|
||||||
|
switchMap(({ organizationId }) => {
|
||||||
|
if (this.platformUtilsService.isSelfHost()) {
|
||||||
|
return from(this.router.navigate(["/settings/subscription"]));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.organizationId = organizationId;
|
||||||
|
return from(this.load());
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected addAccountCredit = async (): Promise<void> => {
|
||||||
|
const dialogRef = openAddCreditDialog(this.dialogService, {
|
||||||
|
data: {
|
||||||
|
organizationId: this.organizationId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await lastValueFrom(dialogRef.closed);
|
||||||
|
|
||||||
|
if (result === AddCreditDialogResult.Added) {
|
||||||
|
await this.load();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
protected load = async (): Promise<void> => {
|
||||||
|
this.loading = true;
|
||||||
|
const { accountCredit, paymentSource, subscriptionStatus } =
|
||||||
|
await this.billingApiService.getOrganizationPaymentMethod(this.organizationId);
|
||||||
|
this.accountCredit = accountCredit;
|
||||||
|
this.paymentSource = paymentSource;
|
||||||
|
this.subscriptionStatus = subscriptionStatus;
|
||||||
|
this.loading = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected updatePaymentMethod = async (): Promise<void> => {
|
||||||
|
const dialogRef = AdjustPaymentDialogV2Component.open(this.dialogService, {
|
||||||
|
data: {
|
||||||
|
initialPaymentMethod: this.paymentSource?.type,
|
||||||
|
organizationId: this.organizationId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await lastValueFrom(dialogRef.closed);
|
||||||
|
|
||||||
|
if (result === AdjustPaymentDialogV2ResultType.Submitted) {
|
||||||
|
await this.load();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
protected updateTaxInformation = async (): Promise<void> => {
|
||||||
|
this.taxInfoComponent.taxFormGroup.updateValueAndValidity();
|
||||||
|
this.taxInfoComponent.taxFormGroup.markAllAsTouched();
|
||||||
|
|
||||||
|
if (this.taxInfoComponent.taxFormGroup.invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const request = new ExpandedTaxInfoUpdateRequest();
|
||||||
|
request.country = this.taxInfoComponent.country;
|
||||||
|
request.postalCode = this.taxInfoComponent.postalCode;
|
||||||
|
request.taxId = this.taxInfoComponent.taxId;
|
||||||
|
request.line1 = this.taxInfoComponent.line1;
|
||||||
|
request.line2 = this.taxInfoComponent.line2;
|
||||||
|
request.city = this.taxInfoComponent.city;
|
||||||
|
request.state = this.taxInfoComponent.state;
|
||||||
|
|
||||||
|
await this.billingApiService.updateOrganizationTaxInformation(this.organizationId, request);
|
||||||
|
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "success",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("taxInfoUpdated"),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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"),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
protected get accountCreditHeaderText(): string {
|
||||||
|
const key = this.accountCredit <= 0 ? "accountBalance" : "accountCredit";
|
||||||
|
return this.i18nService.t(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected 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.PayPal:
|
||||||
|
return ["bwi-paypal text-primary"];
|
||||||
|
default:
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get subscriptionIsUnpaid(): boolean {
|
||||||
|
return this.subscriptionStatus === "unpaid";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get updatePaymentSourceButtonText(): string {
|
||||||
|
const key = this.paymentSource == null ? "addPaymentMethod" : "changePaymentMethod";
|
||||||
|
return this.i18nService.t(key);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<bit-dialog dialogSize="large" [title]="dialogHeader">
|
||||||
|
<ng-container bitDialogContent>
|
||||||
|
<app-payment-v2
|
||||||
|
[showAccountCredit]="false"
|
||||||
|
[showBankAccount]="!!organizationId"
|
||||||
|
[initialPaymentMethod]="initialPaymentMethod"
|
||||||
|
></app-payment-v2>
|
||||||
|
<app-tax-info (onCountryChanged)="onCountryChanged()"></app-tax-info>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container bitDialogFooter>
|
||||||
|
<button type="submit" bitButton bitFormButton buttonType="primary" [bitAction]="submit">
|
||||||
|
{{ "submit" }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
bitButton
|
||||||
|
bitFormButton
|
||||||
|
buttonType="secondary"
|
||||||
|
[bitDialogClose]="ResultType.Closed"
|
||||||
|
>
|
||||||
|
{{ "cancel" | i18n }}
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
</bit-dialog>
|
|
@ -0,0 +1,123 @@
|
||||||
|
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||||
|
import { forwardRef, Component, Inject, ViewChild } from "@angular/core";
|
||||||
|
|
||||||
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
||||||
|
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
||||||
|
import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request";
|
||||||
|
import { UpdatePaymentMethodRequest } from "@bitwarden/common/billing/models/request/update-payment-method.request";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { DialogService, ToastService } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import { TaxInfoComponent } from "../";
|
||||||
|
import { PaymentV2Component } from "../payment/payment-v2.component";
|
||||||
|
|
||||||
|
export interface AdjustPaymentDialogV2Params {
|
||||||
|
initialPaymentMethod?: PaymentMethodType;
|
||||||
|
organizationId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AdjustPaymentDialogV2ResultType {
|
||||||
|
Closed = "closed",
|
||||||
|
Submitted = "submitted",
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: "./adjust-payment-dialog-v2.component.html",
|
||||||
|
})
|
||||||
|
export class AdjustPaymentDialogV2Component {
|
||||||
|
@ViewChild(PaymentV2Component) paymentComponent: PaymentV2Component;
|
||||||
|
@ViewChild(forwardRef(() => TaxInfoComponent)) taxInfoComponent: TaxInfoComponent;
|
||||||
|
|
||||||
|
protected readonly PaymentMethodType = PaymentMethodType;
|
||||||
|
protected readonly ResultType = AdjustPaymentDialogV2ResultType;
|
||||||
|
|
||||||
|
protected dialogHeader: string;
|
||||||
|
protected initialPaymentMethod: PaymentMethodType;
|
||||||
|
protected organizationId?: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private apiService: ApiService,
|
||||||
|
private billingApiService: BillingApiServiceAbstraction,
|
||||||
|
@Inject(DIALOG_DATA) protected dialogParams: AdjustPaymentDialogV2Params,
|
||||||
|
private dialogRef: DialogRef<AdjustPaymentDialogV2ResultType>,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private toastService: ToastService,
|
||||||
|
) {
|
||||||
|
const key = this.dialogParams.initialPaymentMethod ? "changePaymentMethod" : "addPaymentMethod";
|
||||||
|
this.dialogHeader = this.i18nService.t(key);
|
||||||
|
this.initialPaymentMethod = this.dialogParams.initialPaymentMethod ?? PaymentMethodType.Card;
|
||||||
|
this.organizationId = this.dialogParams.organizationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
onCountryChanged = () => {
|
||||||
|
if (this.taxInfoComponent.taxInfo.country === "US") {
|
||||||
|
this.paymentComponent.showBankAccount = !!this.organizationId;
|
||||||
|
} else {
|
||||||
|
this.paymentComponent.showBankAccount = false;
|
||||||
|
if (this.paymentComponent.selected === PaymentMethodType.BankAccount) {
|
||||||
|
this.paymentComponent.select(PaymentMethodType.Card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
submit = async (): Promise<void> => {
|
||||||
|
this.taxInfoComponent.taxFormGroup.updateValueAndValidity();
|
||||||
|
this.taxInfoComponent.taxFormGroup.markAllAsTouched();
|
||||||
|
if (this.taxInfoComponent.taxFormGroup.invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.organizationId) {
|
||||||
|
await this.updatePremiumUserPaymentMethod();
|
||||||
|
} else {
|
||||||
|
await this.updateOrganizationPaymentMethod();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "success",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("updatedPaymentMethod"),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.dialogRef.close(AdjustPaymentDialogV2ResultType.Submitted);
|
||||||
|
};
|
||||||
|
|
||||||
|
private updateOrganizationPaymentMethod = async () => {
|
||||||
|
const paymentSource = await this.paymentComponent.tokenize();
|
||||||
|
|
||||||
|
const request = new UpdatePaymentMethodRequest();
|
||||||
|
request.paymentSource = paymentSource;
|
||||||
|
request.taxInformation = {
|
||||||
|
country: this.taxInfoComponent.country,
|
||||||
|
postalCode: this.taxInfoComponent.postalCode,
|
||||||
|
taxId: this.taxInfoComponent.taxId,
|
||||||
|
line1: this.taxInfoComponent.line1,
|
||||||
|
line2: this.taxInfoComponent.line2,
|
||||||
|
city: this.taxInfoComponent.city,
|
||||||
|
state: this.taxInfoComponent.state,
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.billingApiService.updateOrganizationPaymentMethod(this.organizationId, request);
|
||||||
|
};
|
||||||
|
|
||||||
|
private updatePremiumUserPaymentMethod = async () => {
|
||||||
|
const { type, token } = await this.paymentComponent.tokenize();
|
||||||
|
|
||||||
|
const request = new PaymentRequest();
|
||||||
|
request.paymentMethodType = type;
|
||||||
|
request.paymentToken = token;
|
||||||
|
request.country = this.taxInfoComponent.country;
|
||||||
|
request.postalCode = this.taxInfoComponent.postalCode;
|
||||||
|
await this.apiService.postAccountPayment(request);
|
||||||
|
};
|
||||||
|
|
||||||
|
static open = (
|
||||||
|
dialogService: DialogService,
|
||||||
|
dialogConfig: DialogConfig<AdjustPaymentDialogV2Params>,
|
||||||
|
) =>
|
||||||
|
dialogService.open<AdjustPaymentDialogV2ResultType>(
|
||||||
|
AdjustPaymentDialogV2Component,
|
||||||
|
dialogConfig,
|
||||||
|
);
|
||||||
|
}
|
|
@ -10,8 +10,8 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { DialogService, ToastService } from "@bitwarden/components";
|
import { DialogService, ToastService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { PaymentComponent } from "./payment.component";
|
import { PaymentComponent } from "../payment/payment.component";
|
||||||
import { TaxInfoComponent } from "./tax-info.component";
|
import { TaxInfoComponent } from "../tax-info.component";
|
||||||
|
|
||||||
export interface AdjustPaymentDialogData {
|
export interface AdjustPaymentDialogData {
|
||||||
organizationId: string;
|
organizationId: string;
|
|
@ -12,7 +12,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { DialogService, ToastService } from "@bitwarden/components";
|
import { DialogService, ToastService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { PaymentComponent } from "./payment.component";
|
import { PaymentComponent } from "./payment/payment.component";
|
||||||
|
|
||||||
export interface AdjustStorageDialogData {
|
export interface AdjustStorageDialogData {
|
||||||
storageGbPrice: number;
|
storageGbPrice: number;
|
||||||
|
|
|
@ -4,19 +4,29 @@ import { HeaderModule } from "../../layouts/header/header.module";
|
||||||
import { SharedModule } from "../../shared";
|
import { SharedModule } from "../../shared";
|
||||||
|
|
||||||
import { AddCreditDialogComponent } from "./add-credit-dialog.component";
|
import { AddCreditDialogComponent } from "./add-credit-dialog.component";
|
||||||
import { AdjustPaymentDialogComponent } from "./adjust-payment-dialog.component";
|
import { AdjustPaymentDialogV2Component } from "./adjust-payment-dialog/adjust-payment-dialog-v2.component";
|
||||||
|
import { AdjustPaymentDialogComponent } from "./adjust-payment-dialog/adjust-payment-dialog.component";
|
||||||
import { AdjustStorageComponent } from "./adjust-storage.component";
|
import { AdjustStorageComponent } from "./adjust-storage.component";
|
||||||
import { BillingHistoryComponent } from "./billing-history.component";
|
import { BillingHistoryComponent } from "./billing-history.component";
|
||||||
import { OffboardingSurveyComponent } from "./offboarding-survey.component";
|
import { OffboardingSurveyComponent } from "./offboarding-survey.component";
|
||||||
|
import { PaymentV2Component } from "./payment/payment-v2.component";
|
||||||
|
import { PaymentComponent } from "./payment/payment.component";
|
||||||
import { PaymentMethodComponent } from "./payment-method.component";
|
import { PaymentMethodComponent } from "./payment-method.component";
|
||||||
import { PaymentComponent } from "./payment.component";
|
|
||||||
import { SecretsManagerSubscribeComponent } from "./sm-subscribe.component";
|
import { SecretsManagerSubscribeComponent } from "./sm-subscribe.component";
|
||||||
import { TaxInfoComponent } from "./tax-info.component";
|
import { TaxInfoComponent } from "./tax-info.component";
|
||||||
import { UpdateLicenseDialogComponent } from "./update-license-dialog.component";
|
import { UpdateLicenseDialogComponent } from "./update-license-dialog.component";
|
||||||
import { UpdateLicenseComponent } from "./update-license.component";
|
import { UpdateLicenseComponent } from "./update-license.component";
|
||||||
|
import { VerifyBankAccountComponent } from "./verify-bank-account/verify-bank-account.component";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [SharedModule, PaymentComponent, TaxInfoComponent, HeaderModule],
|
imports: [
|
||||||
|
SharedModule,
|
||||||
|
PaymentComponent,
|
||||||
|
TaxInfoComponent,
|
||||||
|
HeaderModule,
|
||||||
|
PaymentV2Component,
|
||||||
|
VerifyBankAccountComponent,
|
||||||
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
AddCreditDialogComponent,
|
AddCreditDialogComponent,
|
||||||
AdjustPaymentDialogComponent,
|
AdjustPaymentDialogComponent,
|
||||||
|
@ -27,6 +37,7 @@ import { UpdateLicenseComponent } from "./update-license.component";
|
||||||
UpdateLicenseComponent,
|
UpdateLicenseComponent,
|
||||||
UpdateLicenseDialogComponent,
|
UpdateLicenseDialogComponent,
|
||||||
OffboardingSurveyComponent,
|
OffboardingSurveyComponent,
|
||||||
|
AdjustPaymentDialogV2Component,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
SharedModule,
|
SharedModule,
|
||||||
|
@ -38,6 +49,7 @@ import { UpdateLicenseComponent } from "./update-license.component";
|
||||||
UpdateLicenseComponent,
|
UpdateLicenseComponent,
|
||||||
UpdateLicenseDialogComponent,
|
UpdateLicenseDialogComponent,
|
||||||
OffboardingSurveyComponent,
|
OffboardingSurveyComponent,
|
||||||
|
VerifyBankAccountComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class BillingSharedModule {}
|
export class BillingSharedModule {}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export * from "./billing-shared.module";
|
export * from "./billing-shared.module";
|
||||||
export * from "./payment-method.component";
|
export * from "./payment-method.component";
|
||||||
export * from "./payment.component";
|
export * from "./payment/payment.component";
|
||||||
export * from "./sm-subscribe.component";
|
export * from "./sm-subscribe.component";
|
||||||
export * from "./tax-info.component";
|
export * from "./tax-info.component";
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||||
import { Component, Inject } from "@angular/core";
|
import { Component, Inject } from "@angular/core";
|
||||||
import { FormBuilder, Validators } from "@angular/forms";
|
import { FormBuilder, Validators } from "@angular/forms";
|
||||||
|
|
||||||
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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { DialogService, ToastService } from "@bitwarden/components";
|
import { DialogService, ToastService } from "@bitwarden/components";
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { AddCreditDialogResult, openAddCreditDialog } from "./add-credit-dialog.
|
||||||
import {
|
import {
|
||||||
AdjustPaymentDialogResult,
|
AdjustPaymentDialogResult,
|
||||||
openAdjustPaymentDialog,
|
openAdjustPaymentDialog,
|
||||||
} from "./adjust-payment-dialog.component";
|
} from "./adjust-payment-dialog/adjust-payment-dialog.component";
|
||||||
import { TaxInfoComponent } from "./tax-info.component";
|
import { TaxInfoComponent } from "./tax-info.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|
|
@ -43,24 +43,31 @@
|
||||||
<ng-container *ngIf="usingCard">
|
<ng-container *ngIf="usingCard">
|
||||||
<div class="tw-grid tw-grid-cols-2 tw-gap-4 tw-mb-4">
|
<div class="tw-grid tw-grid-cols-2 tw-gap-4 tw-mb-4">
|
||||||
<div class="tw-col-span-1">
|
<div class="tw-col-span-1">
|
||||||
<label for="stripe-card-number">{{ "number" | i18n }}</label>
|
<label for="stripe-card-number">
|
||||||
|
{{ "number" | i18n }}
|
||||||
|
<span class="tw-text-xs tw-font-normal">({{ "required" | i18n }})</span>
|
||||||
|
</label>
|
||||||
<div id="stripe-card-number" class="form-control stripe-form-control"></div>
|
<div id="stripe-card-number" class="form-control stripe-form-control"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tw-col-span-1 tw-flex tw-items-end">
|
<div class="tw-col-span-1 tw-flex tw-items-end">
|
||||||
<img
|
<img
|
||||||
src="../../images/cards.png"
|
src="../../../images/cards.png"
|
||||||
alt="Visa, MasterCard, Discover, AmEx, JCB, Diners Club, UnionPay"
|
alt="Visa, MasterCard, Discover, AmEx, JCB, Diners Club, UnionPay"
|
||||||
class="tw-max-w-full"
|
class="tw-max-w-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="tw-col-span-1">
|
<div class="tw-col-span-1">
|
||||||
<label for="stripe-card-expiry">{{ "expiration" | i18n }}</label>
|
<label for="stripe-card-expiry">
|
||||||
|
{{ "expiration" | i18n }}
|
||||||
|
<span class="tw-text-xs tw-font-normal">({{ "required" | i18n }})</span>
|
||||||
|
</label>
|
||||||
<div id="stripe-card-expiry" class="form-control stripe-form-control"></div>
|
<div id="stripe-card-expiry" class="form-control stripe-form-control"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tw-col-span-1">
|
<div class="tw-col-span-1">
|
||||||
<div class="tw-flex">
|
<div class="tw-flex">
|
||||||
<label for="stripe-card-cvc">
|
<label for="stripe-card-cvc">
|
||||||
{{ "securityCode" | i18n }}
|
{{ "securityCode" | i18n }}
|
||||||
|
<span class="tw-text-xs tw-font-normal">({{ "required" | i18n }})</span>
|
||||||
</label>
|
</label>
|
||||||
<a
|
<a
|
||||||
href="https://www.cvvnumber.com/cvv.html"
|
href="https://www.cvvnumber.com/cvv.html"
|
||||||
|
@ -82,7 +89,7 @@
|
||||||
<app-callout type="warning" title="{{ 'verifyBankAccount' | i18n }}">
|
<app-callout type="warning" title="{{ 'verifyBankAccount' | i18n }}">
|
||||||
{{ "verifyBankAccountInitialDesc" | i18n }} {{ "verifyBankAccountFailureWarning" | i18n }}
|
{{ "verifyBankAccountInitialDesc" | i18n }} {{ "verifyBankAccountFailureWarning" | i18n }}
|
||||||
</app-callout>
|
</app-callout>
|
||||||
<div class="tw-grid tw-grid-cols-2 tw-gap-4" formGroupName="bankInformation">
|
<div class="tw-grid tw-grid-cols-2 tw-gap-4 tw-mb-4" formGroupName="bankInformation">
|
||||||
<bit-form-field class="tw-col-span-1" disableMargin>
|
<bit-form-field class="tw-col-span-1" disableMargin>
|
||||||
<bit-label>{{ "routingNumber" | i18n }}</bit-label>
|
<bit-label>{{ "routingNumber" | i18n }}</bit-label>
|
||||||
<input
|
<input
|
|
@ -1,5 +1,5 @@
|
||||||
import { Component, Input, OnDestroy, OnInit } from "@angular/core";
|
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
|
||||||
import { FormBuilder, Validators } from "@angular/forms";
|
import { FormControl, FormGroup, Validators } from "@angular/forms";
|
||||||
import { Subject } from "rxjs";
|
import { Subject } from "rxjs";
|
||||||
import { takeUntil } from "rxjs/operators";
|
import { takeUntil } from "rxjs/operators";
|
||||||
|
|
||||||
|
@ -9,40 +9,103 @@ import {
|
||||||
StripeServiceAbstraction,
|
StripeServiceAbstraction,
|
||||||
} from "@bitwarden/common/billing/abstractions";
|
} from "@bitwarden/common/billing/abstractions";
|
||||||
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
||||||
import { TokenizedPaymentMethod } from "@bitwarden/common/billing/models/domain";
|
import { TokenizedPaymentSourceRequest } from "@bitwarden/common/billing/models/request/tokenized-payment-source.request";
|
||||||
|
|
||||||
|
import { SharedModule } from "../../../shared";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a form that allows the user to enter their payment method, tokenize it against one of our payment providers and,
|
||||||
|
* optionally, submit it using the {@link onSubmit} function if it is provided.
|
||||||
|
*
|
||||||
|
* This component is meant to replace the existing {@link PaymentComponent} which is using the deprecated Stripe Sources API.
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-select-payment-method",
|
selector: "app-payment-v2",
|
||||||
templateUrl: "./select-payment-method.component.html",
|
templateUrl: "./payment-v2.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [SharedModule],
|
||||||
})
|
})
|
||||||
export class SelectPaymentMethodComponent implements OnInit, OnDestroy {
|
export class PaymentV2Component implements OnInit, OnDestroy {
|
||||||
@Input() protected showAccountCredit: boolean = true;
|
/** Show account credit as a payment option. */
|
||||||
@Input() protected showBankAccount: boolean = true;
|
@Input() showAccountCredit: boolean = true;
|
||||||
@Input() protected showPayPal: boolean = true;
|
/** Show bank account as a payment option. */
|
||||||
@Input() private startWith: PaymentMethodType = PaymentMethodType.Card;
|
@Input() showBankAccount: boolean = true;
|
||||||
@Input() protected onSubmit: (tokenizedPaymentMethod: TokenizedPaymentMethod) => Promise<void>;
|
/** Show PayPal as a payment option. */
|
||||||
|
@Input() showPayPal: boolean = true;
|
||||||
|
|
||||||
|
/** The payment method selected by default when the component renders. */
|
||||||
|
@Input() private initialPaymentMethod: PaymentMethodType = PaymentMethodType.Card;
|
||||||
|
/** If provided, will be invoked with the tokenized payment source during form submission. */
|
||||||
|
@Input() protected onSubmit?: (request: TokenizedPaymentSourceRequest) => Promise<void>;
|
||||||
|
|
||||||
|
@Output() submitted = new EventEmitter<PaymentMethodType>();
|
||||||
|
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
protected formGroup = this.formBuilder.group({
|
protected formGroup = new FormGroup({
|
||||||
paymentMethod: [this.startWith],
|
paymentMethod: new FormControl<PaymentMethodType>(null),
|
||||||
bankInformation: this.formBuilder.group({
|
bankInformation: new FormGroup({
|
||||||
routingNumber: ["", [Validators.required]],
|
routingNumber: new FormControl<string>("", [Validators.required]),
|
||||||
accountNumber: ["", [Validators.required]],
|
accountNumber: new FormControl<string>("", [Validators.required]),
|
||||||
accountHolderName: ["", [Validators.required]],
|
accountHolderName: new FormControl<string>("", [Validators.required]),
|
||||||
accountHolderType: ["", [Validators.required]],
|
accountHolderType: new FormControl<string>("", [Validators.required]),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
protected PaymentMethodType = PaymentMethodType;
|
protected PaymentMethodType = PaymentMethodType;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private billingApiService: BillingApiServiceAbstraction,
|
private billingApiService: BillingApiServiceAbstraction,
|
||||||
private braintreeService: BraintreeServiceAbstraction,
|
private braintreeService: BraintreeServiceAbstraction,
|
||||||
private formBuilder: FormBuilder,
|
|
||||||
private stripeService: StripeServiceAbstraction,
|
private stripeService: StripeServiceAbstraction,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async tokenizePaymentMethod(): Promise<TokenizedPaymentMethod> {
|
ngOnInit(): void {
|
||||||
|
this.formGroup.controls.paymentMethod.patchValue(this.initialPaymentMethod);
|
||||||
|
|
||||||
|
this.stripeService.loadStripe(
|
||||||
|
{
|
||||||
|
cardNumber: "#stripe-card-number",
|
||||||
|
cardExpiry: "#stripe-card-expiry",
|
||||||
|
cardCvc: "#stripe-card-cvc",
|
||||||
|
},
|
||||||
|
this.initialPaymentMethod === PaymentMethodType.Card,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.showPayPal) {
|
||||||
|
this.braintreeService.loadBraintree(
|
||||||
|
"#braintree-container",
|
||||||
|
this.initialPaymentMethod === PaymentMethodType.PayPal,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.formGroup
|
||||||
|
.get("paymentMethod")
|
||||||
|
.valueChanges.pipe(takeUntil(this.destroy$))
|
||||||
|
.subscribe((type) => {
|
||||||
|
this.onPaymentMethodChange(type);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Programmatically select the provided payment method. */
|
||||||
|
select = (paymentMethod: PaymentMethodType) => {
|
||||||
|
this.formGroup.value.paymentMethod = paymentMethod;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected submit = async () => {
|
||||||
|
const { type, token } = await this.tokenize();
|
||||||
|
await this.onSubmit({ type, token });
|
||||||
|
this.submitted.emit(type);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tokenize the payment method information entered by the user against one of our payment providers.
|
||||||
|
*
|
||||||
|
* - {@link PaymentMethodType.Card} => [Stripe.confirmCardSetup]{@link https://docs.stripe.com/js/setup_intents/confirm_card_setup}
|
||||||
|
* - {@link PaymentMethodType.BankAccount} => [Stripe.confirmUsBankAccountSetup]{@link https://docs.stripe.com/js/setup_intents/confirm_us_bank_account_setup}
|
||||||
|
* - {@link PaymentMethodType.PayPal} => [Braintree.requestPaymentMethod]{@link https://braintree.github.io/braintree-web-drop-in/docs/current/Dropin.html#requestPaymentMethod}
|
||||||
|
* */
|
||||||
|
async tokenize(): Promise<{ type: PaymentMethodType; token: string }> {
|
||||||
const type = this.selected;
|
const type = this.selected;
|
||||||
|
|
||||||
if (this.usingStripe) {
|
if (this.usingStripe) {
|
||||||
|
@ -81,36 +144,6 @@ export class SelectPaymentMethodComponent implements OnInit, OnDestroy {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
submit = async () => {
|
|
||||||
const tokenizedPaymentMethod = await this.tokenizePaymentMethod();
|
|
||||||
await this.onSubmit(tokenizedPaymentMethod);
|
|
||||||
};
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.stripeService.loadStripe(
|
|
||||||
{
|
|
||||||
cardNumber: "#stripe-card-number",
|
|
||||||
cardExpiry: "#stripe-card-expiry",
|
|
||||||
cardCvc: "#stripe-card-cvc",
|
|
||||||
},
|
|
||||||
this.startWith === PaymentMethodType.Card,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this.showPayPal) {
|
|
||||||
this.braintreeService.loadBraintree(
|
|
||||||
"#braintree-container",
|
|
||||||
this.startWith === PaymentMethodType.PayPal,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.formGroup
|
|
||||||
.get("paymentMethod")
|
|
||||||
.valueChanges.pipe(takeUntil(this.destroy$))
|
|
||||||
.subscribe((type) => {
|
|
||||||
this.onPaymentMethodChange(type);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.destroy$.next();
|
this.destroy$.next();
|
||||||
this.destroy$.complete();
|
this.destroy$.complete();
|
||||||
|
@ -133,7 +166,7 @@ export class SelectPaymentMethodComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private get selected(): PaymentMethodType {
|
get selected(): PaymentMethodType {
|
||||||
return this.formGroup.value.paymentMethod;
|
return this.formGroup.value.paymentMethod;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
|
|
||||||
import { SharedModule } from "../../shared";
|
import { SharedModule } from "../../../shared";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-payment",
|
selector: "app-payment",
|
|
@ -11,7 +11,7 @@
|
||||||
<input bitInput type="number" step="1" placeholder="xx" formControlName="amount2" />
|
<input bitInput type="number" step="1" placeholder="xx" formControlName="amount2" />
|
||||||
<span bitPrefix>$0.</span>
|
<span bitPrefix>$0.</span>
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
<button type="submit" bitButton bitFormButton buttonType="primary">
|
<button *ngIf="onSubmit" type="submit" bitButton bitFormButton buttonType="primary">
|
||||||
{{ "submit" | i18n }}
|
{{ "submit" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
|
@ -1,13 +1,19 @@
|
||||||
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||||
import { FormBuilder, FormControl, Validators } from "@angular/forms";
|
import { FormBuilder, FormControl, Validators } from "@angular/forms";
|
||||||
|
|
||||||
|
import { VerifyBankAccountRequest } from "@bitwarden/common/billing/models/request/verify-bank-account.request";
|
||||||
|
|
||||||
|
import { SharedModule } from "../../../shared";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-verify-bank-account",
|
selector: "app-verify-bank-account",
|
||||||
templateUrl: "./verify-bank-account.component.html",
|
templateUrl: "./verify-bank-account.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [SharedModule],
|
||||||
})
|
})
|
||||||
export class VerifyBankAccountComponent {
|
export class VerifyBankAccountComponent {
|
||||||
@Input() onSubmit?: (amount1: number, amount2: number) => Promise<void>;
|
@Input() onSubmit?: (request: VerifyBankAccountRequest) => Promise<void>;
|
||||||
@Output() verificationSubmitted = new EventEmitter();
|
@Output() submitted = new EventEmitter();
|
||||||
|
|
||||||
protected formGroup = this.formBuilder.group({
|
protected formGroup = this.formBuilder.group({
|
||||||
amount1: new FormControl<number>(null, [
|
amount1: new FormControl<number>(null, [
|
||||||
|
@ -25,9 +31,11 @@ export class VerifyBankAccountComponent {
|
||||||
constructor(private formBuilder: FormBuilder) {}
|
constructor(private formBuilder: FormBuilder) {}
|
||||||
|
|
||||||
submit = async () => {
|
submit = async () => {
|
||||||
if (this.onSubmit) {
|
const request = new VerifyBankAccountRequest(
|
||||||
await this.onSubmit(this.formGroup.value.amount1, this.formGroup.value.amount2);
|
this.formGroup.value.amount1,
|
||||||
}
|
this.formGroup.value.amount2,
|
||||||
this.verificationSubmitted.emit();
|
);
|
||||||
|
await this.onSubmit(request);
|
||||||
|
this.submitted.emit();
|
||||||
};
|
};
|
||||||
}
|
}
|
|
@ -16,8 +16,6 @@ import {
|
||||||
ManageClientsComponent,
|
ManageClientsComponent,
|
||||||
ManageClientSubscriptionDialogComponent,
|
ManageClientSubscriptionDialogComponent,
|
||||||
ProviderBillingHistoryComponent,
|
ProviderBillingHistoryComponent,
|
||||||
ProviderPaymentMethodComponent,
|
|
||||||
ProviderSelectPaymentMethodDialogComponent,
|
|
||||||
ProviderSubscriptionComponent,
|
ProviderSubscriptionComponent,
|
||||||
ProviderSubscriptionStatusComponent,
|
ProviderSubscriptionStatusComponent,
|
||||||
} from "../../billing/providers";
|
} from "../../billing/providers";
|
||||||
|
@ -80,8 +78,6 @@ import { SetupComponent } from "./setup/setup.component";
|
||||||
ManageClientSubscriptionDialogComponent,
|
ManageClientSubscriptionDialogComponent,
|
||||||
ProviderBillingHistoryComponent,
|
ProviderBillingHistoryComponent,
|
||||||
ProviderSubscriptionComponent,
|
ProviderSubscriptionComponent,
|
||||||
ProviderSelectPaymentMethodDialogComponent,
|
|
||||||
ProviderPaymentMethodComponent,
|
|
||||||
ProviderSubscriptionStatusComponent,
|
ProviderSubscriptionStatusComponent,
|
||||||
],
|
],
|
||||||
providers: [WebProviderService],
|
providers: [WebProviderService],
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Injectable } from "@angular/core";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
|
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 { 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 { PlanType } from "@bitwarden/common/billing/enums";
|
||||||
import { CreateClientOrganizationRequest } from "@bitwarden/common/billing/models/request/create-client-organization.request";
|
import { CreateClientOrganizationRequest } from "@bitwarden/common/billing/models/request/create-client-organization.request";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
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 { Component, Inject, OnInit } from "@angular/core";
|
||||||
import { FormControl, FormGroup, Validators } from "@angular/forms";
|
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 { PlanType } from "@bitwarden/common/billing/enums";
|
||||||
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
|
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
|
||||||
import { ProviderPlanResponse } from "@bitwarden/common/billing/models/response/provider-subscription-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 { Component, Inject } from "@angular/core";
|
||||||
import { FormBuilder, Validators } from "@angular/forms";
|
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 { UpdateClientOrganizationRequest } from "@bitwarden/common/billing/models/request/update-client-organization.request";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { DialogService, ToastService } from "@bitwarden/components";
|
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 { ProviderUserType } from "@bitwarden/common/admin-console/enums";
|
||||||
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
|
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
|
||||||
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
|
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 { hasConsolidatedBilling } from "@bitwarden/common/billing/abstractions/provider-billing.service.abstraction";
|
||||||
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
|
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
export * from "./billing-history/provider-billing-history.component";
|
export * from "./billing-history/provider-billing-history.component";
|
||||||
export * from "./clients";
|
export * from "./clients";
|
||||||
export * from "./guards/has-consolidated-billing.guard";
|
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.component";
|
||||||
export * from "./subscription/provider-subscription-status.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 { ActivatedRoute } from "@angular/router";
|
||||||
import { Subject, concatMap, takeUntil } from "rxjs";
|
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 { TaxInformation } from "@bitwarden/common/billing/models/domain";
|
||||||
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
|
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -2,5 +2,3 @@ export * from "./add-account-credit-dialog/add-account-credit-dialog.component";
|
||||||
export * from "./invoices/invoices.component";
|
export * from "./invoices/invoices.component";
|
||||||
export * from "./invoices/no-invoices.component";
|
export * from "./invoices/no-invoices.component";
|
||||||
export * from "./manage-tax-information/manage-tax-information.component";
|
export * from "./manage-tax-information/manage-tax-information.component";
|
||||||
export * from "./select-payment-method/select-payment-method.component";
|
|
||||||
export * from "./verify-bank-account/verify-bank-account.component";
|
|
||||||
|
|
|
@ -7,8 +7,6 @@ import {
|
||||||
InvoicesComponent,
|
InvoicesComponent,
|
||||||
NoInvoicesComponent,
|
NoInvoicesComponent,
|
||||||
ManageTaxInformationComponent,
|
ManageTaxInformationComponent,
|
||||||
SelectPaymentMethodComponent,
|
|
||||||
VerifyBankAccountComponent,
|
|
||||||
} from "@bitwarden/angular/billing/components";
|
} from "@bitwarden/angular/billing/components";
|
||||||
import {
|
import {
|
||||||
AsyncActionsModule,
|
AsyncActionsModule,
|
||||||
|
@ -116,8 +114,6 @@ import { IconComponent } from "./vault/components/icon.component";
|
||||||
InvoicesComponent,
|
InvoicesComponent,
|
||||||
NoInvoicesComponent,
|
NoInvoicesComponent,
|
||||||
ManageTaxInformationComponent,
|
ManageTaxInformationComponent,
|
||||||
SelectPaymentMethodComponent,
|
|
||||||
VerifyBankAccountComponent,
|
|
||||||
TwoFactorIconComponent,
|
TwoFactorIconComponent,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
|
@ -153,8 +149,6 @@ import { IconComponent } from "./vault/components/icon.component";
|
||||||
InvoicesComponent,
|
InvoicesComponent,
|
||||||
NoInvoicesComponent,
|
NoInvoicesComponent,
|
||||||
ManageTaxInformationComponent,
|
ManageTaxInformationComponent,
|
||||||
SelectPaymentMethodComponent,
|
|
||||||
VerifyBankAccountComponent,
|
|
||||||
TwoFactorIconComponent,
|
TwoFactorIconComponent,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
|
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
|
||||||
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
||||||
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
|
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
|
||||||
import { TokenizedPaymentMethodRequest } from "@bitwarden/common/billing/models/request/tokenized-payment-method.request";
|
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 { VerifyBankAccountRequest } from "@bitwarden/common/billing/models/request/verify-bank-account.request";
|
||||||
import { InvoicesResponse } from "@bitwarden/common/billing/models/response/invoices.response";
|
import { InvoicesResponse } from "@bitwarden/common/billing/models/response/invoices.response";
|
||||||
import { PaymentInformationResponse } from "@bitwarden/common/billing/models/response/payment-information.response";
|
import { PaymentMethodResponse } from "@bitwarden/common/billing/models/response/payment-method.response";
|
||||||
|
|
||||||
import { SubscriptionCancellationRequest } from "../../billing/models/request/subscription-cancellation.request";
|
import { SubscriptionCancellationRequest } from "../../billing/models/request/subscription-cancellation.request";
|
||||||
import { OrganizationBillingMetadataResponse } from "../../billing/models/response/organization-billing-metadata.response";
|
import { OrganizationBillingMetadataResponse } from "../../billing/models/response/organization-billing-metadata.response";
|
||||||
|
@ -33,6 +33,8 @@ export abstract class BillingApiServiceAbstraction {
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
) => Promise<OrganizationBillingMetadataResponse>;
|
) => Promise<OrganizationBillingMetadataResponse>;
|
||||||
|
|
||||||
|
getOrganizationPaymentMethod: (organizationId: string) => Promise<PaymentMethodResponse>;
|
||||||
|
|
||||||
getPlans: () => Promise<ListResponse<PlanResponse>>;
|
getPlans: () => Promise<ListResponse<PlanResponse>>;
|
||||||
|
|
||||||
getProviderClientInvoiceReport: (providerId: string, invoiceId: string) => Promise<string>;
|
getProviderClientInvoiceReport: (providerId: string, invoiceId: string) => Promise<string>;
|
||||||
|
@ -43,37 +45,31 @@ export abstract class BillingApiServiceAbstraction {
|
||||||
|
|
||||||
getProviderInvoices: (providerId: string) => Promise<InvoicesResponse>;
|
getProviderInvoices: (providerId: string) => Promise<InvoicesResponse>;
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated This endpoint is currently deactivated.
|
|
||||||
*/
|
|
||||||
getProviderPaymentInformation: (providerId: string) => Promise<PaymentInformationResponse>;
|
|
||||||
|
|
||||||
getProviderSubscription: (providerId: string) => Promise<ProviderSubscriptionResponse>;
|
getProviderSubscription: (providerId: string) => Promise<ProviderSubscriptionResponse>;
|
||||||
|
|
||||||
|
updateOrganizationPaymentMethod: (
|
||||||
|
organizationId: string,
|
||||||
|
request: UpdatePaymentMethodRequest,
|
||||||
|
) => Promise<void>;
|
||||||
|
|
||||||
|
updateOrganizationTaxInformation: (
|
||||||
|
organizationId: string,
|
||||||
|
request: ExpandedTaxInfoUpdateRequest,
|
||||||
|
) => Promise<void>;
|
||||||
|
|
||||||
updateProviderClientOrganization: (
|
updateProviderClientOrganization: (
|
||||||
providerId: string,
|
providerId: string,
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
request: UpdateClientOrganizationRequest,
|
request: UpdateClientOrganizationRequest,
|
||||||
) => Promise<any>;
|
) => Promise<any>;
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated This endpoint is currently deactivated.
|
|
||||||
*/
|
|
||||||
updateProviderPaymentMethod: (
|
|
||||||
providerId: string,
|
|
||||||
request: TokenizedPaymentMethodRequest,
|
|
||||||
) => Promise<void>;
|
|
||||||
|
|
||||||
updateProviderTaxInformation: (
|
updateProviderTaxInformation: (
|
||||||
providerId: string,
|
providerId: string,
|
||||||
request: ExpandedTaxInfoUpdateRequest,
|
request: ExpandedTaxInfoUpdateRequest,
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
|
|
||||||
/**
|
verifyOrganizationBankAccount: (
|
||||||
* @deprecated This endpoint is currently deactivated.
|
organizationId: string,
|
||||||
*/
|
|
||||||
verifyProviderBankAccount: (
|
|
||||||
providerId: string,
|
|
||||||
request: VerifyBankAccountRequest,
|
request: VerifyBankAccountRequest,
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
export * from "./account/billing-account-profile-state.service";
|
export * from "./account/billing-account-profile-state.service";
|
||||||
export * from "./billilng-api.service.abstraction";
|
export * from "./billing-api.service.abstraction";
|
||||||
export * from "./organization-billing.service";
|
export * from "./organization-billing.service";
|
||||||
export * from "./payment-processors/braintree.service.abstraction";
|
export * from "./payment-processors/braintree.service.abstraction";
|
||||||
export * from "./payment-processors/stripe.service.abstraction";
|
export * from "./payment-processors/stripe.service.abstraction";
|
||||||
|
|
|
@ -1,4 +1,2 @@
|
||||||
export * from "./bank-account";
|
export * from "./bank-account";
|
||||||
export * from "./masked-payment-method";
|
|
||||||
export * from "./tax-information";
|
export * from "./tax-information";
|
||||||
export * from "./tokenized-payment-method";
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
|
||||||
import { MaskedPaymentMethodResponse } from "@bitwarden/common/billing/models/response/masked-payment-method.response";
|
|
||||||
|
|
||||||
export class MaskedPaymentMethod {
|
|
||||||
type: PaymentMethodType;
|
|
||||||
description: string;
|
|
||||||
needsVerification: boolean;
|
|
||||||
|
|
||||||
static from(response: MaskedPaymentMethodResponse | undefined) {
|
|
||||||
if (response === undefined) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...response,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
|
||||||
import { TokenizedPaymentMethod } from "@bitwarden/common/billing/models/domain";
|
|
||||||
|
|
||||||
export class TokenizedPaymentMethodRequest {
|
|
||||||
type: PaymentMethodType;
|
|
||||||
token: string;
|
|
||||||
|
|
||||||
static From(tokenizedPaymentMethod: TokenizedPaymentMethod): TokenizedPaymentMethodRequest {
|
|
||||||
const request = new TokenizedPaymentMethodRequest();
|
|
||||||
request.type = tokenizedPaymentMethod.type;
|
|
||||||
request.token = tokenizedPaymentMethod.token;
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
||||||
|
|
||||||
export type TokenizedPaymentMethod = {
|
export class TokenizedPaymentSourceRequest {
|
||||||
type: PaymentMethodType;
|
type: PaymentMethodType;
|
||||||
token: string;
|
token: string;
|
||||||
};
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
|
||||||
|
import { TokenizedPaymentSourceRequest } from "@bitwarden/common/billing/models/request/tokenized-payment-source.request";
|
||||||
|
|
||||||
|
export class UpdatePaymentMethodRequest {
|
||||||
|
paymentSource: TokenizedPaymentSourceRequest;
|
||||||
|
taxInformation: ExpandedTaxInfoUpdateRequest;
|
||||||
|
}
|
|
@ -1,13 +0,0 @@
|
||||||
import { BaseResponse } from "../../../models/response/base.response";
|
|
||||||
|
|
||||||
export class OrganizationRisksSubscriptionFailureResponse extends BaseResponse {
|
|
||||||
organizationId: string;
|
|
||||||
risksSubscriptionFailure: boolean;
|
|
||||||
|
|
||||||
constructor(response: any) {
|
|
||||||
super(response);
|
|
||||||
|
|
||||||
this.organizationId = this.getResponseProperty("OrganizationId");
|
|
||||||
this.risksSubscriptionFailure = this.getResponseProperty("RisksSubscriptionFailure");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +1,25 @@
|
||||||
import { BaseResponse } from "../../../models/response/base.response";
|
import { BaseResponse } from "../../../models/response/base.response";
|
||||||
|
|
||||||
import { MaskedPaymentMethodResponse } from "./masked-payment-method.response";
|
import { PaymentSourceResponse } from "./payment-source.response";
|
||||||
import { TaxInfoResponse } from "./tax-info.response";
|
import { TaxInfoResponse } from "./tax-info.response";
|
||||||
|
|
||||||
export class PaymentInformationResponse extends BaseResponse {
|
export class PaymentMethodResponse extends BaseResponse {
|
||||||
accountCredit: number;
|
accountCredit: number;
|
||||||
paymentMethod?: MaskedPaymentMethodResponse;
|
paymentSource?: PaymentSourceResponse;
|
||||||
|
subscriptionStatus?: string;
|
||||||
taxInformation?: TaxInfoResponse;
|
taxInformation?: TaxInfoResponse;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
this.accountCredit = this.getResponseProperty("AccountCredit");
|
this.accountCredit = this.getResponseProperty("AccountCredit");
|
||||||
|
|
||||||
const paymentMethod = this.getResponseProperty("PaymentMethod");
|
const paymentSource = this.getResponseProperty("PaymentSource");
|
||||||
if (paymentMethod) {
|
if (paymentSource) {
|
||||||
this.paymentMethod = new MaskedPaymentMethodResponse(paymentMethod);
|
this.paymentSource = new PaymentSourceResponse(paymentSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.subscriptionStatus = this.getResponseProperty("SubscriptionStatus");
|
||||||
|
|
||||||
const taxInformation = this.getResponseProperty("TaxInformation");
|
const taxInformation = this.getResponseProperty("TaxInformation");
|
||||||
if (taxInformation) {
|
if (taxInformation) {
|
||||||
this.taxInformation = new TaxInfoResponse(taxInformation);
|
this.taxInformation = new TaxInfoResponse(taxInformation);
|
|
@ -1,7 +1,7 @@
|
||||||
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
||||||
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
||||||
|
|
||||||
export class MaskedPaymentMethodResponse extends BaseResponse {
|
export class PaymentSourceResponse extends BaseResponse {
|
||||||
type: PaymentMethodType;
|
type: PaymentMethodType;
|
||||||
description: string;
|
description: string;
|
||||||
needsVerification: boolean;
|
needsVerification: boolean;
|
|
@ -1,5 +1,8 @@
|
||||||
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
|
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 { InvoicesResponse } from "@bitwarden/common/billing/models/response/invoices.response";
|
||||||
|
import { PaymentMethodResponse } from "@bitwarden/common/billing/models/response/payment-method.response";
|
||||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { ToastService } from "@bitwarden/components";
|
import { ToastService } from "@bitwarden/components";
|
||||||
|
@ -9,10 +12,7 @@ import { BillingApiServiceAbstraction } from "../../billing/abstractions";
|
||||||
import { PaymentMethodType } from "../../billing/enums";
|
import { PaymentMethodType } from "../../billing/enums";
|
||||||
import { ExpandedTaxInfoUpdateRequest } from "../../billing/models/request/expanded-tax-info-update.request";
|
import { ExpandedTaxInfoUpdateRequest } from "../../billing/models/request/expanded-tax-info-update.request";
|
||||||
import { SubscriptionCancellationRequest } from "../../billing/models/request/subscription-cancellation.request";
|
import { SubscriptionCancellationRequest } from "../../billing/models/request/subscription-cancellation.request";
|
||||||
import { TokenizedPaymentMethodRequest } from "../../billing/models/request/tokenized-payment-method.request";
|
|
||||||
import { VerifyBankAccountRequest } from "../../billing/models/request/verify-bank-account.request";
|
|
||||||
import { OrganizationBillingMetadataResponse } from "../../billing/models/response/organization-billing-metadata.response";
|
import { OrganizationBillingMetadataResponse } from "../../billing/models/response/organization-billing-metadata.response";
|
||||||
import { PaymentInformationResponse } from "../../billing/models/response/payment-information.response";
|
|
||||||
import { PlanResponse } from "../../billing/models/response/plan.response";
|
import { PlanResponse } from "../../billing/models/response/plan.response";
|
||||||
import { ListResponse } from "../../models/response/list.response";
|
import { ListResponse } from "../../models/response/list.response";
|
||||||
import { CreateClientOrganizationRequest } from "../models/request/create-client-organization.request";
|
import { CreateClientOrganizationRequest } from "../models/request/create-client-organization.request";
|
||||||
|
@ -85,6 +85,19 @@ export class BillingApiService implements BillingApiServiceAbstraction {
|
||||||
return new OrganizationBillingMetadataResponse(r);
|
return new OrganizationBillingMetadataResponse(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getOrganizationPaymentMethod(organizationId: string): Promise<PaymentMethodResponse> {
|
||||||
|
const response = await this.execute(() =>
|
||||||
|
this.apiService.send(
|
||||||
|
"GET",
|
||||||
|
"/organizations/" + organizationId + "/billing/payment-method",
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return new PaymentMethodResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
async getPlans(): Promise<ListResponse<PlanResponse>> {
|
async getPlans(): Promise<ListResponse<PlanResponse>> {
|
||||||
const r = await this.apiService.send("GET", "/plans", null, false, true);
|
const r = await this.apiService.send("GET", "/plans", null, false, true);
|
||||||
return new ListResponse(r, PlanResponse);
|
return new ListResponse(r, PlanResponse);
|
||||||
|
@ -123,19 +136,6 @@ export class BillingApiService implements BillingApiServiceAbstraction {
|
||||||
return new InvoicesResponse(response);
|
return new InvoicesResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getProviderPaymentInformation(providerId: string): Promise<PaymentInformationResponse> {
|
|
||||||
const response = await this.execute(() =>
|
|
||||||
this.apiService.send(
|
|
||||||
"GET",
|
|
||||||
"/providers/" + providerId + "/billing/payment-information",
|
|
||||||
null,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return new PaymentInformationResponse(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getProviderSubscription(providerId: string): Promise<ProviderSubscriptionResponse> {
|
async getProviderSubscription(providerId: string): Promise<ProviderSubscriptionResponse> {
|
||||||
const response = await this.execute(() =>
|
const response = await this.execute(() =>
|
||||||
this.apiService.send(
|
this.apiService.send(
|
||||||
|
@ -149,6 +149,32 @@ export class BillingApiService implements BillingApiServiceAbstraction {
|
||||||
return new ProviderSubscriptionResponse(response);
|
return new ProviderSubscriptionResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateOrganizationPaymentMethod(
|
||||||
|
organizationId: string,
|
||||||
|
request: UpdatePaymentMethodRequest,
|
||||||
|
): Promise<void> {
|
||||||
|
return await this.apiService.send(
|
||||||
|
"PUT",
|
||||||
|
"/organizations/" + organizationId + "/billing/payment-method",
|
||||||
|
request,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateOrganizationTaxInformation(
|
||||||
|
organizationId: string,
|
||||||
|
request: ExpandedTaxInfoUpdateRequest,
|
||||||
|
): Promise<void> {
|
||||||
|
return await this.apiService.send(
|
||||||
|
"PUT",
|
||||||
|
"/organizations/" + organizationId + "/billing/tax-information",
|
||||||
|
request,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async updateProviderClientOrganization(
|
async updateProviderClientOrganization(
|
||||||
providerId: string,
|
providerId: string,
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
|
@ -163,19 +189,6 @@ export class BillingApiService implements BillingApiServiceAbstraction {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateProviderPaymentMethod(
|
|
||||||
providerId: string,
|
|
||||||
request: TokenizedPaymentMethodRequest,
|
|
||||||
): Promise<void> {
|
|
||||||
return await this.apiService.send(
|
|
||||||
"PUT",
|
|
||||||
"/providers/" + providerId + "/billing/payment-method",
|
|
||||||
request,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateProviderTaxInformation(providerId: string, request: ExpandedTaxInfoUpdateRequest) {
|
async updateProviderTaxInformation(providerId: string, request: ExpandedTaxInfoUpdateRequest) {
|
||||||
return await this.apiService.send(
|
return await this.apiService.send(
|
||||||
"PUT",
|
"PUT",
|
||||||
|
@ -186,10 +199,13 @@ export class BillingApiService implements BillingApiServiceAbstraction {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async verifyProviderBankAccount(providerId: string, request: VerifyBankAccountRequest) {
|
async verifyOrganizationBankAccount(
|
||||||
|
organizationId: string,
|
||||||
|
request: VerifyBankAccountRequest,
|
||||||
|
): Promise<void> {
|
||||||
return await this.apiService.send(
|
return await this.apiService.send(
|
||||||
"POST",
|
"POST",
|
||||||
"/providers/" + providerId + "/billing/payment-method/verify-bank-account",
|
"/organizations/" + organizationId + "/billing/payment-method/verify-bank-account",
|
||||||
request,
|
request,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
|
|
@ -33,6 +33,7 @@ export enum FeatureFlag {
|
||||||
DelayFido2PageScriptInitWithinMv2 = "delay-fido2-page-script-init-within-mv2",
|
DelayFido2PageScriptInitWithinMv2 = "delay-fido2-page-script-init-within-mv2",
|
||||||
AccountDeprovisioning = "pm-10308-account-deprovisioning",
|
AccountDeprovisioning = "pm-10308-account-deprovisioning",
|
||||||
NotificationBarAddLoginImprovements = "notification-bar-add-login-improvements",
|
NotificationBarAddLoginImprovements = "notification-bar-add-login-improvements",
|
||||||
|
AC2476_DeprecateStripeSourcesAPI = "AC-2476-deprecate-stripe-sources-api",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AllowedFeatureFlagTypes = boolean | number | string;
|
export type AllowedFeatureFlagTypes = boolean | number | string;
|
||||||
|
@ -76,6 +77,7 @@ export const DefaultFeatureFlagValue = {
|
||||||
[FeatureFlag.DelayFido2PageScriptInitWithinMv2]: FALSE,
|
[FeatureFlag.DelayFido2PageScriptInitWithinMv2]: FALSE,
|
||||||
[FeatureFlag.AccountDeprovisioning]: FALSE,
|
[FeatureFlag.AccountDeprovisioning]: FALSE,
|
||||||
[FeatureFlag.NotificationBarAddLoginImprovements]: FALSE,
|
[FeatureFlag.NotificationBarAddLoginImprovements]: FALSE,
|
||||||
|
[FeatureFlag.AC2476_DeprecateStripeSourcesAPI]: FALSE,
|
||||||
} satisfies Record<FeatureFlag, AllowedFeatureFlagTypes>;
|
} satisfies Record<FeatureFlag, AllowedFeatureFlagTypes>;
|
||||||
|
|
||||||
export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue;
|
export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue;
|
||||||
|
|
Loading…
Reference in New Issue