Payment
Infrastructure

Two payment systems. One credit model. Stripe for humans, Base/USDC for AI agents. Full backend implementation for the developer.

💳
Stripe
Human buyers · card payment
Who paysHuman buyer
MethodCredit / debit card
Settlement2–3 seconds
Fee2.9% + $0.30
KYCStripe handles it
CurrencyUSD (fiat)
ModelCredit packs only
Webhookcheckout.session.completed
Base + USDC
AI agents · on-chain autonomous
Who paysAI Agent (autonomous)
MethodUSDC on Base L2
Settlement~2 seconds (1 block)
Fee<$0.01 gas
KYCOn-chain address only
CurrencyUSDC (stablecoin)
ModelPacks + Per-action streaming
WebhookBase blockchain monitor
// FLOW — HUMAN BUYER → STRIPE → CREDITS
👤
Buyer
clicks Buy
💳
Stripe Checkout
hosted payment page
Payment confirmed
webhook fired
Credits added
Supabase updated
📲
Telegram alert
balance confirmed
// STRIPE SETUP — Node.js
BASH — Install
npm install stripe
NODE.JS — Create checkout session
// routes/credits.js
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

const CREDIT_PACKS = {
  99:  { credits: 100, price_id: 'price_stripe_id_99'  },
  299: { credits: 350, price_id: 'price_stripe_id_299' },
  599: { credits: 800, price_id: 'price_stripe_id_599' },
  999: { credits: 1500, price_id: 'price_stripe_id_999' },
};

// POST /credits/purchase
app.post('/credits/purchase', authenticate, async (req, res) => {
  const { pack } = req.body; // 99 | 299 | 599 | 999
  const packInfo = CREDIT_PACKS[pack];
  if (!packInfo) return res.status(400).json({ error: 'Invalid pack' });

  const session = await stripe.checkout.sessions.create({
    mode: 'payment',
    line_items: [{ price: packInfo.price_id, quantity: 1 }],
    customer_email: req.user.email,
    metadata: {
      user_id: req.user.id,
      credits: packInfo.credits,
      pack_amount: pack,
    },
    success_url: `https://sloiai.com/credits/success?session={CHECKOUT_SESSION_ID}`,
    cancel_url:  'https://sloiai.com/credits',
  });

  res.json({ url: session.url }); // redirect buyer to Stripe
});
NODE.JS — Stripe Webhook (credits on payment)
// POST /webhooks/stripe
// Stripe signs every webhook — always verify signature
app.post('/webhooks/stripe',
  express.raw({ type: 'application/json' }), // raw body required
  async (req, res) => {

  const sig = req.headers['stripe-signature'];
  let event;

  try {
    event = stripe.webhooks.constructEvent(
      req.body, sig, process.env.STRIPE_WEBHOOK_SECRET
    );
  } catch (err) {
    return res.status(400).send(`Webhook error: ${err.message}`);
  }

  if (event.type === 'checkout.session.completed') {
    const session = event.data.object;
    const { user_id, credits, pack_amount } = session.metadata;

    // Add credits to Supabase
    await addCredits({
      user_id,
      credits: parseInt(credits),
      source: 'stripe',
      amount_usd: parseInt(pack_amount),
      stripe_session_id: session.id,
    });

    // Notify buyer via Telegram
    await sendTelegram(user_id,
      `⚡ ${credits} credits added to your SLOI AI account.\nBalance: ${await getBalance(user_id)} credits.`
    );
  }

  res.json({ received: true });
});
// SUPABASE — ADD CREDITS FUNCTION
NODE.JS — addCredits() + getBalance()
// lib/credits.js
const { createClient } = require('@supabase/supabase-js');
const supabase = createClient(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_SERVICE_KEY // service role — bypasses RLS
);

async function addCredits({ user_id, credits, source, amount_usd, stripe_session_id, tx_hash }) {
  // 1. Update balance
  await supabase.rpc('increment_credits', { p_user_id: user_id, p_amount: credits });

  // 2. Log transaction
  await supabase.from('credit_transactions').insert({
    user_id, credits, source,
    amount_usd: amount_usd || null,
    stripe_session_id: stripe_session_id || null,
    tx_hash: tx_hash || null, // for Base/USDC payments
    created_at: new Date().toISOString(),
  });
}

