Files
TaxHacker_s23/app/api/stripe/webhook/route.ts
2025-04-24 15:27:44 +02:00

81 lines
2.7 KiB
TypeScript

import config from "@/lib/config"
import { PLANS, stripeClient } from "@/lib/stripe"
import { createUserDefaults, isDatabaseEmpty } from "@/models/defaults"
import { getOrCreateCloudUser, getUserByStripeCustomerId, updateUser } from "@/models/users"
import { NextResponse } from "next/server"
import Stripe from "stripe"
export async function POST(request: Request) {
const signature = request.headers.get("stripe-signature")
const body = await request.text()
if (!signature || !config.stripe.webhookSecret) {
return new NextResponse("Webhook signature or secret missing", { status: 400 })
}
if (!stripeClient) {
return new NextResponse("Stripe client is not initialized", { status: 500 })
}
let event: Stripe.Event
try {
event = stripeClient.webhooks.constructEvent(body, signature, config.stripe.webhookSecret)
} catch (err) {
console.error(`Webhook signature verification failed:`, err)
return new NextResponse("Webhook signature verification failed", { status: 400 })
}
console.log("Webhook event:", event)
// Handle the event
try {
switch (event.type) {
case "customer.subscription.created":
case "customer.subscription.updated": {
const subscription = event.data.object as Stripe.Subscription
const customerId = subscription.customer as string
const item = subscription.items.data[0]
// Get the plan from our plans configuration
const plan = Object.values(PLANS).find((p) => p.stripePriceId === item.price.id)
if (!plan) {
throw new Error(`Plan not found for price ID: ${item.price.id}`)
}
let user = await getUserByStripeCustomerId(customerId)
if (!user) {
const customer = (await stripeClient.customers.retrieve(customerId)) as Stripe.Customer
user = await getOrCreateCloudUser(customer.email as string, {
email: customer.email as string,
name: customer.name as string,
stripeCustomerId: customer.id,
})
if (await isDatabaseEmpty(user.id)) {
await createUserDefaults(user.id)
}
}
await updateUser(user.id, {
membershipPlan: plan.code,
membershipExpiresAt: new Date(item.current_period_end * 1000),
storageLimit: plan.limits.storage,
aiBalance: plan.limits.ai,
updatedAt: new Date(),
})
break
}
default:
console.log(`Unhandled event type ${event.type}`)
}
return new NextResponse("Webhook processed successfully", { status: 200 })
} catch (error) {
console.error("Error processing webhook:", error)
return new NextResponse("Webhook processing failed", { status: 500 })
}
}