zekear/justo-mx-api
GitHub: zekear/justo-mx-api
面向 AI agent 的 Python CLI 工具,通过逆向工程 Jüsto 超市的内部 API 实现对话式自动下单购物。
Stars: 0 | Forks: 0
🛒🤖 justo-mx-api
赋予你的 AI agent 在 Jüsto(墨西哥的在线超市)购买杂货的能力。
这是一个命令行工具,旨在让助手(WhatsApp、Telegram 等)
搜索产品、组装购物车并生成真实订单 —— 全程通过对话和 API 完成。
## 🤖 它有什么用?
想象一下,通过 WhatsApp 对你的助手说:*"嘿,帮我在 Jüsto 订点香蕉、牛奶和 3 瓶可乐"*,然后它就
自己去做了:搜索产品、组装购物车、选择送货时间、向你展示总价,并且 —— 当你
同意后 —— **生成真实的订单**(货到付款)。
这个工具就是那只“手”:一个带有 **JSON 输出**和**单行命令**的简单 CLI,
专为让 AI agent(OpenClaw、自定义 bot 或其他任何工具)替你执行而设计。它解决了
最困难的部分 —— Jüsto 的 checkout API 并不简单(详见 [🔍 内部 API 原理](#-la-api-por-dentro))。
### 专为 Agent 设计
- 🧾 所有命令均提供 **JSON 输出** → 方便 LLM 进行解析。
- 📏 **单行命令**(使用逗号分隔条目) → 在拒绝多行的 runtime 中不会中断。
- ⏳ **后台模式** (`order-bg` / `order-wait`) → 在远程节点上执行时,可避开较短的 RPC 超时限制。
- 🔒 **将“组装”与“购买”分离**:`order` 会将购物车保持为 `STAGED` 状态(不产生花费);只有 `confirm` 才会真正生成订单 → agent 可以在付款前**请求人工确认**。
- 🌎 **阿根廷到墨西哥的别名映射** (`palta`→`aguacate`, `choclo`→`elote`…) → 支持任何方言的列表。
👉 **可直接使用的 skill 示例:** [`examples/openclaw-skill.md`](examples/openclaw-skill.md)
(一个内置了确认机制的 OpenClaw `SKILL.md`)。
## 📦 安装
Python 3,**仅使用标准库** —— 无其他依赖。
```
git clone https://github.com/zekear/justo-mx-api
cd justo-mx-api
chmod +x justo
ln -s "$PWD/justo" ~/.local/bin/justo # opcional: ponelo en el PATH
```
## ⚙️ 配置
通过环境变量提供凭据:
```
export JUSTO_EMAIL="vos@ejemplo.com"
export JUSTO_PASSWORD="tu-password"
export JUSTO_ZIP="11590" # tu código postal de entrega (default 11590)
```
…或者将 `JUSTO_CREDENTIALS` 指向一个 `.env` 风格的文件(参见 [`.env.example`](.env.example))。
**切勿将你的真实凭据上传到代码库中。**
**送货地址**将从你账户的默认地址中获取(你可以在 Jüsto app 中进行管理)。**付款方式为货到付款** (`MANUAL`):不会扣除任何费用,也不会对任何信用卡进行 token化。
## 🚀 用法
```
# 搜索产品 + 价格
justo search "plátano"
# 组装一个 REAL 购物车(出现在你的 Jüsto app 中),包含 slot + contra-entrega — 尚未购买
justo order "Plátano x6, Manzana Roja x10, Leche 1L x4, Pan Bimbo x2, Aguacate Hass x4"
```
`order` 返回一个 `STAGED` 摘要供你查看:
```
{
"status": "STAGED",
"checkout_token": "…",
"total": 710.86,
"currency": "MXN",
"address": "Casa",
"slot": "Recomendado — Mañana, 11:00AM - 01:00PM",
"slot_options": [
{"n": 1, "label": "Recomendado — Mañana, 11:00AM - 01:00PM"},
{"n": 2, "label": "Entrega gratis — Miércoles, 07:00AM - 09:00AM"}
],
"items": [ … ]
}
```
```
# 更改送货时间(使用 slot_options 的 n)
justo set-slot
2
# 生成真实订单(contra entrega)— 之后可以从 Jüsto app 中取消
justo confirm
# -> {"status":"ORDER_PLACED","order_number":"N0111…","total":710.86, …}
```
### 数量与匹配
- 每个条目的数量后缀:`x4`、`×4` 或 `:4`。如果没有后缀,则默认为 1。
- 将条目放在**同一个参数中**并用逗号分隔。
- 匹配采用模糊匹配(匹配名称中的完整单词;否则返回第一个带有价格的结果)。当需要精确匹配特定商品时,请使用具体的词汇(品牌/尺寸)。
### 后台模式(用于远程 agent)
```
job=$(justo order-bg "…lista larga…" | jq -r .job)
justo order-wait "$job" # consultá hasta que esté el resumen STAGED
```
## 🔍 内部 API 原理
Jüsto 的 checkout **并不是标准的 Saleor 流程**:它拥有自定义的 **cart-service** 和 **checkout-bff**。Saleor 公开的 `checkoutComplete` 对**墨西哥地区受限**
(`Creation order for country MX not allowed`),因此直接针对 Saleor 组装的购物车永远无法完成结算。真实的流程是通过阅读网站上的 Next.js bundle 逆向工程出来的。
涉及两个 host,它们均位于 Cloudflare 之后:
| Host | 角色 |
|------|-----|
| `api.justo.mx/graphql/` | Saleor GraphQL — **产品搜索** + 登录 (`tokenCreate`) |
| `api.justo.cloud` | **cart-service** — 创建购物车、添加条目、合并 |
| `client-api-gateway.justo.mx` | checkout 状态、送货时间、付款、**完成订单** |
所有经过身份验证的调用都会发送:`Authorization: JWT `,以及
`x-justo-country: MX`, `x-zip-code: `, `x-justo-platform: web-desktop`, `x-origin: cart_view`。
完整流程:
1. **登录** — Saleor `tokenCreate(email, password)` → JWT。`me.id` (base64 `User:`) → 数字格式的 `userId`。
2. **创建购物车** — `POST api.justo.cloud/cart-service/v3/cart` body `{userId, postalCode, items:[]}` → `{cartId}`。
3. **添加条目** — `POST api.justo.cloud/cart-service/v3/cart/{cartId}/items` body
`{items:[{id:"", quantity}]}`。`productId` 是 Saleor 中该产品的数字 ID
(将节点的 `id` 进行 base64 解码 → `Product:`)。**`id` 必须作为字符串发送。**
4. **合并** — `POST api.justo.cloud/cart-service/v2/cart/{cartId}/consolidate?userId={userId}`
→ `{context:{checkoutId (base64), checkoutToken (uuid), postalCode}}`.
*(注意是 **v2** 并且在 `api.justo.cloud` —— v3 或 gateway 会返回 404/500。)*
5. **Checkout 状态** — `POST client-api-gateway.justo.mx/v2/checkout` body `{checkoutId, zipCode}`
→ UI 组件:地址(账户默认地址)、送货时间 (`slots-v2`,每一个都包含
`context:{shippingMethodId (base64), deliveryDate}`)、支付方式、总计。
6. **设置送货时间** — `POST client-api-gateway.justo.mx/v2/checkout/slots` body
`{checkoutId, shippingMethodId, deliveryDate}`。
7. **设置支付方式** — `POST client-api-gateway.justo.mx/v2/checkout/paymentMethods` body
`{checkoutId, gateway:"MANUAL"}` (货到付款)。**必须设置**:如果没有支付方式,完成订单的操作将被拒绝。
8. **完成** — `POST client-api-gateway.justo.mx/v1/checkout/complete` body
`{cartId, checkoutId, zipcode, additionalData:{}}`.
响应**总是**显示一个“已完成”的弹窗 —— **真实的结果在 `tracking` 中**:
如果成功则为 `orderCompleted`(包含 `order_number` 和 `order_id`),如果失败则为 `orderRejected {reason}`
(`reason: 10` = 缺少支付方式)。
## 🤝 贡献
欢迎提交 Issues 和 PR。如果 Jüsto 的 API 发生变化,可以通过阅读
`https://justo.mx/_next/static/chunks/` 的 chunks 来重新验证 endpoint(`_buildManifest.js` 会映射 routes → chunks)。
## 📄 许可证
[MIT](LICENSE)标签:API集成, Homebrew安装, Python, 人工智能代理, 可观测性, 数字取证, 文档结构分析, 无后门, 电子商务, 自动化脚本, 逆向工具