async function getBalance(user_id) {
  const { data } = await supabase
    .from('credits')
    .select('balance')
    .eq('user_id', user_id)
    .single();
  return data?.balance || 0;
}

async function deductCredits(user_id, amount, action) {
  const balance = await getBalance(user_id);
  if (balance < amount) throw new Error('insufficient_credits');
  await supabase.rpc('decrement_credits', { p_user_id: user_id, p_amount: amount });
  await supabase.from('credit_transactions').insert({
    user_id, credits: -amount, source: 'usage', action
  });
}

module.exports = { addCredits, getBalance, deductCredits };
// SUPABASE SQL — CREDITS TABLE + RPC
SQL — Supabase
-- Credits balance per user
CREATE TABLE credits (
  user_id     UUID PRIMARY KEY REFERENCES users(id),
  balance     INT NOT NULL DEFAULT 0,
  total_purchased INT DEFAULT 0,
  total_spent INT DEFAULT 0,
  updated_at  TIMESTAMPTZ DEFAULT NOW()
);

-- Transaction log
CREATE TABLE credit_transactions (
  id                  UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  user_id             UUID REFERENCES users(id),
  credits             INT NOT NULL,          -- positive=purchase, negative=usage
  source              TEXT,                      -- stripe | usdc | usage
  action              TEXT,                      -- deal_hunter | loi | autopilot
  amount_usd          NUMERIC(10,2),
  stripe_session_id   TEXT,
  tx_hash             TEXT,                      -- Base blockchain tx
  wallet_address      TEXT,                      -- agent wallet
  created_at          TIMESTAMPTZ DEFAULT NOW()
);

-- Atomic increment (thread-safe)
CREATE OR REPLACE FUNCTION increment_credits(p_user_id UUID, p_amount INT)
RETURNS VOID AS $$
  INSERT INTO credits (user_id, balance, total_purchased)
  VALUES (p_user_id, p_amount, p_amount)
  ON CONFLICT (user_id) DO UPDATE SET
    balance = credits.balance + p_amount,
    total_purchased = credits.total_purchased + p_amount,
    updated_at = NOW();
$$ LANGUAGE sql;

-- Atomic decrement
CREATE OR REPLACE FUNCTION decrement_credits(p_user_id UUID, p_amount INT)
RETURNS VOID AS $$
  UPDATE credits SET
    balance = balance - p_amount,
    total_spent = total_spent + p_amount,
    updated_at = NOW()
  WHERE user_id = p_user_id AND balance >= p_amount;
$$ LANGUAGE sql;
// FLOW — AI AGENT → BASE/USDC → CREDITS
🤖
AI Agent
AgentKit wallet
💵
USDC transfer
99/299/599/999
Base L2
~2 seconds
🔍
Blockchain monitor
Alchemy/Moralis
Credits added
instant
// PACK MODEL — USDC
NODE.JS — USDC Pack monitor (Alchemy webhook)
// routes/webhooks/base.js
// Alchemy sends this webhook on every USDC transfer to SLOI_AI_WALLET

const SLOI_AI_WALLET = process.env.SLOI_AI_WALLET_ADDRESS;
const USDC_CONTRACT  = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC on Base

const USDC_PACKS = {
  '99':  100,
  '299': 350,
  '599': 800,
  '999': 1500,
};

