Skip to content

Commit a3fc17b

Browse files
fix: add team installation support for HitPay payment integration (calcom#24738)
- Add teamId support to HitPay add.ts installation handler - Update callback.ts to fetch credentials by teamId for team bookings - Update webhook.ts to fetch credentials by teamId for team bookings - Update setup page to accept and handle teamId from query params - Add permission checks using throwIfNotHaveAdminAccessToTeam - Ensure credentials are created with teamId for team installs, userId for user installs Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent ad4b595 commit a3fc17b

4 files changed

Lines changed: 47 additions & 19 deletions

File tree

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { NextApiRequest, NextApiResponse } from "next";
22

3+
import { throwIfNotHaveAdminAccessToTeam } from "@calcom/app-store/_utils/throwIfNotHaveAdminAccessToTeam";
34
import prisma from "@calcom/prisma";
45

56
import config from "../config.json";
@@ -8,12 +9,19 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
89
if (!req.session?.user?.id) {
910
return res.status(401).json({ message: "You must be logged in to do this" });
1011
}
12+
13+
const { teamId } = req.query;
14+
const teamIdNumber = teamId ? Number(teamId) : null;
15+
16+
await throwIfNotHaveAdminAccessToTeam({ teamId: teamIdNumber, userId: req.session.user.id });
17+
const installForObject = teamIdNumber ? { teamId: teamIdNumber } : { userId: req.session.user.id };
18+
1119
const appType = config.type;
1220
try {
1321
const alreadyInstalled = await prisma.credential.findFirst({
1422
where: {
1523
type: appType,
16-
userId: req.session.user.id,
24+
...installForObject,
1725
},
1826
});
1927
if (alreadyInstalled) {
@@ -23,13 +31,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
2331
data: {
2432
type: appType,
2533
key: {},
26-
userId: req.session.user.id,
2734
appId: "hitpay",
35+
...(teamIdNumber ? { teamId: teamIdNumber } : { userId: req.session.user.id }),
2836
},
2937
});
3038

3139
if (!installation) {
32-
throw new Error("Unable to create user credential for Alby");
40+
throw new Error("Unable to create user credential for HitPay");
3341
}
3442
} catch (error: unknown) {
3543
const message =
@@ -38,5 +46,5 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
3846
return res.status(500).json({ message });
3947
}
4048

41-
return res.status(200).json({ url: "/apps/hitpay/setup" });
49+
return res.status(200).json({ url: `/apps/hitpay/setup${teamIdNumber ? `?teamId=${teamIdNumber}` : ""}` });
4250
}

packages/app-store/hitpay/api/callback.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,18 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
3030
booking: {
3131
select: {
3232
uid: true,
33+
userId: true,
3334
user: {
3435
select: {
3536
email: true,
3637
username: true,
37-
credentials: {
38-
where: {
39-
type: "hitpay_payment",
40-
},
41-
},
4238
},
4339
},
4440
responses: true,
4541
eventType: {
4642
select: {
4743
slug: true,
44+
teamId: true,
4845
},
4946
},
5047
},
@@ -55,7 +52,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
5552
if (!payment) {
5653
throw new HttpCode({ statusCode: 204, message: "Payment not found" });
5754
}
58-
const key = payment.booking?.user?.credentials?.[0].key;
55+
56+
const credential = await prisma.credential.findFirst({
57+
where: {
58+
type: "hitpay_payment",
59+
...(payment.booking?.eventType?.teamId
60+
? { teamId: payment.booking.eventType.teamId }
61+
: { userId: payment.booking?.userId }),
62+
},
63+
});
64+
65+
const key = credential?.key;
5966
if (!key) {
6067
throw new HttpCode({ statusCode: 204, message: "Credential not found" });
6168
}

packages/app-store/hitpay/api/webhook.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
6262
bookingId: true,
6363
booking: {
6464
select: {
65-
user: {
65+
userId: true,
66+
eventType: {
6667
select: {
67-
credentials: {
68-
where: {
69-
type: "hitpay_payment",
70-
},
71-
},
68+
teamId: true,
7269
},
7370
},
7471
},
@@ -79,7 +76,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
7976
if (!payment) {
8077
throw new HttpCode({ statusCode: 204, message: "Payment not found" });
8178
}
82-
const key = payment.booking?.user?.credentials?.[0].key;
79+
80+
const credential = await prisma.credential.findFirst({
81+
where: {
82+
type: "hitpay_payment",
83+
...(payment.booking?.eventType?.teamId
84+
? { teamId: payment.booking.eventType.teamId }
85+
: { userId: payment.booking?.userId }),
86+
},
87+
});
88+
89+
const key = credential?.key;
8390
if (!key) {
8491
throw new HttpCode({ statusCode: 204, message: "Credentials not found" });
8592
}

packages/app-store/hitpay/pages/setup/_getServerSideProps.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { GetServerSidePropsContext } from "next";
22
import { z } from "zod";
33

4+
import { throwIfNotHaveAdminAccessToTeam } from "@calcom/app-store/_utils/throwIfNotHaveAdminAccessToTeam";
45
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
56
import prisma from "@calcom/prisma";
67

@@ -13,7 +14,7 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
1314

1415
if (typeof ctx.params?.slug !== "string") return notFound;
1516

16-
const { req } = ctx;
17+
const { req, query } = ctx;
1718
const session = await getServerSession({ req });
1819

1920
if (!session?.user?.id) {
@@ -22,10 +23,15 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
2223
return redirect;
2324
}
2425

26+
const teamId = query.teamId ? Number(query.teamId) : null;
27+
28+
await throwIfNotHaveAdminAccessToTeam({ teamId, userId: session.user.id });
29+
const installForObject = teamId ? { teamId } : { userId: session.user.id };
30+
2531
const credentials = await prisma.credential.findFirst({
2632
where: {
2733
type: "hitpay_payment",
28-
userId: session?.user.id,
34+
...installForObject,
2935
},
3036
});
3137

0 commit comments

Comments
 (0)