import { db } from '../../utils/db'; import { getPayments } from '../../utils/payments'; import { recordAudit } from '../../utils/audit'; export default defineEventHandler(async (event) => { const raw = await readRawBody(event, 'utf8'); const signature = getHeader(event, 'stripe-signature') ?? null; const provider = getPayments(event); let parsed; try { parsed = await provider.parseWebhook(raw ?? '', signature); } catch (e) { throw createError({ statusCode: 400, statusMessage: e instanceof Error ? e.message : 'Bad webhook' }); } if (parsed.type === 'checkout.completed' && parsed.userId) { const currentPeriodEnd = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000); await db.subscription.upsert({ where: { userId: parsed.userId }, update: { plan: parsed.plan ?? 'pro', status: 'active', provider: provider.name, currentPeriodEnd }, create: { userId: parsed.userId, plan: parsed.plan ?? 'pro', status: 'active', provider: provider.name, currentPeriodEnd }, }); await recordAudit({ actorId: parsed.userId, action: 'billing.activated', target: parsed.plan ?? 'pro' }); } else if (parsed.type === 'subscription.canceled' && parsed.userId) { await db.subscription.update({ where: { userId: parsed.userId }, data: { status: 'canceled' } }).catch(() => undefined); await recordAudit({ actorId: parsed.userId, action: 'billing.canceled', target: parsed.userId }); } // Idempotent: re-delivered events re-apply the same upsert. return { received: true }; });