104 lines
2.7 KiB
TypeScript
104 lines
2.7 KiB
TypeScript
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 };
|
||
});
|