import { PLAN_LIMITS, type ConcreteNetwork, type Plan } from "@fetcha/core"; import { config } from "../config"; /** * Pricing service. Nothing in the execution path hardcodes prices: the executor reports * upstream cost + bytes + network and this module decides what the customer is charged. * * Model: base subscription (plan) + usage. Every request is priced as * per-request component (plan overage rate, 0 while within the included quota — quota is * enforced elsewhere) + bandwidth component for premium networks. */ export interface PriceInput { plan: Plan; network: ConcreteNetwork | null; bytes: number; upstreamCostUsd: number; attempts: number; success: boolean; } export interface PriceBreakdown { requestUsd: number; bandwidthUsd: number; totalUsd: number; marginUsd: number; } export function priceRequest(input: PriceInput): PriceBreakdown { const limits = PLAN_LIMITS[input.plan]; // Private platform: the single plan has no unit prices → nothing is billed. Upstream cost is // still tracked for the admin's unit economics. if (limits.price_usd_month === 0 && limits.overage_per_1k_requests_usd === 0 && limits.residential_per_gb_usd === 0) { return { requestUsd: 0, bandwidthUsd: 0, totalUsd: 0, marginUsd: round6(-input.upstreamCostUsd) }; } // Free/enterprise have no per-request rate; failed requests are never charged a request fee. const requestUsd = input.success ? limits.overage_per_1k_requests_usd / 1000 : 0; let bandwidthUsd = 0; if (input.network && input.network !== "datacenter") { const perGb = limits.residential_per_gb_usd || input.upstreamCostUsd > 0 ? limits.residential_per_gb_usd : 0; bandwidthUsd = perGb > 0 ? (input.bytes / 1_073_741_824) * perGb : input.upstreamCostUsd * config.defaultMargin; } else { bandwidthUsd = input.upstreamCostUsd * config.defaultMargin; } const totalUsd = round6(requestUsd + bandwidthUsd); return { requestUsd: round6(requestUsd), bandwidthUsd: round6(bandwidthUsd), totalUsd, marginUsd: round6(totalUsd - input.upstreamCostUsd) }; } export function round6(n: number): number { return Math.round(n * 1e6) / 1e6; } export function formatUsd(n: number): string { if (n === 0) return "$0.00"; if (n < 0.01) return `$${n.toFixed(5)}`; return `$${n.toFixed(2)}`; }