Moving to supporting more than one sub

This commit is contained in:
Owen
2026-02-04 17:54:29 -08:00
parent 4613aae47d
commit 72d46b7352
5 changed files with 49 additions and 39 deletions

View File

@@ -12,7 +12,7 @@
*/ */
import { getTierPriceSet } from "@server/lib/billing/tiers"; import { getTierPriceSet } from "@server/lib/billing/tiers";
import { getOrgSubscriptionData } from "#private/routers/billing/getOrgSubscription"; import { getOrgSubscriptionsData } from "@server/private/routers/billing/getOrgSubscriptions";
import { build } from "@server/build"; import { build } from "@server/build";
export async function getOrgTierData( export async function getOrgTierData(
@@ -25,22 +25,32 @@ export async function getOrgTierData(
return { tier, active }; return { tier, active };
} }
const { subscription, items } = await getOrgSubscriptionData(orgId); // TODO: THIS IS INEFFICIENT!!! WE SHOULD IMPROVE HOW WE STORE TIERS WITH SUBSCRIPTIONS AND RETRIEVE THEM
if (items && items.length > 0) { const subscriptionsWithItems = await getOrgSubscriptionsData(orgId);
const tierPriceSet = getTierPriceSet();
// Iterate through tiers in order (earlier keys are higher tiers) for (const { subscription, items } of subscriptionsWithItems) {
for (const [tierId, priceId] of Object.entries(tierPriceSet)) { if (items && items.length > 0) {
// Check if any subscription item matches this tier's price ID const tierPriceSet = getTierPriceSet();
const matchingItem = items.find((item) => item.priceId === priceId); // Iterate through tiers in order (earlier keys are higher tiers)
if (matchingItem) { for (const [tierId, priceId] of Object.entries(tierPriceSet)) {
tier = tierId; // Check if any subscription item matches this tier's price ID
break; const matchingItem = items.find((item) => item.priceId === priceId);
if (matchingItem) {
tier = tierId;
break;
}
} }
} }
}
if (subscription && subscription.status === "active") { if (subscription && subscription.status === "active") {
active = true; active = true;
}
// If we found a tier and active subscription, we can stop
if (tier && active) {
break;
}
} }
return { tier, active }; return { tier, active };
} }

View File

@@ -48,7 +48,7 @@ registry.registerPath({
responses: {} responses: {}
}); });
export async function getOrgSubscription( export async function getOrgSubscriptions(
req: Request, req: Request,
res: Response, res: Response,
next: NextFunction next: NextFunction
@@ -66,12 +66,9 @@ export async function getOrgSubscription(
const { orgId } = parsedParams.data; const { orgId } = parsedParams.data;
let subscriptionData = null; let subscriptions = null;
let itemsData: SubscriptionItem[] = [];
try { try {
const { subscription, items } = await getOrgSubscriptionData(orgId); subscriptions = await getOrgSubscriptionsData(orgId);
subscriptionData = subscription;
itemsData = items;
} catch (err) { } catch (err) {
if ((err as Error).message === "Not found") { if ((err as Error).message === "Not found") {
return next( return next(
@@ -86,8 +83,7 @@ export async function getOrgSubscription(
return response<GetOrgSubscriptionResponse>(res, { return response<GetOrgSubscriptionResponse>(res, {
data: { data: {
subscription: subscriptionData, subscriptions
items: itemsData
}, },
success: true, success: true,
error: false, error: false,
@@ -102,9 +98,9 @@ export async function getOrgSubscription(
} }
} }
export async function getOrgSubscriptionData( export async function getOrgSubscriptionsData(
orgId: string orgId: string
): Promise<{ subscription: Subscription | null; items: SubscriptionItem[] }> { ): Promise<Array<{ subscription: Subscription; items: SubscriptionItem[] }>> {
const org = await db const org = await db
.select() .select()
.from(orgs) .from(orgs)
@@ -122,21 +118,21 @@ export async function getOrgSubscriptionData(
.where(eq(customers.orgId, orgId)) .where(eq(customers.orgId, orgId))
.limit(1); .limit(1);
let subscription = null; const subscriptionsWithItems: Array<{
let items: SubscriptionItem[] = []; subscription: Subscription;
items: SubscriptionItem[];
}> = [];
if (customer.length > 0) { if (customer.length > 0) {
// Get subscription for customer // Get all subscriptions for customer
const subs = await db const subs = await db
.select() .select()
.from(subscriptions) .from(subscriptions)
.where(eq(subscriptions.customerId, customer[0].customerId)) .where(eq(subscriptions.customerId, customer[0].customerId));
.limit(1);
if (subs.length > 0) { for (const subscription of subs) {
subscription = subs[0]; // Get subscription items for each subscription
// Get subscription items const items = await db
items = await db
.select() .select()
.from(subscriptionItems) .from(subscriptionItems)
.where( .where(
@@ -145,8 +141,13 @@ export async function getOrgSubscriptionData(
subscription.subscriptionId subscription.subscriptionId
) )
); );
subscriptionsWithItems.push({
subscription,
items
});
} }
} }
return { subscription, items }; return subscriptionsWithItems;
} }

View File

@@ -13,6 +13,6 @@
export * from "./createCheckoutSessionSAAS"; export * from "./createCheckoutSessionSAAS";
export * from "./createPortalSession"; export * from "./createPortalSession";
export * from "./getOrgSubscription"; export * from "./getOrgSubscriptions";
export * from "./getOrgUsage"; export * from "./getOrgUsage";
export * from "./internalGetOrgTier"; export * from "./internalGetOrgTier";

View File

@@ -175,10 +175,10 @@ if (build === "saas") {
); );
authenticated.get( authenticated.get(
"/org/:orgId/billing/subscription", "/org/:orgId/billing/subscriptions",
verifyOrgAccess, verifyOrgAccess,
verifyUserHasAction(ActionsEnum.billing), verifyUserHasAction(ActionsEnum.billing),
billing.getOrgSubscription billing.getOrgSubscriptions
); );
authenticated.get( authenticated.get(

View File

@@ -1,8 +1,7 @@
import { Limit, Subscription, SubscriptionItem, Usage } from "@server/db"; import { Limit, Subscription, SubscriptionItem, Usage } from "@server/db";
export type GetOrgSubscriptionResponse = { export type GetOrgSubscriptionResponse = {
subscription: Subscription | null; subscriptions: Array<{ subscription: Subscription; items: SubscriptionItem[] }>;
items: SubscriptionItem[];
}; };
export type GetOrgUsageResponse = { export type GetOrgUsageResponse = {