159 lines
5.4 KiB
TypeScript
159 lines
5.4 KiB
TypeScript
import { UserPlan } from "@prisma/client";
|
|
import type { NextApiRequest, NextApiResponse } from "next";
|
|
import Stripe from "stripe";
|
|
|
|
import {
|
|
getPremiumPlanMode,
|
|
getPremiumPlanPrice,
|
|
getPremiumPlanProductId,
|
|
} from "@calcom/app-store/stripepayment/lib/utils";
|
|
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 { PRO_PLAN_PRICE, PRO_PLAN_PRODUCT_ID } from "../lib/constants";
|
|
import { getStripeCustomerIdFromUserId } from "../lib/customer";
|
|
import stripe from "../lib/server";
|
|
|
|
export enum UsernameChangeStatusEnum {
|
|
NORMAL = "NORMAL",
|
|
UPGRADE = "UPGRADE",
|
|
DOWNGRADE = "DOWNGRADE",
|
|
}
|
|
|
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
if (req.method === "GET") {
|
|
const userId = req.session?.user.id;
|
|
let { intentUsername = null } = req.query;
|
|
const { action, callbackUrl } = 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;
|
|
|
|
const return_url = `${process.env.NEXT_PUBLIC_WEBAPP_URL}/api/integrations/stripepayment/paymentCallback?checkoutSessionId={CHECKOUT_SESSION_ID}&callbackUrl=${callbackUrl}`;
|
|
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" });
|
|
}
|
|
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 && (userData.plan === UserPlan.FREE || userData.plan === UserPlan.TRIAL)) {
|
|
const subscriptionPrice = checkPremiumResult.premium ? getPremiumPlanPrice() : PRO_PLAN_PRICE;
|
|
const checkoutSession = await stripe.checkout.sessions.create({
|
|
mode: getPremiumPlanMode(),
|
|
payment_method_types: ["card"],
|
|
customer: customerId,
|
|
line_items: [
|
|
{
|
|
price: subscriptionPrice,
|
|
quantity: 1,
|
|
},
|
|
],
|
|
success_url: return_url,
|
|
cancel_url: return_url,
|
|
allow_promotion_codes: true,
|
|
});
|
|
// Save the intentUsername in the metadata
|
|
await prisma.user.update({
|
|
where: { id: userId },
|
|
data: {
|
|
metadata: {
|
|
...(userData.metadata as Prisma.JsonObject),
|
|
checkoutSessionId: checkoutSession.id,
|
|
intentUsername,
|
|
},
|
|
},
|
|
});
|
|
if (checkoutSession && checkoutSession.url) {
|
|
return res.redirect(checkoutSession.url).end();
|
|
}
|
|
return res.status(404).json({ message: "Couldn't redirect to stripe checkout session" });
|
|
}
|
|
|
|
if (action && userData) {
|
|
let actionText = "";
|
|
const customProductsSession = [];
|
|
if (action === UsernameChangeStatusEnum.UPGRADE) {
|
|
actionText = "Upgrade your plan account";
|
|
if (checkPremiumResult.premium) {
|
|
customProductsSession.push({ prices: [getPremiumPlanPrice()], product: getPremiumPlanProductId() });
|
|
} 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,
|
|
},
|
|
},
|
|
});
|
|
await prisma.user.update({
|
|
where: { id: userId },
|
|
data: {
|
|
metadata: {
|
|
...(userData.metadata as Prisma.JsonObject),
|
|
intentUsername,
|
|
},
|
|
},
|
|
});
|
|
if (configuration) {
|
|
createSessionParams.configuration = configuration.id;
|
|
}
|
|
}
|
|
const stripeSession = await stripe.billingPortal.sessions.create(createSessionParams);
|
|
|
|
res.redirect(stripeSession.url).end();
|
|
}
|
|
}
|