104 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Stripe from "stripe";
import { usePrisma } from "../../utils/prisma";
/**
* POST /api/stripe/webhook
* Stripe Webhook verarbeitet Subscription-Events.
* Aktualisiert profiles.plan + stripe_* Felder.
*/
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig();
const stripe = new Stripe(config.stripeSecretKey);
const body = await readRawBody(event);
const sig = getHeader(event, "stripe-signature");
if (!body || !sig) {
throw createError({
statusCode: 400,
message: "Missing body or signature",
});
}
let stripeEvent: Stripe.Event;
try {
stripeEvent = stripe.webhooks.constructEvent(
body,
sig,
config.stripeWebhookSecret,
);
} catch (err: any) {
throw createError({
statusCode: 400,
message: `Webhook Error: ${err.message}`,
});
}
const db = usePrisma();
switch (stripeEvent.type) {
case "checkout.session.completed": {
const session = stripeEvent.data.object as Stripe.Checkout.Session;
const userId = session.metadata?.user_id || session.client_reference_id;
const plan = session.metadata?.plan || "legend";
if (userId) {
await db.profile.update({
where: { id: userId },
data: {
plan:
plan === "legend" ? "legend" : plan === "pro" ? "pro" : "free",
stripeCustomerId: session.customer as string,
stripeSubId: session.subscription as string,
},
});
}
break;
}
case "customer.subscription.updated": {
const sub = stripeEvent.data.object as Stripe.Subscription;
const customerId = sub.customer as string;
const profile = await db.profile.findFirst({
where: { stripeCustomerId: customerId },
select: { id: true, plan: true },
});
if (profile) {
const isActive = ["active", "trialing"].includes(sub.status);
await db.profile.update({
where: { id: profile.id },
data: {
plan: isActive ? profile.plan : "free",
premiumUntil: sub.current_period_end
? new Date(sub.current_period_end * 1000)
: null,
},
});
}
break;
}
case "customer.subscription.deleted": {
const sub = stripeEvent.data.object as Stripe.Subscription;
const customerId = sub.customer as string;
const profile = await db.profile.findFirst({
where: { stripeCustomerId: customerId },
select: { id: true },
});
if (profile) {
await db.profile.update({
where: { id: profile.id },
data: { plan: "free", premiumUntil: null },
});
}
break;
}
}
return { received: true };
});