Free Setup Guide

Visa-style 1‑tap checkout at home with Ollama + Playwright

Typing the same shipping, card, and billing info is the slowest part of buying online. Visa is wiring payments into ChatGPT so an agent can find an item, confirm the price, and skip the boring form fill. You still approve the pay step, but the grind goes away.

Adam BurgeAdam BurgeNano Flow

You can get most of that flow today with free tools. I used Ollama (a free local LLM runner) with Playwright (a browser automation toolkit) to auto-fill checkout on repeat buys. It runs on your laptop, keeps your profile local, and stops for your approval before the final click.

Here is the plan. Run a small local model with Ollama, point a short Python script at a product page, let it read the title and price, then have Playwright autofill shipping and card fields on whitelisted stores. You confirm the totals, then it takes you to the last step so you can press pay.

Install Ollama first. On macOS or Linux, this one-liner gets you the CLI and background service.

bash
curl -fsSL https://ollama.com/install.sh | sh

Pull a compact model and sanity check the runtime. I used Llama 3.1 8B Instruct for fast answers on a CPU, then swapped to GPU later.

bash
ollama pull llama3.1:8b-instruct
ollama run llama3.1:8b-instruct -q "2+3?"

You should see 5 printed. Next, set up Playwright and a tiny script that opens a product URL, extracts the price, and asks for your OK before filling forms. We will keep it headful so you can watch it work. Stop here if you are just testing. The full guide below covers per-OS setup, a ready script, whitelisting, and test-card flows.


Here is the plan. Run a small local model with Ollama, point a short Python script at a product page, let it read the title and price, then have Playwright autofill shipping and card fields on whitelisted stores. You confirm the totals, then it takes you to the last step so you can press pay.

Install Ollama first. On macOS or Linux, this one-liner gets you the CLI and background service.

bash
curl -fsSL https://ollama.com/install.sh | sh

Pull a compact model and sanity check the runtime. I used Llama 3.1 8B Instruct for fast answers on a CPU, then swapped to GPU later.

bash
ollama pull llama3.1:8b-instruct
ollama run llama3.1:8b-instruct -q "2+3?"

You should see 5 printed. Next, set up Playwright and a tiny script that opens a product URL, extracts the price, and asks for your OK before filling forms. We will keep it headful so you can watch it work. Stop here if you are just testing. The full guide below covers per-OS setup, a ready script, whitelisting, and test-card flows.

Setup

Ollama (a free local LLM runner) handles the reasoning. Playwright (a browser automation toolkit) handles the clicks and form fill.

macOS

bash
# Ollama
brew install ollama
ollama pull llama3.1:8b-instruct

# Python env
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install playwright python-dotenv requests
python -m playwright install chromium

Linux (Ubuntu/Debian)

bash
# Ollama
curl -fsSL https://ollama.com/install.sh | sh
ollama pull llama3.1:8b-instruct

# Python env
sudo apt-get update && sudo apt-get install -y python3-venv python3-pip
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install playwright python-dotenv requests
python -m playwright install chromium
python -m playwright install-deps

Windows 11

bash
# Ollama
winget install Ollama.Ollama
# Reopen terminal after install
ollama pull llama3.1:8b-instruct

# Python env (install Python 3.11+ first)
py -3.11 -m venv .venv
.\.venv\Scripts\activate
pip install --upgrade pip
pip install playwright python-dotenv requests
python -m playwright install chromium

Project files

Create a .env with test data. These are safe Stripe Visa test values for dev checkouts. Do not use real cards.


NAME=Alex Doe
[email protected]
PHONE=555-0101
ADDRESS=123 Pine St
CITY=Austin
STATE=TX
ZIP=73301
CARD_NUMBER=4242424242424242
CARD_EXP=12/30
CARD_CVC=123
ALLOWLIST=store.example.com,checkout.stripe.dev

Create agent_checkout.py:

py
import os, sys, time, json
from urllib.parse import urlparse
import requests
from dotenv import load_dotenv
from playwright.sync_api import sync_playwright

load_dotenv()

MODEL = "llama3.1:8b-instruct"
OLLAMA_URL = "http://127.0.0.1:11434/api/chat"
ALLOWLIST = [d.strip() for d in os.getenv("ALLOWLIST", "").split(",") if d.strip()]

PROFILE = {
"name": os.getenv("NAME", "Alex Doe"),
"email": os.getenv("EMAIL", "[email protected]"),
"phone": os.getenv("PHONE", "555-0101"),
"address": os.getenv("ADDRESS", "123 Pine St"),
"city": os.getenv("CITY", "Austin"),
"state": os.getenv("STATE", "TX"),
"zip": os.getenv("ZIP", "73301"),
"card_number": os.getenv("CARD_NUMBER", "4242424242424242"),
"card_exp": os.getenv("CARD_EXP", "12/30"),
"card_cvc": os.getenv("CARD_CVC", "123"),
}

SELECTORS = [
("input[name=fullName], input[name=name], input#name", "name"),
("input[name=email], input#email", "email"),
("input[name=phone], input#phone", "phone"),
("input[name=address1], input[name=address], input#address1", "address"),
("input[name=city], input#city", "city"),
("select[name=state], input[name=state], input#region", "state"),
("input[name=zip], input[name=postal], input#zip", "zip"),
("input[name=cardnumber], iframe[name^=__privateStripeFrame]", "card_number"),
("input[name=exp-date], input[name=cc-exp]", "card_exp"),
("input[name=cvc], input[name=securityCode]", "card_cvc"),
]


