1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-22 04:14:04 +00:00

fix(billing): Rename and update upgrade payment service

This commit is contained in:
Stephon Brown
2025-10-01 18:36:52 -04:00
parent cdaa55421c
commit 51768557a6
2 changed files with 24 additions and 54 deletions

View File

@@ -3,7 +3,6 @@ import { mock, mockReset } from "jest-mock-extended";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationResponse } from "@bitwarden/common/admin-console/models/response/organization.response";
import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { OrganizationBillingServiceAbstraction } from "@bitwarden/common/billing/abstractions";
import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction";
import { PaymentMethodType, PlanType } from "@bitwarden/common/billing/enums";
@@ -12,16 +11,15 @@ import { SyncService } from "@bitwarden/common/platform/sync";
import { UserId } from "@bitwarden/common/types/guid";
import { LogService } from "@bitwarden/logging";
import { SubscriberBillingClient } from "../../../../clients";
import { AccountBillingClient } from "../../../../clients";
import { TokenizedPaymentMethod } from "../../../../payment/types";
import { BitwardenSubscriber } from "../../../../types";
import { PersonalSubscriptionPricingTierIds } from "../../../../types/subscription-pricing-tier";
import { UpgradePaymentService, PlanDetails } from "./upgrade-payment.service";
describe("UpgradePaymentService", () => {
const mockOrganizationBillingService = mock<OrganizationBillingServiceAbstraction>();
const mockSubscriberBillingClient = mock<SubscriberBillingClient>();
const mockAccountBillingClient = mock<AccountBillingClient>();
const mockTaxService = mock<TaxServiceAbstraction>();
const mockLogService = mock<LogService>();
const mockApiService = mock<ApiService>();
@@ -32,12 +30,11 @@ describe("UpgradePaymentService", () => {
let sut: UpgradePaymentService;
const mockSubscriber: BitwardenSubscriber = {
type: "account",
data: {
id: "user-id" as UserId,
email: "test@example.com",
} as Account,
const mockAccount = {
id: "user-id" as UserId,
email: "test@example.com",
emailVerified: true,
name: "Test User",
};
const mockTokenizedPaymentMethod: TokenizedPaymentMethod = {
@@ -91,7 +88,7 @@ describe("UpgradePaymentService", () => {
beforeEach(() => {
mockReset(mockOrganizationBillingService);
mockReset(mockSubscriberBillingClient);
mockReset(mockAccountBillingClient);
mockReset(mockTaxService);
mockReset(mockLogService);
@@ -103,7 +100,7 @@ describe("UpgradePaymentService", () => {
provide: OrganizationBillingServiceAbstraction,
useValue: mockOrganizationBillingService,
},
{ provide: SubscriberBillingClient, useValue: mockSubscriberBillingClient },
{ provide: AccountBillingClient, useValue: mockAccountBillingClient },
{ provide: TaxServiceAbstraction, useValue: mockTaxService },
{ provide: LogService, useValue: mockLogService },
{ provide: ApiService, useValue: mockApiService },
@@ -187,16 +184,15 @@ describe("UpgradePaymentService", () => {
});
describe("upgradeToPremium", () => {
it("should call subscriberBillingClient to purchase premium subscription and refresh data", async () => {
it("should call accountBillingClient to purchase premium subscription and refresh data", async () => {
// Arrange
mockSubscriberBillingClient.purchasePremiumSubscription.mockResolvedValue();
mockAccountBillingClient.purchasePremiumSubscription.mockResolvedValue();
// Act
await sut.upgradeToPremium(mockSubscriber, mockTokenizedPaymentMethod, mockBillingAddress);
await sut.upgradeToPremium(mockTokenizedPaymentMethod, mockBillingAddress);
// Assert
expect(mockSubscriberBillingClient.purchasePremiumSubscription).toHaveBeenCalledWith(
mockSubscriber,
expect(mockAccountBillingClient.purchasePremiumSubscription).toHaveBeenCalledWith(
mockTokenizedPaymentMethod,
mockBillingAddress,
);
@@ -210,7 +206,7 @@ describe("UpgradePaymentService", () => {
// Act & Assert
await expect(
sut.upgradeToPremium(mockSubscriber, incompletePaymentMethod, mockBillingAddress),
sut.upgradeToPremium(incompletePaymentMethod, mockBillingAddress),
).rejects.toThrow("Payment method type or token is missing");
});
@@ -220,7 +216,7 @@ describe("UpgradePaymentService", () => {
// Act & Assert
await expect(
sut.upgradeToPremium(mockSubscriber, mockTokenizedPaymentMethod, incompleteBillingAddress),
sut.upgradeToPremium(mockTokenizedPaymentMethod, incompleteBillingAddress),
).rejects.toThrow("Billing address information is incomplete");
});
});
@@ -236,7 +232,7 @@ describe("UpgradePaymentService", () => {
// Act
await sut.upgradeToFamilies(
mockSubscriber,
mockAccount,
mockFamiliesPlanDetails,
mockTokenizedPaymentMethod,
{
@@ -295,7 +291,7 @@ describe("UpgradePaymentService", () => {
// Act & Assert
await expect(
sut.upgradeToFamilies(mockSubscriber, invalidPlanDetails, mockTokenizedPaymentMethod, {
sut.upgradeToFamilies(mockAccount, invalidPlanDetails, mockTokenizedPaymentMethod, {
organizationName: "Test Organization",
billingAddress: mockBillingAddress,
}),
@@ -303,27 +299,11 @@ describe("UpgradePaymentService", () => {
expect(mockOrganizationBillingService.purchaseSubscription).toHaveBeenCalledTimes(1);
});
it("should throw error if subscriber is not an account", async () => {
const invalidSubscriber = { type: "organization" } as BitwardenSubscriber;
await expect(
sut.upgradeToFamilies(
invalidSubscriber,
mockFamiliesPlanDetails,
mockTokenizedPaymentMethod,
{
organizationName: "Test Organization",
billingAddress: mockBillingAddress,
},
),
).rejects.toThrow("Subscriber must be an account for families upgrade");
});
it("should throw error if payment method is incomplete", async () => {
const incompletePaymentMethod = { type: "card" } as TokenizedPaymentMethod;
await expect(
sut.upgradeToFamilies(mockSubscriber, mockFamiliesPlanDetails, incompletePaymentMethod, {
sut.upgradeToFamilies(mockAccount, mockFamiliesPlanDetails, incompletePaymentMethod, {
organizationName: "Test Organization",
billingAddress: mockBillingAddress,
}),

View File

@@ -14,13 +14,12 @@ import { PreviewOrganizationInvoiceRequest } from "@bitwarden/common/billing/mod
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { LogService } from "@bitwarden/logging";
import { SubscriberBillingClient } from "../../../../clients";
import { AccountBillingClient } from "../../../../clients";
import {
BillingAddress,
TokenizablePaymentMethod,
TokenizedPaymentMethod,
} from "../../../../payment/types";
import { BitwardenSubscriber } from "../../../../types";
import {
PersonalSubscriptionPricingTier,
PersonalSubscriptionPricingTierId,
@@ -53,7 +52,7 @@ export type PaymentFormValues = {
export class UpgradePaymentService {
constructor(
private organizationBillingService: OrganizationBillingServiceAbstraction,
private subscriberBillingClient: SubscriberBillingClient,
private accountBillingClient: AccountBillingClient,
private taxService: TaxServiceAbstraction,
private logService: LogService,
private apiService: ApiService,
@@ -126,17 +125,12 @@ export class UpgradePaymentService {
* Process premium upgrade
*/
async upgradeToPremium(
subscriber: BitwardenSubscriber,
paymentMethod: TokenizedPaymentMethod,
billingAddress: Pick<BillingAddress, "country" | "postalCode">,
): Promise<void> {
this.validatePaymentAndBillingInfo(paymentMethod, billingAddress);
await this.subscriberBillingClient.purchasePremiumSubscription(
subscriber,
paymentMethod,
billingAddress,
);
await this.accountBillingClient.purchasePremiumSubscription(paymentMethod, billingAddress);
await this.refreshAndSync();
}
@@ -145,15 +139,11 @@ export class UpgradePaymentService {
* Process families upgrade
*/
async upgradeToFamilies(
subscriber: BitwardenSubscriber,
account: Account,
planDetails: PlanDetails,
paymentMethod: TokenizedPaymentMethod,
formValues: PaymentFormValues,
): Promise<OrganizationResponse> {
if (subscriber.type !== "account") {
throw new Error("Subscriber must be an account for families upgrade");
}
const user = subscriber.data as Account;
const billingAddress = formValues.billingAddress;
if (!formValues.organizationName) {
@@ -167,7 +157,7 @@ export class UpgradePaymentService {
const subscriptionInformation: SubscriptionInformation = {
organization: {
name: formValues.organizationName,
billingEmail: user.email, // Use account email as billing email
billingEmail: account.email, // Use account email as billing email
},
plan: {
type: PlanType.FamiliesAnnually,
@@ -187,7 +177,7 @@ export class UpgradePaymentService {
const result = await this.organizationBillingService.purchaseSubscription(
subscriptionInformation,
user.id,
account.id,
);
await this.refreshAndSync();
return result;