SPB Git forge

spb/fetcha

Public
11commits 1branches 0releases
1.5 MBsize
maindefault branch
16 days agolast push
TypeScript 97.5% SQL 1.4% Python 0.8%
2.3 KB · 57 lines typescript
Raw Blame History
1import { PLAN_LIMITS, type ConcreteNetwork, type Plan } from "@fetcha/core";2import { config } from "../config";34/**5 * Pricing service. Nothing in the execution path hardcodes prices: the executor reports6 * upstream cost + bytes + network and this module decides what the customer is charged.7 *8 * Model: base subscription (plan) + usage. Every request is priced as9 *   per-request component (plan overage rate, 0 while within the included quota — quota is10 *   enforced elsewhere) + bandwidth component for premium networks.11 */12export interface PriceInput {13  plan: Plan;14  network: ConcreteNetwork | null;15  bytes: number;16  upstreamCostUsd: number;17  attempts: number;18  success: boolean;19}2021export interface PriceBreakdown {22  requestUsd: number;23  bandwidthUsd: number;24  totalUsd: number;25  marginUsd: number;26}2728export function priceRequest(input: PriceInput): PriceBreakdown {29  const limits = PLAN_LIMITS[input.plan];30  // Private platform: the single plan has no unit prices → nothing is billed. Upstream cost is31  // still tracked for the admin's unit economics.32  if (limits.price_usd_month === 0 && limits.overage_per_1k_requests_usd === 0 && limits.residential_per_gb_usd === 0) {33    return { requestUsd: 0, bandwidthUsd: 0, totalUsd: 0, marginUsd: round6(-input.upstreamCostUsd) };34  }35  // Free/enterprise have no per-request rate; failed requests are never charged a request fee.36  const requestUsd = input.success ? limits.overage_per_1k_requests_usd / 1000 : 0;37  let bandwidthUsd = 0;38  if (input.network && input.network !== "datacenter") {39    const perGb = limits.residential_per_gb_usd || input.upstreamCostUsd > 0 ? limits.residential_per_gb_usd : 0;40    bandwidthUsd = perGb > 0 ? (input.bytes / 1_073_741_824) * perGb : input.upstreamCostUsd * config.defaultMargin;41  } else {42    bandwidthUsd = input.upstreamCostUsd * config.defaultMargin;43  }44  const totalUsd = round6(requestUsd + bandwidthUsd);45  return { requestUsd: round6(requestUsd), bandwidthUsd: round6(bandwidthUsd), totalUsd, marginUsd: round6(totalUsd - input.upstreamCostUsd) };46}4748export function round6(n: number): number {49  return Math.round(n * 1e6) / 1e6;50}5152export function formatUsd(n: number): string {53  if (n === 0) return "$0.00";54  if (n < 0.01) return `$${n.toFixed(5)}`;55  return `$${n.toFixed(2)}`;56}57