fix: platform billing portal (#23975)

* fix: check if team is platform or not when sending billingPortalUrl

* update team repository

* fix: pass teamId explicitely for platform team

* fix: coderabbit feedback

* fix: merge conflicts

* fix: merge conflicts

* fix: make sure we pass in the correct subsciption id

* fix: implement PR feedback
This commit is contained in:
Rajiv Sahal
2025-09-24 20:07:46 +00:00
committed by GitHub
parent 386f60ca6d
commit f11c4dd7ec
3 changed files with 56 additions and 12 deletions
@@ -30,16 +30,18 @@ declare global {
export default function PlatformBillingUpgrade() {
const pathname = usePathname();
const { t } = useLocale();
const { isUserLoading, isUserBillingDataLoading, isPlatformUser, userBillingData, isPaidUser, userOrgId } =
useGetUserAttributes();
const returnTo = pathname;
const billingHref = `/api/integrations/stripepayment/portal?returnTo=${WEBAPP_URL}${returnTo}`;
const teamId = `teamId=${userOrgId}`;
const billingHref = `/api/integrations/stripepayment/portal?returnTo=${WEBAPP_URL}${returnTo}&${teamId}`;
const onContactSupportClick = async () => {
if (window.Support) {
window.Support.open();
}
};
const { isUserLoading, isUserBillingDataLoading, isPlatformUser, userBillingData, isPaidUser, userOrgId } =
useGetUserAttributes();
const { mutateAsync: removeTeamSubscription, isPending: isRemoveTeamSubscriptionLoading } =
useUnsubscribeTeamToStripe({
@@ -1,4 +1,5 @@
import logger from "@calcom/lib/logger";
import type { Prisma } from "@calcom/prisma/client";
import { MembershipRole } from "@calcom/prisma/enums";
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
@@ -18,25 +19,53 @@ export class TeamBillingPortalService extends BillingPortalService {
});
}
async getCustomerId(teamId: number): Promise<string | null> {
const log = logger.getSubLogger({ prefix: ["TeamBillingPortalService", "getCustomerId"] });
const team = await this.teamRepository.findById({ id: teamId });
if (!team) return null;
const teamMetadataParsed = teamMetadataSchema.safeParse(team.metadata);
async getValidatedTeamSubscriptionId(metadata: Prisma.JsonValue) {
const teamMetadataParsed = teamMetadataSchema.safeParse(metadata);
if (!teamMetadataParsed.success || !teamMetadataParsed.data?.subscriptionId) {
return null;
}
return teamMetadataParsed.data.subscriptionId;
}
async getValidatedTeamSubscriptionIdForPlatform(subscriptionId?: string | null) {
if (!subscriptionId) {
return null;
}
return subscriptionId;
}
async getCustomerId(teamId: number): Promise<string | null> {
const log = logger.getSubLogger({ prefix: ["TeamBillingPortalService", "getCustomerId"] });
const team = await this.teamRepository.findByIdIncludePlatformBilling({ id: teamId });
if (!team) return null;
let teamSubscriptionId = "";
if (team.isPlatform) {
const subscriptionId = await this.getValidatedTeamSubscriptionIdForPlatform(
team.platformBilling?.subscriptionId
);
if (!subscriptionId) return null;
teamSubscriptionId = subscriptionId;
} else {
const subscriptionId = await this.getValidatedTeamSubscriptionId(team.metadata);
if (!subscriptionId) return null;
teamSubscriptionId = subscriptionId;
}
try {
const subscription = await getSubscriptionFromId(teamMetadataParsed.data.subscriptionId);
const subscription = await getSubscriptionFromId(teamSubscriptionId);
if (!subscription?.customer) {
log.warn("Subscription found but no customer ID", {
teamId,
subscriptionId: teamMetadataParsed.data.subscriptionId,
teamSubscriptionId,
});
return null;
}
+13
View File
@@ -183,6 +183,19 @@ export class TeamRepository {
return getParsedTeam(team);
}
async findByIdIncludePlatformBilling({ id }: { id: number }) {
const team = await this.prismaClient.team.findUnique({
where: {
id,
},
select: { ...teamSelect, platformBilling: true },
});
if (!team) {
return null;
}
return getParsedTeam(team);
}
async findAllByParentId({
parentId,
select = teamSelect,