Files
tyapi-server/docs/查询白名单公开接口-下游对接说明.md
2026-06-19 11:33:56 +08:00

325 lines
10 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 用户 / 合作方**对接使用。
> 用于配置「查询为空」屏蔽规则:命中后,指定人员在指定接口上的查询将返回 `1000 查询为空`。
---
## 1. 服务地址
| 环境 | 基础地址 |
|------|----------|
| 生产 | `https://api.tianyuanapi.com` |
完整路径 = 基础地址 + 下表路径,例如:
`https://api.tianyuanapi.com/api/v1/query-whitelist/entries`
---
## 2. 接口一览
| 接口 | 方法 | 路径 | 说明 |
|------|------|------|------|
| 创建规则 | POST | `/api/v1/query-whitelist/entries` | 首次为某身份证建立屏蔽规则 |
| 追加接口 | POST | `/api/v1/query-whitelist/entries/append` | 向已有规则追加产品编码(去重合并) |
**说明:**
- 规则仅对**当前 `Access-Id` 对应账号**生效。
- 本接口为**配置类接口**,不产生业务查询、**不扣费**,也不会出现在常规 API 调用记录中。
- 规则生效后,该账号调用对应产品接口时,若命中屏蔽条件,将返回 `1000 查询为空`
---
## 3. 鉴权
每次请求需同时满足以下三项。
### 3.1 请求头
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `Access-Id` | string | 是 | 您的 API 账号 Access-Id |
| `Whitelist-Mgmt-Key` | string | 是 | 平台单独下发的**白名单管理密钥**(与 Access Key 不同,请向商务/技术支持索取) |
| `Content-Type` | string | 是 | 固定为 `application/json` |
### 3.2 请求体加密
与业务 API 调用方式一致,外层仅传 `data`
```json
{
"data": "<AES-128-CBC Base64 密文>"
}
```
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `data` | string | 是 | 业务参数经 Access Key 加密后的 Base64 字符串 |
- 使用您账号的 **Access Key**16 进制字符串)进行 AES 加密。
- Access Key **仅用于本地加密**,不要写入明文 JSON。
- 服务端根据请求头 `Access-Id` 查找密钥并解密;**解密成功即完成身份校验**。
### 3.3 IP 白名单
调用方出口 IP 须已加入该账号在控制台配置的 IP 白名单(与业务 API 要求一致)。未在白名单内的 IP 将返回 `1004`
---
## 4. 业务参数(加密前明文 JSON
创建与追加接口的明文字段相同:
```json
{
"name": "*",
"id_card": "350681198611130611",
"api_codes": ["FLXG0V4B", "JRZQ8A2D"],
"remark": "可选备注"
}
```
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | string | 是 | 姓名;填 `*` 表示只按身份证匹配,不校验姓名 |
| `id_card` | string | 是 | 18 位中国大陆身份证号 |
| `api_codes` | string[] | 是 | 生效的产品编码列表,见下表约束 |
| `remark` | string | 否 | 备注,最长 500 字符;追加接口传入非空时会覆盖原备注 |
### 4.1 `api_codes` 约束
| 传参方式 | 是否允许 | 示例 |
|----------|----------|------|
| 字符串数组 | 允许 | `["FLXG0V4B", "JRZQ8A2D"]` |
| 单个字符串 | 不允许 | `"FLXG0V4B"` |
| 通配 `*` | 不允许 | `["*"]` |
| 空数组 | 不允许 | `[]` |
| 非字符串元素 | 不允许 | `[123]` |
---
## 5. 创建与追加的区别
同一账号下,**同一身份证号 + 同一 `name`** 仅对应**一条**规则;多个生效接口配置在该规则的 `api_codes` 数组中。
### 5.1 创建 `POST /entries`
| 情况 | 结果 |
|------|------|
| 尚无该身份证+姓名的规则 | 创建成功 |
| 规则已存在 | 失败,`1013 规则已存在`(不会自动合并) |
### 5.2 追加 `POST /entries/append`
| 情况 | 结果 |
|------|------|
| 规则已存在 | 成功;将本次 `api_codes` 与已有列表**去重合并**(保留原顺序,新编码追加在后) |
| 本次编码均已存在 | 仍返回成功(幂等),列表不变 |
| 规则不存在 | 失败,`1014 规则不存在,请先调用创建接口` |
**示例:**
```
创建api_codes = ["FLXG0V4B"]
追加api_codes = ["JRZQ8A2D"]
→ 合并后为 ["FLXG0V4B", "JRZQ8A2D"](同一条规则,非新建)
再次追加api_codes = ["JRZQ8A2D", "FLXG2E8F"]
→ ["FLXG0V4B", "JRZQ8A2D", "FLXG2E8F"]JRZQ8A2D 已存在则跳过)
```
### 5.3 推荐流程
1. 首次配置 → 调用**创建接口**(可一次传齐全部 `api_codes`
2. 后续增补产品 → 调用**追加接口**(只传新增编码)
3. 创建返回 `1013` → 改调**追加接口**
---
## 6. 加密算法
与业务 API 完全一致:
1. Access Key 为 16 进制字符串,解码为 16 字节 AES 密钥
2. 采用 **AES-128-CBC****PKCS7** 填充
3. 每次加密随机生成 16 字节 IV置于密文前
4. IV + 密文整体做 **Base64** 编码,放入 `data`
**加密步骤:**
```
明文 JSON → AES-128-CBC 加密 → [IV(16字节) + 密文] → Base64 → 填入 data 字段
```
**解密响应 `data` 时:** 取 Base64 解码后前 16 字节为 IV余下为密文用同一 Access Key 解密。
---
## 7. 响应格式
HTTP 状态码均为 `200`。以响应体 `code` 判断业务是否成功。
### 7.1 外层结构
```json
{
"code": 0,
"message": "业务成功",
"transaction_id": "550e8400-e29b-41d4-a716-446655440000",
"data": "<AES 加密字符串>"
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `code` | int | `0` 表示成功,非 `0` 见错误码表 |
| `message` | string | 结果描述 |
| `transaction_id` | string | 本次请求流水号 |
| `data` | string | 成功时返回,为规则详情的 AES 密文,需用 Access Key 解密 |
### 7.2 `data` 解密后结构
```json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "*",
"id_card_masked": "350681********0611",
"api_codes": ["FLXG0V4B", "JRZQ8A2D"],
"status": "enabled",
"remark": "",
"created_at": "2026-06-18T10:00:00+08:00",
"updated_at": "2026-06-18T10:00:00+08:00"
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | string | 规则唯一标识 |
| `name` | string | 姓名规则 |
| `id_card_masked` | string | 脱敏身份证号 |
| `api_codes` | string[] | 当前生效的产品编码列表 |
| `status` | string | `enabled` 启用 / `disabled` 禁用 |
| `remark` | string | 备注 |
| `created_at` | string | 创建时间ISO 8601 |
| `updated_at` | string | 最近更新时间ISO 8601 |
---
## 8. 错误码
| code | message | 说明 |
|------|---------|------|
| 0 | 业务成功 | 创建或追加成功 |
| 1001 | 接口异常 | 系统内部错误 |
| 1002 | 解密失败 | `data` 无法解密 |
| 1003 | 请求参数结构不正确 | 参数缺失、格式错误或 `api_codes` 不合法 |
| 1004 | 未经授权的IP | 调用 IP 不在账号白名单 |
| 1005 | 缺少Access-Id | 未传 `Access-Id` 请求头 |
| 1006 | 未经授权的AccessId | Access-Id 无效或账号已冻结 |
| 1010 | 缺少管理密钥 | 未传 `Whitelist-Mgmt-Key` |
| 1011 | 管理密钥无效 | 管理密钥错误 |
| 1012 | 接口未开放 | 功能未对该账号/环境开放 |
| 1013 | 规则已存在 | 创建时:该身份证+姓名规则已存在 |
| 1014 | 规则不存在 | 追加时:须先调用创建接口 |
---
## 9. 调用示例Node.js
```javascript
const crypto = require('crypto');
const BASE_URL = 'https://api.tianyuanapi.com';
const ACCESS_ID = '您的Access-Id';
const ACCESS_KEY = '您的AccessKey'; // 16 进制,用于加解密
const MGMT_KEY = '平台下发的Whitelist-Mgmt-Key';
function encrypt(plainText, keyHex) {
const key = Buffer.from(keyHex, 'hex');
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
cipher.setAutoPadding(true);
let enc = cipher.update(plainText, 'utf8');
enc = Buffer.concat([iv, enc, cipher.final()]);
return enc.toString('base64');
}
function decrypt(cipherBase64, keyHex) {
const key = Buffer.from(keyHex, 'hex');
const buf = Buffer.from(cipherBase64, 'base64');
const iv = buf.slice(0, 16);
const decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
decipher.setAutoPadding(true);
let dec = decipher.update(buf.slice(16));
dec = Buffer.concat([dec, decipher.final()]);
return dec.toString('utf8');
}
async function callWhitelistApi(path, payload) {
const res = await fetch(`${BASE_URL}${path}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Access-Id': ACCESS_ID,
'Whitelist-Mgmt-Key': MGMT_KEY,
},
body: JSON.stringify({
data: encrypt(JSON.stringify(payload), ACCESS_KEY),
}),
});
const json = await res.json();
if (json.code === 0 && json.data) {
json.decrypted = JSON.parse(decrypt(json.data, ACCESS_KEY));
}
return json;
}
// 创建
callWhitelistApi('/api/v1/query-whitelist/entries', {
name: '*',
id_card: '350681198611130611',
api_codes: ['FLXG0V4B'],
remark: '',
}).then(console.log);
// 追加
callWhitelistApi('/api/v1/query-whitelist/entries/append', {
name: '*',
id_card: '350681198611130611',
api_codes: ['JRZQ8A2D'],
}).then(console.log);
```
---
## 10. 常见问题
### `name` 填 `*` 是什么意思?
只按**身份证号**匹配。只要业务请求中的身份证与该规则一致,无论传入什么姓名,均会返回「查询为空」。
### 与业务 API 的 Access Key 是什么关系?
- **Access Key**:加密请求/响应,证明账号身份(与业务 API 相同)。
- **Whitelist-Mgmt-Key**:平台另行下发的管理授权密钥,证明有权调用白名单配置接口。
两者缺一不可。
### 调用成功后,常规 API 调用记录里能看到吗?
不能。本接口为配置类操作,不计入业务 API 调用次数,也不扣费。
### 屏蔽何时生效?
接口返回 `code = 0` 后即生效(通常数秒内)。之后该账号调用 `api_codes` 中包含的产品且入参命中身份证(及姓名规则)时,将返回 `1000 查询为空`
---
## 11. 联系方式
- **Access-Id / Access Key**:登录控制台 → API 密钥
- **Whitelist-Mgmt-Key**:向平台商务或技术支持申请
- **IP 白名单**:登录控制台 → API 设置中配置