1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 08:13:42 +00:00

[PM-18794] Allow provider payment method (#13825)

* Allow provider payment method

* Run prettier
This commit is contained in:
Alex Morask
2025-03-14 11:33:21 -04:00
committed by GitHub
parent 4d68952ef3
commit 2ecfac40b7
9 changed files with 198 additions and 23 deletions

View File

@@ -7,6 +7,7 @@ import { JslibModule } from "@bitwarden/angular/jslib.module";
import { SearchModule } from "@bitwarden/components";
import { DangerZoneComponent } from "@bitwarden/web-vault/app/auth/settings/account/danger-zone.component";
import { OrganizationPlansComponent } from "@bitwarden/web-vault/app/billing";
import { VerifyBankAccountComponent } from "@bitwarden/web-vault/app/billing/shared/verify-bank-account/verify-bank-account.component";
import { OssModule } from "@bitwarden/web-vault/app/oss.module";
import {
@@ -49,6 +50,7 @@ import { VerifyRecoverDeleteProviderComponent } from "./verify-recover-delete-pr
ProvidersLayoutComponent,
DangerZoneComponent,
ScrollingModule,
VerifyBankAccountComponent,
],
declarations: [
AcceptProviderComponent,

View File

@@ -63,15 +63,40 @@
</div>
</ng-container>
<!-- Account Credit -->
<ng-container>
<bit-section>
<h2 bitTypography="h2">
{{ "accountCredit" | i18n }}
</h2>
<p class="tw-text-lg tw-font-bold">{{ subscription.accountCredit | currency: "$" }}</p>
<p bitTypography="body1">{{ "creditAppliedDesc" | i18n }}</p>
</ng-container>
</bit-section>
<!-- Payment Method -->
<bit-section *ngIf="allowProviderPaymentMethod$ | async">
<h2 bitTypography="h2">{{ "paymentMethod" | i18n }}</h2>
<p *ngIf="!subscription.paymentSource" bitTypography="body1">
{{ "noPaymentMethod" | i18n }}
</p>
<ng-container *ngIf="subscription.paymentSource">
<app-verify-bank-account
*ngIf="subscription.paymentSource.needsVerification"
[onSubmit]="verifyBankAccount"
(submitted)="load()"
>
</app-verify-bank-account>
<p>
<i class="bwi bwi-fw" [ngClass]="paymentSourceClasses"></i>
{{ subscription.paymentSource.description }}
<span *ngIf="subscription.paymentSource.needsVerification"
>- {{ "unverified" | i18n }}</span
>
</p>
</ng-container>
<button type="button" bitButton buttonType="secondary" [bitAction]="updatePaymentMethod">
{{ updatePaymentSourceButtonText }}
</button>
</bit-section>
<!-- Tax Information -->
<ng-container>
<bit-section>
<h2 bitTypography="h2" class="tw-mt-16">{{ "taxInformation" | i18n }}</h2>
<p>{{ "taxInformationDesc" | i18n }}</p>
<app-manage-tax-information
@@ -80,6 +105,6 @@
[onSubmit]="updateTaxInformation"
(taxInformationUpdated)="load()"
/>
</ng-container>
</bit-section>
</ng-container>
</bit-container>

View File

@@ -2,17 +2,26 @@
// @ts-strict-ignore
import { Component, OnDestroy, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { Subject, concatMap, takeUntil } from "rxjs";
import { concatMap, lastValueFrom, Subject, takeUntil } from "rxjs";
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
import { 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 {
ProviderPlanResponse,
ProviderSubscriptionResponse,
} from "@bitwarden/common/billing/models/response/provider-subscription-response";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { DialogService, ToastService } from "@bitwarden/components";
import { BillingNotificationService } from "@bitwarden/web-vault/app/billing/services/billing-notification.service";
import {
AdjustPaymentDialogComponent,
AdjustPaymentDialogResultType,
} from "@bitwarden/web-vault/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component";
@Component({
selector: "app-provider-subscription",
@@ -29,11 +38,18 @@ export class ProviderSubscriptionComponent implements OnInit, OnDestroy {
protected readonly TaxInformation = TaxInformation;
protected readonly allowProviderPaymentMethod$ = this.configService.getFeatureFlag$(
FeatureFlag.PM18794_ProviderPaymentMethod,
);
constructor(
private billingApiService: BillingApiServiceAbstraction,
private i18nService: I18nService,
private route: ActivatedRoute,
private billingNotificationService: BillingNotificationService,
private dialogService: DialogService,
private toastService: ToastService,
private configService: ConfigService,
) {}
async ngOnInit() {
@@ -66,6 +82,21 @@ export class ProviderSubscriptionComponent implements OnInit, OnDestroy {
}
}
protected updatePaymentMethod = async (): Promise<void> => {
const dialogRef = AdjustPaymentDialogComponent.open(this.dialogService, {
data: {
initialPaymentMethod: this.subscription.paymentSource?.type,
providerId: this.providerId,
},
});
const result = await lastValueFrom(dialogRef.closed);
if (result === AdjustPaymentDialogResultType.Submitted) {
await this.load();
}
};
protected updateTaxInformation = async (taxInformation: TaxInformation) => {
try {
const request = ExpandedTaxInfoUpdateRequest.From(taxInformation);
@@ -76,6 +107,15 @@ export class ProviderSubscriptionComponent implements OnInit, OnDestroy {
}
};
protected verifyBankAccount = async (request: VerifyBankAccountRequest): Promise<void> => {
await this.billingApiService.verifyProviderBankAccount(this.providerId, request);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("verifiedBankAccount"),
});
};
protected getFormattedCost(
cost: number,
seatMinimum: number,
@@ -133,4 +173,28 @@ export class ProviderSubscriptionComponent implements OnInit, OnDestroy {
return "month";
}
}
protected get paymentSourceClasses() {
if (this.subscription.paymentSource == null) {
return [];
}
switch (this.subscription.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 updatePaymentSourceButtonText(): string {
const key =
this.subscription.paymentSource == null ? "addPaymentMethod" : "changePaymentMethod";
return this.i18nService.t(key);
}
}