Files
atlas-seller-kit/docs/api/stores-and-platforms.md
T

186 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 平台上下文与店铺管理 API
## 1. 平台目录与页头上下文
`GET /api/platforms`
返回当前可选择、已启用的平台及客户端所需的非敏感能力:
```json
{
"data": [{
"id": "uuid",
"code": "OZON",
"name": "Ozon",
"supported_currencies": ["CNY", "RUB"],
"default_currency_code": "CNY",
"default_market_code": "RU",
"default_locale": "ru-RU"
}]
}
```
`GET /api/platform-context`
要求 `X-Atlas-Platform-Id`,返回平台摘要、该工作空间在此平台的默认店铺摘要和店铺数。没有默认店铺不是错误:`default_store` 为 `null`,编辑/发布类接口会另外要求可用店铺。
## 2. 店铺资源形状
```json
{
"id": "uuid",
"platform_id": "uuid",
"name": "Ozon 俄罗斯店",
"platform_client_id": "123456",
"currency_code": "RUB",
"is_default": true,
"note": "主店",
"status": "active",
"credential_status": "valid",
"credential_checked_at": "2026-09-02T08:00:00Z",
"last_error": null,
"api_key_expires_at": null,
"product_limit": null,
"product_limit_checked_at": null,
"warehouse_count": 3,
"warehouses_synced_at": "2026-09-02T08:01:00Z",
"cookie_status": null,
"watermark_template": { "id": "uuid", "name": "右下角品牌" },
"created_at": "2026-09-02T08:00:00Z",
"updated_at": "2026-09-02T08:00:00Z",
"revision": 1
}
```
`api_key_expires_at` 和 `product_limit` 无平台事实来源时为 `null`,前端显示 `—`;不能估算。首期 `cookie_status` 固定为 `null`,不暗示 Cookie 可用于正式发布。响应绝不包含 `api_key`、密文、指纹或仓库原始平台 payload。
## 3. 查询店铺
`GET /api/stores?platform_id={uuid}&cursor={opaque}&limit=20`
`platform_id` 必填,按 `created_at DESC, id DESC` 返回当前工作空间未删除店铺。该管理列表可使用查询参数,不能同时传平台 header。
`GET /api/stores/{store_id}`
返回店铺资源。资源不属于当前工作空间或已删除时返回 `404`。
## 4. 添加店铺授权
`POST /api/stores`
Headers:`Idempotency-Key` 必填。
```json
{
"platform_id": "uuid",
"name": "Ozon 俄罗斯店",
"client_id": "123456",
"api_key": "secret-only-in-request",
"currency_code": "RUB",
"is_default": true,
"note": "主店"
}
```
校验 `currency_code` 是否为目标平台可用币种。服务端立即把 API Key 信封加密保存、创建 `status=verifying` 的店铺,并投递低风险凭据验证操作;不得将 Key 放入任务 payload、日志或响应。仓库同步是独立的后续操作,不随创建自动触发。
返回 `202 Accepted`:
```json
{
"data": {
"store": { "id": "uuid", "status": "verifying", "credential_status": "unknown" },
"operation": { "id": "uuid", "kind": "store_credential_verification", "status": "queued" }
}
}
```
首家未删除店铺强制为默认。对已有店铺提交 `is_default=true` 时,服务端在同一事务取消旧默认店铺;校验失败不把店铺置为 `active`,失败原因以可展示的脱敏 `last_error` 摘要(`code`、`message`、`at`)返回给后续读取接口。
### 4.1 Ozon 凭据验证与前端展示
Worker 只调用一个无写副作用的 Ozon Seller API 探针:
```http
POST /v1/description-category/tree
Client-Id: <store platform_client_id>
Api-Key: <decrypted secret, worker memory only>
Content-Type: application/json
{ "language": "ZH_HANS" }
```
该调用只用于验证 Ozon 是否接受当前 Client ID 与 API Key,不创建、修改或同步任何 Ozon 数据。它不携带 API Key 至队列、日志、operation 或 HTTP 响应。
| Worker 结果 | 店铺最终状态 | 前端文案与行为 |
|---|---|---|
| operation 为 `queued` / `running` | `status=verifying`、`credential_status=unknown` | 仅显示“验证中”;不要同时展示“未验证”。可轮询 operation,并在终态后刷新店铺列表。 |
| Ozon 返回 2xx | `active` / `valid` | 显示“正常”。 |
| Ozon 返回 401 | `credential_invalid` / `invalid` | 显示“凭据无效”,提示用户核对 Client ID 与 API Key。 |
| Ozon 返回 403 | `credential_invalid` / `invalid` | 显示“访问被拒绝”,提示 Client ID 与 API Key 必须属于同一账户,且密钥需要读取权限。 |
| Ozon 返回其他确定性 4xx | `sync_error` / 保留当前凭据状态 | 显示 `last_error.message`;该状态**不代表 API Key 无效**。 |
| 网络超时、429 或 5xx | 保持验证中,Worker 重试;重试耗尽后为 `sync_error` | 显示“暂时无法验证,请稍后重试”。 |
页面最终以 `GET /api/stores` 返回的店铺状态为准;`GET /api/operations/{operation_id}` 只用于得知异步过程是否结束及读取脱敏错误。点击“重新验证凭据”会重新发起上述一次类目树请求。
## 5. 编辑、默认店铺、验证与删除
`PATCH /api/stores/{store_id}`
Headers:`If-Match` 与 `Idempotency-Key` 必填。
```json
{
"name": "Ozon 俄罗斯主店",
"currency_code": "RUB",
"note": "更新备注",
"api_key": "optional-replacement-secret"
}
```
不可修改 `platform_id` 和 `client_id`,避免把一个授权连接变成另一个平台/账户。未传 `api_key` 代表保留旧密钥;传入时替换、撤销旧密钥并再次触发凭据验证。普通编辑返回 `200`;包含新 Key 时返回 `202` 和验证 `operation`。
`POST /api/stores/{store_id}/set-default`
Headers:`Idempotency-Key` 必填。路径店铺在其平台下成为默认店铺,返回更新后的店铺摘要。无 body。
`POST /api/stores/{store_id}/credential-verifications`
Headers:`Idempotency-Key` 必填。手动重试凭据校验,返回 `202` 与 operation。店铺停用或删除时返回 `422`。
`POST /api/stores/{store_id}/category-tree-syncs`
Headers:`Idempotency-Key` 必填。同步请求 Ozon `POST /v1/description-category/tree` 并写入目标门店缓存;只有完整响应成功落库后才返回 `200` 与类目数量。必须是凭据已验证的 Ozon 门店,否则返回 `422 STORE_CREDENTIAL_UNAVAILABLE`。Ozon 请求或落库失败返回脱敏错误,旧缓存保持不变。此手动操作不经 Worker 或队列,API Key 仅在服务端请求期间短暂存在。
> 此端点是现有门店级能力,不是未来系统管理员同步入口。迁移至[平台级参考目录](../architecture/platform-product-reference-catalog.md)时,须另行修订/废弃该同步语义;管理员操作应按平台排入统一后台任务,不复用本同步 HTTP 路径直接请求 Ozon。
`POST /api/stores/{store_id}/warehouse-syncs`
Headers:`Idempotency-Key` 必填。触发 Ozon `POST /v1/warehouse/list` 同步,返回 `202` 与 operation。凭据无效时不调用平台,返回 `422 STORE_CREDENTIAL_UNAVAILABLE`。
`DELETE /api/stores/{store_id}`
Headers:`Idempotency-Key` 必填。仅软删除 Atlas 内的店铺连接,不影响 Ozon 后台。删除当前默认店铺时:若同平台仍有其他未删除店铺,返回 `422 DEFAULT_STORE_REPLACEMENT_REQUIRED`,客户端必须先选择新的默认店铺;若没有其他店铺,可以删除,平台默认店铺变为 `null`。
## 6. 仓库与推荐水印
`GET /api/stores/{store_id}/warehouses`
返回最近一次同步成功的仓库摘要,按名称排序:
```json
{ "data": [{ "id": "uuid", "platform_warehouse_id": "123", "name": "Москва", "fulfillment": "rfbs", "is_rfbs": true, "status": "active", "synced_at": "2026-09-02T08:01:00Z" }] }
```
从未成功同步时返回空数组及 `meta.warehouses_synced_at = null`,不把空数组解释为平台没有仓库。
`PUT /api/stores/{store_id}/watermark-binding`
Headers:`Idempotency-Key` 必填。
```json
{ "template_id": "uuid" }
```
传 `{ "template_id": null }` 表示“暂不绑定”。模板必须是当前工作空间的活动模板。服务端在事务内结束旧绑定、创建新绑定(或保持未绑定),返回店铺中的 `watermark_template` 摘要。此绑定只代表图片编辑弹窗的未来推荐值;首期不会自动改写已有图片或在发布时添加水印。