// POST /webhooks/base
app.post('/webhooks/base', async (req, res) => {
  const { event } = req.body;

  for (const activity of event.activity) {
    const { fromAddress, toAddress, value, asset, hash } = activity;

    // Only USDC transfers to our wallet
    if (toAddress.toLowerCase() !== SLOI_AI_WALLET.toLowerCase()) continue;
    if (asset !== 'USDC') continue;

    const amountUsdc = Math.round(value).toString();
    const credits = USDC_PACKS[amountUsdc];

    if (!credits) {
      // Non-standard amount — handle as streaming payment
      await handleStreamingPayment(fromAddress, value, hash);
      continue;
    }

    // Find agent by wallet address
    const agent = await getAgentByWallet(fromAddress);
    if (!agent) {
      console.warn('Unknown wallet:', fromAddress);
      continue;
    }

    // Add credits
    await addCredits({
      user_id: agent.user_id,
      credits,
      source: 'usdc',
      amount_usd: parseInt(amountUsdc),
      tx_hash: hash,
      wallet_address: fromAddress,
    });

    console.log(`✓ ${credits} credits → ${agent.name} (${amountUsdc} USDC, tx: ${hash})`);
  }

  res.json({ ok: true });
});
// STREAMING MODEL — PAY PER ACTION
Credit streaming: Agent sends exact USDC amount for each action. No pack purchase needed. Agent can start with $0 balance and pay per-use. Ideal for low-frequency agents.
Pack pricing: $0.67–0.99/credit · Streaming pricing: $1.00/credit (10c premium for no commitment)
NODE.JS — Streaming payment handler
// Streaming: agent sends exact USDC for one action
const STREAMING_PRICES = {
  watcher:    1.00,  // $1.00 USDC / day
  deal_hunter: 10.00, // $10.00 USDC / session
  autopilot:  20.00, // $20.00 USDC / session
  loi:         5.00,  // $5.00 USDC / LOI
};

async function handleStreamingPayment(fromAddress, value, txHash) {
  const amount = Math.round(value * 100) / 100; // 2 decimal precision
  const agent = await getAgentByWallet(fromAddress);
  if (!agent) return;

  // Find matching action
  const action = Object.entries(STREAMING_PRICES)
    .find(([_, price]) => Math.abs(price - amount) < 0.01)?.[0];

  if (!action) return; // unknown amount

  // Issue one-time authorization token
  const token = await createAuthToken({
    agent_id: agent.id,
    action,
    tx_hash: txHash,
    expires_in: 3600, // 1 hour to use
  });

  // Notify agent via webhook
  await notifyAgent(agent.id, {
    type: 'payment_confirmed',
    action,
    auth_token: token,
    amount_usdc: amount,
    tx_hash: txHash,
  });
}
// AGENT REGISTRATION — WALLET LINKING
NODE.JS — POST /agents/register
// POST /v1/agents/register
app.post('/v1/agents/register', async (req, res) => {
  const { name, email, framework, wallet_address } = req.body;

  // Create agent account
  const { data: agent } = await supabase
    .from('agents')
    .insert({ name, email, framework, wallet_address, tier: 'read' })
    .select().single();

  // Generate API key
  const api_key = 'sk-sloi-' + generateSecureKey(32);
  await supabase.from('api_keys').insert({ agent_id: agent.id, key_hash: hash(api_key) });

  res.json({
    agent_id: agent.id,
    api_key,
    tier: 'read',
    sloi_wallet: process.env.SLOI_AI_WALLET_ADDRESS, // agent sends USDC here
    payment_methods: {
      usdc_on_base: {
        address: process.env.SLOI_AI_WALLET_ADDRESS,
        network: 'base-mainnet',
        token: 'USDC',
        pack_amounts: [99, 299, 599, 999],
        streaming_prices: STREAMING_PRICES,
      }
    }
  });
});
// HYBRID LOGIC — AUTO-SELECT PAYMENT METHOD
The platform detects who is calling — human or agent — and routes to the right payment method automatically. Agents with a wallet address always use USDC. Humans always use Stripe. No manual selection needed.
NODE.JS — Unified credit purchase endpoint
// POST /v1/credits/purchase
// Works for both humans (Stripe) and agents (USDC)
app.post('/v1/credits/purchase', authenticate, async (req, res) => {
  const { pack } = req.body; // 99 | 299 | 599 | 999
  const caller = req.caller; // set by authenticate middleware

  if (caller.type === 'agent' && caller.wallet_address) {
    // Agent → instruct to send USDC on Base
    return res.json({
      method: 'usdc_on_base',
      instruction: `Send ${pack} USDC to ${process.env.SLOI_AI_WALLET_ADDRESS} on Base mainnet`,
      wallet: process.env.SLOI_AI_WALLET_ADDRESS,
      amount_usdc: pack,
      credits_to_receive: CREDIT_PACKS[pack].credits,
      confirmation: 'Credits added within 1 block (~2 seconds)',
    });
  }

  // Human buyer → Stripe checkout
  const session = await stripe.checkout.sessions.create({ /* ... */ });
  res.json({ method: 'stripe', url: session.url });
});

