export type BillingPeriod = "month" | "year"; export function getPublicBetaPresentation(active: boolean) { return active ? { status: "公测期间服务费全免", subline: "不绑支付方式 · 不会自动扣款", factValue: "¥0", factLabel: "公测服务费", priceHeading: "当前公测", priceValue: "¥0", priceDetail: "全部已允许主机", cardStatus: "当前有效", cardTitle: "公开公测", cardDescription: "公测期间,所有已经允许接入的主机槽位不收服务费。", cta: "进入公测控制台", comparison: "当前公测免费;未来方案未定", } : { status: "公测免费政策已结束", subline: "不会自动扣款 · 付费入口仍关闭", factValue: "关闭", factLabel: "公测免费", priceHeading: "公测政策", priceValue: "已结束", priceDetail: "不会转为自动扣款", cardStatus: "已经结束", cardTitle: "公开公测", cardDescription: "免费政策已结束;付费入口仍由上线门禁关闭,不会自动扣款。", cta: "进入控制台", comparison: "公测已结束;收费入口未开放", }; } export function getBillingEntitlementPresentation( mode: "public_beta" | "grant" | "none", publicBetaState: "open" | "gated" | "inactive" = "inactive", ) { if (mode === "public_beta") { return { tone: "good" as const, status: "公测免费", title: "当前应付 ¥0", description: "不需要支付方式;公测结束不会自动生成付款或扣款。", emptyOrders: "免费公测不提供报价,也不会生成订单或付款单。", }; } if (mode === "grant") { return { tone: "info" as const, status: "闭测邀请", title: "当前权益有效", description: "当前由有期限的非货币权益覆盖;不会生成付款或自动扣款。", emptyOrders: "闭测邀请不会生成报价、订单或付款单。", }; } if (publicBetaState === "gated") { return { tone: "warn" as const, status: "公开接入冻结", title: "当前没有有效闭测邀请", description: "免费政策已经预设,但安全门禁尚未齐全;不会要求付款或自动创建订单。", emptyOrders: "公开接入冻结期间不会生成报价、订单或付款单。", }; } return { tone: "neutral" as const, status: "无有效免费资格", title: "当前无免费权益", description: "公开公测当前未开放,该账户也没有有效闭测邀请;不会自动生成付款或扣款。", emptyOrders: "收费功能仍未开放,不会自动生成报价、订单或付款单。", }; } /** * Advance a fixed-term order by one calendar billing period while clamping * month-end dates. This prevents January 31 from rolling into March and keeps * leap-day yearly orders on the final valid day of February. */ export function addBillingPeriod( startInput: Date | string, period: BillingPeriod, ): string { const start = new Date(startInput); if (Number.isNaN(start.valueOf())) { throw new RangeError("Invalid billing term start"); } const requestedDay = start.getUTCDate(); const result = new Date(start); result.setUTCDate(1); if (period === "month") { result.setUTCMonth(result.getUTCMonth() + 1); } else { result.setUTCFullYear(result.getUTCFullYear() + 1); } const lastDay = new Date( Date.UTC(result.getUTCFullYear(), result.getUTCMonth() + 1, 0), ).getUTCDate(); result.setUTCDate(Math.min(requestedDay, lastDay)); return result.toISOString(); } /** Return the next time the currently summarized entitlement can change. */ export function nextEntitlementExpiry( values: Array, ): string | null { return values .filter((value): value is string => Boolean(value)) .sort() .at(0) ?? null; } export type EntitlementComponent = { source: string; capacity: number | null; endsAt: string | null; }; export function summarizeEntitlementComponents( components: EntitlementComponent[], activeSlots: number, reservedSlots: number, ) { const unlimitedComponents = components.filter( (component) => component.capacity === null, ); const unlimited = unlimitedComponents.length > 0; const capacitySlots = unlimited ? null : components.reduce((sum, component) => sum + (component.capacity ?? 0), 0); const effectiveUntil = unlimited ? unlimitedComponents.some((component) => component.endsAt === null) ? null : nextEntitlementExpiry(unlimitedComponents.map((component) => component.endsAt)) : nextEntitlementExpiry(components.map((component) => component.endsAt)); return { unlimited, capacitySlots, availableSlots: unlimited ? null : Math.max(0, (capacitySlots ?? 0) - activeSlots - reservedSlots), effectiveUntil, sources: [...new Set(components.map((component) => component.source))], }; }