# 商家 OpenAPI 集成说明

## 访问入口

平台运行后提供机器可读的 OpenAPI 文档：

- JSON：`GET /api-docs/openapi.json`
- Swagger UI：`GET /swagger-ui/`（启用 `swagger-ui` feature 时提供）

接口使用商家登录后的 JWT。请求中的 `subjectId` 必须属于当前账号授权的经营主体，服务端会再次校验主体和数据范围。

## ERP 推送接口

| 方法 | 路径 | 用途 |
| --- | --- | --- |
| GET | `/api/erp-integration?subjectId={id}` | 查询主体的 ERP 集成配置 |
| PUT | `/api/erp-integration` | 保存或更新主体的 ERP 集成配置 |
| GET | `/api/erp-sync-logs?subjectId={id}` | 查询最近 100 条同步日志（无分页） |
| POST | `/api/erp-integration/test` | 使用当前配置发送测试事件 |
| POST | `/api/erp-sync-logs/retry` | 重试一条失败的同步日志 |

配置当前支持 `integrationType=webhook`、`syncDirection=push`。生产环境的 webhook 地址必须使用 HTTPS，签名密钥至少 16 个字符。启用前先保存配置，再调用测试接口确认对端能完成验签和幂等处理。

## Webhook 验签

平台请求会携带以下请求头：

- `X-Subject-Id`：经营主体 ID
- `X-Timestamp`：Unix 时间戳（秒）
- `X-Nonce`：本次投递的随机串
- `X-Signature`：HMAC-SHA256 签名的十六进制小写值

签名原文为：

```text
<timestamp>\n<nonce>\n<raw request body>
```

接收方应先校验时间窗口和 nonce，再用原始请求体计算 HMAC，并以 `eventId` 做幂等。平台等待 5 秒；返回任意 2xx 即视为成功，其他状态会进入同步日志并支持重试。

## 连锁接入流程

1. 为每个经营主体配置同一个连锁 ERP 的 webhook 地址和密钥，使用 `subjectId` 区分主体，使用 `merchantId` 与 `externalStoreCode` 做门店映射。
2. 调用测试接口，确认签名、主体映射、事件幂等和错误响应均符合约定。
3. 在连锁 ERP 中保存 `eventId`、主体 ID 和事件类型，按主体隔离订单、商品和库存数据。
4. 通过同步日志排查失败投递，修复对端后再按日志 ID 重试。

角色配置、门店范围和 OpenAPI 权限仍由平台商家端账号控制；ERP 凭据只用于主体级的系统对接，不会创建额外的登录角色。

## 总部自有系统：门店授权的机器 API

原有 ERP Webhook 是事件推送能力。连锁自有系统现在另有独立应用凭据与逐门店授权：总管在开放平台创建应用，门店所有者本人确认权限与期限，机器请求携带 `X-Chain-App-Key`，不复用店长登录密码或个人 JWT。

实际机器端点为 `GET /api/openapi/chain/v1/stores`、`GET /api/openapi/chain/v1/stores/{id}/catalog`，分别使用 `stores:read`、`catalog:read`；外卖另提供 `/api/openapi/chain/v1/marketing/plans`、`/marketing/plans/{id}/stores`、`/marketing/summary` 三个读取接口，使用 `marketing:read`。旧 `POST /api/openapi/chain/v1/stores/{id}/content` 已停用，不能申请 `content:write`。菜品模板由店长预览后确认选用，保留本店定价、库存和自有菜品。撤销应用或门店授权后，相关机器请求即时失效。

开放平台开发文档页提供 `chain-openapi.json` 与 `chain-management.md` 下载。当前不提供支付或客户个人信息权限。

## 事件与业务范围

正式 `order.created`、`order.cancelled`、`order.completed`、`order.refund.updated` 来源为外卖和即时零售配送订单；`inventory.updated` 来源为商家仓库库存。邮寄电商订单/售后及酒店民宿房态房价、预订尚未接入该通道，不能用配置成功或联调成功作为这些业务已接通的证明。

