import { NextResponse } from "next/server"; import { z } from "zod"; import { requirePlatformRole } from "@/lib/session"; import { listSkillPricing, setSkillPricing } from "@/lib/db"; import { getPackageDef } from "@/lib/packages"; import { safeError } from "@/lib/errors"; /** * GET /api/admin/billing/skill-pricing * List all configured skill prices. * * PUT /api/admin/billing/skill-pricing * Upsert a daily price for a single skill. Body: * { skillId: string, dailyPriceChf: number } * * Both endpoints are platform-only. * * Note on skillId validation: we accept any package id that exists * in PACKAGE_CATALOG. The PIN to "skills only" is enforced at the * UI layer, not here, so admins can price a non-skill package in * an emergency without code changes. */ const upsertSchema = z.object({ skillId: z.string().min(1).max(100), dailyPriceChf: z.number().min(0).max(1_000_000), // Optional with default 0 so existing API callers keep working. // Setup fee fires once per (tenant, skill); see billing.ts. setupFeeChf: z.number().min(0).max(1_000_000).optional().default(0), }); export async function GET() { try { await requirePlatformRole(); } catch { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); } const rows = await listSkillPricing(); return NextResponse.json(rows); } export async function PUT(request: Request) { try { await requirePlatformRole(); } catch { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); } const body = await request.json().catch(() => ({})); const parsed = upsertSchema.safeParse(body); if (!parsed.success) { return NextResponse.json( { error: "Invalid payload", details: parsed.error.flatten() }, { status: 400 } ); } // Validate the skill id exists in PACKAGE_CATALOG. Returns null // for unknown ids; we reject those rather than persist a row that // would never match a real toggle event. const pkg = getPackageDef(parsed.data.skillId); if (!pkg) { return NextResponse.json( { error: `Unknown package id: ${parsed.data.skillId}` }, { status: 400 } ); } try { const row = await setSkillPricing( parsed.data.skillId, parsed.data.dailyPriceChf, parsed.data.setupFeeChf ); return NextResponse.json(row); } catch (e) { console.error("Failed to upsert skill pricing:", e); return NextResponse.json( { error: safeError(e, "Upsert failed") }, { status: 500 } ); } }