Files
kan/apps/web/src/pages/api/stripe/webhook.ts

74 lines
1.9 KiB
TypeScript

import type { NextApiRequest, NextApiResponse } from "next";
import type { Readable } from "node:stream";
import { createNextApiContext } from "@kan/api/trpc";
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
import { createStripeClient } from "@kan/stripe";
async function buffer(readable: Readable) {
const chunks = [];
for await (const chunk of readable) {
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
}
return Buffer.concat(chunks);
}
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const stripe = createStripeClient();
if (req.method !== "POST") {
return res.status(405).json({ message: "Method not allowed" });
}
const sig = req.headers["stripe-signature"];
if (!sig) {
return res.status(400).json({ message: "No signature found" });
}
try {
const buf = await buffer(req);
const rawBody = buf.toString("utf8");
const event = stripe.webhooks.constructEvent(
rawBody,
sig,
process.env.STRIPE_WEBHOOK_SECRET!,
);
const { db } = await createNextApiContext(req);
switch (event.type) {
case "checkout.session.completed": {
const checkoutSession = event.data.object;
const metaData = checkoutSession.metadata;
if (metaData?.workspacePublicId && metaData.workspaceSlug) {
await workspaceRepo.update(db, metaData.workspacePublicId, {
slug: metaData.workspaceSlug,
plan: "pro",
});
}
break;
}
default:
console.log(`Unhandled event type: ${event.type}`);
}
return res.status(200).json({ received: true });
} catch (err) {
console.error("Webhook error:", err);
return res.status(400).json({ message: "Webhook handler failed" });
}
}
export const config = {
api: {
bodyParser: false,
},
};