测试接口只接受上述已订阅事件。省略 `orderNo` 时发送 `testOnly:true` 的合成数据，事件 ID 使用 UUID；库存事件使用 `inventory`，订单事件使用 `order`，接收方不得当作真实业务执行。携带真实 `orderNo` 仅支持订单事件且核验所属主体，不允许用订单冒充库存。测试返回 `queued:true` 只说明入队，必须在同步日志确认投递结果。

兼容历史接口：`enabledEvents:[]` 表示全部受支持事件；停用须使用 `status:"disabled"`。官网会将这种历史配置显示为全部勾选，启用保存必须明确至少选择一个事件。使用真实订单号测试时保留业务事件 ID，重复投递应被接收方按同一事件幂等处理；不会生成新的业务订单。

## 电商订单机器 API 与大型商家同步

电商总部应用新增独立权限 `ecommerce:orders:read`，门店所有者在控制台明确授权；
原 `catalog:read`、`stores:read` 不会自动扩权。仍需有效的总部认证、经营关系、当前门店所有者及有效期限。
应用撤销、门店换所有者、权限撤销与授权过期后拒绝读取。外卖、零售、酒店数据不经这些接口返回。
统一前缀 `/api/openapi/chain/v1/stores/{id}`，认证头 `X-Chain-App-Key`，仅用于可信服务端。

| 方法与后缀 | 参数 | 返回 |
| --- | --- | --- |
| GET `/ecommerce/orders` | `afterId` 非负整数字符串，`limit` 1–200（默认100） | `orders,hasMore,nextAfterId` |
| GET `/ecommerce/orders/sync-state` | 无 | `schemaVersion:1,merchantId,partitionCount:64,partitions` |
| GET `/ecommerce/orders/changes` | `partition` 0–63、`afterRevision` 非负整数字符串、`limit` 1–200 | `items,hasMore,nextAfterRevision,highWatermark` |
| POST `/ecommerce/orders/batch-query` | JSON `{orderNos:[...]}`，1–100个不同订单号 | `orders,unavailableOrderNos` |
| GET `/ecommerce/orders/detail` | `orderNo`，兼容历史订单号 | `order`，包含商品行 `items` |

所有成功结果仍在 ApiResponse 的 data 中。每应用的以上5个接口共享每分钟1,200次额度，使用共享Redis，
服务实例之间不会各自放大额度；429带 Retry-After。限流不可用时503，不回退为不限流。网关/IP限流仍然生效，
正式接入需按峰值核对网关额度。每次请求独立核验授权；订单只含已付款、部分退款、已退款记录。
订单ID、门店ID和版本使用字符串，金额为整数分；不提供姓名、电话、收件地址或支付凭证。
此只读授权支持订单同步、拣货数据和状态核对，不授予发货写入或支付能力；现有商家端履约入口仍按原权限工作。

同步客户端先把每个门店的64个分区游标初始化为字符串“0”。首次通过 changes 遍历每个分区，
每页先将订单状态与 nextAfterRevision 在本地同一事务提交，再读取下一页；重试原游标允许重复读取。
后续读取 sync-state，只查询版本高于本地游标的分区。可并行处理不同分区，建议起始并发4–8，遵守429退避。
按订单号幂等覆盖，拒绝旧版本覆盖新版本；visible=false 表示移出本地可操作队列。
服务器保存每单最新同步状态，重复变更会合并；这不是逐笔事件日志，不能替代支付账务流水或审计。
不要用目录 afterId 代替增量游标：较早创建的订单可能较晚付款，订单目录用于浏览与按号核对。

每个分区的版本增长与投影写入属于订单写入事务，版本头持有行锁直到提交。
读取头与变化使用同一数据库语句快照，不依赖时间戳窗口或提交顺序不一致的全局序列。
同一商家分为64个写入分区，订单首页全局数量继续使用原有256桶计数投影。
列表均有有限批次与索引游标，无 OFFSET 深分页和反复全表 COUNT。

100万单/日的平均值为11.57单/秒；若峰值是平均值的10倍/100倍，则约116/1,157单/秒。
这些是负载换算，不是已承诺的整套系统容量。实际容量须同时测支付回调、热门SKU库存锁、批量履约、
订单/售后投影、物流服务、数据库WAL/副本、失败重试与数据保留。
本机仅能验证功能与受控数据集的查询/写入表现，不能替代生产环境整链路和故障恢复验收。
