import express from "express"; import cors from "cors"; import crypto from "crypto"; import { readFileSync } from "fs"; import Stripe from "stripe"; import https from "https"; const app = express(); const PORT = 7860; // ─── Config ────────────────────────────────────────────────────────────────── const pricing = JSON.parse(readFileSync("pricing.json", "utf-8")); const JWT_SECRET = process.env.JWT_SECRET || crypto.randomBytes(32).toString("hex"); const GATEWAY_URL = "https://scottzillasystems-scottzilla-gateway.hf.space"; // Provider configs (from Space secrets) const STRIPE_KEY = process.env.STRIPE_SECRET_KEY || ""; const STRIPE_WH = process.env.STRIPE_WEBHOOK_SECRET || ""; const PAYPAL_ID = process.env.PAYPAL_CLIENT_ID || ""; const PAYPAL_SECRET = process.env.PAYPAL_CLIENT_SECRET || ""; const COINBASE_KEY = process.env.COINBASE_API_KEY || ""; const COINBASE_WH = process.env.COINBASE_WEBHOOK_SECRET || ""; const SQUARE_TOKEN = process.env.SQUARE_ACCESS_TOKEN || ""; const GUMROAD_TOKEN = process.env.GUMROAD_ACCESS_TOKEN || ""; const stripe = STRIPE_KEY ? new Stripe(STRIPE_KEY) : null; // ─── In-memory store (swap for DB in production) ───────────────────────────── const apiKeys = new Map(); // key → {tier, email, created, usage_today, usage_total, balance, active} const sessions = new Map(); // session_id → {key, provider, tier, status} app.use(cors()); // Raw body for Stripe webhooks app.use("/api/webhooks/stripe", express.raw({ type: "application/json" })); app.use(express.json()); // ─── Helpers ───────────────────────────────────────────────────────────────── function generateApiKey() { return "sz_" + crypto.randomBytes(24).toString("hex"); } function getKeyData(key) { if (!key) return null; const clean = key.replace(/^Bearer\s+/i, "").trim(); return apiKeys.get(clean) || null; } function checkRateLimit(keyData) { const tier = pricing.tiers[keyData.tier]; if (!tier) return false; if (tier.requests_per_day === -1) return true; // unlimited // Reset daily counter const today = new Date().toISOString().split("T")[0]; if (keyData.last_reset !== today) { keyData.usage_today = 0; keyData.last_reset = today; } return keyData.usage_today < tier.requests_per_day; } function canAccessModel(keyData, modelAlias) { const tier = pricing.tiers[keyData.tier]; if (!tier) return false; if (tier.models.includes("all")) return true; return tier.models.includes(modelAlias); } // ─── Health / Dashboard ────────────────────────────────────────────────────── app.get("/", (req, res) => { const providers = { stripe: !!STRIPE_KEY, paypal: !!PAYPAL_ID, coinbase: !!COINBASE_KEY, square: !!SQUARE_TOKEN, gumroad: !!GUMROAD_TOKEN, }; res.send(` Scottzilla Payments

💰 Scottzilla Payments

Multi-provider payment server for the Scottzilla AI platform.

Payment Providers

ProviderStatusSupports
Stripe${providers.stripe ? "✅ Connected" : "⚠️ Add STRIPE_SECRET_KEY"}Cards, Apple Pay, Google Pay
PayPal${providers.paypal ? "✅ Connected" : "⚠️ Add PAYPAL_CLIENT_ID"}PayPal, cards
Coinbase Commerce${providers.coinbase ? "✅ Connected" : "⚠️ Add COINBASE_API_KEY"}BTC, ETH, USDC
Square (Cash App)${providers.square ? "✅ Connected" : "⚠️ Add SQUARE_ACCESS_TOKEN"}Cash App Pay
Gumroad${providers.gumroad ? "✅ Connected" : "⚠️ Add GUMROAD_ACCESS_TOKEN"}Simple checkout + affiliates

Pricing Tiers

${Object.entries(pricing.tiers).map(([id, t]) => `

${t.name}

$${t.price_monthly}/mo

${t.requests_per_day === -1 ? "Unlimited" : t.requests_per_day} requests/day

${t.features.join(", ")}

`).join("")}

Quick Start

1. Get a free API key:

