Every payment, checked first. Site: paycheck-guard.vercel.app

AI shoppers paid or handed over card details in 34 of 52 trick runs; with PayCheck, 4 of 51.

Better than Claude Haiku 5.5 for checkout safety.

  • 0 of 150 vs 16 of 150 honest checkouts wrongly blocked.
  • It even catches some trick types it never saw in training.
  • Matches Claude Haiku 5.5 on overall accuracy (F1 0.78 vs 0.77), with a threshold calibrated on our attacker dev set.
  • Cheaper than Claude Haiku 5.5: \$0 vs \$0.22 per 1,000 checks, running on your Mac or phone.
  • 0.7s vs 1.5s on a typical check, on one cloud GPU.

Inspiration

Shopping is about to change hands. Instead of clicking through checkout ourselves, we'll tell an AI agent "get me the cheapest eggs" or "reorder my charger," and it will browse, compare, fill the cart and press Pay for us. Agents like OpenAI's Operator and Browser Use already shop on real sites, and payment companies are building rails and signed identities so agents can pay on their own. We think that's the future of commerce, and we're excited for it.

But an agent that can spend your money can also be tricked into spending it badly, and today's agents fall for the same checkout tricks we do:

  • Asked only to find cheap eggs, OpenAI's Operator went ahead and bought them, and the price it reported wasn't the price it paid (The Washington Post, Feb 7, 2025).
  • On shopping sites seeded with dark patterns, a Browser Use agent usually left a pre-checked paid add-on selected (SusBench, IUI 2026).
  • In an ACL 2026 benchmark, five shopping agents checked out with a padded total 75–100% of the time, even when told to check it (WebDecept).

A person might notice a price that jumped at checkout, a fake "verify your card" page or a copycat payment form. An agent running thousands of errands won't stop and squint. Identity and payment rails tell a merchant who the agent is, but nothing checks what's on the page right before money moves.

How we want to guardrail it

We want every agent payment to pass one fast, independent check first, the way a card transaction passes fraud screening:

  • Check the page before Pay. Right before paying, the agent sends the checkout page and cart to PayCheck, which answers pass or block with a plain reason: the kind of trick, what it saw, and what the agent should do next.
  • Fail closed. A hard gate sits on the pay request itself. If PayCheck says block, or the check is missing, the payment doesn't go through, no matter what the agent decided.
  • Signed when safe. On a pass, the request is signed with our implementation of Visa's Trusted Agent Protocol, so the merchant knows a checked, trusted agent sent it. Tampered or unsigned requests are rejected.
  • Private by design. PayCheck is a small 4B model, so it runs on your own Mac or phone and your card details never have to leave the device.
  • Don't cry wolf. A guard that blocks honest checkouts gets switched off, so we measured that directly: 0 of 150 honest checkouts wrongly blocked, vs 16 of 150 for Claude Haiku 5.5.

What it does

PayCheck sits between a shopping agent and the Pay button.

  1. A Browser Use agent shops and reaches checkout.
  2. Before paying, it sends the page and cart to the guard's /check.
  3. The guard, a fine-tuned Qwen3-4B, returns pass or block, and says why: the kind of trick, what it saw on the page, and a next step for the agent.
  4. A hard gate on the pay request fails closed. On a pass, the request is signed with our implementation of Visa's Trusted Agent Protocol and the mock payment goes through. A clean checkout and an honest disclosed fee went through; a raised price was blocked; a tampered or unsigned request got a 403.

Coach prototype. We also prototyped PayCheck riding along for the whole session: it checks every page as the agent shops and gives go or stop, the reason, and a tip the shopping agent can learn from.

The site shows real recorded guard runs on our demo stores. During judging (12:15–2:05 PM PT), the try-it calls the live guard; outside that window it shows the recorded answer. The live guard is the 8-bit copy of r4 giving its plain answer; the 0.78 F1 is the full-precision r4 with the frozen threshold.

