Newshot
Account

Show HN: SayStreet – Voice shopping for blind users; the LLM never touches money

Original text(via HN AI)

SayStreet

Accessible online shopping for blind and low-vision people.

Find, compare, buy, track and return in one conversation. Nothing is paid until you say the exact total.

Try the live demo · Devpost · Run your own · Landing page

The live demo needs no sign-up, keys or install. It runs on the PayPal sandbox: no real money moves and nothing ships.

Why

A screen reader announces a button without a label as just "button". Is it Pay or Cancel? On a checkout page, that's a question about your money.

About 7 million people in the US have vision loss that glasses can't correct, and more than 700,000 of them are blind (CDC). The web keeps failing them: in February 2026, 95.9% of the top million home pages had accessibility errors that an automated scan can find. Half had form fields without labels, and almost a third had buttons with no name (WebAIM Million). Screen reader users rank CAPTCHA as the most problematic thing on the web (WebAIM, 2024).

They already turn to AI to get around it: in WebAIM's 2026 survey, 91.8% of screen reader users said they use AI to improve their experience online, and 40.6% use it for guidance with an online task (WebAIM). Shopping is heading the same way for everyone: Morgan Stanley estimates that AI shopping agents could account for $190–385 billion of US e-commerce spending by 2030, 10–20% of online retail (Morgan Stanley). If those agents are built only for sighted shoppers, the same people get left out again.

What SayStreet changes

  • One conversation, every step. Search more than 100 million products, hear three options at a time, ask anything about them, buy, then track or return the order in the same chat.
  • Your words move the money, not the AI. The agent can only suggest a purchase. It's paid when you say the exact total; "yes" never pays.
  • Nothing to fight with. No CAPTCHA, no payment pop-up, and any time limit can be stretched by saying "more time". It's built for VoiceOver, NVDA and the keyboard, or it reads replies aloud itself.
  • PayPal behind every step. A wallet saved once with PayPal pays without leaving the conversation, and tracking numbers and refunds go through PayPal too.

Try it in two minutes

  • Open demo.saystreet.app and choose who reads the replies: your screen reader, or SayStreet's voice. On the Connect PayPal step, press Skip to pay with a shared sandbox buyer, so there's nothing to log in to, or connect your own PayPal sandbox account.
  • Hold Talk (or the Space bar) and say, or type: "Find a whistling kettle under 40 dollars."
  • Ask about the options: "How much is the second one?", "Which one has better reviews?", "Does the first one work on induction?"
  • "Buy option one." SayStreet reads the total, the shipping and the address.
  • Say the total you heard, for example "Pay thirty-four ninety-nine", or press Pay. The receipt shows what PayPal answered.
  • Then: "Where is it?", "How much have I spent this week?", "Return my French press." The demo account comes with four sample orders for this.

Also try "more", "the blue one", "size ten and a half", "buy it again", or paste a product link from a store.

How it works


        flowchart LR
    you(["You<br>voice or keyboard"])
    agent["AI agent<br>OpenAI"]
    channel3["Channel3<br>product catalog"]
    engine["Payment engine<br>plain code, no AI"]
    paypal["PayPal"]

    you -- "ask" --> agent
    agent -- "find, compare" --> channel3
    agent -- "suggest an option" --> engine
    you -- "say the total" --> engine
    engine -- "pay, track, refund" --> paypal
      

The agent finds and explains products, and can only suggest a purchase. The payment engine pays, and only after the person says the exact total; it runs no AI.

ServiceWhat SayStreet uses it forCode
PayPalSaves the wallet once, pays by voice without a pop-up, adds tracking numbers, refunds returnspaypal/
Channel3 (sponsor)The catalog: search across more than 100 million products, product details, the live price right before paying, buying from a pasted linkcatalog/
Render (sponsor)Hosts the live demo and its Postgres database; one-click deploy of your own copyrender.yaml
OpenAIThe agent that searches, answers and suggests ( gpt-6-luna ), and speech to text ( gpt-transcribe )agent/ , speech/

PayPal

All calls go to the PayPal sandbox REST API from the backend (packages/backend/src/paypal):

WhatAPI
Save a PayPal wallet once: the shared demo buyer, or the person's own sandbox accountVault v3: POST /v3/vault/setup-tokens , POST /v3/vault/payment-tokens
Pay with the saved wallet, with no pop-up or redirectOrders v2: POST /v2/checkout/orders with payment_source.paypal.vault_id , then /capture
Ship with a tracking numberOrders v2: POST /v2/checkout/orders/{id}/track
ReturnPayments v2: POST /v2/payments/captures/{id}/refund
Settle pending payments and refundsWebhooks: POST /v1/notifications/verify-webhook-signature , then PAYMENT.CAPTURE.* events

The vault is what makes paying by voice possible: the wallet is connected ahead of time, so paying never leaves the conversation. Anyone can connect their own PayPal sandbox account from the welcome dialog or Settings, and SayStreet then ships to its name and address. A sandbox buyer login for this is in the Devpost submission's testing instructions.

