Skip to content

Commit

Permalink
feat: implement subscription status refresh logic in BillingInvoiceSe…
Browse files Browse the repository at this point in the history
…rvice and UserAPI
  • Loading branch information
simlarsen committed Jan 17, 2025
1 parent 6bfd9b6 commit 0931705
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 32 deletions.
34 changes: 2 additions & 32 deletions Common/Server/API/BillingInvoiceAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
import Response from "../Utils/Response";
import BaseAPI from "./BaseAPI";
import BaseModel from "Common/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
import SubscriptionStatus from "Common/Types/Billing/SubscriptionStatus";
import BadDataException from "Common/Types/Exception/BadDataException";
import { JSONObject } from "Common/Types/JSON";
import Permission, { UserPermission } from "Common/Types/Permission";
Expand Down Expand Up @@ -127,37 +126,8 @@ export default class UserAPI extends BaseAPI<
},
});

// refresh subscription status.
const subscriptionState: SubscriptionStatus =
await BillingService.getSubscriptionStatus(
project.paymentProviderSubscriptionId as string,
);

const meteredSubscriptionState: SubscriptionStatus =
await BillingService.getSubscriptionStatus(
project.paymentProviderMeteredSubscriptionId as string,
);

// if subscription is cancelled, create a new subscription and update project.

if (
meteredSubscriptionState === SubscriptionStatus.Canceled ||
subscriptionState === SubscriptionStatus.Canceled
) {
await ProjectService.reactiveSubscription(project.id!);
}

await ProjectService.updateOneById({
id: project.id!,
data: {
paymentProviderSubscriptionStatus: subscriptionState,
paymentProviderMeteredSubscriptionStatus:
meteredSubscriptionState,
},
props: {
isRoot: true,
ignoreHooks: true,
},
await BillingInvoiceService.refreshSubscriptionStatus({
projectId: project.id!,
});

return Response.sendEmptySuccessResponse(req, res);
Expand Down
109 changes: 109 additions & 0 deletions Common/Server/Services/BillingInvoiceService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,116 @@ import Model, {
InvoiceStatus,
} from "Common/Models/DatabaseModels/BillingInvoice";
import Project from "Common/Models/DatabaseModels/Project";
import SubscriptionStatus from "../../Types/Billing/SubscriptionStatus";
import ObjectID from "../../Types/ObjectID";

export class Service extends DatabaseService<Model> {
public constructor() {
super(Model);
this.setDoNotAllowDelete(true);
}


public async refreshSubscriptionStatus(data: {
projectId: ObjectID;
}) {
let project: Project | null = await ProjectService.findOneById({
id: data.projectId,
props: {
isRoot: true,
},
select: {
_id: true,
paymentProviderCustomerId: true,
paymentProviderSubscriptionId: true,
paymentProviderMeteredSubscriptionId: true,
},
});

// refresh the subscription status. This is a hack to ensure that the subscription status is always up to date.
// This is because the subscription status can change at any time and we need to ensure that the subscription status is always up to date.

if (!project) {
throw new BadDataException("Project not found");
}

if (!project.paymentProviderCustomerId) {
throw new BadDataException("Payment provider customer id not found.");
}

let subscriptionState: SubscriptionStatus =
await BillingService.getSubscriptionStatus(
project.paymentProviderSubscriptionId as string,
);

let meteredSubscriptionState: SubscriptionStatus =
await BillingService.getSubscriptionStatus(
project.paymentProviderMeteredSubscriptionId as string,
);

if (
meteredSubscriptionState === SubscriptionStatus.Canceled ||
subscriptionState === SubscriptionStatus.Canceled
) {

// check if all invoices are paid. If yes, then reactivate the subscription.

const invoices: Array<Invoice> = await BillingService.getInvoices(
project.paymentProviderCustomerId,
);

let allInvoicesPaid: boolean = true;

for (const invoice of invoices) {
if (invoice.status === InvoiceStatus.Open || invoice.status === InvoiceStatus.Uncollectible) {
allInvoicesPaid = false;
break;
}
}

if (allInvoicesPaid) {

await ProjectService.reactiveSubscription(project.id!);
project = await ProjectService.findOneById({
id: data.projectId,
props: {
isRoot: true,
},
select: {
_id: true,
paymentProviderCustomerId: true,
paymentProviderSubscriptionId: true,
paymentProviderMeteredSubscriptionId: true,
},
});

if (!project) {
throw new BadDataException("Project not found");
}

subscriptionState = await BillingService.getSubscriptionStatus(
project.paymentProviderSubscriptionId as string,
);

meteredSubscriptionState = await BillingService.getSubscriptionStatus(
project.paymentProviderMeteredSubscriptionId as string,
);
}
}

await ProjectService.updateOneById({
id: project.id!,
data: {
paymentProviderSubscriptionStatus: subscriptionState,
paymentProviderMeteredSubscriptionStatus: meteredSubscriptionState,
},
props: {
isRoot: true,
ignoreHooks: true,
},
});
}

protected override async onBeforeFind(
findBy: FindBy<Model>,
): Promise<OnFind<Model>> {
Expand All @@ -37,6 +140,11 @@ export class Service extends DatabaseService<Model> {
},
});

// refresh the subscription status. This is a hack to ensure that the subscription status is always up to date.
// This is because the subscription status can change at any time and we need to ensure that the subscription status is always up to date.

await this.refreshSubscriptionStatus({ projectId: findBy.props.tenantId! });

if (!project) {
throw new BadDataException("Project not found");
}
Expand All @@ -45,6 +153,7 @@ export class Service extends DatabaseService<Model> {
throw new BadDataException("Payment provider customer id not found.");
}


const invoices: Array<Invoice> = await BillingService.getInvoices(
project.paymentProviderCustomerId,
);
Expand Down
2 changes: 2 additions & 0 deletions Common/Server/Services/ProjectService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1224,6 +1224,8 @@ export class ProjectService extends DatabaseService<Model> {
isRoot: true,
},
});


}

public getActiveProjectStatusQuery(): Query<Model> {
Expand Down

0 comments on commit 0931705

Please sign in to comment.