def chat(prompt: str) -> str:
data = {"model": MODEL, "messages": [{"role": "user", "content": prompt}]}
r = requests.post(OLLAMA_URL, json=data, timeout=60)
text = ""
for line in r.iter_lines():
if not line:
continue
obj = json.loads(line)
if obj.get("message", {}).get("content"):
text += obj["message"]["content"]
return text.strip()


def summarize(page) -> str:
title = page.title()
price_el = page.query_selector("[data-test=price], .price, [itemprop=price], [class*=price]")
price = price_el.inner_text().strip() if price_el else ""
prompt = f"Summarize product and price in one line: '{title}' price '{price}'."
return chat(prompt)


def in_allowlist(url: str) -> bool:
host = urlparse(url).hostname or ""
return any(host.endswith(d) for d in ALLOWLIST)


def fill_fields(page):
for css, key in SELECTORS:
el = page.query_selector(css)
if not el:
continue
val = PROFILE.get(key, "")
if not val:
continue
tag = el.evaluate("e => e.tagName.toLowerCase()")
if tag == "select":
try:
el.select_option(label=val)
except Exception:
el.select_option(value=val)
else:
try:
el.fill(val)
except Exception:
# Some gateways use iframes. Skip real card entry unless you trust the domain.
pass


def main():
if len(sys.argv) < 2:
print("Usage: python agent_checkout.py <product_url>")
sys.exit(1)
url = sys.argv[1]
with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=150)
ctx = browser.new_context()
page = ctx.new_page()
page.goto(url, timeout=60000)
page.wait_for_load_state("domcontentloaded")
line = summarize(page)
print(f"Found: {line}")
if not in_allowlist(url):
print("Domain not in allowlist. Add it to ALLOWLIST in .env if you trust it.")
browser.close()
return
ans = input("Approve autofill and proceed to checkout? [y/N] ").strip().lower()
if ans != "y":
browser.close()
return
# Try common add-to-cart and checkout flows
for sel in ["button:add-to-cart", "button[name='add']", "button:has-text('Add to cart')"]:
try:
if page.query_selector(sel):
page.click(sel)
break
except Exception:
pass
for sel in ["a:has-text('Checkout')", "button:has-text('Checkout')", "a[href*='checkout']"]:
try:
if page.query_selector(sel):
page.click(sel)
break
except Exception:
pass
page.wait_for_timeout(1500)
fill_fields(page)
page.screenshot(path="checkout_preview.png", full_page=True)
print("Filled fields where possible. Screenshot saved to checkout_preview.png. Review and click Pay yourself.")
time.sleep(3)
browser.close()

if __name__ == "__main__":
main()

Verify

  • Model sanity:
bash
ollama run llama3.1:8b-instruct -q "what is 7*6?"

Expected: 42.

  • Script dry run on an allowlisted test domain. Add checkout.stripe.dev to ALLOWLIST in .env, then:
bash
python agent_checkout.py https://checkout.stripe.dev/preview

Expected output snippet:


Found: Payment form preview. price ''
Approve autofill and proceed to checkout? [y/N] y
Filled fields where possible. Screenshot saved to checkout_preview.png.

You will see Chromium open, the form populate where selectors match, then pause for you to press the final button.

Configuration tips

  • Domain allowlist: keep ALLOWLIST short. Example ALLOWLIST=shop.example,checkout.stripe.dev. The script refuses to fill on other domains.
  • Headful vs headless: keep headless=False while tuning. Switch to True once stable to speed it up.
  • Selector overrides per site: add CSS selectors that match your store. Put the common ones first, then site-specific ones.
  • Card iframe handling: many gateways use iframes for card fields. Leave those for manual input or extend the script with frame traversal only for trusted domains.
  • Slow mo and waits: increase slow_mo=250 or add page.wait_for_selector(...) on flaky pages.

Troubleshooting

  • Ollama not listening on 11434:
  • - Symptom: connection error to http://127.0.0.1:11434. - Fix: run ollama serve in one terminal, then retry. Or start a model once with ollama run llama3.1:8b-instruct to boot the server.

  • Playwright missing browser or Linux deps:
  • - Symptom: Executable doesn't exist or GLIBC errors. - Fix: run python -m playwright install chromium and on Linux also python -m playwright install-deps.

  • Selectors do not match checkout:
  • - Symptom: fields stay empty. - Fix: open DevTools, inspect the input names, then add new entries to SELECTORS mapping. Keep the value key set to one of name,email,phone,address,city,state,zip,card_number,card_exp,card_cvc.

When it beats ChatGPT + Visa

  • Repeat buys on the same 2 to 3 stores. Your allowlist and selectors stay stable, so the agent fills in seconds.
  • You need local control. Profile and test cards stay on your machine, not in a hosted chat.
  • Stores with simple carts that already work with your browser’s autofill. The agent handles the last 10 percent, like city and ZIP mismatches.
  • Regions where the official ChatGPT payment add-on is not rolled out yet. This runs offline aside from the site you visit.

Sources

  • No external sources provided.

Want a hand?

Book a 30-min call.

Walk through your stack with us. We'll find the bottleneck and map out the exact wiring you need — free.

Book the callFree intro · 30 min · cal.com
Nano Flow

© 2026 Nano Flow. All rights reserved.