// Authenticate middleware — detects human vs agent
async function authenticate(req, res, next) {
  const key = req.headers['x-api-key'];
  const token = req.headers['authorization']?.replace('Bearer ', '');

  if (key?.startsWith('sk-sloi-')) {
    // API key → agent
    const agent = await getAgentByKey(key);
    req.caller = { type: 'agent', ...agent };
  } else if (token) {
    // JWT → human (Supabase auth)
    const user = await supabase.auth.getUser(token);
    req.caller = { type: 'human', ...user.data.user };
  } else {
    return res.status(401).json({ error: 'unauthorized' });
  }
  next();
}
// AUTO-TOPUP — AGENT SELF-FUNDING
TYPESCRIPT — Agent side (runs before every session)
// Agent calls this before every negotiation
async function ensureCredits(minCredits = 15) {
  const { data } = await sloi.get('/credits/balance');

  if (data.balance >= minCredits) return; // enough

  // Choose pack — buy smallest that covers need
  const pack = data.balance < 15 ? 99 : 299;

  // Check USDC balance on Base
  const usdcBalance = await agentkit.getBalance({ token: 'usdc' });

  if (usdcBalance >= pack) {
    // Pay with USDC on Base
    const tx = await agentkit.transfer({
      to: SLOI_AI_WALLET,
      amount: pack.toString(),
      token: 'usdc',
      network: 'base-mainnet',
    });
    await tx.wait(); // ~2 seconds
    console.log(`Paid ${pack} USDC on Base. Credits incoming.`);

  } else {
    // Fallback: request human top-up via Telegram Bot
    await notifyBoss({
      type: 'low_credits',
      message: `Agent needs top-up. Balance: ${data.balance}. Please add USDC to agent wallet or buy credits at sloiai.com/credits`
    });
    throw new Error('insufficient_funds');
  }
}
// ENVIRONMENT VARIABLES — Railway
VariableValueWhere to get it
STRIPE_SECRET_KEYsk_live_...dashboard.stripe.com → API keys
STRIPE_WEBHOOK_SECRETwhsec_...Stripe → Webhooks → signing secret
STRIPE_PRICE_ID_99price_...Stripe → Products → $99 pack
STRIPE_PRICE_ID_299price_...Stripe → Products → $299 pack
STRIPE_PRICE_ID_599price_...Stripe → Products → $599 pack
STRIPE_PRICE_ID_999price_...Stripe → Products → $999 pack
SLOI_AI_WALLET_ADDRESS0x...Coinbase CDP → create wallet on Base
SLOI_AI_WALLET_PRIVATE_KEY0x...Coinbase CDP → export key (keep secret)
ALCHEMY_API_KEYalchemy_...alchemy.com → Base mainnet app
ALCHEMY_WEBHOOK_AUTH...Alchemy → Notify → webhook auth token
SUPABASE_URLhttps://xxx.supabase.coSupabase → Project Settings → API
SUPABASE_SERVICE_KEYeyJh...Supabase → service_role key
// STRIPE SETUP CHECKLIST
Create Stripe account + complete KYC
Create 4 Products: $99 / $299 / $599 / $999 — one-time payment
Copy Price IDs to ENV vars
Register webhook: https://api.sloiai.com/webhooks/stripe
Event to listen: checkout.session.completed
Copy Webhook Secret to ENV
Test with Stripe CLI: stripe trigger checkout.session.completed
// BASE/USDC SETUP CHECKLIST
Create Coinbase CDP account at portal.cdp.coinbase.com
Create SLOI AI wallet on Base Mainnet
Fund with small USDC for testing (~$10)
Copy wallet address to ENV: SLOI_AI_WALLET_ADDRESS
Create Alchemy account at alchemy.com
Create Base Mainnet app → get API key
Create Alchemy Notify webhook for USDC transfers to SLOI_AI_WALLET
Point webhook to: https://api.sloiai.com/webhooks/base
Test: send 99 USDC from test wallet → verify credits added
// WEBHOOK URLS — REGISTER THESE
URLs
Stripe webhook:   https://api.sloiai.com/webhooks/stripe
Base webhook:     https://api.sloiai.com/webhooks/base
Telegram Bot events:  https://api.sloiai.com/webhooks/telegram-bot