mirror of
https://github.com/AeThex-Corporation/AeThex-OS.git
synced 2026-04-17 22:27:19 +00:00
- Create server/auth.ts with requireAuth, optionalAuth, requireAdmin middleware - Fix os.tsx: add Target/Check imports, fix useLayout->usePlatformLayout, fix achievements types - Fix game-routes.ts: add all Request/Response types, fix session access - Fix revenue.ts: org_id -> organization_id - Fix votes.ts: currentSplit scope, created_by type - Fix dashboard.ts: remove unsupported .distinct() method - Fix game-dev-apis.ts: header/body type assertions - Upgrade api/execute.ts: add Python simulation, JSON validation, HTML/CSS passthrough - Upgrade app-registry.ts: full implementation with 15 apps, RBAC, categories - Clean up Java heap error logs
100 lines
2.8 KiB
TypeScript
100 lines
2.8 KiB
TypeScript
import { supabase } from "./supabase.js";
|
|
import type { InsertRevenueEvent } from "../shared/schema.js";
|
|
|
|
/**
|
|
* Format a number to a decimal string with 2 places.
|
|
* Safe for use with Postgres decimal columns.
|
|
* @param n The number to format
|
|
* @returns String like "12.34"
|
|
*/
|
|
export function toDecimalString(n: number): string {
|
|
return (Math.round(n * 100) / 100).toFixed(2);
|
|
}
|
|
|
|
export interface RecordRevenueEventInput {
|
|
source_type: "marketplace" | "api" | "subscription" | "donation";
|
|
source_id: string;
|
|
gross_amount: number;
|
|
platform_fee?: number;
|
|
currency?: string;
|
|
project_id?: string | null;
|
|
org_id?: string | null;
|
|
metadata?: Record<string, any> | null;
|
|
requester_org_id?: string; // For access control
|
|
}
|
|
|
|
/**
|
|
* Record a revenue event in the ledger.
|
|
* Validates amounts and computes net server-side.
|
|
* Enforces org isolation if requester_org_id is provided.
|
|
*/
|
|
export async function recordRevenueEvent(
|
|
input: RecordRevenueEventInput
|
|
): Promise<{ success: boolean; id?: string; error?: string }> {
|
|
const {
|
|
source_type,
|
|
source_id,
|
|
gross_amount,
|
|
platform_fee = 0,
|
|
currency = "USD",
|
|
project_id = null,
|
|
org_id = null,
|
|
metadata = null,
|
|
requester_org_id,
|
|
} = input;
|
|
|
|
// Validate amounts
|
|
if (gross_amount < 0) {
|
|
return { success: false, error: "gross_amount cannot be negative" };
|
|
}
|
|
if (platform_fee < 0) {
|
|
return { success: false, error: "platform_fee cannot be negative" };
|
|
}
|
|
|
|
const net_amount = gross_amount - platform_fee;
|
|
if (net_amount < 0) {
|
|
return {
|
|
success: false,
|
|
error: "net_amount (gross_amount - platform_fee) cannot be negative",
|
|
};
|
|
}
|
|
|
|
// Org isolation: if requester_org_id is provided and differs from org_id, reject (unless admin bypass)
|
|
if (requester_org_id && org_id && requester_org_id !== org_id) {
|
|
return { success: false, error: "Org mismatch: cannot write to different org" };
|
|
}
|
|
|
|
// Convert amounts to safe decimal strings
|
|
const gross_amount_str = toDecimalString(gross_amount);
|
|
const platform_fee_str = toDecimalString(platform_fee);
|
|
const net_amount_str = toDecimalString(net_amount);
|
|
|
|
const event: InsertRevenueEvent = {
|
|
source_type,
|
|
source_id,
|
|
gross_amount: gross_amount_str,
|
|
platform_fee: platform_fee_str,
|
|
net_amount: net_amount_str,
|
|
currency,
|
|
project_id,
|
|
organization_id: org_id || '',
|
|
metadata,
|
|
};
|
|
|
|
try {
|
|
const { data, error } = await supabase
|
|
.from("revenue_events")
|
|
.insert([event])
|
|
.select("id");
|
|
|
|
if (error) {
|
|
console.error("Revenue event insert error:", error);
|
|
return { success: false, error: error.message };
|
|
}
|
|
|
|
return { success: true, id: data?.[0]?.id };
|
|
} catch (err) {
|
|
console.error("Unexpected error recording revenue event:", err);
|
|
return { success: false, error: "Internal server error" };
|
|
}
|
|
}
|