How we built it

  • A gym of practice stores. Practice pages are generated stores whose trick text comes from the ec-darkpattern dataset, built on the 11K-site crawl in Mathur et al., "Dark Patterns at Scale: Findings from a Crawl of 11K Shopping Websites" (Princeton; ACM CSCW 2019). Every trick page has a clean twin, and labels come from the generator, not a model.
  • A locked test. 300 checkout pages, half with a hidden trick, half honest, split off and frozen before training, with leak checks before locking. Each declared model setting, ours included, was graded on it once; Claude Haiku 5.5 ran with a higher token limit.
  • Training on veri.studio. bf16 LoRA on Qwen3-4B-Instruct-2507, as custom jobs with answer-only loss, with a supervisor that resumes or relaunches failed jobs.
  • An attacker round. Round 4 added 300 attacker-written pages to the training set. That's r4.
  • A frozen threshold. The block threshold was set on our attacker dev set and frozen before the locked test was touched, then applied once.
  • Real agents. Browser Use shoppers run the same errands guarded and unguarded; the store's order log decides who paid for a trick. Those are the runs behind the counts at the top.
  • Serving. An 8-bit copy on Modal. On 100 development pages it gave the same verdict as the full-precision model every time.

The math behind a verdict. For a checkout page $x$, the model scores how likely it is to hide a trick, and the gate blocks when that score clears a threshold $\tau$:

$$ \text{verdict}(x) = \begin{cases} \textbf{block}, & p_\theta(\text{block} \mid x) \ge \tau \ \textbf{pass}, & \text{otherwise} \end{cases} $$

$\tau$ was picked on our attacker dev set and frozen before the locked test was opened. We score with F1, which balances catching tricks (recall $R$) against not blocking honest checkouts (precision $P$):

$$ F_1 = \frac{2PR}{P + R}, \qquad P = \frac{TP}{TP + FP}, \qquad R = \frac{TP}{TP + FN} $$

Challenges we ran into

  • Our training wasn't training the decision. Veri's managed SFT put the loss on the whole prompt, so the short answer barely registered. Loss fell to near zero from memorising the prompt, and 0 of 8,156 outputs from earlier fine-tuned adapters used the trained answer format. We moved to custom jobs with answer-only loss and added a quick "did it learn?" format probe before every eval.
  • Our payment gate, not our model. We traced a miss to a format mismatch in our payment gate and fixed it; the model was unchanged.
  • Practice pages didn't look like the test. Early dev pages were about half the test length and scores didn't carry over. Round 3 matched lengths and stores.
  • A generator bug reused trick wording in honest popups, inflating false blocks. Fixed in round 3.
  • Infrastructure. GPU queues, a missing compiler, and repeated platform failures killed jobs mid-run. Frequent checkpoints and an auto-resume supervisor came out of that.
  • Over-blocking. In our live AI shopper test, PayCheck leaned cautious. On trick errands where buying was actually the right call, it stopped 14 of 27 runs before purchase. Without PayCheck, none of its 28 runs were stopped, but 10 paid for the trick. Counting the right call as buying correctly or walking away safely, PayCheck got 33 of 51 trick errands right vs 18 of 52 without it.
  • What still gets through. A checkout total padded by a few dollars with no fee mentioned, and a fee folded into the total on our Visa checkout, both got past the guard.

Accomplishments that we're proud of

  • A small open model guarding real AI shoppers on mock payments, live during judging.
  • Results we can defend: every accuracy number comes from one graded pass on a test frozen before training.

What we learned

  • A falling loss doesn't mean a model learned the task. Check the output format before spending on evals.
  • Practice data has to look like the test: length, stores, layouts.
  • Train twice. A result that shows up on one seed can vanish on the next.

What's next

  • In every agent that spends money. A default check before payment in every agent framework, so no AI shopper pays without one.
  • Inside the payment network. Banks and card networks screen agent payments at authorization, paired with Visa's signed agent identity.
  • A shared scam immune system. PayCheck rides along the whole session coaching the agent, and every new scam caught anywhere trains the next version.
  • On every phone and laptop. Ships on-device in phones, laptops and browsers, so card details never leave the device. No API bill per check and no network round trip, and banks can screen agent payments without sending customer data to an outside AI company.

Built With

Share this project:

Updates

Submission history