# 91卡券查询订单接口

## 1. 接口说明

本文档为当前项目对接 `91卡券` 的正式技术文档，用于提交客服审核。

本接口用于在异步卡密下单后，由 `91卡券` 主动查询订单状态与最终卡密内容。

当前项目的最终交付物不是传统卡号密码，而是：

- 我方系统生成的**领取链接**；
- 该链接将作为最终卡密内容，通过 `cards` 字段返回给 `91卡券`；
- `91卡券` 再使用自动发货能力，将该领取链接展示给买家。

## 2. 请求方向

- `91卡券平台 -> 接入方系统`

## 3. 请求 URL

请按以下地址配置：

```text
POST https://你的域名/api/v1/open/91/orders/query
```

## 4. 请求方式

- `POST`
- `Content-Type: application/json;charset=utf-8`

## 5. 签名规则

签名规则采用 2.签名规则示例 中的约定：

- 仅使用请求 JSON body 中实际传入的参数参与签名；
- 除 `sign` 外，所有 body 参数按字段名 ASCII 升序排序；
- 使用 `key=value&key=value` 方式拼接；
- 前后拼接商户密钥；
- 取 `MD5`，输出 32 位大写字符串；
- 已传入的空值参数参与签名；
- 不要额外加入 `userId`、商户号、内部配置项或未传入的可选字段。

## 6. 请求参数

| 参数名 | 必填 | 类型 | 说明 |
| --- | --- | --- | --- |
| `orderNo` | 是 | string | 商家订单号。 |
| `timestamp` | 是 | long | 10 位秒级 Unix 时间戳，用于请求时效校验。 |
| `version` | 是 | string | 固定传 `1.0`。 |
| `sign` | 是 | string | 签名。 |

## 7. 请求示例

```json
{
  "orderNo": "P91KS202605040001",
  "timestamp": 1777867260,
  "version": "1.0",
  "sign": "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"
}
```

## 8. MD5 源串示例

假设：

- 商户密钥为：`your_secret_key`

则源串示例如下：

```text
your_secret_keyorderNo=P91KS202605040001&timestamp=1777867260&version=1.0your_secret_key
```

## 9. 业务处理规则

我方系统收到查询请求后，按以下规则返回：

1. 校验签名与时间戳；
2. 根据 `orderNo` 查询内部订单；
3. 若订单存在但商品履约配置尚未补齐，返回 `orderStatus = 10`；
4. 若订单存在但领取链接尚未准备完成，返回 `orderStatus = 10`；
5. 若领取链接已生成，可交付，返回 `orderStatus = 20`；
6. 若订单无法履约，返回 `orderStatus = 30`；
7. 当返回 `20` 时，通过 `cards` 返回最终卡密内容。

## 10. 响应参数

| 参数名 | 类型 | 必须返回 | 说明 |
| --- | --- | --- | --- |
| `orderNo` | string | 必须返回 | 商家订单号，来源 `91卡券` 下单请求。 |
| `outTradeNo` | string | 必须返回 | 我方系统内部订单号。 |
| `orderStatus` | int | 必须返回 | 订单状态。`10`：处理中；`20`：成功；`30`：失败。 |
| `failCode` | int | 失败时建议返回 | 失败代码。可以不返回该字段，但不要返回 `null`。 |
| `failReason` | string | 失败时建议返回 | 失败原因。 |
| `orderCost` | decimal(14,4) | 成功时建议返回 | 订单总成本，单位：元。 |
| `cards` | string | `orderStatus = 20` 时必须 | 卡密数据加密串。当前项目通过该字段返回领取链接。 |
| `cards[0].cardNo` | string | 成功时必须 | 最终交付的领取链接，格式为 `https://你的域名/#/claim/{token}`。 |
| `cards[0].cardPwd` | string | 可选 | 当前场景固定为空字符串。 |
| `cards[0].expireTime` | string | 可选 | 领取链接过期时间，支持 `yyyy-MM-dd HH:mm:ss` 或 10 位秒级 Unix 时间戳。 |
| `cards[0].jumpLink` | string | 可选 | 与 `cardNo` 保持一致，用于兼容链接展示场景。 |

## 11. cards 原始结构约定

由于已确认允许将链接作为最终卡密内容展示给买家，当前项目约定：

- `cardNo`：直接放领取链接；
- `cardPwd`：留空；
- `expireTime`：放领取链接过期时间；
- `jumpLink`：与 `cardNo` 一致。

原始结构示例如下：

```json
[
  {
    "cardNo": "https://你的域名/#/claim/abc123xyz",
    "cardPwd": "",
    "expireTime": "2026-05-05 12:00:00",
    "jumpLink": "https://你的域名/#/claim/abc123xyz"
  }
]
```

若 `buyNum > 1`，则 `cards` 原始结构中返回多个卡项，每个卡项对应一个独立领取链接。

## 12. 响应示例

### 12.1 处理中

```json
{
  "code": 200,
  "message": "接口调用成功",
  "data": {
    "orderNo": "P91KS202605040001",
    "outTradeNo": "OS202605040001",
    "orderStatus": 10,
    "failCode": 0,
    "failReason": "",
    "orderCost": 0.0000,
    "cards": ""
  }
}
```

### 12.2 查询成功

```json
{
  "code": 200,
  "message": "接口调用成功",
  "data": {
    "orderNo": "P91KS202605040001",
    "outTradeNo": "OS202605040001",
    "orderStatus": 20,
    "failCode": 0,
    "failReason": "",
    "orderCost": 0.0000,
    "cards": "加密后的cards字符串"
  }
}
```

`cards` 解密前原始结构示例：

```json
[
  {
    "cardNo": "https://你的域名/#/claim/abc123xyz",
    "cardPwd": "",
    "expireTime": "2026-05-05 12:00:00",
    "jumpLink": "https://你的域名/#/claim/abc123xyz"
  }
]
```

### 12.3 查询失败

```json
{
  "code": 200,
  "message": "接口调用成功",
  "data": {
    "orderNo": "P91KS202605040001",
    "outTradeNo": "OS202605040001",
    "orderStatus": 30,
    "failCode": 1204,
    "failReason": "商品未配置或订单无法履约"
  }
}
```

## 13. 对接说明

- 本接口是 `91卡券` 自动发货的关键接口；
- 当返回 `orderStatus = 20` 时，表示我方已准备好最终交付内容；
- 当返回 `orderStatus = 10` 时，可能是履约任务正在准备，也可能是后台正在补齐 `productNo` 对应的履约配置；
- 最终交付内容是领取链接，不是传统卡号密码；
- 领取链接域名按实际部署域名配置；
- 买家收到链接后，会进入我方系统领取页完成后续快手核销与履约流程；
- 建议 `91卡券` 侧确认：
  - `cards.cardNo` 可直接展示完整链接；
  - `jumpLink` 可按链接字段兼容展示；
  - 查询频率按异步商品标准轮询配置。
