feat: allow multiple subscriptions
This commit is contained in:
@@ -3,6 +3,7 @@ import { env } from "next-runtime-env";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNextApiContext } from "@kan/api/trpc";
|
import { createNextApiContext } from "@kan/api/trpc";
|
||||||
|
import * as subscriptionRepo from "@kan/db/repository/subscription.repo";
|
||||||
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
|
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
|
||||||
import { createStripeClient } from "@kan/stripe";
|
import { createStripeClient } from "@kan/stripe";
|
||||||
|
|
||||||
@@ -40,17 +41,22 @@ export default async function handler(
|
|||||||
const body = req.body as CheckoutSessionRequest;
|
const body = req.body as CheckoutSessionRequest;
|
||||||
const { successUrl, cancelUrl, slug, workspacePublicId } = body;
|
const { successUrl, cancelUrl, slug, workspacePublicId } = body;
|
||||||
|
|
||||||
if (!successUrl || !cancelUrl || !slug || !workspacePublicId) {
|
if (!successUrl || !cancelUrl || !workspacePublicId) {
|
||||||
return res.status(400).json({ error: "Missing required fields" });
|
return res.status(400).json({ error: "Missing required fields" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const slugResult = workspaceSlugSchema.safeParse(slug);
|
if (slug) {
|
||||||
|
const slugResult = workspaceSlugSchema.safeParse(slug);
|
||||||
|
|
||||||
if (!slugResult.success) {
|
if (!slugResult.success) {
|
||||||
return new Response(JSON.stringify({ error: "Invalid workspace slug" }), {
|
return new Response(
|
||||||
status: 400,
|
JSON.stringify({ error: "Invalid workspace slug" }),
|
||||||
headers: { "Content-Type": "application/json" },
|
{
|
||||||
});
|
status: 400,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const workspace = await workspaceRepo.getAllByUserId(db, user.id);
|
const workspace = await workspaceRepo.getAllByUserId(db, user.id);
|
||||||
@@ -66,6 +72,20 @@ export default async function handler(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const subscription = await subscriptionRepo.create(db, {
|
||||||
|
plan: "pro",
|
||||||
|
referenceId: workspacePublicId,
|
||||||
|
userId: user.id,
|
||||||
|
stripeCustomerId: user.stripeCustomerId ?? "",
|
||||||
|
status: "incomplete",
|
||||||
|
});
|
||||||
|
|
||||||
|
const subscriptionId = subscription?.id;
|
||||||
|
|
||||||
|
if (!subscriptionId) {
|
||||||
|
return res.status(500).json({ error: "Error creating subscription" });
|
||||||
|
}
|
||||||
|
|
||||||
const session = await stripe.checkout.sessions.create({
|
const session = await stripe.checkout.sessions.create({
|
||||||
mode: "subscription",
|
mode: "subscription",
|
||||||
line_items: [
|
line_items: [
|
||||||
@@ -76,10 +96,13 @@ export default async function handler(
|
|||||||
],
|
],
|
||||||
success_url: `${env("NEXT_PUBLIC_BASE_URL")}${successUrl}`,
|
success_url: `${env("NEXT_PUBLIC_BASE_URL")}${successUrl}`,
|
||||||
cancel_url: `${env("NEXT_PUBLIC_BASE_URL")}${cancelUrl}`,
|
cancel_url: `${env("NEXT_PUBLIC_BASE_URL")}${cancelUrl}`,
|
||||||
|
client_reference_id: workspacePublicId,
|
||||||
customer: user.stripeCustomerId ?? undefined,
|
customer: user.stripeCustomerId ?? undefined,
|
||||||
metadata: {
|
metadata: {
|
||||||
workspaceSlug: slug,
|
...(slug && { workspaceSlug: slug }),
|
||||||
workspacePublicId,
|
workspacePublicId,
|
||||||
|
userId: user.id,
|
||||||
|
subscriptionId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
import { t } from "@lingui/core/macro";
|
import { t } from "@lingui/core/macro";
|
||||||
import { HiBolt, HiCheckBadge } from "react-icons/hi2";
|
import { HiBolt, HiCheckBadge } from "react-icons/hi2";
|
||||||
|
|
||||||
import { authClient } from "@kan/auth/client";
|
|
||||||
|
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
import { useModal } from "~/providers/modal";
|
import { useModal } from "~/providers/modal";
|
||||||
import { usePopup } from "~/providers/popup";
|
import { usePopup } from "~/providers/popup";
|
||||||
|
|
||||||
export function UpgradeToProConfirmation({
|
export function UpgradeToProConfirmation({
|
||||||
userId,
|
|
||||||
workspacePublicId,
|
workspacePublicId,
|
||||||
}: {
|
}: {
|
||||||
userId: string;
|
userId: string;
|
||||||
@@ -18,25 +15,28 @@ export function UpgradeToProConfirmation({
|
|||||||
const { showPopup } = usePopup();
|
const { showPopup } = usePopup();
|
||||||
|
|
||||||
const handleUpgrade = async () => {
|
const handleUpgrade = async () => {
|
||||||
const { data, error } = await authClient.subscription.upgrade({
|
try {
|
||||||
plan: "pro",
|
const response = await fetch("/api/stripe/create_checkout_session", {
|
||||||
referenceId: workspacePublicId,
|
method: "POST",
|
||||||
metadata: {
|
headers: {
|
||||||
userId,
|
"Content-Type": "application/json",
|
||||||
workspacePublicId,
|
},
|
||||||
...(entityId && { workspaceSlug: entityId }),
|
body: JSON.stringify({
|
||||||
},
|
...(entityId && { workspaceSlug: entityId }),
|
||||||
successUrl: "/settings",
|
workspacePublicId: workspacePublicId,
|
||||||
cancelUrl: "/settings",
|
cancelUrl: "/settings",
|
||||||
returnUrl: "/settings",
|
successUrl: "/settings",
|
||||||
disableRedirect: true,
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (data?.url) {
|
const { url } = (await response.json()) as { url: string };
|
||||||
window.location.href = data.url;
|
|
||||||
}
|
if (url) {
|
||||||
|
window.location.href = url;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error creating checkout session:", error);
|
||||||
|
|
||||||
if (error) {
|
|
||||||
showPopup({
|
showPopup({
|
||||||
header: t`Error upgrading subscription`,
|
header: t`Error upgrading subscription`,
|
||||||
message: t`Please try again later, or contact customer support.`,
|
message: t`Please try again later, or contact customer support.`,
|
||||||
|
|||||||
@@ -75,3 +75,17 @@ export const getByReferenceId = async (db: dbClient, referenceId: string) => {
|
|||||||
where: eq(subscription.referenceId, referenceId),
|
where: eq(subscription.referenceId, referenceId),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const create = async (
|
||||||
|
db: dbClient,
|
||||||
|
data: {
|
||||||
|
plan: string;
|
||||||
|
referenceId: string;
|
||||||
|
userId: string;
|
||||||
|
stripeCustomerId: string;
|
||||||
|
status: string;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const [result] = await db.insert(subscription).values(data).returning();
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user