diff --git a/apps/web/src/app/billing/individual/billing-history-view.component.html b/apps/web/src/app/billing/individual/billing-history-view.component.html
index 2491fc42c7..7dbd8d1792 100644
--- a/apps/web/src/app/billing/individual/billing-history-view.component.html
+++ b/apps/web/src/app/billing/individual/billing-history-view.component.html
@@ -2,17 +2,6 @@
{{ "billingHistory" | i18n }}
-
{{ "loading" | i18n }}
-
-
+
+
+
diff --git a/apps/web/src/app/billing/individual/billing-history-view.component.ts b/apps/web/src/app/billing/individual/billing-history-view.component.ts
index 3ee6415d6d..e88d9bb154 100644
--- a/apps/web/src/app/billing/individual/billing-history-view.component.ts
+++ b/apps/web/src/app/billing/individual/billing-history-view.component.ts
@@ -1,8 +1,11 @@
import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
-import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { BillingHistoryResponse } from "@bitwarden/common/billing/models/response/billing-history.response";
+import { AccountBillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/account/account-billing-api.service.abstraction";
+import {
+ BillingInvoiceResponse,
+ BillingTransactionResponse,
+} from "@bitwarden/common/billing/models/response/billing.response";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@Component({
@@ -11,12 +14,14 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
export class BillingHistoryViewComponent implements OnInit {
loading = false;
firstLoaded = false;
- billing: BillingHistoryResponse;
+ invoices: BillingInvoiceResponse[] = [];
+ transactions: BillingTransactionResponse[] = [];
+ hasAdditionalHistory: boolean = false;
constructor(
- private apiService: ApiService,
private platformUtilsService: PlatformUtilsService,
private router: Router,
+ private accountBillingApiService: AccountBillingApiServiceAbstraction,
) {}
async ngOnInit() {
@@ -35,7 +40,27 @@ export class BillingHistoryViewComponent implements OnInit {
return;
}
this.loading = true;
- this.billing = await this.apiService.getUserBillingHistory();
+
+ const invoicesPromise = this.accountBillingApiService.getBillingInvoices(
+ this.invoices.length > 0 ? this.invoices[this.invoices.length - 1].id : null,
+ );
+
+ const transactionsPromise = this.accountBillingApiService.getBillingTransactions(
+ this.transactions.length > 0
+ ? this.transactions[this.transactions.length - 1].createdDate
+ : null,
+ );
+
+ const accountInvoices = await invoicesPromise;
+ const accountTransactions = await transactionsPromise;
+ const pageSize = 5;
+
+ this.invoices = [...this.invoices, ...accountInvoices];
+ this.transactions = [...this.transactions, ...accountTransactions];
+ this.hasAdditionalHistory = !(
+ accountInvoices.length < pageSize && accountTransactions.length < pageSize
+ );
+
this.loading = false;
}
}
diff --git a/apps/web/src/app/billing/organizations/organization-billing-history-view.component.html b/apps/web/src/app/billing/organizations/organization-billing-history-view.component.html
index 087009b291..20bf0475ee 100644
--- a/apps/web/src/app/billing/organizations/organization-billing-history-view.component.html
+++ b/apps/web/src/app/billing/organizations/organization-billing-history-view.component.html
@@ -1,17 +1,4 @@
-
-
-
+
@@ -22,7 +9,16 @@
>
{{ "loading" | i18n }}
-
-
+
+
+
diff --git a/apps/web/src/app/billing/organizations/organization-billing-history-view.component.ts b/apps/web/src/app/billing/organizations/organization-billing-history-view.component.ts
index cd29345200..00ab3fa777 100644
--- a/apps/web/src/app/billing/organizations/organization-billing-history-view.component.ts
+++ b/apps/web/src/app/billing/organizations/organization-billing-history-view.component.ts
@@ -2,8 +2,11 @@ import { Component, OnDestroy, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { concatMap, Subject, takeUntil } from "rxjs";
-import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
-import { BillingHistoryResponse } from "@bitwarden/common/billing/models/response/billing-history.response";
+import { OrganizationBillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/organizations/organization-billing-api.service.abstraction";
+import {
+ BillingInvoiceResponse,
+ BillingTransactionResponse,
+} from "@bitwarden/common/billing/models/response/billing.response";
@Component({
templateUrl: "organization-billing-history-view.component.html",
@@ -11,13 +14,15 @@ import { BillingHistoryResponse } from "@bitwarden/common/billing/models/respons
export class OrgBillingHistoryViewComponent implements OnInit, OnDestroy {
loading = false;
firstLoaded = false;
- billing: BillingHistoryResponse;
+ invoices: BillingInvoiceResponse[] = [];
+ transactions: BillingTransactionResponse[] = [];
organizationId: string;
+ hasAdditionalHistory: boolean = false;
private destroy$ = new Subject();
constructor(
- private organizationApiService: OrganizationApiServiceAbstraction,
+ private organizationBillingApiService: OrganizationBillingApiServiceAbstraction,
private route: ActivatedRoute,
) {}
@@ -43,8 +48,28 @@ export class OrgBillingHistoryViewComponent implements OnInit, OnDestroy {
if (this.loading) {
return;
}
+
this.loading = true;
- this.billing = await this.organizationApiService.getBillingHistory(this.organizationId);
+
+ const invoicesPromise = this.organizationBillingApiService.getBillingInvoices(
+ this.organizationId,
+ this.invoices.length > 0 ? this.invoices[this.invoices.length - 1].id : null,
+ );
+
+ const transactionsPromise = this.organizationBillingApiService.getBillingTransactions(
+ this.organizationId,
+ this.transactions.length > 0
+ ? this.transactions[this.transactions.length - 1].createdDate
+ : null,
+ );
+
+ const invoices = await invoicesPromise;
+ const transactions = await transactionsPromise;
+ const pageSize = 5;
+
+ this.invoices = [...this.invoices, ...invoices];
+ this.transactions = [...this.transactions, ...transactions];
+ this.hasAdditionalHistory = !(invoices.length < pageSize && transactions.length < pageSize);
this.loading = false;
}
}
diff --git a/apps/web/src/app/billing/shared/billing-history.component.ts b/apps/web/src/app/billing/shared/billing-history.component.ts
index ec85da7d33..ac16b3dc72 100644
--- a/apps/web/src/app/billing/shared/billing-history.component.ts
+++ b/apps/web/src/app/billing/shared/billing-history.component.ts
@@ -1,7 +1,10 @@
import { Component, Input } from "@angular/core";
import { PaymentMethodType, TransactionType } from "@bitwarden/common/billing/enums";
-import { BillingHistoryResponse } from "@bitwarden/common/billing/models/response/billing-history.response";
+import {
+ BillingInvoiceResponse,
+ BillingTransactionResponse,
+} from "@bitwarden/common/billing/models/response/billing.response";
@Component({
selector: "app-billing-history",
@@ -9,19 +12,14 @@ import { BillingHistoryResponse } from "@bitwarden/common/billing/models/respons
})
export class BillingHistoryComponent {
@Input()
- billing: BillingHistoryResponse;
+ invoices: BillingInvoiceResponse[];
+
+ @Input()
+ transactions: BillingTransactionResponse[];
paymentMethodType = PaymentMethodType;
transactionType = TransactionType;
- get invoices() {
- return this.billing != null ? this.billing.invoices : null;
- }
-
- get transactions() {
- return this.billing != null ? this.billing.transactions : null;
- }
-
paymentMethodClasses(type: PaymentMethodType) {
switch (type) {
case PaymentMethodType.Card:
diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts
index 5ccba9ead3..cdf6a27390 100644
--- a/libs/angular/src/services/jslib-services.module.ts
+++ b/libs/angular/src/services/jslib-services.module.ts
@@ -125,9 +125,13 @@ import {
BillingApiServiceAbstraction,
OrganizationBillingServiceAbstraction,
} from "@bitwarden/common/billing/abstractions";
+import { AccountBillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/account/account-billing-api.service.abstraction";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
+import { OrganizationBillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/organizations/organization-billing-api.service.abstraction";
+import { AccountBillingApiService } from "@bitwarden/common/billing/services/account/account-billing-api.service";
import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service";
import { BillingApiService } from "@bitwarden/common/billing/services/billing-api.service";
+import { OrganizationBillingApiService } from "@bitwarden/common/billing/services/organization/organization-billing-api.service";
import { OrganizationBillingService } from "@bitwarden/common/billing/services/organization-billing.service";
import { AppIdService as AppIdServiceAbstraction } from "@bitwarden/common/platform/abstractions/app-id.service";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
@@ -985,6 +989,16 @@ const safeProviders: SafeProvider[] = [
// subscribes to sync notifications and will update itself based on that.
deps: [ApiServiceAbstraction, SyncService],
}),
+ safeProvider({
+ provide: OrganizationBillingApiServiceAbstraction,
+ useClass: OrganizationBillingApiService,
+ deps: [ApiServiceAbstraction],
+ }),
+ safeProvider({
+ provide: AccountBillingApiServiceAbstraction,
+ useClass: AccountBillingApiService,
+ deps: [ApiServiceAbstraction],
+ }),
safeProvider({
provide: DefaultConfigService,
useClass: DefaultConfigService,
diff --git a/libs/common/src/billing/abstractions/account/account-billing-api.service.abstraction.ts b/libs/common/src/billing/abstractions/account/account-billing-api.service.abstraction.ts
new file mode 100644
index 0000000000..4b67ce55c3
--- /dev/null
+++ b/libs/common/src/billing/abstractions/account/account-billing-api.service.abstraction.ts
@@ -0,0 +1,12 @@
+import {
+ BillingInvoiceResponse,
+ BillingTransactionResponse,
+} from "@bitwarden/common/billing/models/response/billing.response";
+
+export class AccountBillingApiServiceAbstraction {
+ getBillingInvoices: (id: string, startAfter?: string) => Promise;
+ getBillingTransactions: (
+ id: string,
+ startAfter?: string,
+ ) => Promise;
+}
diff --git a/libs/common/src/billing/abstractions/organizations/organization-billing-api.service.abstraction.ts b/libs/common/src/billing/abstractions/organizations/organization-billing-api.service.abstraction.ts
new file mode 100644
index 0000000000..4b3592bb6d
--- /dev/null
+++ b/libs/common/src/billing/abstractions/organizations/organization-billing-api.service.abstraction.ts
@@ -0,0 +1,12 @@
+import {
+ BillingInvoiceResponse,
+ BillingTransactionResponse,
+} from "@bitwarden/common/billing/models/response/billing.response";
+
+export class OrganizationBillingApiServiceAbstraction {
+ getBillingInvoices: (id: string, startAfter?: string) => Promise;
+ getBillingTransactions: (
+ id: string,
+ startAfter?: string,
+ ) => Promise;
+}
diff --git a/libs/common/src/billing/models/response/billing.response.ts b/libs/common/src/billing/models/response/billing.response.ts
index 45d3cf1e67..b94fc1b64b 100644
--- a/libs/common/src/billing/models/response/billing.response.ts
+++ b/libs/common/src/billing/models/response/billing.response.ts
@@ -29,6 +29,7 @@ export class BillingSourceResponse extends BaseResponse {
}
export class BillingInvoiceResponse extends BaseResponse {
+ id: string;
url: string;
pdfUrl: string;
number: string;
@@ -38,6 +39,7 @@ export class BillingInvoiceResponse extends BaseResponse {
constructor(response: any) {
super(response);
+ this.id = this.getResponseProperty("Id");
this.url = this.getResponseProperty("Url");
this.pdfUrl = this.getResponseProperty("PdfUrl");
this.number = this.getResponseProperty("Number");
diff --git a/libs/common/src/billing/services/account/account-billing-api.service.ts b/libs/common/src/billing/services/account/account-billing-api.service.ts
new file mode 100644
index 0000000000..ddd5bad02e
--- /dev/null
+++ b/libs/common/src/billing/services/account/account-billing-api.service.ts
@@ -0,0 +1,34 @@
+import { ApiService } from "../../../abstractions/api.service";
+import { AccountBillingApiServiceAbstraction } from "../../abstractions/account/account-billing-api.service.abstraction";
+import {
+ BillingInvoiceResponse,
+ BillingTransactionResponse,
+} from "../../models/response/billing.response";
+
+export class AccountBillingApiService implements AccountBillingApiServiceAbstraction {
+ constructor(private apiService: ApiService) {}
+
+ async getBillingInvoices(startAfter?: string): Promise {
+ const queryParams = startAfter ? `?startAfter=${startAfter}` : "";
+ const r = await this.apiService.send(
+ "GET",
+ `/accounts/billing/invoices${queryParams}`,
+ null,
+ true,
+ true,
+ );
+ return r?.map((i: any) => new BillingInvoiceResponse(i)) || [];
+ }
+
+ async getBillingTransactions(startAfter?: string): Promise {
+ const queryParams = startAfter ? `?startAfter=${startAfter}` : "";
+ const r = await this.apiService.send(
+ "GET",
+ `/accounts/billing/transactions${queryParams}`,
+ null,
+ true,
+ true,
+ );
+ return r?.map((i: any) => new BillingTransactionResponse(i)) || [];
+ }
+}
diff --git a/libs/common/src/billing/services/organization/organization-billing-api.service.ts b/libs/common/src/billing/services/organization/organization-billing-api.service.ts
new file mode 100644
index 0000000000..acf12e8320
--- /dev/null
+++ b/libs/common/src/billing/services/organization/organization-billing-api.service.ts
@@ -0,0 +1,37 @@
+import { ApiService } from "../../../abstractions/api.service";
+import { OrganizationBillingApiServiceAbstraction } from "../../abstractions/organizations/organization-billing-api.service.abstraction";
+import {
+ BillingInvoiceResponse,
+ BillingTransactionResponse,
+} from "../../models/response/billing.response";
+
+export class OrganizationBillingApiService implements OrganizationBillingApiServiceAbstraction {
+ constructor(private apiService: ApiService) {}
+
+ async getBillingInvoices(id: string, startAfter?: string): Promise {
+ const queryParams = startAfter ? `?startAfter=${startAfter}` : "";
+ const r = await this.apiService.send(
+ "GET",
+ `/organizations/${id}/billing/invoices${queryParams}`,
+ null,
+ true,
+ true,
+ );
+ return r?.map((i: any) => new BillingInvoiceResponse(i)) || [];
+ }
+
+ async getBillingTransactions(
+ id: string,
+ startAfter?: string,
+ ): Promise {
+ const queryParams = startAfter ? `?startAfter=${startAfter}` : "";
+ const r = await this.apiService.send(
+ "GET",
+ `/organizations/${id}/billing/transactions${queryParams}`,
+ null,
+ true,
+ true,
+ );
+ return r?.map((i: any) => new BillingTransactionResponse(i)) || [];
+ }
+}