/**
* GET /api/admin/accounts/:sub/billing (billing.read + a reason): the account's Voices plan, credit purchases, balance
* and period usage, the org subscriptions it owns or pays for (no Stripe ids), and, when a restricted Stripe read key
* is configured, live Stripe subscription status and invoices per org (`live`, omitted otherwise).
* Reason: the X-Admin-Reason header, or a fresh marker from an earlier reason for this account in this session
* (15 minutes), so reloading doesn't re-prompt. One audit entry `billing.view` per session+account per 15 minutes.
*/
export const GET = adminRoute("billing.read", async (_request, ctx, { params }: Context) => {
const { sub } = await params;
if (!SUB.test(sub)) throw notFound("Account");
const target = billingTarget(sub);
const marked = await accessReason(ctx, target);
const reason = ctx.reason && ctx.reason.length >= 3 && ctx.reason.length <= 300 ? ctx.reason : marked;
if (!reason) throw new CloudError(400, "reason_required", "Say why you need this (3–300 characters). It goes into the audit log.");
if (!marked) {
// Audit first: if the entry can't be written, nothing is shown.
await recordAdminAudit(ctx, { action: "billing.view", targetType: "account", targetId: sub, reason });
await grantAccess(ctx, target, reason);
}
const orgRows = await billableOrgs(sub);
const billing = await accountBilling(sub, Date.now(), orgRows);
const body: Record<string, unknown> = { ...billing, reason: { cached: !!marked } };
if (await stripeReadConfigured()) {
const orgs = orgRows.filter((org) => org.stripe.subscriptionId || org.stripe.customerId);
body.live = await Promise.all(orgs.map(async ({ view, stripe }) => {
try {
const [subscription, invoices] = await Promise.all([
stripe.subscriptionId ? liveSubscription(stripe.subscriptionId) : Promise.resolve<LiveSubscription | null>(null),
stripe.customerId ? liveInvoices(stripe.customerId) : Promise.resolve<LiveInvoice[]>([]),
]);
return { orgId: view.orgId, status: "ok" as const, subscription, invoices };
} catch (error) {
return { orgId: view.orgId, status: "error" as const, code: error instanceof CloudError ? error.code : "error" };
}
}));
}
return adminJson(body);
}, { reason: false });