Skip to content

Instantly share code, notes, and snippets.

@simonorzel26
Created November 24, 2024 10:29
Show Gist options
  • Save simonorzel26/fd0784ddc7c411b215f90419e5889290 to your computer and use it in GitHub Desktop.
Save simonorzel26/fd0784ddc7c411b215f90419e5889290 to your computer and use it in GitHub Desktop.
Next.js 14+ App Router: Stripe Webhook Handler for Payment Links with TypeScript, Error Handling, and Database Update
import { NextRequest, NextResponse } from "next/server";
import { headers } from "next/headers";
import Stripe from "stripe";
// Prisma db instance or other ORM
import { db } from "~/server/db";
// Stripe instance (new Stripe()...)
import stripe from "~/server/stripe";
async function constructStripeEvent(body: string, signature: string): Promise<Stripe.Event> {
if (!process.env.STRIPE_WEBHOOK_SECRET) {
throw new Error("STRIPE_WEBHOOK_SECRET is not set");
}
return stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET
);
}
async function handleStripeEvent(event: Stripe.Event): Promise<void> {
if (event.type === "checkout.session.completed") {
const session = event.data.object as Stripe.Checkout.Session;
// Reconcile your database with the original user id/data sent with the payment link (session.metadata.profile_id)
await updateProfileSubscriptionStatus(session);
}
}
async function updateProfileSubscriptionStatus(session: Stripe.Checkout.Session): Promise<void> {
const profileId = session.metadata?.profile_id;
if (!profileId) {
throw new Error("No profile_id found in the session");
}
await db.user.update({
where: { id: profileId },
data: { isSubscribed: true },
});
}
function handleWebhookError(error: unknown): NextResponse {
const errorMessage = error instanceof Error ? error.message : "Unknown error";
console.error(`Webhook Error: ${errorMessage}`);
return NextResponse.json(
{ message: `Webhook Error: ${errorMessage}` },
{ status: 400 }
);
}
export async function POST(req: NextRequest): Promise<NextResponse> {
const body = await req.text();
const headersList = headers();
const signature = headersList.get("stripe-signature");
if (!signature) {
return NextResponse.json(
{ message: "Missing stripe-signature header" },
{ status: 400 }
);
}
try {
const event = await constructStripeEvent(body, signature);
await handleStripeEvent(event);
return NextResponse.json({ success: true });
} catch (error) {
return handleWebhookError(error);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment