From 239c8a6fa96d0b448de06a1d4a06f27dec0613d1 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Thu, 6 Feb 2025 20:57:03 +0000 Subject: [PATCH] chore: migrate /api/team/create (#19105) --- apps/web/app/api/teams/create/route.ts | 103 +++++++++++++++++++++++++ apps/web/pages/api/teams/create.ts | 89 --------------------- 2 files changed, 103 insertions(+), 89 deletions(-) create mode 100644 apps/web/app/api/teams/create/route.ts delete mode 100644 apps/web/pages/api/teams/create.ts diff --git a/apps/web/app/api/teams/create/route.ts b/apps/web/app/api/teams/create/route.ts new file mode 100644 index 0000000000..57b10a4655 --- /dev/null +++ b/apps/web/app/api/teams/create/route.ts @@ -0,0 +1,103 @@ +import type { NextRequest } from "next/server"; +import { NextResponse } from "next/server"; +import type Stripe from "stripe"; +import { z } from "zod"; + +import stripe from "@calcom/features/ee/payments/server/stripe"; +import { HttpError } from "@calcom/lib/http-error"; +import prisma from "@calcom/prisma"; +import { MembershipRole } from "@calcom/prisma/enums"; + +const querySchema = z.object({ + session_id: z.string().min(1), +}); + +const checkoutSessionMetadataSchema = z.object({ + teamName: z.string(), + teamSlug: z.string(), + userId: z.string().transform(Number), +}); + +const generateRandomString = () => { + return Math.random().toString(36).substring(2, 10); +}; + +export async function GET(req: NextRequest) { + try { + const searchParams = req.nextUrl.searchParams; + const { session_id } = querySchema.parse({ + session_id: searchParams.get("session_id"), + }); + + const checkoutSession = await stripe.checkout.sessions.retrieve(session_id, { + expand: ["subscription"], + }); + if (!checkoutSession) throw new HttpError({ statusCode: 404, message: "Checkout session not found" }); + + const subscription = checkoutSession.subscription as Stripe.Subscription; + + if (checkoutSession.payment_status !== "paid") + throw new HttpError({ statusCode: 402, message: "Payment required" }); + + // Let's query to ensure that the team metadata carried over from the checkout session. + const parseCheckoutSessionMetadata = checkoutSessionMetadataSchema.safeParse(checkoutSession.metadata); + + if (!parseCheckoutSessionMetadata.success) { + console.error( + "Team metadata not found in checkout session", + parseCheckoutSessionMetadata.error, + checkoutSession.id + ); + } + + if (!checkoutSession.metadata?.userId) { + throw new HttpError({ + statusCode: 400, + message: "Can't publish team/org without userId", + }); + } + + const checkoutSessionMetadata = parseCheckoutSessionMetadata.success + ? parseCheckoutSessionMetadata.data + : { + teamName: checkoutSession?.metadata?.teamName ?? generateRandomString(), + teamSlug: checkoutSession?.metadata?.teamSlug ?? generateRandomString(), + userId: checkoutSession.metadata.userId, + }; + + const team = await prisma.team.create({ + data: { + name: checkoutSessionMetadata.teamName, + slug: checkoutSessionMetadata.teamSlug, + members: { + create: { + userId: checkoutSessionMetadata.userId as number, + role: MembershipRole.OWNER, + accepted: true, + }, + }, + metadata: { + paymentId: checkoutSession.id, + subscriptionId: subscription.id || null, + subscriptionItemId: subscription.items.data[0].id || null, + }, + }, + }); + + // redirect to team screen + return NextResponse.redirect( + new URL(`/settings/teams/${team.id}/onboard-members?event=team_created`, req.nextUrl.origin), + { status: 302 } + ); + } catch (error) { + if (error instanceof HttpError) { + throw error; + } + + if (error instanceof z.ZodError) { + return NextResponse.json({ message: "Invalid request data" }, { status: 400 }); + } + + return NextResponse.json({ message: "Internal server error" }, { status: 500 }); + } +} diff --git a/apps/web/pages/api/teams/create.ts b/apps/web/pages/api/teams/create.ts deleted file mode 100644 index d8665968ad..0000000000 --- a/apps/web/pages/api/teams/create.ts +++ /dev/null @@ -1,89 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; -import type Stripe from "stripe"; -import { z } from "zod"; - -import stripe from "@calcom/features/ee/payments/server/stripe"; -import { HttpError } from "@calcom/lib/http-error"; -import { defaultHandler, defaultResponder } from "@calcom/lib/server"; -import prisma from "@calcom/prisma"; -import { MembershipRole } from "@calcom/prisma/enums"; - -const querySchema = z.object({ - session_id: z.string().min(1), -}); - -const checkoutSessionMetadataSchema = z.object({ - teamName: z.string(), - teamSlug: z.string(), - userId: z.string().transform(Number), -}); - -const generateRandomString = () => { - return Math.random().toString(36).substring(2, 10); -}; - -async function handler(req: NextApiRequest, res: NextApiResponse) { - const { session_id } = querySchema.parse(req.query); - - const checkoutSession = await stripe.checkout.sessions.retrieve(session_id, { - expand: ["subscription"], - }); - if (!checkoutSession) throw new HttpError({ statusCode: 404, message: "Checkout session not found" }); - - const subscription = checkoutSession.subscription as Stripe.Subscription; - - if (checkoutSession.payment_status !== "paid") - throw new HttpError({ statusCode: 402, message: "Payment required" }); - - // Let's query to ensure that the team metadata carried over from the checkout session. - const parseCheckoutSessionMetadata = checkoutSessionMetadataSchema.safeParse(checkoutSession.metadata); - - if (!parseCheckoutSessionMetadata.success) { - console.error( - "Team metadata not found in checkout session", - parseCheckoutSessionMetadata.error, - checkoutSession.id - ); - } - - if (!checkoutSession.metadata?.userId) { - throw new HttpError({ - statusCode: 400, - message: "Can't publish team/org without userId", - }); - } - - const checkoutSessionMetadata = parseCheckoutSessionMetadata.success - ? parseCheckoutSessionMetadata.data - : { - teamName: checkoutSession?.metadata?.teamName ?? generateRandomString(), - teamSlug: checkoutSession?.metadata?.teamSlug ?? generateRandomString(), - userId: checkoutSession.metadata.userId, - }; - - const team = await prisma.team.create({ - data: { - name: checkoutSessionMetadata.teamName, - slug: checkoutSessionMetadata.teamSlug, - members: { - create: { - userId: checkoutSessionMetadata.userId as number, - role: MembershipRole.OWNER, - accepted: true, - }, - }, - metadata: { - paymentId: checkoutSession.id, - subscriptionId: subscription.id || null, - subscriptionItemId: subscription.items.data[0].id || null, - }, - }, - }); - - // redirect to team screen - res.redirect(302, `/settings/teams/${team.id}/onboard-members?event=team_created`); -} - -export default defaultHandler({ - GET: Promise.resolve({ default: defaultResponder(handler) }), -});