优化发货接口对接流程

This commit is contained in:
yml2213
2026-07-30 23:44:50 +08:00
parent a16e518b6c
commit ee956e2e01
14 changed files with 428 additions and 141 deletions
+71
View File
@@ -0,0 +1,71 @@
# API 对接关系总览
> 更新日期:2026-07-30
> 用途:快速判断接口调用方、鉴权方式、状态流和回调方向
项目里有三条外部通信链路,容易混在一起。排查问题时先判断请求属于哪一条。
## 1. 三条链路
| 链路 | 调用方 | 接收方 | 路径 / 入口 | 鉴权 | 作用 |
|------|--------|--------|-------------|------|------|
| 商户侧开放 API | 商户自己的商城 / 系统 | 本平台 | `/api/client/v1` | `X-App-Key` + HMAC | 查商品、创建订单、查订单、取消订单、查钱包 |
| 源头侧发货 API | 上游发货平台 | 本平台 | `/api/open/v1` | `X-Api-Key` + HMAC | 按订单号查可发货信息,回传发货成功 / 失败 |
| 商户回调 | 本平台 | 商户自己的回调 URL | 商户后台配置 URL | `X-Event-ID` + `X-Timestamp` + `X-Sign` | 把订单创建、履约变化、取消等事件推给商户 |
## 2. 订单流
```text
商户系统
↓ POST /api/client/v1/orders
本平台创建订单、扣商户钱包、生成 order_no
上游发货平台
↓ GET /api/open/v1/orders/{order_no}
查询商品 sku、买家信息、can_ship
上游完成发货
↓ POST /api/open/v1/orders/ship-notify
只回传 success 或 failed
本平台更新履约状态
商户回调 / 商户查询订单
```
## 3. 鉴权不要混用
| 项 | 商户侧 `/api/client/v1` | 源头侧 `/api/open/v1` |
|----|--------------------------|------------------------|
| Key Header | `X-App-Key` | `X-Api-Key` |
| Secret 来源 | 商户后台创建 API 客户端后展示一次 | 服务端环境变量 `OPEN_API_SECRET` |
| body 参与签名 | `body_sha256` | 原始 body 字符串 |
| nonce 防重 | 数据库持久化 | 数据库持久化 |
| 多租户 | 按 API 客户端绑定商户 | 全局源头 Key,按 `order_no` 找订单 |
两套签名串不同,即使 Header 名相似也不能互相套用。
## 4. 状态语言
内部订单使用 `payment_status` + `fulfillment_status` 两组状态;源头侧为了兼容对接,返回的是更贴近发货系统的旧状态名。
| 内部 payment_status | 内部 fulfillment_status | 源头侧 `status` | 含义 | 是否可发货 |
|---------------------|-------------------------|-----------------|------|------------|
| `paid` | `pending` | `paid` | 已付款,等待发货 | 是 |
| `paid` | `processing` | `delivering` | 履约中 | 否 |
| `paid` | `succeeded` | `delivered` | 已交付 | 否 |
| `paid` | `failed` | `ship_failed` | 发货失败,可重试 | 是 |
| `refunded` | `cancelled` | `cancelled` | 已取消 / 已退款 | 否 |
注意:源头侧返回的 `status=paid` 不是单独的支付状态,而是“这个订单已支付且处于待发货履约状态”的兼容表达。
## 5. ship_notify 当前契约
- 路径:`POST /api/open/v1/orders/ship-notify`
- `ship_status` 只接受 `success` / `failed`
- `failed``fail_reason` 必填,必须写详细原因
- `success` 会清空订单失败原因,并清掉结果 JSON 中历史 `fail_reason`
- 已交付订单重复推 `success` 按幂等成功处理
- 已交付订单再推 `failed` 会被拒绝,并保留审计记录
详细请求体见:[发货通知约定.md](发货通知约定.md)
+72
View File
@@ -0,0 +1,72 @@
# 发货通知约定(速查)
> 更新日期:2026-07-30
> 适用对象:上游发货系统、联调排查、后续快速检索
本文是 `ship_notify` 的当前对接口径速查版。完整鉴权与接口说明见:[开放接口-皮肤源头对接.md](开放接口-皮肤源头对接.md)
## 1. 当前口径
- `ship_notify` 只推送两种状态:`success` / `failed`
- `success` 表示发货成功
- `failed` 表示发货失败
- `failed` 时,`fail_reason` 必填,且要写详细失败原因
- `processing` 是旧口径里曾出现过的中间状态,当前 `ship_notify` 不再接受
## 2. 推荐推送格式
### 发货成功
```json
{
"order_no": "O202607240733306742",
"ship_status": "success",
"provider_order_no": "6a6322a81b3b421994137260",
"shipped_at": "2026-07-24T08:30:33.000Z",
"fail_reason": "",
"game_uid": "4808146277",
"role_name": "巫师哈丁12",
"game_channel": "安卓-QQ",
"pay_score": 360
}
```
### 发货失败
```json
{
"order_no": "O202607240733306742",
"ship_status": "failed",
"provider_order_no": "6a6322a81b3b421994137260",
"shipped_at": "2026-07-24T08:30:33.000Z",
"fail_reason": "角色名不存在,渠道服校验失败",
"game_uid": "4808146277",
"role_name": "巫师哈丁12",
"game_channel": "安卓-QQ",
"pay_score": 360
}
```
## 3. 字段要求
| 字段 | 必填 | 说明 |
|------|------|------|
| `order_no` | 是 | 店铺订单号 |
| `ship_status` | 是 | 仅使用 `success` / `failed` |
| `provider_order_no` | 否 | 上游发货单号 |
| `shipped_at` | 否 | RFC3339 时间;`success` 未传时可由服务端补时间 |
| `fail_reason` | 失败时是 | `failed` 时必须填详细原因 |
| `game_channel` | 否 | 账号区服 |
| `game_uid` | 否 | 游戏角色 UUID |
| `role_name` | 否 | 角色名 |
| `pay_score` | 否 | 消耗积分 |
## 4. 快速排查
- 只有订单查询日志,没有 `ship_notify` 日志,通常表示上游只查了单,没有回调发货结果
- `failed``fail_reason` 为空,不符合当前约定
- `success` 后订单应进入已交付状态,重复 `success` 一般按幂等处理
## 5. 备注
2026-07-30 的测试环境日志里,只看到 `/api/open/v1/orders/{order_no}` 的查询请求,没有看到 `ship_notify` 推送请求,说明当次联调只做了查询。
+4 -5
View File
@@ -215,6 +215,8 @@ GET /api/open/v1/orders/{order_no}
## 4. 接口二:发货结果推送
> 当前接口只接受 `success` / `failed` 两种最终结果。`processing` 是旧口径里曾出现过的中间状态,当前 `ship_notify` 不再接受;当 `ship_status=failed` 时,`fail_reason` 必须填写详细失败原因。
### 4.1 请求
```http
@@ -235,16 +237,15 @@ Content-Type: application/json
| 字段 | 必填 | 说明 |
|------|------|------|
| `order_no` | 是 | 店铺订单号 |
| `ship_status` | 是 | `success` / `failed` / `processing` |
| `ship_status` | 是 | `success` / `failed` |
| `provider_order_no` | 否 | 你们系统的发货单号 |
| `shipped_at` | 否 | RFC3339success 未传则用服务端时间 |
| `fail_reason` | 否 | 失败原因(failed 时建议填) |
| `fail_reason` | 失败时是 | `failed` 时必填,填写详细失败原因 |
### 4.2 ship_status → 我们订单状态
| ship_status | 订单变为 | 说明 |
|-------------|---------|------|
| `processing` | `delivering` | 已接单 / 发货中 |
| `success` | `delivered` | 发货成功 |
| `failed` | `ship_failed` | 失败,允许之后再次查询并重试 |
@@ -279,8 +280,6 @@ GET 订单查询
can_ship == false → 停止,展示 cannot_ship_reason
↓ true
(可选)POST ship_status=processing
按 product.sku 发货
POST ship_status=success 或 failed