mirror of
https://github.com/marcogll/TaxHacker_s23.git
synced 2026-01-13 13:25:18 +00:00
112 lines
3.7 KiB
TypeScript
112 lines
3.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 "checkout.session.completed": {
|
|
const session = event.data.object as Stripe.Checkout.Session
|
|
const customerId = session.customer as string
|
|
const subscriptionId = session.subscription as string
|
|
const subscription = await stripeClient.subscriptions.retrieve(subscriptionId)
|
|
|
|
for (const item of subscription.items.data) {
|
|
await handleUserSubscriptionUpdate(customerId, item)
|
|
}
|
|
break
|
|
}
|
|
|
|
case "customer.subscription.created":
|
|
case "customer.subscription.updated":
|
|
case "customer.subscription.deleted": {
|
|
const subscription = event.data.object as Stripe.Subscription
|
|
const customerId = subscription.customer as string
|
|
|
|
for (const item of subscription.items.data) {
|
|
await handleUserSubscriptionUpdate(customerId, item)
|
|
}
|
|
break
|
|
}
|
|
|
|
default:
|
|
console.log(`Unhandled event type ${event.type}`)
|
|
return new NextResponse("No handler for event type", { status: 400 })
|
|
}
|
|
|
|
return new NextResponse("Webhook processed successfully", { status: 200 })
|
|
} catch (error) {
|
|
console.error("Error processing webhook:", error)
|
|
return new NextResponse("Webhook processing failed", { status: 500 })
|
|
}
|
|
}
|
|
|
|
async function handleUserSubscriptionUpdate(
|
|
customerId: string,
|
|
item: Stripe.SubscriptionItem
|
|
) {
|
|
if (!stripeClient) {
|
|
return new NextResponse("Stripe client is not initialized", { status: 500 })
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
const newMembershipExpiresAt = new Date(item.current_period_end * 1000)
|
|
|
|
await updateUser(user.id, {
|
|
membershipPlan: plan.code,
|
|
membershipExpiresAt:
|
|
user.membershipExpiresAt && user.membershipExpiresAt > newMembershipExpiresAt
|
|
? user.membershipExpiresAt
|
|
: newMembershipExpiresAt,
|
|
storageLimit: plan.limits.storage,
|
|
aiBalance: plan.limits.ai,
|
|
updatedAt: new Date(),
|
|
})
|
|
|
|
console.log(`Updated user ${user.id} with plan ${plan.code} and expires at ${newMembershipExpiresAt}`)
|
|
}
|