cal.pub0.org/packages/app-store/stripepayment/api/subscription.ts

160 lines
5.0 KiB
TypeScript

import { UserPlan } from "@prisma/client";
import type { NextApiRequest, NextApiResponse } from "next";
import Stripe from "stripe";
import { checkPremiumUsername } from "@calcom/features/ee/common/lib/checkPremiumUsername";
import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata";
import prisma from "@calcom/prisma";
import { Prisma } from "@calcom/prisma/client";
import {
PREMIUM_PLAN_PRICE,
PREMIUM_PLAN_PRODUCT_ID,
PRO_PLAN_PRICE,
PRO_PLAN_PRODUCT_ID,
} from "../lib/constants";
import { getStripeCustomerIdFromUserId } from "../lib/customer";
import stripe from "../lib/server";
enum UsernameChangeStatusEnum {
NORMAL = "NORMAL",
UPGRADE = "UPGRADE",
DOWNGRADE = "DOWNGRADE",
}
const obtainNewConditionAction = ({
userCurrentPlan,
isNewUsernamePremium,
}: {
userCurrentPlan: UserPlan;
isNewUsernamePremium: boolean;
}) => {
if (userCurrentPlan === UserPlan.PRO) {
if (isNewUsernamePremium) return UsernameChangeStatusEnum.UPGRADE;
return UsernameChangeStatusEnum.DOWNGRADE;
}
return UsernameChangeStatusEnum.NORMAL;
};
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === "GET") {
const userId = req.session?.user.id;
let { intentUsername = null } = req.query;
if (!userId || !intentUsername) {
res.status(404).end();
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, plan: true, metadata: true },
});
if (!userData) {
res.status(404).json({ message: "Missing user data" });
return;
}
const isCurrentlyPremium = hasKeyInMetadata(userData, "isPremium") && !!userData.metadata.isPremium;
// Save the intentUsername in the metadata
await prisma.user.update({
where: { id: userId },
data: {
metadata: {
...(userData.metadata as Prisma.JsonObject),
intentUsername,
},
},
});
const return_url = `${process.env.NEXT_PUBLIC_WEBAPP_URL}/settings/profile`;
const createSessionParams: Stripe.BillingPortal.SessionCreateParams = {
customer: customerId,
return_url,
};
const checkPremiumResult = await checkPremiumUsername(intentUsername);
if (!checkPremiumResult.available) {
return res.status(404).json({ message: "Intent username not available" });
}
if (userData && (userData.plan === UserPlan.FREE || userData.plan === UserPlan.TRIAL)) {
const subscriptionPrice = checkPremiumResult.premium ? PREMIUM_PLAN_PRICE : PRO_PLAN_PRICE;
const checkoutSession = await stripe.checkout.sessions.create({
mode: "subscription",
payment_method_types: ["card"],
customer: customerId,
line_items: [
{
price: subscriptionPrice,
quantity: 1,
},
],
success_url: return_url,
cancel_url: return_url,
allow_promotion_codes: true,
});
if (checkoutSession && checkoutSession.url) {
return res.redirect(checkoutSession.url).end();
}
return res.status(404).json({ message: "Couldn't redirect to stripe checkout session" });
}
const action = obtainNewConditionAction({
userCurrentPlan: userData?.plan ?? UserPlan.FREE,
isNewUsernamePremium: checkPremiumResult.premium,
});
if (action && userData) {
let actionText = "";
const customProductsSession = [];
if (action === UsernameChangeStatusEnum.UPGRADE) {
actionText = "Upgrade your plan account";
if (checkPremiumResult.premium) {
customProductsSession.push({ prices: [PREMIUM_PLAN_PRICE], product: PREMIUM_PLAN_PRODUCT_ID });
} else {
customProductsSession.push({ prices: [PRO_PLAN_PRICE], product: PRO_PLAN_PRODUCT_ID });
}
} else if (action === UsernameChangeStatusEnum.DOWNGRADE) {
actionText = "Downgrade your plan account";
if (isCurrentlyPremium) {
customProductsSession.push({ prices: [PRO_PLAN_PRICE], product: PRO_PLAN_PRODUCT_ID });
}
}
const configuration = await stripe.billingPortal.configurations.create({
business_profile: {
headline: actionText,
},
features: {
payment_method_update: {
enabled: true,
},
subscription_update: {
enabled: true,
proration_behavior: "always_invoice",
default_allowed_updates: ["price"],
products: customProductsSession,
},
},
});
if (configuration) {
createSessionParams.configuration = configuration.id;
}
}
const stripeSession = await stripe.billingPortal.sessions.create(createSessionParams);
res.redirect(stripeSession.url).end();
}
}