SayStreet
为盲人和低视力人群提供的无障碍在线购物。
在一次对话中完成查找、比较、购买、跟踪和退货。在你亲口说出确切总额之前,不会支付任何款项。
试用在线演示 · Devpost · 自行部署 · 落地页
在线演示无需注册、密钥或安装。它运行在 PayPal 沙箱上:不会动用真实资金,也不会发货。
为什么
屏幕阅读器会把没有标签的按钮只读成“按钮”。它是“付款”还是“取消”?在结账页面上,这关乎你的钱。
美国约有 7 million 人存在戴眼镜也无法矫正的视力丧失,其中超过 700,000 人全盲(CDC)。网络仍然让他们失望:2026 年 2 月,排名前一百万的主页中有 95.9% 存在自动化扫描能发现的无障碍错误。一半有缺少标签的表单字段,近三分之一有缺少名称的按钮(WebAIM Million)。屏幕阅读器用户将 CAPTCHA 列为网络上问题最大的东西(WebAIM,2024)。
他们已经在借助 AI 绕过这些障碍:在 WebAIM 2026 年的调查中,91.8% 的屏幕阅读器用户表示他们会使用 AI 来改善在线体验,40.6% 会用 AI 指导完成在线任务(WebAIM)。购物对所有人来说也正朝同一方向发展:Morgan Stanley 估计,到 2030 年,AI 购物智能体可能占美国电商支出的 $190–385 billion,即在线零售的 10–20%(Morgan Stanley)。如果这些智能体只为视力正常的购物者构建,那么同样的一群人就会再次被落下。
SayStreet 改变了什么
- 一次对话,覆盖每一步。搜索超过 1 亿件商品,一次听三个选项,就这些选项提出任何问题,购买,然后在同一个聊天中跟踪或退回订单。
- 动钱的是你的话,不是 AI。智能体只能建议购买。只有你说出确切总额时才会付款;“是”永远不触发付款。
- 没有什么要对抗的。没有 CAPTCHA,没有支付弹窗,任何时间限制都可以通过说“更多时间”来延长。它为 VoiceOver、NVDA 和键盘而构建,或者它自己把回复读出来。
- 每一步背后都是 PayPal。用 PayPal 保存一次钱包,就能不离开对话完成支付,跟踪号和退款也都通过 PayPal。
两分钟试用
- 打开 demo.saystreet.app,选择由谁来读回复:你的屏幕阅读器,还是 SayStreet 的语音。在“连接 PayPal”步骤,按“跳过”以共享沙箱买家付款,这样无需登录任何账户,或者连接你自己的 PayPal 沙箱账户。
- 按住“说话”(或空格键)并说出,或输入:“找一把 40 美元以下的鸣笛水壶。”
- 询问这些选项:“第二个多少钱?”,“哪个评价更好?”,“第一个能在电磁炉上用吗?”
- “买第一个选项。”SayStreet 会读出总额、运费和地址。
- 说出你听到的总额,例如“支付三十四点九九”,或按“支付”。收据会显示 PayPal 的答复。
- 然后:“它在哪?”,“我这周花了多少?”,“退回我的法压壶。”演示账户为此附带四个示例
文章标题:Show HN: SayStreet – 面向盲人用户的语音购物;LLM 从不碰钱
正文:
| 服务 | SayStreet 使用它的用途 | 代码 |
|---|---|---|
| PayPal | 一次性保存钱包,通过语音付款且不弹出窗口,添加追踪号,处理退货退款 | paypal/ |
| Channel3(赞助商) | 商品目录:在超过 1 亿件商品中搜索、商品详情、付款前的实时价格、从粘贴的链接购买 | catalog/ |
| Render(赞助商) | 托管在线演示及其 Postgres 数据库;一键部署你自己的副本 | render.yaml |
| OpenAI | 负责搜索、回答和建议的智能体( gpt-6-luna ),以及语音转文字( gpt-transcribe ) | agent/ , speech/ |
PayPal
所有调用都从后端发往 PayPal 沙盒 REST API(packages/backend/src/paypal):
| 用途 | API |
|---|---|
| 一次性保存 PayPal 钱包:共享演示买家,或用户自己的沙盒账户 | Vault v3: POST /v3/vault/setup-tokens , POST /v3/vault/payment-tokens |
| 用已保存的钱包付款,无弹窗或跳转 | Orders v2: POST /v2/checkout/orders 搭配 payment_source.paypal.vault_id ,然后 /capture |
| 用追踪号发货 | Orders v2: POST /v2/checkout/orders/{id}/track |
| 退货 | Payments v2: POST /v2/payments/captures/{id}/refund |
| 结算待处理的付款和退款 | Webhooks: POST /v1/notifications/verify-webhook-signature ,然后处理 PAYMENT.CAPTURE.* 事件 |
正是 vault 让语音付款成为可能:钱包事先已连接好,因此付款永远不会离开对话。任何人都可以在欢迎对话框或 Settings 中连接自己的 PayPal 沙盒账户,SayStreet 随后会按其姓名和地址发货。用于此目的的沙盒买家登录信息见 Devpost 提交中的测试说明。
要查看 PayPal 的实际运行,打开一个订单并查看 Payment → PayPal details:SayStreet 为其发起的每一次调用和每一个已验证的 webhook,都包含 HTTP 状态、PayPal 的 id、PayPal-Request-Id 以及耗时。聊天中的收据也会显示 PayPal 的应答,而 Settings → Developer tools 中有支付引擎的日志:每一次支付决策及其原因。
付款如何保持安全
语言模型从不移动资金,也从不报出金额。
- 价格来自商品目录,从不来自模型。模型只能按编号提出某个选项。
- 用户需确认准确的总价,可以自己说出来,也可以按下一个标明总价的按钮。"Yes" 永远不够,而且普通代码会拒绝否定说法("I don't want to pay…")。
- 付款前会再次与商店核对价格。如果价格变了,则不付款,用户会听到新价格。
- 每笔和每周的限额在数据库锁内检查,因此两个标签页或双击都无法重复付款。
- PayPal 调用是幂等的:购买 id 就是 PayPal-Request-Id。超时会让付款保持待处理状态,直到 PayPal 的 webhook 结算它;绝不会作为新扣款重试。
- 用户听到的每一个金额都来自服务器模板。提到金钱的模型文本会在朗读前被丢弃。
AI
- 智能体:OpenAI Responses API 配合函数调用( gpt-6-luna )。它通过工具进行搜索、根据商品列表作答、比较、提出购买建议并处理订单。每一轮它都会从数据库获得聊天的最新视图,因此"第二个"始终指正确的商品。
- 语音转文字:OpenAI gpt-transcribe 。回复由用户的屏幕阅读器或浏览器自带的语音朗读,因此无需付费的文本转语音。
- 无需模型的地方不用模型:付款、取消、"repeat"、"more" 和 "more time" 都由普通代码执行。
Channel3
SayStreet 使用 Channel3 的 POST /v1/search 查找商品,GET /v1/products/{id} 回答问题并在付款前核对实时价格,POST /v1/lookup 从粘贴的链接购买,GET /v1/websites/search 把 "from Target" 变成商店筛选条件。
文章标题:Show HN: SayStreet——为盲人用户打造的语音购物;LLM 全程不接触资金
正文: 搜索结果是专为聆听设计的:每个型号只给一个选项,附带其最佳报价(先是品牌自己的商店,然后是知名零售商,最后是最低价格),除非用户主动要求,否则不列出配件,每次播报三个。如果 Channel3 无法访问,搜索会回退到预先录制的结果,因此演示仍能正常使用。
无障碍
优先适配 VoiceOver、NVDA 和键盘操作:每页只有一个标题,导航时会获得焦点;只有一个 polite 实时区域;48 px 的点击目标;文本对比度至少 7:1;支持强制颜色模式;没有超时倒计时,只有“更多时间”选项;任何地方都没有 CAPTCHA。
限制与后续计划
演示运行在 PayPal 沙盒上,使用美国地址和美元,每次购买限一件商品,语言为英语。运费统一为 $5.00,配送为模拟。每小时和每日限额保护共享沙盒。
下一步:接入真实 PayPal 和用户的真实账户;支持其他国家和货币,因为 Channel3 已覆盖 20 个市场(“支付三十一欧元”);支持一个订单多件商品。
自行运行
你并非必须这么做:在线演示就是这份代码。要运行自己的副本,你需要一个 PayPal 沙盒应用、一个 Channel3 密钥和一个 OpenAI 密钥。
在 Render 上:按钮会读取 render.yaml,创建 Web 服务及其 Postgres 数据库,并询问密钥。然后连接沙盒买家(见下文),并在服务的 Environment 下添加 DEMO_VAULT_ID。
在本机使用 Docker,服务地址为 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
开发模式下支持热重载(Node 26、pnpm,地址 localhost:5173):
pnpm install
pnpm db:up # Postgres only, in Docker
pnpm --filter @saystreet/backend exec prisma migrate deploy
pnpm dev
一次性连接沙盒买家,这样新账户无需 PayPal 登录即可付款:在你的服务器上打开 /admin/vault?key=<ADMIN_TOKEN>,点击“Connect sandbox buyer”,用沙盒个人账户登录,然后把显示的 DEMO_VAULT_ID 填入 .env(或 Render 的 Environment)并重启。在此之前其他功能都正常,而连接了自己 PayPal 沙盒账户的账户已经可以付款。
检查:pnpm test 运行 212 个单元测试(金额解析、排序、变体、资金防护、退货规则),pnpm typecheck 在每次推送时于 CI 中运行。pnpm --filter @saystreet/backend smoke [url] 会针对运行中的服务器执行一次真实的沙盒支付、发货、退款、价格变更和账户隔离测试。
| Path | What |
|---|---|
| packages/backend | Hono API、Prisma 和 Postgres、agent、支付引擎、PayPal 和 Channel3 客户端 |
| packages/frontend | React 19 Web 应用:Vite、React Router、TanStack Query、Tailwind |
| packages/shared | 前后端共用的类型和金额辅助函数 |
| docs/api.md | 前端与后端之间的 API 契约 |
我们如何构建
SayStreet 是为 PayPal AI Hackathon 从空仓库开始构建的;提交历史就是完整记录。在线演示通过同一个 render.yaml 运行在 Render 上;落地页有独立的仓库。
我们使用 Claude Code 和赞助商提供的 agent 技能编写代码:Channel3 的 channel3-api 用于商品目录,Render 的 render-deploy 用于部署,此外还有用于无障碍、Hono、Prisma 和 Vite 的技能。它们都固定在 skills-lock.json 中。