Inspiration

Players of quick mobile games buy credits in tiny amounts, again and again. Every top-up means a checkout, a redirect and a break in the game. An AI agent could handle that for them, but nobody wants to give an agent their wallet.

We wanted to answer one question: can an AI agent pay on someone's behalf without ever being able to overspend? we build TikTato to find our answer. It's a live 1v1 tic-tac-toe arena where the coins come from PayPal and a Claude-powered wallet agent, Tato, manages them inside limits the player sets.

What it does

  • 1v1 skill contests. A player posts an entry fee, another player accepts, and both play live on a real-time board. Both entries are held in escrow until the match ends. The winner takes the pool minus a platform fee, the loser loses their entry, and a draw refunds both players.
  • Coins via PayPal. 100 coins = \$1, with packs from \$5 to \$100. Orders are created and captured entirely on the server.
  • Tato, the AI wallet agent. In plain words, players can check their balance, buy a pack, set a weekly spending limit, or post, accept or cancel a contest. Tato uses 11 strict tools. Anything that moves money outside a mandate needs the player's approval on PayPal or a tap on a Confirm card.
  • Tato Autopilot (the headline feature). The player saves PayPal once (Vault v3) and approves a mandate: a pack size, a "refill below" level, a weekly cap and an expiry date. The agent can then charge the saved PayPal with no redirect, only inside those terms. Pause and revoke take effect immediately.
  • Activity and audit dashboard. Live AG Grid views of every agent action, every ledger row, every PayPal order and every match. An admin view runs a live "ledger balanced" check.
  • Responsible play. Weekly limits are enforced on every top-up. Lowering a limit is instant; raising one takes a 24-hour cool-down. Players must be 18+, and free practice against a minimax AI is always available.

How we built it

  • Backend: plain PHP 8.2 and MySQL on ordinary shared hosting, with no framework and no build step. There are no websockets, so live updates use short polling every 1–2 seconds.
  • PayPal:
    • OAuth2 client credentials.
    • Orders v2: create (the server sets the amount) → buyer approval → capture. PayPal-Request-Id makes both calls idempotent. Before crediting, the server checks the capture status, amount, currency and reference id.
    • Vault v3: a setup token, the buyer's approval, then a payment token. If the redirect is lost, the server finishes the save by reading the setup token's status.
    • Off-session Orders with payment_source.paypal.vault_id for Autopilot charges.
    • Webhooks, signature-verified, as a backup path for crediting payments.
  • AI: Claude Opus 5.5 through the official Anthropic PHP SDK, in a server-side agent loop. The tools use strict schemas, refusals fall back to another model on the server, effort is set to low for quick chat replies, and each player is rate-limited. Every message, tool call and confirmation goes to an agent_log table.
  • Money safety: a double-entry ledger with three accounts (player, escrow and platform). Every row has a unique idempotency key. Debits are atomic: UPDATE users SET coins = coins - ? WHERE id = ? AND coins >= ? Matches settle exactly once, inside a transaction that locks the row.
  • Frontend: vanilla JavaScript and CSS, with Web Animations for the coin effects and a canvas for confetti. AG Grid Community runs under a strict Content Security Policy, using a per-request style nonce.

The fee math

A top-up of $d$ dollars buys $100d$ coins, and the platform keeps 5%:

$$\text{credited} = 100d \cdot (1 - 0.05) = 95d \qquad \text{e.g. } \$10 \rightarrow 950 \text{ coins}$$

In a contest where each player pays an entry $s$, the pool is $2s$. The platform takes 20% of it, and only when someone wins:

$$\text{payout} = 2s - \left\lfloor \frac{20 \cdot 2s}{100} \right\rfloor = 1.6\,s \qquad \text{e.g. } s = 100 \rightarrow 160$$

The ledger must always satisfy:

$$\sum_{\text{rows}} \text{amount} \;=\; \sum_{\text{completed PayPal orders}} \text{coins}_{\text{gross}}$$

$$\sum_{\text{players}} \text{coins} = \sum_{\text{player rows}} \text{amount}, \qquad \sum_{\text{escrow rows}} \text{amount} = \text{open entries} + \text{active pools}$$

The dashboard checks all three live.

Challenges we ran into

  • Letting an agent spend safely. Our first version only let Tato prepare actions. For real agentic commerce we needed a way to pre-authorize spending. The solution was a mandate with server-side guards checked on every charge:
    • the mandate's pack size and weekly cap
    • at most 3 packs a day
    • a 2-minute cool-down, claimed atomically with a conditional UPDATE so two requests can't both charge
    • the player's own weekly limit
    • the expiry date
    • an automatic pause after declined charges
  • Exactly-once money on shared hosting. We used row locks, idempotency keys, lazy timeouts that settle abandoned matches, and lock ordering with deadlock retries.
  • A bug that only showed up live. Some players stopped seeing new contests. A request that arrived while a file was being deployed got an empty page, and the host marked that page cacheable for two days. Browsers then kept serving lobby polls from that cached copy. We fixed it by making every PHP response no-store (even when PHP fails) and making the client fetch with cache: 'no-store'.
  • Session locking. A 10-second Claude call held the player's PHP session lock and froze their game polls. Now each request releases the lock right after checking login and CSRF, and the agent saves its state at the end with a brief re-lock.
  • PayPal sandbox details. Paying with the seller account fails, return redirects get lost, and the checkout page language follows the server's location. We fixed these with English checkout pages, capture by order token, and a status check on pending saved-account approvals.

Accomplishments that we're proud of

  • Real off-session PayPal charges by an AI agent, inside a mandate the player approved, with refusals the player can see: "Again? Refused: 2-minute cool-down".
  • The ledger balances, and the dashboard proves it live.
  • Asking Tato for free coins fails, because no tool can create coins.
  • It's deployed and playable now, on a phone browser.

What we learned

Agentic payments work when the agent has narrow, verifiable tools and the human sets the rules once: mandate = consent, caps = safety, audit = trust. The model never needs to be trusted with money. The server only needs to refuse everything outside the mandate.

What's next

  • Refunds of unused top-ups through the PayPal Refunds API, requested through Tato.
  • An AI "responsible play" coach that spots chasing losses and offers to lower limits.
  • Turning Autopilot into a reusable pattern for any app that sells credits: games, AI tools and creators.
  • Before any real money is involved, PayPal pre-approval and the licences each country requires.

Everything runs in the PayPal sandbox. Coins have no cash value and can't be withdrawn.

Built With

Share this project:

Updates

Submission history