fix: Add org-level payment credential fallback for no-show fee charging (#23071)

* fix: feat: Add org-level payment credential fallback for team bookings

* use repository pattern
This commit is contained in:
Anik Dhabal Babu
2025-08-14 11:30:38 +00:00
committed by GitHub
parent c268925330
commit a784af3e47
3 changed files with 101 additions and 19 deletions
@@ -229,4 +229,61 @@ export class CredentialRepository {
static async updateWhereId({ id, data }: { id: number; data: { key: Prisma.InputJsonValue } }) {
return prisma.credential.update({ where: { id }, data });
}
static async findPaymentCredentialByAppIdAndTeamId({
appId,
teamId,
}: {
appId: string | null;
teamId: number;
}) {
return await prisma.credential.findFirst({
where: {
teamId,
appId,
},
include: {
app: true,
},
});
}
static async findPaymentCredentialByAppIdAndUserId({
appId,
userId,
}: {
appId: string | null;
userId: number;
}) {
return await prisma.credential.findFirst({
where: {
userId,
appId,
},
include: {
app: true,
},
});
}
static async findPaymentCredentialByAppIdAndUserIdOrTeamId({
appId,
userId,
teamId,
}: {
appId: string | null;
userId: number;
teamId?: number | null;
}) {
const idToSearchObject = teamId ? { teamId } : { userId };
return await prisma.credential.findFirst({
where: {
...idToSearchObject,
appId,
},
include: {
app: true,
},
});
}
}
+17
View File
@@ -333,4 +333,21 @@ export class TeamRepository {
},
});
}
async findParentOrganizationByTeamId(teamId: number) {
const team = await this.prismaClient.team.findUnique({
where: {
id: teamId,
},
select: {
parent: {
select: {
id: true,
},
},
},
});
return team?.parent;
}
}
@@ -4,6 +4,9 @@ import { sendNoShowFeeChargedEmail } from "@calcom/emails";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { ErrorWithCode } from "@calcom/lib/errors";
import { getTranslation } from "@calcom/lib/server/i18n";
import { CredentialRepository } from "@calcom/lib/server/repository/credential";
import { MembershipRepository } from "@calcom/lib/server/repository/membership";
import { TeamRepository } from "@calcom/lib/server/repository/team";
import type { PrismaClient } from "@calcom/prisma";
import type { EventTypeMetadata } from "@calcom/prisma/zod-utils";
import type { CalendarEvent } from "@calcom/types/Calendar";
@@ -20,6 +23,7 @@ interface ChargeCardHandlerOptions {
}
export const chargeCardHandler = async ({ ctx, input }: ChargeCardHandlerOptions) => {
const { prisma } = ctx;
const teamRepository = new TeamRepository(prisma);
const booking = await prisma.booking.findUnique({
where: {
@@ -84,18 +88,14 @@ export const chargeCardHandler = async ({ ctx, input }: ChargeCardHandlerOptions
},
};
const idToSearchObject = booking.eventType?.teamId
? { teamId: booking.eventType.teamId }
: { userId: ctx.user.id };
const userId = ctx.user.id;
const teamId = booking.eventType?.teamId;
const appId = booking.payment[0].appId;
if (booking.eventType?.teamId) {
const userIsInTeam = await prisma.membership.findUnique({
where: {
userId_teamId: {
userId: ctx.user.id,
teamId: booking.eventType?.teamId,
},
},
if (teamId) {
const userIsInTeam = await MembershipRepository.findUniqueByUserIdAndTeamId({
userId,
teamId,
});
if (!userIsInTeam) {
@@ -103,16 +103,24 @@ export const chargeCardHandler = async ({ ctx, input }: ChargeCardHandlerOptions
}
}
const paymentCredential = await prisma.credential.findFirst({
where: {
...idToSearchObject,
appId: booking.payment[0].appId,
},
include: {
app: true,
},
let paymentCredential = await CredentialRepository.findPaymentCredentialByAppIdAndUserIdOrTeamId({
appId,
userId,
teamId,
});
if (!paymentCredential && teamId) {
// See if the team event belongs to an org
const org = await teamRepository.findParentOrganizationByTeamId(teamId);
if (org) {
paymentCredential = await CredentialRepository.findPaymentCredentialByAppIdAndTeamId({
appId,
teamId: org.id,
});
}
}
if (!paymentCredential?.app) {
throw new TRPCError({ code: "BAD_REQUEST", message: "Invalid payment credential" });
}