1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-03 09:03:32 +00:00

[PM-21881] Manage payment details outside of checkout (#15458)

* Add billable-entity

* Add payment types

* Add billing.client

* Update stripe.service

* Add payment method components

* Add address.pipe

* Add billing address components

* Add account credit components

* Add component index

* Add feature flag

* Re-work organization warnings code

* Add organization-payment-details.component

* Backfill translations

* Set up organization FF routing

* Add account-payment-details.component

* Set up account FF routing

* Add provider-payment-details.component

* Set up provider FF routing

* Use inline component templates for re-usable payment components

* Remove errant rebase file

* Removed public accessibility modifier

* Fix failing test
This commit is contained in:
Alex Morask
2025-07-10 08:32:40 -05:00
committed by GitHub
parent 8c3c5ab861
commit a53b1e9ffb
60 changed files with 4268 additions and 151 deletions

View File

@@ -1,14 +1,14 @@
<app-free-trial-warning
<app-organization-free-trial-warning
*ngIf="useOrganizationWarningsService$ | async"
[organization]="organization"
(clicked)="navigateToPaymentMethod()"
>
</app-free-trial-warning>
<app-reseller-renewal-warning
</app-organization-free-trial-warning>
<app-organization-reseller-renewal-warning
*ngIf="useOrganizationWarningsService$ | async"
[organization]="organization"
>
</app-reseller-renewal-warning>
</app-organization-reseller-renewal-warning>
<ng-container *ngIf="freeTrialWhenWarningsServiceDisabled$ | async as freeTrial">
<bit-banner
id="free-trial-banner"

View File

@@ -78,8 +78,8 @@ import {
DecryptionFailureDialogComponent,
PasswordRepromptService,
} from "@bitwarden/vault";
import { OrganizationWarningsService } from "@bitwarden/web-vault/app/billing/services/organization-warnings.service";
import { ResellerRenewalWarningComponent } from "@bitwarden/web-vault/app/billing/warnings/reseller-renewal-warning.component";
import { OrganizationResellerRenewalWarningComponent } from "@bitwarden/web-vault/app/billing/warnings/components/organization-reseller-renewal-warning.component";
import { OrganizationWarningsService } from "@bitwarden/web-vault/app/billing/warnings/services/organization-warnings.service";
import { BillingNotificationService } from "../../../billing/services/billing-notification.service";
import {
@@ -88,7 +88,7 @@ import {
} from "../../../billing/services/reseller-warning.service";
import { TrialFlowService } from "../../../billing/services/trial-flow.service";
import { FreeTrial } from "../../../billing/types/free-trial";
import { FreeTrialWarningComponent } from "../../../billing/warnings/free-trial-warning.component";
import { OrganizationFreeTrialWarningComponent } from "../../../billing/warnings/components/organization-free-trial-warning.component";
import { SharedModule } from "../../../shared";
import { AssignCollectionsWebComponent } from "../../../vault/components/assign-collections";
import {
@@ -125,7 +125,7 @@ import {
BulkCollectionsDialogResult,
} from "./bulk-collections-dialog";
import { CollectionAccessRestrictedComponent } from "./collection-access-restricted.component";
import { getNestedCollectionTree, getFlatCollectionTree } from "./utils";
import { getFlatCollectionTree, getNestedCollectionTree } from "./utils";
import { VaultFilterModule } from "./vault-filter/vault-filter.module";
import { VaultHeaderComponent } from "./vault-header/vault-header.component";
@@ -150,8 +150,8 @@ enum AddAccessStatusType {
SharedModule,
BannerModule,
NoItemsModule,
FreeTrialWarningComponent,
ResellerRenewalWarningComponent,
OrganizationFreeTrialWarningComponent,
OrganizationResellerRenewalWarningComponent,
],
providers: [
RoutedVaultFilterService,
@@ -749,10 +749,13 @@ export class VaultComponent implements OnInit, OnDestroy {
}
async navigateToPaymentMethod() {
await this.router.navigate(
["organizations", `${this.organization?.id}`, "billing", "payment-method"],
{ state: { launchPaymentModalAutomatically: true } },
const managePaymentDetailsOutsideCheckout = await this.configService.getFeatureFlag(
FeatureFlag.PM21881_ManagePaymentDetailsOutsideCheckout,
);
const route = managePaymentDetailsOutsideCheckout ? "payment-details" : "payment-method";
await this.router.navigate(["organizations", `${this.organization?.id}`, "billing", route], {
state: { launchPaymentModalAutomatically: true },
});
}
addAccessToggle(e: AddAccessStatusType) {

View File

@@ -74,7 +74,11 @@
>
<bit-nav-item [text]="'subscription' | i18n" route="billing/subscription"></bit-nav-item>
<ng-container *ngIf="(showPaymentAndHistory$ | async) && (organizationIsUnmanaged$ | async)">
<bit-nav-item [text]="'paymentMethod' | i18n" route="billing/payment-method"></bit-nav-item>
@let paymentDetailsPageData = paymentDetailsPageData$ | async;
<bit-nav-item
[text]="paymentDetailsPageData.textKey | i18n"
[route]="paymentDetailsPageData.route"
></bit-nav-item>
<bit-nav-item [text]="'billingHistory' | i18n" route="billing/history"></bit-nav-item>
</ng-container>
</bit-nav-group>

View File

@@ -64,6 +64,11 @@ export class OrganizationLayoutComponent implements OnInit {
protected showSponsoredFamiliesDropdown$: Observable<boolean>;
protected canShowPoliciesTab$: Observable<boolean>;
protected paymentDetailsPageData$: Observable<{
route: string;
textKey: string;
}>;
constructor(
private route: ActivatedRoute,
private organizationService: OrganizationService,
@@ -135,6 +140,16 @@ export class OrganizationLayoutComponent implements OnInit {
),
),
);
this.paymentDetailsPageData$ = this.configService
.getFeatureFlag$(FeatureFlag.PM21881_ManagePaymentDetailsOutsideCheckout)
.pipe(
map((managePaymentDetailsOutsideCheckout) =>
managePaymentDetailsOutsideCheckout
? { route: "billing/payment-details", textKey: "paymentDetails" }
: { route: "billing/payment-method", textKey: "paymentMethod" },
),
);
}
canShowVaultTab(organization: Organization): boolean {