Partner API
HTTP /api/v1 — 菜单、结账、ETA 报价、创建和读取订单。契约来自 OpenAPI 3.1。
Partner API v1 是 https://delieta.pl/api/v1 下的 HTTP 表面。Delieta 签发给一家公司的 API 钥匙决定租户。已发布操作为 GET /menu、POST /checkout、POST /quotes、POST /orders 和 GET /orders/{id}。机器描述:GET /api/v1/openapi(OpenAPI 3.1,无需钥匙)。
flowchart LR
bearer["Authorization Bearer"] --> verify["钥匙就是公司"]
verify --> scope{"scope"}
scope -->|"catalog:read"| menu["GET /menu"]
scope -->|"checkout:create"| checkout["POST /checkout"]
scope -->|"orders:write"| handoff["POST /orders"]
handoff --> same["和面板同一张订单"]钥匙与范围
Header:
Authorization: Bearer dlt_live_…
缺少或错误钥匙 → 401 unauthorized。
| Scope | 操作 |
|---|---|
catalog:read | GET /api/v1/menu (可发布)。menu:read 是旧钥匙别名。 |
checkout:create | POST /api/v1/checkout (可发布)。除 catalog:read + checkout:create 以外没有。 |
eta:quote | POST /api/v1/quotes (秘密) |
orders:write | POST /api/v1/orders and GET /api/v1/orders/{id} (秘密) |
空 scope 列表什么也不开 — 403 scope_required。可发布钥匙 不能 拿到 orders:write。秘密钥匙不会从静态店调用结账。
dlt_test_… 真实地读和报价。POST /api/v1/orders 拒绝 403 test_key_readonly。没有单独测试数据集:测试读公司的生产目录。
错误
每个 v1 拒绝都是 application/problem+json。按 code 分支。
{
"type": "https://delieta.pl/problems/unauthorized",
"title": "Unauthorized",
"status": 401,
"code": "unauthorized",
"detail": "Provide a valid API key as `Authorization: Bearer dlt_live_…`."
}
OpenAPI 记载、路由使用的代码:
| code | status | 何时 |
|---|---|---|
unauthorized | 401 | 缺少或错误钥匙 |
scope_required | 403 | 钥匙没有所需 scope |
test_key_readonly | 403 | dlt_test_… 试图写入 |
invalid_request | 400 | body 不是 JSON 或校验失败;细节在 errors[] |
idempotency_key_required | 400 | 创建订单没有 Idempotency-Key(8–255 字符) |
idempotency_key_conflict | 409 | 同一幂等钥匙,不同 body |
rate_limited | 429 | 每分钟配额;遵守 Retry-After 以及 RateLimit-Limit / RateLimit-Remaining / RateLimit-Reset |
not_found | 404 | GET /orders/{id} — 对这把钥匙没有这张订单(别人的 id 回答相同) |
tenant_not_found | 404 | 钥匙的公司已不存在 |
venue_not_configured | 409 | 报价,而公司没有地点坐标 |
shop_closed | 409 | 已设营业时间且店现在关门 |
shop_unconfigured | 409 | 没有为 Stripe 返回 URL 保存的店铺 origin |
connect_not_ready | 409 | Connect 账户还不能收款 |
duplicate_external_ref | 409 | 这个 externalRef 已经为这家公司创建过订单 |
line_price_missing / line_name_missing / total_out_of_range | 400 | 一行或合计无法计价 |
idempotency_unavailable | 503 | 幂等存储宕了;用同一把钥匙重试 |
限额按钥匙,60 秒窗口。写入(创建订单、报价)和读取(菜单、GET 订单)有分开的配额,免得轮询吃掉整个晚上。
钱
只有整 grosz。3000 是 30.00 兹罗提。没有浮点,金额上没有货币字段。响应里的 currency 是常量 "PLN"。
GET /api/v1/menu
钥匙所属公司的目录。Scope catalog:read。不可用条目被省略 — DTO 没有给客户端遵守的 available 标志。形状冻结。
GET /api/v1/menu
Authorization: Bearer dlt_live_…
{
"currency": "PLN",
"generatedAt": "2026-08-26T12:00:00.000Z",
"categories": [
{ "id": "c1", "slug": "pizza", "name": "Pizza", "sort": 0 }
],
"items": [
{
"id": "i1",
"slug": "margherita",
"categoryId": "c1",
"name": "Margherita",
"description": null,
"basePriceGrosze": 3200,
"allergens": [],
"modifierGroups": [],
"sort": 0
}
]
}
空的分类和条目数组是有效响应,不是错误。Cache-Control: no-store。
POST /api/v1/checkout
店铺购物车。Scope checkout:create(可发布钥匙)。顾客发送是什么,从不发送多少钱:{ items: [{ itemId, quantity }] }。价格、附加费和合计由 Delieta 按钥匙所属公司目录、以整 grosz PLN 计算。body 里的 unitPrice / total / companyId 被忽略。成功和取消 URL 来自租户上的店铺配置,从不来自请求。
POST /api/v1/checkout
Authorization: Bearer dlt_live_…
Content-Type: application/json
Idempotency-Key: chk-2026-08-26-42
{ "items": [{ "itemId": "i1", "quantity": 2 }] }
201
{
"url": "https://checkout.stripe.com/c/pay/cs_test_…",
"totalGrosze": 6400,
"currency": "PLN",
"pricingSource": "catalog"
}
把客人重定向到 url。未知或不可用条目 → 422 unprocessable。关门 → 409 shop_closed。缺少店铺 origin → 409 shop_unconfigured。Connect 账户未就绪 → 409 connect_not_ready(没有假支付)。
POST /api/v1/quotes
订单存在之前的路程估计。Scope eta:quote。起点是公司地点;你只发送目的地。结果永远是区间(minMinutes / maxMinutes)。guaranteed 字段是常量 false — 这不是 SLA。expiresAt 之后再问。
{ "destination": { "lat": 53.123456, "lng": 18.012345 } }
{
"quote": {
"currency": "PLN",
"minMinutes": 35,
"maxMinutes": 50,
"guaranteed": false,
"breakdown": {
"prepMinutes": 15,
"loadMinutes": 4,
"travelMinutes": 12,
"padMinutes": 8
},
"distanceMeters": 4200,
"computedAt": "2026-08-26T12:00:00.000Z",
"expiresAt": "2026-08-26T12:05:00.000Z"
}
}
两端都展示。合作方 UI 里一个数字是对契约撒谎。缺少地点坐标 → 409 venue_not_configured。坏 body → 400 invalid_request。
POST /api/v1/orders
为钥匙所属公司创建订单。Scope orders:write。服务器加总各行并拒绝你送来的合计。今天单价可以来自调用方(pricingSource: "caller");当计价来自目录时,响应会说 "catalog"。
Idempotency-Key
必填头,trim 后 8–255 字符。每个逻辑尝试一把钥匙。网络错误或 503 idempotency_unavailable 后用同一钥匙和同一 body 重试。成功的 201 被冻结:重复返回同一信封和 Idempotent-Replayed: true。同一钥匙、不同 body → 409 idempotency_key_conflict。
Body
没有租户字段。带调用方计价的典型调用:
{
"customerName": "Jan Kowalski",
"customerPhone": "+48123456789",
"destinationAddress": "ul. Przykładowa 1, 85-000 Bydgoszcz",
"destinationPoint": { "lat": 53.123456, "lng": 18.012345 },
"items": [
{ "name": "Margherita 32cm", "qty": 1, "unitPrice": 3200 },
{ "name": "Cola 0,5l", "qty": 2, "unitPrice": 800 }
],
"paymentMethod": "cash",
"channel": "web",
"externalRef": "your-storefront-order-42"
}
| 字段 | 必填 | 备注 |
|---|---|---|
customerName | 是 | 1–200 字符 |
customerPhone | 是 | 1–40 字符 |
destinationAddress | 是 | 1–500 字符 |
destinationPoint | 否 | { lat, lng } WGS84 |
items | 是 | 1–50 行;qty 1–99 |
items[].name / items[].unitPrice | with caller pricing | grosz;服务器重算 totalGrosze |
items[].menuItemId | 否 | 从菜单计价时的目录 id |
paymentMethod | 否 | cash | card | online (默认 cash) |
channel | 否 | phone | web | pos | integration (默认 web) |
externalRef | 否 | 你这边去重;冲突 → duplicate_external_ref |
POST /api/v1/orders
Authorization: Bearer dlt_live_…
Content-Type: application/json
Idempotency-Key: ord-2026-08-26-storefront-42
201
{
"order": {
"id": "1",
"shortId": "a1b2c3d4",
"status": "accepted",
"createdAt": "2026-08-26T12:00:00.000Z",
"tenantSlug": "bella-pizza",
"externalRef": "your-storefront-order-42",
"currency": "PLN",
"totalGrosze": 4800,
"lines": [
{ "name": "Margherita 32cm", "qty": 1, "unitPrice": 3200 },
{ "name": "Cola 0,5l", "qty": 2, "unitPrice": 800 }
],
"pricingSource": "caller",
"trackingUrl": "https://delieta.pl/o/a1b2c3d4"
}
}
status 是 accepted 或 pending_acceptance,取决于公司接受模式。trackingUrl 是你给顾客的。那是唯一的公开跟踪 URL。
GET /api/v1/orders/
读取钥匙所属公司拥有的一张订单。scope 相同:orders:write — scope 集合已关闭并已发到活钥匙上;新的 orders:read 会弄坏现有集成。轮询花的是读取配额,不是创建。
id 是 201 响应里的 order.id。别人的或不存在的 id → 404 not_found,没有存在神谕。
响应比创建信封更窄。合作方拿到生命周期,不是内部列:
{
"order": {
"id": "1",
"shortId": "a1b2c3d4",
"status": "pending_acceptance",
"externalRef": "your-storefront-order-42",
"createdAt": "2026-08-26T12:00:00.000Z",
"promisedAt": null,
"acceptedAt": null,
"rejectedAt": null,
"rejectionReason": null,
"acceptanceDeadlineAt": 1756200000000
}
}
acceptedAt、rejectedAt 和 acceptanceDeadlineAt 是 unix 毫秒或 null。你会看到的状态:pending_acceptance、accepted、preparing、on_the_way、delivered、rejected。
OpenAPI
GET /api/v1/openapi
公开 OpenAPI 3.1 文档。不想手写 curl 时从它生成客户端。本页与它保持一致 — 若漂移,OpenAPI 和仓库测试赢。
没有 webhook 操作。你在这里用 GET 读状态。见 Webhook。

