ymt_v3_2-20260721-151016/ymt_v3_2/refine.md

351 lines
11 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.

文档类型:调用文档
## 环境地址
- 测试环境https://gateway.dev.cdlsxd.cn
- 正式环境https://market.api.86698.cn
## 测试参数
- app_id: "xxx"
- private_key: "xxx"(应用客户私钥,用于请求签名)
- public_key: "xxx"(应用平台公钥,用于响应或回调验签)
- key: "xxxx"业务参数加密key
- activity_no: "xxxx"
- sign_type: "RSA"
## 认证、加密、签名
### 业务参数加密
1. 将业务参数去掉值为“零”的参数零值判断规则未明确通常指空字符串、0等按字母升序排序后转为JSON字符串得到 `plaintext`
2. 使用应用 `key``plaintext` 进行加密,加密方式为 AES(ECB模式) 或 SM4(CBC模式),得到 `ciphertext`
### 签名规则
- 签名字符串拼接:`appid` + `timestamp` + `ciphertext`
- 使用应用私钥(`private_key`)对签名字符串进行签名,生成 `Sign`
### 请求模式
- Header 传递:`Appid`、`Timestamp`、`Sign`、`Content-Type: application/json`
- Body 传递:`{"ciphertext": "加密后的业务报文"}`
### 响应说明
- 业务响应数据通过 `data.ciphertext` 返回,需使用应用 `key` 解密解密方式与加密一致AES ECB / SM4 CBC
- 解密后得到业务响应 JSON。
### 回调验签
1. 获取 Header 中的签名信息。
2. 获取 Body 中的业务参数 `data`
3.`data` 去掉“零”值参数,按字母升序排序后转 JSON 字符串得到 `plaintext`
4. 使用应用 `key` 加密 `plaintext` 得到 `ciphertext`
5. 拼接签名字符串:`appid` + `timestamp` + `ciphertext`
6. 使用应用公钥(`public_key`)验签。
### 时间戳规则
- 格式:`yyyy-MM-dd HH:mm:ss`
- 请求时间与服务端时间误差不能超过 3 分钟。
### SDK
- Go: https://gitee.com/chengdu_blue_brothers/ymt-openapi-go-sdk.git
- Java: https://codeup.aliyun.com/lsxd/marketing/ymt-openapi-java-sdk.git
## 公共参数
### 公共 Header 请求参数
| 字段名称 | 类型 | 描述 | 示例值 |
| --- | --- | --- | --- |
| Appid | string | 分配给开发者的应用 ID | 123456 |
| Timestamp | string | 发送请求的时间,格式 yyyy-MM-dd HH:mm:ss | 2026-06-22 15:30:00 |
| Sign | string | 请求签名串 | 详见 SDK 示例 |
| Content-Type | string | 请求数据格式 | application/json |
示例:
```json
{
"Sign": ["签名字符串"],
"Appid": ["应用ID"],
"Timestamp": ["请求时间"],
"Content-Type": ["application/json"]
}
```
### 公共请求参数Body
| 字段名称 | 类型 | 描述 | 示例值 |
| --- | --- | --- | --- |
| ciphertext | string | 请求业务参数加密串 | 详见 SDK 示例 |
示例:
```json
{
"ciphertext": "加密后的业务报文"
}
```
### 公共响应参数
| 字段名称 | 类型 | 描述 |
| --- | --- | --- |
| code | int32 | 200 成功 |
| message | string | 请求描述 |
| reason | string | 错误原因,错误时返回 |
| data.ciphertext | string | 业务响应加密串 |
## 错误码
### 公共错误码
| code | reason | 错误原因 | 解决 |
| --- | --- | --- | --- |
| 500 | PANIC/其它 | 系统错误 | 联系平台处理 |
| 400 | INVALID_PAYLOAD | 请求外壳格式错误 | 请检查请求 JSON 结构 |
| 400 | MISSING_PARAM | 缺少必要参数 | 请检查 app_id、timestamp、sign、ciphertext |
| 400 | INVALID_TIMESTAMP | 时间格式错误 | 请检查时间格式 |
| 400 | DECRYPT_FAILED | 业务参数解密失败 | 请检查加密方式与密钥 |
| 401 | APP_NOT_FOUND | 应用不存在 | 请检查应用 ID |
| 401 | INVALID_SIGNATURE | 签名错误 | 请检查签名串与私钥 |
| 401 | EXPIRED_TIMESTAMP | 请求已过期 | 请检查客户端时间 |
| 429 | DUPLICATE_REQUEST | 重复请求,请稍后重试 | 请避免短时间内重复提交同一业务号 |
### 业务错误码
| code | reason | 错误原因 | 解决 |
| --- | --- | --- | --- |
| 401 | ACTIVITY_NOT_AUTH | 活动未授权 | 请检查活动授权状态 |
| 401 | MERCHANT_NOT_EXIST | 客户不存在 | 请检查客户是否存在 |
| 401 | MERCHANT_NOT_AUTH | 客户冻结 | 请检查客户授权状态 |
| 401 | MERCHANT_APP_INCOMPLETE | 客户应用配置未完善 | 请检查客户应用配置 |
| 401 | MERCHANT_APP_NOT_AUTH | 应用不存在或未授权 | 请检查客户应用授权状态 |
| 403 | ACTIVITY_EXPIRE | 活动已结束 | 请检查活动是否有效 |
| 403 | ACTIVITY_OUT_OF_STOCK | 活动剩余量不足 | 请检查活动库存 |
| 404 | ACTIVITY_NOT_EXIST | 活动不存在 | 请检查活动编号 |
| 404 | MERCHANT_ORDER_NOT_EXIST | 订单不存在 | 请检查交易号或外部业务号 |
| 404 | KEY_NOT_EXIST | key码不存在 | 请检查订单信息 |
| 400 | PARAM_FAIL | 参数错误 | 请检查业务参数 |
| 400 | PARAM_DECRYPT_FAIL | 明文参数格式错误 | 请检查密文解密后的业务报文 |
## 接口列表
### 1. 获取券码
- **请求方式**POST
- **Content-Type**application/json
- **请求路由**`/openapi/v1/key/order`
#### 业务请求参数(加密前明文)
| 字段名称 | 类型 | 描述 | 是否必填 | 示例值 |
| --- | --- | --- | --- | --- |
| out_biz_no | string | 外部业务号,幂等 | M | 123456 |
| activity_no | string | 活动编号 | M | ACT20260622001 |
| account | string | 账号,按活动类型透传 | N | 18666666666 |
| notify_url | string | 回调通知地址 | N | https://notify.example.com/openapi |
#### 业务响应参数(解密后)
| 字段名称 | 类型 | 是否必填 | 描述 |
| --- | --- | --- | --- |
| out_biz_no | string | M | 外部业务号 |
| trade_no | string | M | 交易号 |
| key | string | N | 卡密 |
| url | string | N | 链接型活动返回短链接key/url 不会同时为空 |
| valid_begin_time | string | N | 生效时间 |
| valid_end_time | string | N | 失效时间 |
| usable_num | uint32 | M | 可用次数 |
| usage_num | uint32 | M | 已使用次数 |
| status | uint32 | M | 状态1 正常2 已核销3 已作废 |
| settlement_price | float | N | 结算价 |
| account | string | N | 上报账号 |
#### 请求示例
明文业务参数:
```json
{
"out_biz_no": "order_001",
"activity_no": "ACT20260622001",
"account": "18666666666",
"notify_url": "https://notify.example.com/openapi"
}
```
请求体:
```json
{
"ciphertext": "加密后的业务报文"
}
```
#### 响应示例
异常:
```json
{
"code": 401,
"message": "Signature verification failed",
"reason": "INVALID_SIGNATURE"
}
```
成功:
```json
{
"code": 200,
"data": {
"ciphertext": "加密后的响应报文"
},
"message": "成功"
}
```
解密后示例:
```json
{
"out_biz_no": "order_001",
"trade_no": "7251449503000383488",
"key": "aZKdU9BymzR6qGRzJM",
"url": "",
"valid_begin_time": "2026-06-22 15:30:00",
"valid_end_time": "2026-12-31 23:59:59",
"usable_num": 1,
"usage_num": 0,
"status": 1,
"settlement_price": 9.9,
"account": "18666666666"
}
```
### 2. 券码查询
- **请求方式**POST
- **Content-Type**application/json
- **请求路由**`/openapi/v1/key/query`
#### 业务请求参数(加密前明文)
| 字段名称 | 类型 | 描述 | 是否必填 | 示例值 |
| --- | --- | --- | --- | --- |
| out_biz_no | string | 外部业务号,与 trade_no 二选一 | N | order_001 |
| trade_no | string | 交易号,与 out_biz_no 二选一 | N | 7251449503000383488 |
#### 业务响应参数
与“获取券码”响应参数一致。
#### 响应示例
```json
{
"code": 200,
"data": {
"out_biz_no": "order_001",
"trade_no": "7251449503000383488",
"key": "aZKdU9BymzR6qGRzJM",
"url": "",
"valid_begin_time": "2026-06-22 15:30:00",
"valid_end_time": "2026-12-31 23:59:59",
"usable_num": 1,
"usage_num": 0,
"status": 1,
"settlement_price": 9.9,
"account": "18666666666",
"ciphertext": ""
},
"message": "成功"
}
```
### 3. 券码作废
- **请求方式**POST
- **Content-Type**application/json
- **请求路由**`/openapi/v1/key/discard`
#### 业务请求参数(加密前明文)
| 字段名称 | 类型 | 描述 | 是否必填 | 示例值 |
| --- | --- | --- | --- | --- |
| out_biz_no | string | 外部业务号,与 trade_no 二选一 | N | order_001 |
| trade_no | string | 交易号,与 out_biz_no 二选一 | N | 7251449503000383488 |
#### 业务响应参数(解密后)
| 字段名称 | 类型 | 是否必填 | 描述 |
| --- | --- | --- | --- |
| out_biz_no | string | M | 外部业务号 |
| trade_no | string | M | 交易号 |
| status | uint32 | M | 3 表示已作废 |
#### 响应示例
```json
{
"code": 200,
"data": {
"out_biz_no": "order_001",
"trade_no": "7251449503000383488",
"status": 3,
"ciphertext": ""
},
"message": "成功"
}
```
### 4. 批量发卡
- **请求方式**POST
- **Content-Type**application/json
- **请求路由**`/openapi/v1/key/batch_order`
#### 业务请求参数(加密前明文)
| 字段名称 | 类型 | 描述 | 是否必填 | 示例值 |
| --- | --- | --- | --- | --- |
| out_biz_no | string | 外部业务号,幂等 | M | batch_001 |
| activity_no | string | 活动编号 | M | ACT20260622001 |
| number | int32 | 发卡数量 | M | 100 |
| notify_url | string | 回调通知地址 | N | https://notify.example.com/openapi |
#### 业务响应参数(解密后)
| 字段名称 | 类型 | 是否必填 | 描述 |
| --- | --- | --- | --- |
| out_biz_no | string | M | 外部业务号 |
| trade_no | string | M | 交易号 |
| status | string | M | 任务状态,初始返回 processing |
#### 响应示例
成功:
```json
{
"code": 200,
"data": {
"ciphertext": "加密后的响应报文"
},
"message": "成功"
}
```
解密后示例:
```json
{
"out_biz_no": "batch_001",
"trade_no": "7251449503000383499",
"status": "processing"
}
```
### 5. 批量查询
- **请求方式**POST
- **Content-Type**application/json
- **请求路由**`/openapi/v1/key/batch_query`
#### 业务请求参数(加密前明文)
| 字段名称 | 类型 | 描述 | 是否必填 | 示例值 |
| --- | --- | --- | --- | --- |
| out_biz_no | string | 外部业务号,与 trade_no 二选一 | N | batch_001 |
| trade_no | string | 交易号,与 out_biz_no 二选一 | N | 7251449503000383499 |
#### 业务响应参数(解密后)
| 字段名称 | 类型 | 是否必填 | 描述 |
| --- | --- | --- | --- |
| out_biz_no | string | M | 外部业务号 |
| trade_no | string | M | 交易号 |
| status | string | M | processing / success / failed |
| download_url | string | N | 批量任务成功后返回下载地址 |
| zip_password | string | N | 批量任务成功后返回压缩包密码 |
#### 响应示例
成功:
```json
{
"code": 200,
"data": {
"ciphertext": "加密后的响应报文"
},
"message": "成功"
}
```
解密后示例:
```json
{
"out_biz_no": "batch_001",
"trade_no": "7251449503000383499",
"status": "success",
"download_url": "https://oss.example.com/openapi_7251449503000383499.zip",
"zip_password": "123456"
}
```