Skip to content

Commit a784af3

Browse files
authored
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
1 parent c268925 commit a784af3

File tree

3 files changed

+102
-20
lines changed

3 files changed

+102
-20
lines changed

packages/lib/server/repository/credential.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,4 +229,61 @@ export class CredentialRepository {
229229
static async updateWhereId({ id, data }: { id: number; data: { key: Prisma.InputJsonValue } }) {
230230
return prisma.credential.update({ where: { id }, data });
231231
}
232+
233+
static async findPaymentCredentialByAppIdAndTeamId({
234+
appId,
235+
teamId,
236+
}: {
237+
appId: string | null;
238+
teamId: number;
239+
}) {
240+
return await prisma.credential.findFirst({
241+
where: {
242+
teamId,
243+
appId,
244+
},
245+
include: {
246+
app: true,
247+
},
248+
});
249+
}
250+
251+
static async findPaymentCredentialByAppIdAndUserId({
252+
appId,
253+
userId,
254+
}: {
255+
appId: string | null;
256+
userId: number;
257+
}) {
258+
return await prisma.credential.findFirst({
259+
where: {
260+
userId,
261+
appId,
262+
},
263+
include: {
264+
app: true,
265+
},
266+
});
267+
}
268+
269+
static async findPaymentCredentialByAppIdAndUserIdOrTeamId({
270+
appId,
271+
userId,
272+
teamId,
273+
}: {
274+
appId: string | null;
275+
userId: number;
276+
teamId?: number | null;
277+
}) {
278+
const idToSearchObject = teamId ? { teamId } : { userId };
279+
return await prisma.credential.findFirst({
280+
where: {
281+
...idToSearchObject,
282+
appId,
283+
},
284+
include: {
285+
app: true,
286+
},
287+
});
288+
}
232289
}

packages/lib/server/repository/team.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,4 +333,21 @@ export class TeamRepository {
333333
},
334334
});
335335
}
336+
337+
async findParentOrganizationByTeamId(teamId: number) {
338+
const team = await this.prismaClient.team.findUnique({
339+
where: {
340+
id: teamId,
341+
},
342+
select: {
343+
parent: {
344+
select: {
345+
id: true,
346+
},
347+
},
348+
},
349+
});
350+
351+
return team?.parent;
352+
}
336353
}

packages/trpc/server/routers/viewer/payments/chargeCard.handler.ts

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import { sendNoShowFeeChargedEmail } from "@calcom/emails";
44
import { ErrorCode } from "@calcom/lib/errorCodes";
55
import { ErrorWithCode } from "@calcom/lib/errors";
66
import { getTranslation } from "@calcom/lib/server/i18n";
7+
import { CredentialRepository } from "@calcom/lib/server/repository/credential";
8+
import { MembershipRepository } from "@calcom/lib/server/repository/membership";
9+
import { TeamRepository } from "@calcom/lib/server/repository/team";
710
import type { PrismaClient } from "@calcom/prisma";
811
import type { EventTypeMetadata } from "@calcom/prisma/zod-utils";
912
import type { CalendarEvent } from "@calcom/types/Calendar";
@@ -20,6 +23,7 @@ interface ChargeCardHandlerOptions {
2023
}
2124
export const chargeCardHandler = async ({ ctx, input }: ChargeCardHandlerOptions) => {
2225
const { prisma } = ctx;
26+
const teamRepository = new TeamRepository(prisma);
2327

2428
const booking = await prisma.booking.findUnique({
2529
where: {
@@ -84,35 +88,39 @@ export const chargeCardHandler = async ({ ctx, input }: ChargeCardHandlerOptions
8488
},
8589
};
8690

87-
const idToSearchObject = booking.eventType?.teamId
88-
? { teamId: booking.eventType.teamId }
89-
: { userId: ctx.user.id };
90-
91-
if (booking.eventType?.teamId) {
92-
const userIsInTeam = await prisma.membership.findUnique({
93-
where: {
94-
userId_teamId: {
95-
userId: ctx.user.id,
96-
teamId: booking.eventType?.teamId,
97-
},
98-
},
91+
const userId = ctx.user.id;
92+
const teamId = booking.eventType?.teamId;
93+
const appId = booking.payment[0].appId;
94+
95+
if (teamId) {
96+
const userIsInTeam = await MembershipRepository.findUniqueByUserIdAndTeamId({
97+
userId,
98+
teamId,
9999
});
100100

101101
if (!userIsInTeam) {
102102
throw new TRPCError({ code: "UNAUTHORIZED", message: "User is not in team" });
103103
}
104104
}
105105

106-
const paymentCredential = await prisma.credential.findFirst({
107-
where: {
108-
...idToSearchObject,
109-
appId: booking.payment[0].appId,
110-
},
111-
include: {
112-
app: true,
113-
},
106+
let paymentCredential = await CredentialRepository.findPaymentCredentialByAppIdAndUserIdOrTeamId({
107+
appId,
108+
userId,
109+
teamId,
114110
});
115111

112+
if (!paymentCredential && teamId) {
113+
// See if the team event belongs to an org
114+
const org = await teamRepository.findParentOrganizationByTeamId(teamId);
115+
116+
if (org) {
117+
paymentCredential = await CredentialRepository.findPaymentCredentialByAppIdAndTeamId({
118+
appId,
119+
teamId: org.id,
120+
});
121+
}
122+
}
123+
116124
if (!paymentCredential?.app) {
117125
throw new TRPCError({ code: "BAD_REQUEST", message: "Invalid payment credential" });
118126
}

0 commit comments

Comments
 (0)