Files
anxinyan/docs/api/third-party-openapi.md
wushumin 9aac78b8da first
2026-05-11 15:28:27 +08:00

386 lines
12 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.
# 第三方订单对接文档
版本v1
更新日期2026-05-08
## 1. 对接说明
本文档用于第三方系统对接安心验开放接口。第三方推送订单时,只需要提供第三方自己的订单号 `external_order_no`,不需要提前传物品信息。具体物品信息会在鉴定师鉴定时由平台侧补充完善。
接口域名以实际环境为准,本文统一使用:
```text
https://{api-domain}
```
## 2. 凭证与安全
平台会为每个对接方分配:
| 参数 | 说明 |
| --- | --- |
| `app_key` | 调用方身份标识 |
| `app_secret` | 签名密钥,请妥善保管,不要传给前端或泄露到日志中 |
所有开放接口都需要签名。请求必须使用 HTTPS并携带以下请求头
| Header | 必填 | 说明 |
| --- | --- | --- |
| `Content-Type` | 是 | 固定为 `application/json` |
| `X-AXY-App-Key` | 是 | 平台分配的 `app_key` |
| `X-AXY-Timestamp` | 是 | Unix 秒级时间戳,有效期 300 秒 |
| `X-AXY-Nonce` | 是 | 随机字符串,同一个 `app_key` 下不可重复使用 |
| `X-AXY-Signature` | 是 | 请求签名 |
### 2.1 签名算法
签名使用 HMAC-SHA256小写十六进制输出。
```text
body_hash = sha256(raw_body)
base = UPPERCASE_HTTP_METHOD + path_with_query + timestamp + nonce + body_hash
signature = hex_hmac_sha256(base, app_secret)
```
字段说明:
| 字段 | 说明 |
| --- | --- |
| `raw_body` | 原始请求体字符串。GET 请求没有请求体时为空字符串 |
| `path_with_query` | 请求路径加查询字符串,例如 `/api/open/v1/orders?external_order_no=T202605080001` |
| `timestamp` | 与 `X-AXY-Timestamp` 完全一致 |
| `nonce` | 与 `X-AXY-Nonce` 完全一致 |
注意事项:
- `path_with_query` 必须与实际请求完全一致,包括查询参数顺序和 URL 编码。
- POST 请求签名时使用实际发送的 JSON 字符串,不要签名一个格式化版本、发送另一个压缩版本。
- GET 请求的 `body_hash``sha256("")`
- `nonce` 会做防重放校验,重试请求需要重新生成 `nonce` 和签名。
### 2.2 Node.js 签名示例
```js
import crypto from 'node:crypto';
function sign({ method, pathWithQuery, body = '', timestamp, nonce, appSecret }) {
const bodyHash = crypto.createHash('sha256').update(body).digest('hex');
const base = method.toUpperCase() + pathWithQuery + timestamp + nonce + bodyHash;
return crypto.createHmac('sha256', appSecret).update(base).digest('hex');
}
```
### 2.3 PHP 签名示例
```php
function sign_request(string $method, string $pathWithQuery, string $body, string $timestamp, string $nonce, string $appSecret): string
{
$bodyHash = hash('sha256', $body);
$base = strtoupper($method) . $pathWithQuery . $timestamp . $nonce . $bodyHash;
return hash_hmac('sha256', $base, $appSecret);
}
```
## 3. 通用响应格式
成功响应:
```json
{
"code": 0,
"message": "ok",
"data": {}
}
```
失败响应:
```json
{
"code": 422,
"message": "external_order_no 不能为空",
"data": {}
}
```
常见错误码:
| code | 说明 |
| --- | --- |
| `401` | 鉴权失败、签名错误、时间戳过期、`nonce` 重复 |
| `404` | 订单不存在 |
| `409` | 幂等冲突,例如同一个 `external_order_no` 请求内容不一致 |
| `422` | 请求参数不合法 |
| `500` | 服务端处理失败 |
## 4. 创建订单
```text
POST /api/open/v1/orders
```
第三方创建订单时只需要传 `external_order_no`。平台会创建一笔待收货订单,后续物品信息由鉴定师在鉴定工作台补充。
### 4.1 请求参数
| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `external_order_no` | string | 是 | 第三方订单号。同一对接客户下必须唯一 |
| `service_provider` | string | 否 | 服务方,可选 `anxinyan``zhongjian`,默认 `anxinyan` |
| `product_info` | object | 否 | 物品信息,当前可不传 |
| `materials` | array | 否 | 鉴定资料图片 URL 列表,当前可不传 |
| `return_address` | object | 否 | 退回地址,当前可不传;如传任一地址字段,则必填完整地址 |
| `inbound_logistics` | object | 否 | 寄入物流信息,当前可不传 |
| `express_company` | string | 否 | 寄入快递公司,可替代 `inbound_logistics.express_company` |
| `tracking_no` | string | 否 | 寄入运单号,可替代 `inbound_logistics.tracking_no` |
| `extra_info` | object | 否 | 扩展信息,当前可不传 |
### 4.2 最小请求示例
```json
{
"external_order_no": "THIRD202605080001"
}
```
### 4.3 带可选字段请求示例
```json
{
"external_order_no": "THIRD202605080002",
"service_provider": "anxinyan",
"inbound_logistics": {
"express_company": "顺丰速运",
"tracking_no": "SF1234567890"
},
"return_address": {
"consignee": "张三",
"mobile": "13800138000",
"province": "浙江省",
"city": "杭州市",
"district": "西湖区",
"detail_address": "文三路 1 号"
}
}
```
### 4.4 cURL 示例
```bash
curl -X POST 'https://{api-domain}/api/open/v1/orders' \
-H 'Content-Type: application/json' \
-H 'X-AXY-App-Key: your_app_key' \
-H 'X-AXY-Timestamp: 1778227200' \
-H 'X-AXY-Nonce: 7b7b2a2f9c9e4d1f' \
-H 'X-AXY-Signature: calculated_signature' \
-d '{"external_order_no":"THIRD202605080001"}'
```
### 4.5 成功响应示例
```json
{
"code": 0,
"message": "订单已创建",
"data": {
"idempotent": false,
"order": {
"customer_id": "CUST001",
"customer_code": "CUST001",
"external_order_no": "THIRD202605080001",
"order_id": 123,
"order_no": "AXY20260508120000123",
"appraisal_no": "AXY-APP-20260508-1001",
"order_status": "pending_shipping",
"display_status": "待寄送商品",
"payment_status": "paid",
"pay_amount": 99,
"estimated_finish_time": "2026-05-09 12:00:00",
"created_at": "2026-05-08 12:00:00",
"timeline": [
{
"node_code": "created",
"node_text": "下单成功",
"node_desc": "大客户订单已推送并创建成功",
"occurred_at": "2026-05-08 12:00:00"
},
{
"node_code": "pending_shipping",
"node_text": "待寄送商品",
"node_desc": "请将商品寄送至鉴定中心",
"occurred_at": "2026-05-08 12:00:00"
}
],
"inbound_logistics": null,
"return_logistics": null,
"report_summary": null
}
}
}
```
### 4.6 幂等规则
同一个对接客户下,`external_order_no` 作为幂等键:
- 第一次请求会创建订单。
- 后续使用相同 `external_order_no` 且请求内容一致时,不会重复创建订单,会返回已有订单,`data.idempotent``true`
- 后续使用相同 `external_order_no` 但请求内容不一致时,返回 `409`
建议第三方重试创建订单时保持请求 JSON 内容一致,仅重新生成 `timestamp``nonce``signature`
## 5. 查询订单
支持按第三方订单号或平台订单号查询订单进度。
### 5.1 按第三方订单号查询
```text
GET /api/open/v1/orders/{external_order_no}
```
示例:
```bash
curl -X GET 'https://{api-domain}/api/open/v1/orders/THIRD202605080001' \
-H 'Content-Type: application/json' \
-H 'X-AXY-App-Key: your_app_key' \
-H 'X-AXY-Timestamp: 1778227200' \
-H 'X-AXY-Nonce: f0f74a6baf764d8f' \
-H 'X-AXY-Signature: calculated_signature'
```
### 5.2 通过查询参数查询
```text
GET /api/open/v1/orders?external_order_no=THIRD202605080001
GET /api/open/v1/orders?order_no=AXY20260508120000123
```
### 5.3 响应示例
```json
{
"code": 0,
"message": "ok",
"data": {
"order": {
"customer_id": "CUST001",
"customer_code": "CUST001",
"external_order_no": "THIRD202605080001",
"order_id": 123,
"order_no": "AXY20260508120000123",
"appraisal_no": "AXY-APP-20260508-1001",
"order_status": "report_published",
"display_status": "报告已发布",
"payment_status": "paid",
"pay_amount": 99,
"estimated_finish_time": "2026-05-09 12:00:00",
"created_at": "2026-05-08 12:00:00",
"timeline": [],
"inbound_logistics": {
"express_company": "顺丰速运",
"tracking_no": "SF1234567890",
"tracking_status": "submitted",
"latest_desc": "客户已提交寄送运单:顺丰速运 SF1234567890等待鉴定中心签收。",
"latest_time": "2026-05-08 12:00:00"
},
"return_logistics": null,
"report_summary": {
"report_no": "R202605080001",
"report_title": "鉴定报告",
"report_status": "published",
"publish_time": "2026-05-08 18:00:00",
"verify_url": "https://{h5-domain}/verify?id=xxx",
"report_page_url": "https://{h5-domain}/report/xxx",
"verify_status": "valid"
}
}
}
}
```
## 6. 订单状态
常见订单状态如下,最终以接口返回的 `order_status``display_status` 为准。
| order_status | display_status | 说明 |
| --- | --- | --- |
| `pending_shipping` | 待寄送商品 | 订单已创建,等待物品到仓或人工确认收货 |
| `received` | 鉴定中心已收货 | 物品已到仓 |
| `appraising` | 物品鉴定中 | 鉴定师正在鉴定 |
| `generating_report` | 物品鉴定完成 | 鉴定完成,报告生成中 |
| `report_published` | 报告已发布 | 报告已发布,可查看报告摘要 |
| `return_shipped` | 物品已寄回 | 物品已退回寄出 |
| `completed` | 已完成 | 订单完成 |
| `pending_supplement` | 需要补充资料 | 需要补充资料 |
## 7. Webhook 事件回调
如需接收订单状态变化通知,第三方需向平台提供可公网访问的 `webhook_url`,并由平台开启回调。
平台会以 POST JSON 方式推送事件:
| Header | 说明 |
| --- | --- |
| `Content-Type` | `application/json` |
| `X-AXY-App-Key` | 平台分配给该客户的 `app_key` |
当前 webhook 仅携带 `X-AXY-App-Key`,暂未实现回调签名。如第三方需要回调验签,可与平台另行约定后升级。
回调超时时间:
| 项目 | 值 |
| --- | --- |
| 连接超时 | 3 秒 |
| 总超时 | 6 秒 |
| 成功判定 | HTTP 状态码为 2xx 且无网络错误 |
### 7.1 回调报文
```json
{
"event_code": "order_created",
"event_text": "订单创建",
"customer_id": "CUST001",
"customer_code": "CUST001",
"external_order_no": "THIRD202605080001",
"order_no": "AXY20260508120000123",
"appraisal_no": "AXY-APP-20260508-1001",
"status_code": "pending_shipping",
"status_text": "待寄送商品",
"occurred_at": "2026-05-08 12:00:00",
"data": {},
"event_id": 1
}
```
### 7.2 事件类型
| event_code | event_text | status_code | status_text |
| --- | --- | --- | --- |
| `order_created` | 订单创建 | `pending_shipping` | 待寄送商品 |
| `inbound_received` | 快递已到仓 | `received` | 鉴定中心已收货 |
| `appraising` | 物品鉴定中 | `appraising` | 物品鉴定中 |
| `appraisal_finished` | 物品鉴定完成 | `generating_report` | 物品鉴定完成 |
| `report_published` | 报告已发布 | `report_published` | 报告已发布 |
| `return_shipped` | 物品已寄回 | `return_shipped` | 物品已寄回 |
| `completed` | 订单已完成 | `completed` | 已完成 |
| `supplement_required` | 需要补充资料 | `pending_supplement` | 需要补充资料 |
### 7.3 回调接收建议
第三方接收 webhook 时建议:
- 使用 `event_id` 做事件幂等,避免重复处理。
- 收到事件后返回 HTTP 2xx。
- 如需强一致的最新状态,可以收到 webhook 后再调用订单查询接口确认。
## 8. 对接流程建议
1. 平台分配 `app_key``app_secret`
2. 第三方完成签名调试。
3. 第三方调用创建订单接口,只传 `external_order_no` 即可。
4. 第三方可通过查询接口主动查询订单状态。
5. 如启用 webhook平台在订单状态变化时主动通知第三方。