curl -X POST ${req.headers.host ? "https://" + req.headers.host : ""}/api/keys/create -H "Content-Type: application/json" -d '{"email":"you@example.com"}'

2. Use it with the Scottzilla Gateway:

curl ${GATEWAY_URL}/v1/chat/completions -H "Authorization: Bearer YOUR_KEY" -H "Content-Type: application/json" -d '{"model":"auto","messages":[{"role":"user","content":"Hello"}]}'

3. Upgrade for more models + requests:

curl -X POST ${req.headers.host ? "https://" + req.headers.host : ""}/api/checkout/stripe -H "Content-Type: application/json" -d '{"api_key":"YOUR_KEY","tier":"pro"}'

API Reference

EndpointDescription
POST /api/keys/createCreate free API key (body: {email})
GET /api/keys/verify/:keyVerify key, get tier + usage
POST /api/checkout/:providerCreate payment checkout (body: {api_key, tier})
POST /api/webhooks/:providerPayment confirmation webhooks
GET /api/usage/:keyDetailed usage stats
GET /api/pricingCurrent pricing tiers
POST /api/usage/recordRecord API usage (called by gateway)

Scottzilla Gateway · All Models · Active keys: ${apiKeys.size}

`); }); // ─── API Key Management ────────────────────────────────────────────────────── app.post("/api/keys/create", (req, res) => { const { email } = req.body || {}; if (!email) return res.status(400).json({ error: "email required" }); const key = generateApiKey(); apiKeys.set(key, { tier: "free", email, created: new Date().toISOString(), usage_today: 0, usage_total: 0, balance: 0, active: true, last_reset: new Date().toISOString().split("T")[0], }); res.json({ api_key: key, tier: "free", limits: pricing.tiers.free, message: "API key created. Use it with the Scottzilla Gateway.", gateway: GATEWAY_URL, }); }); app.get("/api/keys/verify/:key", (req, res) => { const data = apiKeys.get(req.params.key); if (!data) return res.status(404).json({ error: "invalid API key" }); const tier = pricing.tiers[data.tier]; const withinLimit = checkRateLimit(data); res.json({ valid: true, active: data.active, tier: data.tier, tier_name: tier?.name, usage_today: data.usage_today, limit_today: tier?.requests_per_day, usage_total: data.usage_total, balance: data.balance, within_limit: withinLimit, models: tier?.models || [], features: tier?.features || [], }); }); // ─── Usage Recording (called by gateway) ───────────────────────────────────── app.post("/api/usage/record", (req, res) => { const { api_key, model, tokens } = req.body || {}; const data = apiKeys.get(api_key); if (!data) return res.status(404).json({ error: "invalid key" }); data.usage_today++; data.usage_total++; // Pay-as-you-go: deduct from balance if (data.tier === "free" && data.balance > 0) { data.balance -= pricing.payg.price_per_request; } res.json({ recorded: true, usage_today: data.usage_today }); }); app.get("/api/usage/:key", (req, res) => { const data = apiKeys.get(req.params.key); if (!data) return res.status(404).json({ error: "invalid key" }); res.json({ usage_today: data.usage_today, usage_total: data.usage_total, balance: data.balance, tier: data.tier, created: data.created, }); }); // ─── Pricing ───────────────────────────────────────────────────────────────── app.get("/api/pricing", (req, res) => res.json(pricing)); // ─── Stripe Checkout ───────────────────────────────────────────────────────── app.post("/api/checkout/stripe", async (req, res) => { if (!stripe) return res.status(503).json({ error: "Stripe not configured. Add STRIPE_SECRET_KEY secret." }); const { api_key, tier, amount } = req.body; const data = apiKeys.get(api_key); if (!data) return res.status(404).json({ error: "invalid API key" }); try { const tierConfig = pricing.tiers[tier]; if (tier && tierConfig) { // Subscription checkout const session = await stripe.checkout.sessions.create({ mode: "subscription", line_items: [{ price: tierConfig.stripe_price_id, quantity: 1 }], success_url: `https://scottzillasystems-scottzilla-payments.hf.space/?success=true&key=${api_key}`, cancel_url: `https://scottzillasystems-scottzilla-payments.hf.space/?cancelled=true`, metadata: { api_key, tier }, }); return res.json({ checkout_url: session.url, session_id: session.id }); } else if (amount) { // Pay-as-you-go top-up const session = await stripe.checkout.sessions.create({ mode: "payment", line_items: [{ price_data: { currency: "usd", product_data: { name: "Scottzilla API Credits" }, unit_amount: Math.round(amount * 100) }, quantity: 1 }], success_url: `https://scottzillasystems-scottzilla-payments.hf.space/?success=true&key=${api_key}`, cancel_url: `https://scottzillasystems-scottzilla-payments.hf.space/?cancelled=true`, metadata: { api_key, type: "topup", amount: String(amount) }, }); return res.json({ checkout_url: session.url, session_id: session.id }); } res.status(400).json({ error: "Specify tier or amount" }); } catch (err) { res.status(500).json({ error: err.message }); } }); // ─── PayPal Checkout ───────────────────────────────────────────────────────── app.post("/api/checkout/paypal", async (req, res) => { if (!PAYPAL_ID) return res.status(503).json({ error: "PayPal not configured. Add PAYPAL_CLIENT_ID + PAYPAL_CLIENT_SECRET secrets." }); const { api_key, tier, amount } = req.body; const data = apiKeys.get(api_key); if (!data) return res.status(404).json({ error: "invalid API key" }); const tierConfig = pricing.tiers[tier]; const payAmount = amount || tierConfig?.price_monthly || 0; // Get PayPal access token const authResp = await fetch("https://api-m.paypal.com/v1/oauth2/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", Authorization: `Basic ${Buffer.from(`${PAYPAL_ID}:${PAYPAL_SECRET}`).toString("base64")}` }, body: "grant_type=client_credentials", }); const { access_token } = await authResp.json(); const orderResp = await fetch("https://api-m.paypal.com/v2/checkout/orders", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${access_token}` }, body: JSON.stringify({ intent: "CAPTURE", purchase_units: [{ amount: { currency_code: "USD", value: String(payAmount) }, description: `Scottzilla ${tier || "credits"} - ${api_key.slice(0, 8)}...` }], application_context: { return_url: `https://scottzillasystems-scottzilla-payments.hf.space/api/webhooks/paypal?key=${api_key}&tier=${tier || "topup"}&amount=${payAmount}`, cancel_url: "https://scottzillasystems-scottzilla-payments.hf.space/?cancelled=true", }, }), }); const order = await orderResp.json(); const approveLink = order.links?.find((l) => l.rel === "approve"); res.json({ checkout_url: approveLink?.href, order_id: order.id }); }); // ─── Coinbase Commerce Checkout ────────────────────────────────────────────── app.post("/api/checkout/coinbase", async (req, res) => { if (!COINBASE_KEY) return res.status(503).json({ error: "Coinbase not configured. Add COINBASE_API_KEY secret." }); const { api_key, tier, amount } = req.body; const data = apiKeys.get(api_key); if (!data) return res.status(404).json({ error: "invalid API key" }); const tierConfig = pricing.tiers[tier]; const payAmount = amount || tierConfig?.price_monthly || 0; const resp = await fetch("https://api.commerce.coinbase.com/charges", { method: "POST", headers: { "Content-Type": "application/json", "X-CC-Api-Key": COINBASE_KEY, "X-CC-Version": "2018-03-22" }, body: JSON.stringify({ name: `Scottzilla ${tier || "Credits"}`, description: `API access - ${tier || "pay-as-you-go"}`, pricing_type: "fixed_price", local_price: { amount: String(payAmount), currency: "USD" }, metadata: { api_key, tier: tier || "topup" }, redirect_url: `https://scottzillasystems-scottzilla-payments.hf.space/?success=true&key=${api_key}`, cancel_url: "https://scottzillasystems-scottzilla-payments.hf.space/?cancelled=true", }), }); const charge = await resp.json(); res.json({ checkout_url: charge.data?.hosted_url, charge_id: charge.data?.id }); }); // ─── Square / Cash App Checkout ────────────────────────────────────────────── app.post("/api/checkout/square", async (req, res) => { if (!SQUARE_TOKEN) return res.status(503).json({ error: "Square not configured. Add SQUARE_ACCESS_TOKEN secret." }); const { api_key, tier, amount } = req.body; const data = apiKeys.get(api_key); if (!data) return res.status(404).json({ error: "invalid API key" }); const tierConfig = pricing.tiers[tier]; const payAmount = Math.round((amount || tierConfig?.price_monthly || 0) * 100); const resp = await fetch("https://connect.squareup.com/v2/online-checkout/payment-links", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${SQUARE_TOKEN}`, "Square-Version": "2024-01-18" }, body: JSON.stringify({ idempotency_key: crypto.randomUUID(), quick_pay: { name: `Scottzilla ${tier || "Credits"}`, price_money: { amount: payAmount, currency: "USD" }, location_id: "main" }, checkout_options: { redirect_url: `https://scottzillasystems-scottzilla-payments.hf.space/?success=true&key=${api_key}` }, pre_populated_data: { buyer_email: data.email }, }), }); const link = await resp.json(); res.json({ checkout_url: link.payment_link?.long_url || link.payment_link?.url, link_id: link.payment_link?.id }); }); // ─── Gumroad Checkout ──────────────────────────────────────────────────────── app.post("/api/checkout/gumroad", async (req, res) => { const { api_key, tier } = req.body; const data = apiKeys.get(api_key); if (!data) return res.status(404).json({ error: "invalid API key" }); const tierConfig = pricing.tiers[tier]; const productId = tierConfig?.gumroad_product_id; // Gumroad uses hosted checkout pages — return the URL directly res.json({ checkout_url: productId ? `https://scottzillasystems.gumroad.com/l/${productId}?api_key=${api_key}` : "https://scottzillasystems.gumroad.com", note: "Complete purchase on Gumroad, then your key will be upgraded via webhook.", }); }); // ─── Webhooks ──────────────────────────────────────────────────────────────── app.post("/api/webhooks/stripe", (req, res) => { if (!stripe) return res.sendStatus(200); try { const sig = req.headers["stripe-signature"]; const event = STRIPE_WH ? stripe.webhooks.constructEvent(req.body, sig, STRIPE_WH) : JSON.parse(req.body); if (event.type === "checkout.session.completed") { const session = event.data.object; const { api_key, tier, type, amount } = session.metadata || {}; const data = apiKeys.get(api_key); if (data) { if (type === "topup") { data.balance += parseFloat(amount || 0); } else if (tier) { data.tier = tier; } console.log(`[stripe] ${api_key.slice(0, 10)}... → ${tier || "topup $" + amount}`); } } res.json({ received: true }); } catch (err) { console.error("[stripe webhook]", err.message); res.status(400).json({ error: err.message }); } }); app.post("/api/webhooks/paypal", (req, res) => { const { key, tier, amount } = req.query; const data = apiKeys.get(key); if (data && tier && tier !== "topup") { data.tier = tier; console.log(`[paypal] ${key?.slice(0, 10)}... → ${tier}`); } else if (data && amount) { data.balance += parseFloat(amount); console.log(`[paypal] ${key?.slice(0, 10)}... → topup $${amount}`); } res.redirect(`https://scottzillasystems-scottzilla-payments.hf.space/?success=true`); }); app.post("/api/webhooks/coinbase", (req, res) => { const event = req.body?.event; if (event?.type === "charge:confirmed") { const { api_key, tier } = event.data?.metadata || {}; const data = apiKeys.get(api_key); if (data && tier && tier !== "topup") { data.tier = tier; console.log(`[coinbase] ${api_key?.slice(0, 10)}... → ${tier}`); } } res.json({ received: true }); }); app.post("/api/webhooks/gumroad", (req, res) => { const { email, custom_fields } = req.body || {}; // Gumroad sends custom_fields with the api_key const api_key = custom_fields?.api_key || req.body?.api_key; const data = apiKeys.get(api_key); if (data) { data.tier = "pro"; // upgrade to pro on any Gumroad purchase console.log(`[gumroad] ${api_key?.slice(0, 10)}... → pro`); } res.json({ received: true }); }); // ─── Start ─────────────────────────────────────────────────────────────────── app.listen(PORT, "0.0.0.0", () => { console.log(`💰 Scottzilla Payments listening on :${PORT}`); console.log(` Providers: Stripe=${!!STRIPE_KEY} PayPal=${!!PAYPAL_ID} Coinbase=${!!COINBASE_KEY} Square=${!!SQUARE_TOKEN} Gumroad=${!!GUMROAD_TOKEN}`); });