33 lines
1.5 KiB
TypeScript
33 lines
1.5 KiB
TypeScript
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 };
|
|
});
|