Files
cal.diy/packages/app-store/stripepayment/api/subscription.ts
T
Amit Sharma 81224f324a
Post release / release (push) Canceled after 0s
Release Docker / Release AMD64 (push) Canceled after 0s
Release Docker / Release ARM (push) Canceled after 0s
feat: google ads conversion tracking (#25198)
* feat: google ads conversion tracking

* gaClientid

* store gclid in stripe metadata

* tracking only in the us

* track google campaign id as well

* rename gclid -> google ads

* fix: build

* fix

* refactor

* fix: type check

* fix: type check

* fix: type check

* fix: type check

* fix: store it in cookie

* refactor

* fix

* cleanup

* linked ads tracking

* refactor checkout session tracking
2025-11-25 12:58:10 +00:00

99 lines
3.6 KiB
TypeScript

import type { NextApiRequest, NextApiResponse } from "next";
import type Stripe from "stripe";
import { getPremiumMonthlyPlanPriceId } from "@calcom/app-store/stripepayment/lib/utils";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { checkPremiumUsername } from "@calcom/features/ee/common/lib/checkPremiumUsername";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { getTrackingFromCookies } from "@calcom/lib/tracking";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { getStripeCustomerIdFromUserId } from "../lib/customer";
import stripe from "../lib/server";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === "GET") {
const session = await getServerSession({ req });
const userId = session?.user?.id;
let { intentUsername = null } = req.query;
const { callbackUrl } = req.query;
if (!userId || !intentUsername) {
res.status(404).json({ message: "Missing required parameters: userId or intentUsername" });
return;
}
if (intentUsername && typeof intentUsername === "object") {
intentUsername = intentUsername[0];
}
const customerId = await getStripeCustomerIdFromUserId(userId);
if (!customerId) {
res.status(404).json({ message: "Missing customer id" });
return;
}
const userData = await prisma.user.findFirst({
where: { id: userId },
select: { id: true, metadata: true },
});
if (!userData) {
res.status(404).json({ message: "Missing user data" });
return;
}
const tracking = getTrackingFromCookies(req.cookies);
const return_url = `${WEBAPP_URL}/api/integrations/stripepayment/paymentCallback?checkoutSessionId={CHECKOUT_SESSION_ID}&callbackUrl=${callbackUrl}`;
const createSessionParams: Stripe.Checkout.SessionCreateParams = {
mode: "subscription",
line_items: [
{
quantity: 1,
price: getPremiumMonthlyPlanPriceId(),
},
],
allow_promotion_codes: true,
customer: customerId,
success_url: return_url,
cancel_url: return_url,
metadata: {
userId: userId.toString(),
intentUsername,
...(tracking?.googleAds?.gclid && { gclid: tracking.googleAds.gclid, campaignId: tracking.googleAds.campaignId }),
...(tracking?.linkedInAds?.liFatId && { liFatId: tracking.linkedInAds.liFatId, linkedInCampaignId: tracking.linkedInAds?.campaignId }),
},
};
const checkPremiumResult = await checkPremiumUsername(intentUsername);
if (!checkPremiumResult.available) {
return res.status(404).json({ message: "Intent username not available" });
}
const stripeCustomer = await stripe.customers.retrieve(customerId);
if (!stripeCustomer || stripeCustomer.deleted) {
return res.status(400).json({ message: "Stripe customer not found or deleted" });
}
await stripe.customers.update(customerId, {
metadata: {
...stripeCustomer.metadata,
username: intentUsername,
},
});
if (userData) {
await prisma.user.update({
where: { id: userId },
data: {
metadata: {
...((userData.metadata as Prisma.JsonObject) || {}),
isPremium: false,
},
},
});
}
const checkoutSession = await stripe.checkout.sessions.create(createSessionParams);
if (checkoutSession && checkoutSession.url) {
return res.redirect(checkoutSession.url).end();
}
return res.status(404).json({ message: "Couldn't redirect to stripe checkout session" });
}
}