To see PayPal at work, open an order and look under Payment → PayPal details: every call SayStreet made for it and every verified webhook, with the HTTP status, PayPal's ids, the PayPal-Request-Id and the time it took. The receipt in the chat shows PayPal's answers too, and Settings → Developer tools has the payment engine's log: every payment decision and why.

How paying stays safe

The language model never moves money and never states an amount.

  • The price comes from the catalog, never from the model. The model can only propose an option by its number.
  • The person confirms the exact total, by saying it or with a button that names it. "Yes" is never enough, and plain code refuses negations ("I don't want to pay…").
  • The price is checked with the store again right before paying. If it changed, nothing is paid and the person hears the new price.
  • Limits per purchase and per week are checked inside a database lock, so two tabs or a double tap can't pay twice.
  • PayPal calls are idempotent: the purchase id is the PayPal-Request-Id. A timeout leaves the payment pending until PayPal's webhook settles it; it's never retried as a new charge.
  • Every amount the person hears comes from a server template. Model text that names money is dropped before it's spoken.

AI

  • Agent: OpenAI Responses API with function calling (gpt-6-luna). It searches, answers from the product listing, compares, proposes purchases and handles orders through tools. Each turn it gets a fresh picture of the chat from the database, so "the second one" always means the right product.
  • Speech to text: OpenAI gpt-transcribe. Replies are read by the person's screen reader or the browser's own voice, so there's no paid text-to-speech.
  • No model where it isn't needed: paying, cancelling, "repeat", "more" and "more time" run as plain code.

Channel3

SayStreet uses Channel3's POST /v1/search to find products, GET /v1/products/{id} to answer questions and check the live price before paying, POST /v1/lookup to buy from a pasted link, and GET /v1/websites/search to turn "from Target" into a store filter.

Results are made for listening: one option per model with its best offer (the brand's own store first, then a known retailer, then the lowest price), no accessories unless asked for, three at a time. If Channel3 is unreachable, search falls back to recorded results, so the demo keeps working.

Accessibility

Built for VoiceOver, NVDA and the keyboard first: one heading per page that takes focus on navigation, one polite live region, 48 px targets, text contrast of at least 7:1, forced-colors support, no timers without "more time", and no CAPTCHA anywhere.

Limits and what's next

The demo runs on the PayPal sandbox with US addresses and US dollars, one item per purchase, in English. Shipping is a flat $5.00 and delivery is simulated. Hourly and daily limits protect the shared sandbox.

Next: live PayPal with the person's real account; other countries and currencies, since Channel3 already covers 20 markets ("pay thirty-one euros"); several items in one order.

Run it yourself

You don't have to: the live demo is this code. To run your own copy you need a PayPal sandbox app, a Channel3 key and an OpenAI key.

On Render: the button reads render.yaml, creates the web service and its Postgres database, and asks for the keys. Then connect the sandbox buyer (below) and add DEMO_VAULT_ID under the service's Environment.

On your machine with Docker, served at localhost:8080:


        git clone https://github.com/saystreet/demo.git && cd demo
cp .env.example .env    # each variable says where to get it
docker compose --profile app up --build
      

For development with live reload (Node 26, pnpm, at localhost:5173):


        pnpm install
pnpm db:up    # Postgres only, in Docker
pnpm --filter @saystreet/backend exec prisma migrate deploy
pnpm dev
      

Connect the sandbox buyer once, so new accounts can pay without a PayPal login: open /admin/vault?key=<ADMIN_TOKEN> on your server, press "Connect sandbox buyer", log in with a sandbox personal account, then put the DEMO_VAULT_ID it shows into .env (or Render's Environment) and restart. Until then everything else works, and an account that connects its own PayPal sandbox account can already pay.

Checks: pnpm test runs 212 unit tests (amount parsing, ranking, variants, the money guard, return rules) and pnpm typecheck run in CI on every push. pnpm --filter @saystreet/backend smoke [url] runs a real sandbox payment, shipping, a refund, a price change and account isolation against a running server.

PathWhat
packages/backendHono API, Prisma and Postgres, the agent, the payment engine, PayPal and Channel3 clients
packages/frontendReact 19 web app: Vite, React Router, TanStack Query, Tailwind
packages/sharedTypes and money helpers used by both
docs/api.mdThe API contract between frontend and backend

How we built it

SayStreet was built for the PayPal AI Hackathon, starting from an empty repository; the commit history is the full record. The live demo runs on Render from the same render.yaml; the landing page has its own repository.

We wrote the code with Claude Code and sponsors' own agent skills: Channel3's channel3-api for the catalog and Render's render-deploy for the deployment, along with skills for accessibility, Hono, Prisma and Vite. They're pinned in skills-lock.json.

License

The text above was extracted automatically from the source page and may miss images, tables or code blocks. The original is authoritative.

Published by HN AI
This site only summarises and aggregates; copyright stays with the original author. Headlines and summaries follow the source — where they differ, the original wins.
Read the original ↗