From c6a4c60807c99749197afab32e5d3f282143bba9 Mon Sep 17 00:00:00 2001 From: Henry Date: Sun, 7 Sep 2025 22:44:59 +0100 Subject: [PATCH] feat: skip updating subscription if unlimited seats --- packages/api/src/routers/member.ts | 56 ++++++++++++---------- packages/shared/src/utils/subscriptions.ts | 5 ++ 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/packages/api/src/routers/member.ts b/packages/api/src/routers/member.ts index 1a245a75..17134d0e 100644 --- a/packages/api/src/routers/member.ts +++ b/packages/api/src/routers/member.ts @@ -2,8 +2,10 @@ import { TRPCError } from "@trpc/server"; import { z } from "zod"; import * as memberRepo from "@kan/db/repository/member.repo"; +import * as subscriptionRepo from "@kan/db/repository/subscription.repo"; import * as userRepo from "@kan/db/repository/user.repo"; import * as workspaceRepo from "@kan/db/repository/workspace.repo"; +import { getSubscriptionByPlan, hasUnlimitedSeats } from "@kan/shared/utils"; import { updateSubscriptionSeats } from "@kan/stripe"; import { createTRPCRouter, protectedProcedure } from "../trpc"; @@ -62,29 +64,34 @@ export const memberRouter = createTRPCRouter({ } if (process.env.NEXT_PUBLIC_KAN_ENV === "cloud") { - const subscriptions = await ctx.auth.api.listActiveSubscriptions({ - workspacePublicId: workspace.publicId, - }); - - // get the active subscription - const activeSubscription = subscriptions.find( - (sub) => - sub.status === "active" || - (sub.status === "trialing" && sub.plan === "team"), + const subscriptions = await subscriptionRepo.getByReferenceId( + ctx.db, + workspace.publicId, ); - if (!activeSubscription) { + // get the active subscriptions + const activeTeamSubscription = getSubscriptionByPlan( + subscriptions, + "team", + ); + const activeProSubscription = getSubscriptionByPlan( + subscriptions, + "pro", + ); + const unlimitedSeats = hasUnlimitedSeats(subscriptions); + + if (!activeTeamSubscription && !activeProSubscription) { throw new TRPCError({ message: `Workspace with public ID ${workspace.publicId} does not have an active subscription`, code: "NOT_FOUND", }); } - // Update the Stripe subscription to add a seat with immediate proration - if (activeSubscription.stripeSubscriptionId) { + // Update the Stripe subscription + if (activeTeamSubscription?.stripeSubscriptionId && !unlimitedSeats) { try { await updateSubscriptionSeats( - activeSubscription.stripeSubscriptionId, + activeTeamSubscription.stripeSubscriptionId, 1, ); } catch (error) { @@ -204,22 +211,23 @@ export const memberRouter = createTRPCRouter({ // Handle subscription seat decrement for cloud environment if (process.env.NEXT_PUBLIC_KAN_ENV === "cloud") { - const subscriptions = await ctx.auth.api.listActiveSubscriptions({ - workspacePublicId: workspace.publicId, - }); - - // get the active subscription - const activeSubscription = subscriptions.find( - (sub) => - sub.status === "active" || - (sub.status === "trialing" && sub.plan === "team"), + const subscriptions = await subscriptionRepo.getByReferenceId( + ctx.db, + workspace.publicId, ); + // get the active subscriptions + const activeTeamSubscription = getSubscriptionByPlan( + subscriptions, + "team", + ); + const unlimitedSeats = hasUnlimitedSeats(subscriptions); + // Only decrease seats if there's an active subscription and stripeSubscriptionId - if (activeSubscription?.stripeSubscriptionId) { + if (activeTeamSubscription?.stripeSubscriptionId && !unlimitedSeats) { try { await updateSubscriptionSeats( - activeSubscription.stripeSubscriptionId, + activeTeamSubscription.stripeSubscriptionId, -1, ); } catch (error) { diff --git a/packages/shared/src/utils/subscriptions.ts b/packages/shared/src/utils/subscriptions.ts index 326b59a5..0779306e 100644 --- a/packages/shared/src/utils/subscriptions.ts +++ b/packages/shared/src/utils/subscriptions.ts @@ -14,6 +14,11 @@ export interface Subscription { unlimitedSeats: boolean; periodStart: Date | null; periodEnd: Date | null; + referenceId: string; + stripeSubscriptionId: string | null; + stripeCustomerId: string | null; + createdAt: Date; + updatedAt: Date; } export const getActiveSubscriptions = (