Inspiration

AI agents are starting to act for us: they browse, compare and soon they will pay. That raises a question I couldn't stop thinking about: what stops an agent from spending money it shouldn't? A confident model can misread a request, get stuck in a loop, or follow instructions hidden inside a product listing.

I had already built a payment-recovery agent with a hard retry limit, and it taught me one lesson: the more autonomy an agent gets, the more its limits have to live in code, not in a prompt. This hackathon's theme, putting AI behind the wheel of money movement, was the right place to build that idea properly with PayPal.

What it does

Guarded Checkout is an AI shopping agent that can only spend what your rules allow.

You chat with it ("buy a USB-C charger"). It searches a catalog, fills a cart and submits an order. A deterministic policy engine, plain code and not the LLM, then returns one of three verdicts:

  • Allowed: the order goes to PayPal and you get a "Pay with PayPal" button.
  • Needs your approval: the order waits in the app, and nothing reaches PayPal until you click Approve.
  • Blocked: the order is refused with plain-language reasons, and PayPal is never contacted.

You set the rules in the UI: per-order limit, daily limit, auto-approve threshold, approved merchants and blocked categories. Every decision by the agent, the policy engine, the user and PayPal is recorded in a visible audit log.

How we built it

  • Agent: an explicit LangGraph state machine (agent node, tool node, bounded loop) driving an LLM on Groq (openai/gpt-oss-120b) with tool calling. The tools can only search, add, remove, view and submit. There is deliberately no tool to approve an order or set a price.
  • Policy engine: a pure Python function that takes the cart, the rules and the last 24 hours of spending and returns allow, require approval or deny with reasons. Deny always beats approval, and rules are re-checked at approval time.
  • PayPal: real sandbox payments through Orders v2 (create, buyer approval redirect, capture), with PayPal-Request-Id plus a database unique key so a repeated checkout can never create two orders. Webhooks are verified through PayPal's signature API and deduplicated by event id.
  • Stack: FastAPI, SQLAlchemy, PostgreSQL, a vanilla JavaScript UI, Docker, and Render for hosting.
  • Prices never come from the model. They are looked up server-side from the catalog by SKU, so a prompt-injected price cannot change what is charged.
  • 37 automated tests cover the rules, the order lifecycle, idempotency, webhooks, and the agent end to end using a scripted LLM.

Challenges we ran into

  • A retired model: my first choice of Groq model had been shut down, so chat failed with a generic error. I made the model configurable and added readable error messages.
  • PayPal sandbox currency errors: payments between India-based test accounts failed with "seller doesn't accept payments in your currency." Creating United States sandbox accounts fixed it.
  • An over-cautious agent: it applied a budget from earlier in the chat to a new request and refused things it should have submitted. I rewrote the system prompt so it acts only on the current request and lets the policy engine do the enforcing.
  • Idempotency design: I had to decide what happens to a cart after a denied order, so denied carts stay editable while viable ones are consumed exactly once.

Accomplishments that we're proud of

  • A complete working loop: chat, agent, policy engine, human approval, PayPal order, buyer approval and captured sandbox payment, deployed live.
  • A test where the model deliberately obeys a malicious product listing and still cannot cause a payment, because the policy engine sits outside the model.
  • A full audit trail that makes every automated decision explainable.

What we learned

  • The model should propose, and code should decide. Prompts are good for behaviour, but safety belongs in deterministic code.
  • Idempotency needs to exist at more than one layer (the database and PayPal's request id).
  • Testing agents is practical when the LLM is injectable, because you can script exactly what the model does, including its mistakes.
  • Small details of a payment platform, such as the sandbox account's country, can decide whether a flow works.

What's next for Guarded-Checkout

  • Real authentication and per-user spending rules.
  • A live product-search API in place of the local catalog, with prices still kept server-side.
  • Persistent chat memory and rate limiting.
  • Running verified webhooks against live PayPal events in production, a refund flow, and spending analytics.

Built With

Share this project:

Updates

Submission history