import { log } from "@clack/prompts" import { defineCommand } from "../lib/command" import { globalOptions, globalOptionsSchema } from "../lib/global-options" import open from "open" import z from "zod" import { apiClient } from "../lib/api-client" import { ORG_OPT, orgOptsSchema, output, sessionIntro, requireSession, useOrganization, ORG_OPTION_DESCRIPTIONS, } from "../utils" import { dimmed } from "../lib/io" import { isJsonOutput } from "../lib/json-output" import { clearSpinner, startSpinner } from "../lib/spinner" import { printEnterpriseContact, upgradeOrganization } from "./org-upgrade" import { blue } from "ansis" export const orgBillingCommand = defineCommand({ name: "billing", description: "Open the billing portal for an organization", options: { ...globalOptions, ...ORG_OPT, }, optionDescriptions: { ...ORG_OPTION_DESCRIPTIONS, }, schema: globalOptionsSchema.extend(orgOptsSchema.shape), run: openBillingPortal, }) /** * Opens Stripe Billing Portal for the resolved organization. * * @param opts - Validated options selecting the organization. */ export async function openBillingPortal(opts: z.infer) { const session = await requireSession("manage organization billing") output.normal(() => sessionIntro(session)) const selectedOrg = await useOrganization(opts) startSpinner("Fetching billing details") const org = await apiClient.org.get({ organizationId: selectedOrg.id }) clearSpinner() if (org.plan === "enterprise") { printEnterpriseContact(org) return } if (!org.activeSubscription) { await upgradeOrganization( { org: org.id, plan: "pro", }, { printSessionIntro: false }, ) return } startSpinner("Opening billing portal") const portalSession = await apiClient.org.createBillingPortal({ organizationId: org.id, }) clearSpinner() output .normal(() => { log.success("Billing portal ready") log.message( `${dimmed`URL:`} ${blue.underline.link(portalSession.url, "Open billing portal")}`, { symbol: blue`↗`, }, ) }) .json({ url: portalSession.url, organization: org }) if (isJsonOutput()) return await open(portalSession.url, { wait: false }).catch(() => null) }