Initial commit
This commit is contained in:
94
app/api/stripe/webhook/route.ts
Normal file
94
app/api/stripe/webhook/route.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { headers } from "next/headers";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { getPrisma } from "../../../../lib/db";
|
||||
import { loadSystemConfig } from "../../../../lib/system-config";
|
||||
import { getStripe } from "../../../../lib/stripe";
|
||||
import { sendEmail } from "../../../../lib/email";
|
||||
import { createWebinarCalendarEvent } from "../../../../lib/calendar";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const prisma = await getPrisma();
|
||||
const stripe = await getStripe();
|
||||
const cfg = await loadSystemConfig();
|
||||
|
||||
const secret = cfg.stripe?.webhookSecret || process.env.STRIPE_WEBHOOK_SECRET;
|
||||
|
||||
if (!prisma || !stripe || !secret) {
|
||||
// must still 200 to avoid retries in misconfigured env
|
||||
return NextResponse.json({ ok: true });
|
||||
}
|
||||
|
||||
const headersList = await headers();
|
||||
const sig = headersList.get("stripe-signature");
|
||||
const body = await req.text();
|
||||
|
||||
if (!sig) return NextResponse.json({ ok: true });
|
||||
|
||||
let event: any;
|
||||
try {
|
||||
event = stripe.webhooks.constructEvent(body, sig, secret);
|
||||
} catch {
|
||||
return NextResponse.json({ ok: true });
|
||||
}
|
||||
|
||||
if (event.type === "checkout.session.completed") {
|
||||
const session = event.data.object as any;
|
||||
const registrationId = session.metadata?.registrationId as string | undefined;
|
||||
const paymentIntent = session.payment_intent as string | undefined;
|
||||
|
||||
if (registrationId) {
|
||||
try {
|
||||
await prisma.webinarRegistration.update({
|
||||
where: { id: registrationId },
|
||||
data: { status: "PAID", stripePaymentIntentId: paymentIntent ?? null },
|
||||
});
|
||||
|
||||
// Send confirmation email with calendar invite
|
||||
const registration = await prisma.webinarRegistration.findUnique({
|
||||
where: { id: registrationId },
|
||||
include: { user: true, webinar: true },
|
||||
});
|
||||
|
||||
if (registration && cfg.email?.enabled) {
|
||||
const { icsContent } = await createWebinarCalendarEvent(
|
||||
registration.webinar,
|
||||
registration.user.email
|
||||
);
|
||||
|
||||
const meetingInfo = registration.webinar.meetingInfo as any;
|
||||
const meetingLink = meetingInfo?.meetingLink || "TBD";
|
||||
const htmlContent = `
|
||||
<h2>Webinar Registration Confirmed</h2>
|
||||
<p>Hi ${registration.user.firstName},</p>
|
||||
<p>Thank you for registering for <strong>${registration.webinar.title}</strong>.</p>
|
||||
<p><strong>Date & Time:</strong> ${new Date(registration.webinar.startAt).toLocaleString()}</p>
|
||||
<p><strong>Duration:</strong> ${registration.webinar.duration} minutes</p>
|
||||
<p><strong>Join Link:</strong> <a href="${meetingLink}">${meetingLink}</a></p>
|
||||
<p>A calendar invitation is attached to this email.</p>
|
||||
<p>See you there!</p>
|
||||
`;
|
||||
|
||||
await sendEmail({
|
||||
to: registration.user.email,
|
||||
subject: `Registration Confirmed: ${registration.webinar.title}`,
|
||||
html: htmlContent,
|
||||
attachments: [
|
||||
{
|
||||
filename: "webinar-invite.ics",
|
||||
content: icsContent,
|
||||
contentType: "text/calendar",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[WEBHOOK] Error processing payment:", error);
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.json({ ok: true });
|
||||
}
|
||||
Reference in New Issue
Block a user