开放平台(Open API)接入指南
在线文档(推荐):https://docs.rizzitgo.com — API 参考与本文同步更新
适用对象:外部合作方 / 第三方开发者
语言版本:中文(本文) · English
本指南说明如何接入本系统的 /open-api/* 开放接口。开放接口与 C 端 /app-api、管理端 /admin-api 物理隔离,统一采用 OAuth2 client_credentials(客户端模式) 鉴权,并按 scope(授权范围)控制每个应用可调用的接口集合。
1. 概述与架构
- 鉴权模型:标准 OAuth2 客户端模式。合作方使用平台下发的
clientId + clientSecret换取短期access_token,再用令牌调用业务接口。开放平台不涉及用户登录、授权码、密码等模式。 - 接口隔离:所有开放接口挂载在
/open-api/**前缀下,由网关独立路由。开放令牌的用户类型固定为OPEN_API,无法访问/admin-api、/app-api,从源头规避越权。 - 权限粒度:每个接口由其所需的
scope控制(如商品详情接口需goods:detail:read)。应用只能调用其被授予的 scope 对应的接口。 - 多租户:请求需携带
tenant-id,且必须与换取令牌时一致。
调用时序:
sequenceDiagram
participant P as 合作方应用
participant GW as API 网关
participant SYS as 系统服务
participant BIZ as 业务服务
P->>GW: POST /open-api/system/oauth2/token<br/>Basic(clientId:clientSecret)
GW->>SYS: 转发取令牌请求
SYS-->>P: access_token(userType=OPEN_API, expires_in)
P->>GW: POST /open-api/goods/detail<br/>Bearer access_token + tenant-id
GW->>BIZ: 校验令牌 + scope 后转发
BIZ-->>P: 业务数据(统一 {code,data,msg})
2. 名词术语表
| 名词 | 说明 |
|---|---|
clientId |
客户端编号,应用的公开标识。 |
clientSecret |
客户端密钥,仅创建/重置时展示一次,等同口令,务必保密。 |
scope |
授权范围,决定可调用哪些接口,多个用空格分隔(如 goods:detail:read)。 |
tenant-id |
租户编号,多租户隔离标识,放入请求头。 |
access_token |
访问令牌,调用业务接口的凭证,有时效(见 expires_in)。 |
expires_in |
令牌剩余有效期,单位秒。 |
userType |
用户类型,开放平台令牌固定为 OPEN_API。 |
3. 接入流程
- 联系平台运营,提交:合作方名称、联系人(姓名/邮箱/电话)、回调来源 IP(可选,用于 IP 白名单)、需要的接口范围(scope)。
- 运营在管理后台「开放平台 / 合作方应用」创建应用,下发:
clientId(客户端编号)clientSecret(客户端密钥,仅展示一次,请妥善保存)- 授权
scope列表(如goods:detail:read) tenant-id(租户编号)
- 合作方使用
clientId + clientSecret换取access_token,再携带令牌调用业务接口。
上线前自检清单:
- 已安全保存
clientSecret(建议放入密钥管理系统,禁止写入前端/客户端代码或日志)。 - 已确认所需
scope均已开通。 - 调用方出口 IP 已加入白名单(若运营启用了白名单)。
- 已实现令牌缓存(按
expires_in复用,避免每次请求都换取令牌)。 - 已实现
401自动重新取令牌、429退避重试。
4. 环境与域名
| 环境 | 网关域名 | 说明 |
|---|---|---|
| 沙箱 / 测试 | api-qa.rizzitbuy.com |
联调使用,数据与生产隔离。 |
| 生产 | api.rizzitgo.com |
正式环境。 |
生产网关域名为
api.rizzitgo.com(文档示例已采用),测试/沙箱网关域名为api-qa.rizzitbuy.com。{租户编号}、{clientId}、{clientSecret}等占位符请替换为运营下发的实际值。
5. 获取访问令牌
- 方法与路径:
POST /open-api/system/oauth2/token - 认证:HTTP Basic,将
clientId:clientSecret拼接后做 Base64 编码,放入Authorization头(即Authorization: Basic base64(clientId:clientSecret))。 - Content-Type:
application/x-www-form-urlencoded
请求参数:
| 参数 | 位置 | 必填 | 说明 |
|---|---|---|---|
Authorization |
Header | 是 | Basic base64(clientId:clientSecret) |
tenant-id |
Header | 是 | 租户编号 |
grant_type |
Body | 是 | 固定为 client_credentials |
scope |
Body | 否 | 多个用空格分隔;不传则使用应用已授权的全部 scope |
请求示例:
curl -X POST 'https://api.rizzitgo.com/open-api/system/oauth2/token' \
-H 'Authorization: Basic {base64(clientId:clientSecret)}' \
-H 'tenant-id: {租户编号}' \
-d 'grant_type=client_credentials' \
-d 'scope=goods:detail:read'
响应字段:
| 字段 | 类型 | 说明 |
|---|---|---|
access_token |
string | 访问令牌,后续业务请求携带 |
refresh_token |
string | 客户端模式下为空字符串(无刷新令牌,过期后重新换取) |
token_type |
string | 固定 Bearer |
expires_in |
number | 令牌有效期(秒),示例 1800 |
scope |
string | 实际授予的授权范围,多个用空格分隔 |
响应示例:
{
"code": 0,
"data": {
"access_token": "xxxxxxxx",
"refresh_token": "",
"token_type": "Bearer",
"expires_in": 1800,
"scope": "goods:detail:read"
},
"msg": ""
}
说明:开放平台仅支持
client_credentials;传入其它grant_type(如authorization_code/password/refresh_token)会返回400,提示「开放平台仅支持 client_credentials 授权模式」。令牌的userType固定为OPEN_API,无法访问/admin-api、/app-api接口。
6. 调用业务接口
所有业务请求需带:
| Header | 必填 | 说明 |
|---|---|---|
Authorization |
是 | Bearer {access_token} |
tenant-id |
是 | 与换取令牌时一致的租户编号 |
Content-Type |
是(有 body 时) | application/json |
7. 接口参考
7.1 查询商品详情(试点)
- 方法与路径:
POST /open-api/goods/detail - 所需 scope:
goods:detail:read
请求参数(application/json):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
goodsId |
string | 是 | 商品 ID,不能为空,且不能为 0 |
source |
integer | 是 | 商品来源:1=淘宝,2=1688,3=微店 |
请求示例:
curl -X POST 'https://api.rizzitgo.com/open-api/goods/detail' \
-H 'Authorization: Bearer {access_token}' \
-H 'tenant-id: {租户编号}' \
-H 'Content-Type: application/json' \
-d '{"goodsId":"123456","source":1}'
响应字段:
| 字段 | 类型 | 说明 |
|---|---|---|
goodsId |
string | 商品 ID |
goodsTitle |
string | 商品标题 |
goodsDetailUrl |
string | 商品详情页地址 |
goodsPicUrl |
string | 商品主图 |
price |
string | 商品价格(元) |
priceInCents |
number | 商品价格(分) |
orginalPrice |
string | 商品原价(元) |
stockNum |
string | 商品库存 |
salesCount |
string | 商品销售数量 |
goodsImageUrlList |
string[] | 商品图片列表 |
postFee |
string | 商品运费(元) |
goodsSource |
integer | 商品来源:1=淘宝,2=1688,3=微店 |
响应示例:
{
"code": 0,
"data": {
"goodsId": "123456",
"goodsTitle": "示例商品",
"goodsDetailUrl": "https://example.com/item/123456",
"goodsPicUrl": "https://example.com/img/123456.jpg",
"price": "99.00",
"priceInCents": 9900,
"orginalPrice": "129.00",
"stockNum": "1000",
"salesCount": "532",
"goodsImageUrlList": [
"https://example.com/img/1.jpg",
"https://example.com/img/2.jpg"
],
"postFee": "0.00",
"goodsSource": 1
},
"msg": ""
}
7.2 查询商品质检详情
- 方法与路径:
POST /open-api/goods/qc-detail - 所需 scope:
goods:qc:read
请求参数(application/json):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
goodsId |
string | 是 | 商品 ID,不能为空,且不能为 0 |
source |
integer | 是 | 商品来源:1=淘宝,2=1688,3=微店 |
请求示例:
curl -X POST 'https://api.rizzitgo.com/open-api/goods/qc-detail' \
-H 'Authorization: Bearer {access_token}' \
-H 'tenant-id: {租户编号}' \
-H 'Content-Type: application/json' \
-d '{"goodsId":"123456","source":1}'
响应字段:
| 字段 | 类型 | 说明 |
|---|---|---|
goodsId |
string | 商品 ID |
qcPathList |
string[] | 质检照片列表 |
length |
string | 长(cm) |
width |
string | 宽(cm) |
height |
string | 高(cm) |
weight |
string | 重量(g) |
volume |
string | 体积(cm³) |
completionTime |
string | 质检完成时间(yyyy-MM-dd HH:mm:ss) |
说明:出于内部信息保护,质检接口不返回质检员/拍照员的 ID 与姓名等员工信息。
响应示例:
{
"code": 0,
"data": {
"goodsId": "123456",
"qcPathList": [
"https://example.com/qc/1.jpg",
"https://example.com/qc/2.jpg"
],
"length": "30",
"width": "20",
"height": "10",
"weight": "500",
"volume": "6000",
"completionTime": "2026-06-02 18:30:00"
},
"msg": ""
}
7.3 查询商品质检图片列表
- 方法与路径:
POST /open-api/goods/qc-images - 所需 scope:
goods:qc:read - 请求参数:同 7.2(
goodsId+source)
请求示例:
curl -X POST 'https://api.rizzitgo.com/open-api/goods/qc-images' \
-H 'Authorization: Bearer {access_token}' \
-H 'tenant-id: {租户编号}' \
-H 'Content-Type: application/json' \
-d '{"goodsId":"123456","source":1}'
响应示例: data 为质检图片地址数组。
{
"code": 0,
"data": [
"https://example.com/qc/1.jpg",
"https://example.com/qc/2.jpg"
],
"msg": ""
}
7.4 按时间分页查询质检数据
- 方法与路径:
POST /open-api/goods/qc-page - 所需 scope:
goods:qc:read
请求参数(application/json):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
pageNo |
integer | 是 | 页码,从 1 开始 |
pageSize |
integer | 是 | 每页条数 |
goodsId |
string | 否 | 按商品 ID 精确过滤 |
source |
integer | 否 | 商品来源:1=淘宝,2=1688,3=微店 |
createTime |
string[] | 否 | 记录创建时间区间 [开始, 结束],格式 yyyy-MM-dd HH:mm:ss |
completionTime |
string[] | 否 | 质检完成时间区间 [开始, 结束],格式 yyyy-MM-dd HH:mm:ss |
请求示例:
curl -X POST 'https://api.rizzitgo.com/open-api/goods/qc-page' \
-H 'Authorization: Bearer {access_token}' \
-H 'tenant-id: {租户编号}' \
-H 'Content-Type: application/json' \
-d '{
"pageNo": 1,
"pageSize": 10,
"completionTime": ["2026-06-01 00:00:00", "2026-06-02 23:59:59"]
}'
响应字段: data.list 为质检详情数组(字段同 7.2),data.total 为总条数。
{
"code": 0,
"data": {
"list": [
{
"goodsId": "123456",
"qcPathList": ["https://example.com/qc/1.jpg"],
"length": "30",
"width": "20",
"height": "10",
"weight": "500",
"volume": "6000",
"completionTime": "2026-06-02 18:30:00"
}
],
"total": 1
},
"msg": ""
}
7.5 图片识别搜索商品列表
- 方法与路径:
POST /open-api/goods/search-by-images - 所需 scope:
goods:search:read
请求参数(application/json):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
imagesUrl |
string | 是 | 图片 URL,需为可公网访问的图片地址(支持常见图片后缀,如 .jpg/.png 等);格式不合法返回「图片链接格式不合法」 |
page |
integer | 是 | 当前页,从 1 开始 |
source |
integer | 是 | 商品来源:1=淘宝,2=1688,3=微店 |
请求示例:
curl -X POST 'https://api.rizzitgo.com/open-api/goods/search-by-images' \
-H 'Authorization: Bearer {access_token}' \
-H 'tenant-id: {租户编号}' \
-H 'Content-Type: application/json' \
-d '{"imagesUrl":"https://example.com/img/query.jpg","page":1,"source":1}'
响应字段:
| 字段 | 类型 | 说明 |
|---|---|---|
goodsList |
object[] | 商品列表,元素字段见下表 |
page |
integer | 当前页 |
size |
integer | 页大小 |
totalPage |
integer | 总页数 |
totalCount |
integer | 总条数 |
goodsList 元素字段:
| 字段 | 类型 | 说明 |
|---|---|---|
goodsId |
string | 商品 ID |
goodsTitle |
string | 商品标题 |
goodsDetailUrl |
string | 商品详情页地址 |
goodsPicUrl |
string | 商品主图 |
goodsSource |
integer | 商品来源:1=淘宝,2=1688,3=微店 |
price |
string | 商品价格(元) |
priceInCents |
number | 商品价格(分) |
originPriceInCents |
number | 商品原价/划线价(分) |
salesCount |
string | 商品销售数量 |
hasDiscount |
boolean | 是否享受优惠活动 |
discountPriceInCents |
number | 优惠后价格(分) |
说明:出于内部信息保护,开放接口不返回会员维度的收藏状态、个性化分享链接等内部字段。
响应示例:
{
"code": 0,
"data": {
"goodsList": [
{
"goodsId": "123456",
"goodsTitle": "示例商品",
"goodsDetailUrl": "https://example.com/item/123456",
"goodsPicUrl": "https://example.com/img/123456.jpg",
"goodsSource": 1,
"price": "99.00",
"priceInCents": 9900,
"originPriceInCents": 9900,
"salesCount": "532",
"hasDiscount": true,
"discountPriceInCents": 8900
}
],
"page": 1,
"size": 20,
"totalPage": 5,
"totalCount": 100
},
"msg": ""
}
7.6 商品链接解析
- 方法与路径:
POST /open-api/goods/search-url-parse - 所需 scope:
goods:search:read - 说明:解析淘宝/天猫、1688、微店及常见代购站(cssbuy、cnfans、hoobuy 等)的短链或长链,提取平台商品 ID 与来源。无法识别的链接返回「商品链接解析失败」。
请求参数(application/json):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
searchUrl |
string | 是 | 待解析的商品链接,支持短链、长链及含杂质的粘贴文本(会自动剔除空格、中文等) |
rno |
string | 否 | 推广码;非空时拼接到响应 targetGoodsDetailUrl 中 |
请求示例:
curl -X POST 'https://api.rizzitgo.com/open-api/goods/search-url-parse' \
-H 'Authorization: Bearer {access_token}' \
-H 'tenant-id: {租户编号}' \
-H 'Content-Type: application/json' \
-d '{"searchUrl":"https://detail.tmall.com/item.htm?id=768650559131","rno":"ABC123"}'
响应字段:
| 字段 | 类型 | 说明 |
|---|---|---|
goodsId |
string | 平台商品 ID |
goodsSource |
integer | 商品来源:1=淘宝,2=1688,3=微店,4=其它 |
goodsSourceName |
string | 商品来源名称(taobao/alibaba/weidian) |
goodsDetailUrl |
string | 原平台商品详情页地址 |
targetGoodsDetailUrl |
string | 站内分享链接(rno 非空时含推广码) |
success |
boolean | 是否解析成功 |
searchUrl |
string | 清洗后的输入链接 |
响应示例:
{
"code": 0,
"data": {
"goodsId": "768650559131",
"goodsSource": 1,
"goodsSourceName": "taobao",
"goodsDetailUrl": "https://detail.tmall.com/item.htm?id=768650559131",
"targetGoodsDetailUrl": "https://www.rizzitgo.com/detailPage?goodsId=768650559131&source=1&rno=ABC123",
"success": true,
"searchUrl": "https://detail.tmall.com/item.htm?id=768650559131"
},
"msg": ""
}
7.7 优惠券随机兑换(按邮箱发券)
- 方法与路径:
POST /open-api/promotion/redeem-code/redeem - 所需 scope:
promotion:redeem-code:redeem - 说明:使用平台后台预先配置好的兑换码(可绑定 N 张优惠券模板,并按权重随机命中一张),为指定邮箱对应的会员发放优惠券。会员必须已存在(按邮箱精确匹配),否则返回「会员不存在」,本接口不会自动创建会员。
请求参数(application/json):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
code |
string | 是 | 兑换码 |
email |
string | 是 | 收券会员邮箱,需与平台会员账号一致(精确匹配) |
requestId |
string | 否 | 外部请求流水号,用于幂等防重;同一 requestId 在 10 分钟内仅处理一次,重复请求返回「请求重复,请勿重试」 |
请求示例:
curl -X POST 'https://api.rizzitgo.com/open-api/promotion/redeem-code/redeem' \
-H 'Authorization: Bearer {access_token}' \
-H 'tenant-id: {租户编号}' \
-H 'Content-Type: application/json' \
-d '{"code":"E602F4DC626E4948","email":"[email protected]","requestId":"REQ-20260604-0001"}'
响应字段:
| 字段 | 类型 | 说明 |
|---|---|---|
code |
string | 兑换码 |
email |
string | 收券会员邮箱 |
prize |
string | 本次中奖/获得的奖品描述,按券折扣自动生成:折扣券为 30% OFF,满减券为 $10 OFF(金额固定美元,由分换算为元);多张时以逗号分隔,无折扣信息时回退为券标题 |
success |
boolean | 兑换是否成功 |
message |
string | 兑换结果描述 |
coupons |
object[] | 本次兑换得到的优惠券列表(随机模式下通常为 1 张),元素字段见下表 |
coupons 元素字段:
| 字段 | 类型 | 说明 |
|---|---|---|
templateId |
number | 优惠券模板 ID |
title |
string | 优惠券标题 |
couponType |
integer | 优惠券类型:1=运费券,2=商品券,3=全场券 |
discountType |
integer | 优惠类型:1=满元减,2=满元折 |
discountPrice |
number | 优惠金额(单位:分) |
discountPercent |
number | 折扣百分比(discountType=2 时有效,如 80 表示 8 折) |
usePriceCondition |
number | 门槛:满多少金额可用(单位:分,0 表示不限制) |
validStartTime |
string | 生效开始时间 |
validEndTime |
string | 生效结束时间 |
响应示例:
{
"code": 0,
"data": {
"code": "E602F4DC626E4948",
"email": "[email protected]",
"prize": "30% OFF",
"success": true,
"message": "兑换成功,共获得 1 张优惠券",
"coupons": [
{
"templateId": 1001,
"title": "满100减10",
"couponType": 2,
"discountType": 1,
"discountPrice": 1000,
"discountPercent": null,
"usePriceCondition": 10000,
"validStartTime": "2026-06-01 00:00:00",
"validEndTime": "2026-06-30 23:59:59"
}
]
},
"msg": ""
}
业务错误:邮箱为空返回「邮箱不能为空」;按邮箱查不到会员返回「会员不存在」;兑换码不存在/已过期/已作废、超过每人或每日(含 IP)限兑次数等,沿用兑换码模块既有错误码与提示。兑换码的限领/限兑/有效期等限制在后台配置,请按运营约定的额度调用。
8. 撤销令牌(可选)
- 方法与路径:
POST /open-api/system/oauth2/token/revoke - 认证:同获取令牌(HTTP Basic)
- 参数:
token={access_token}(application/x-www-form-urlencoded)
curl -X POST 'https://api.rizzitgo.com/open-api/system/oauth2/token/revoke' \
-H 'Authorization: Basic {base64(clientId:clientSecret)}' \
-H 'tenant-id: {租户编号}' \
-d 'token={access_token}'
9. 统一响应结构与错误码
所有接口统一返回结构 { code, data, msg }:code = 0 表示成功,其余为失败,msg 为提示信息。
高频错误:
| HTTP / code | 含义 | 处理建议 |
|---|---|---|
401 |
令牌缺失 / 失效 / 过期 | 重新调用 token 接口获取新令牌后重试 |
403 |
scope 不足 / 用户类型不符 | 联系运营开通对应 scope |
429 |
触发限流(按 clientId 维度) | 降低调用频率,指数退避重试,必要时申请提升 QPS |
400 |
参数错误 / 不支持的 grant_type | 检查请求参数与授权类型 |
10. 安全与限制
- IP 白名单:如运营为应用配置了
ipWhitelist(逗号分隔,多个 IP),仅白名单内来源 IP 可调用;为空表示不限制。 - 限流:可按应用配置每秒 QPS(
rateLimitQps,0表示不限)。超限返回429。 - 密钥保管:
clientSecret仅展示一次;切勿写入前端/客户端代码、日志或版本库。如泄漏请立即联系运营在「合作方应用」中「重置密钥」,旧密钥即时失效。 - 令牌缓存与并发刷新:请在客户端缓存
access_token,在expires_in内复用,避免高频换取令牌;建议在过期前留出缓冲(如剩余 < 60s 时提前刷新),并对刷新过程加锁,防止并发重复换取。 - 重试与退避:遇
401自动重新取令牌并重试一次;遇429/网络抖动采用指数退避(如 1s、2s、4s)重试,设上限。 - 多租户:请求务必携带与令牌一致的
tenant-id,避免跨租户访问被拒绝。
11. 代码示例(取令牌 → 调商品详情)
cURL
# 1) 取令牌
TOKEN=$(curl -s -X POST 'https://api.rizzitgo.com/open-api/system/oauth2/token' \
-H 'Authorization: Basic {base64(clientId:clientSecret)}' \
-H 'tenant-id: {租户编号}' \
-d 'grant_type=client_credentials' \
-d 'scope=goods:detail:read' | jq -r '.data.access_token')
# 2) 调商品详情
curl -X POST 'https://api.rizzitgo.com/open-api/goods/detail' \
-H "Authorization: Bearer ${TOKEN}" \
-H 'tenant-id: {租户编号}' \
-H 'Content-Type: application/json' \
-d '{"goodsId":"123456","source":1}'
Java(JDK 11+ HttpClient)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Base64;
public class OpenApiDemo {
static final String GATEWAY = "https://api.rizzitgo.com";
static final String TENANT_ID = "{租户编号}";
static final String CLIENT_ID = "{clientId}";
static final String CLIENT_SECRET = "{clientSecret}";
public static void main(String[] args) throws Exception {
HttpClient http = HttpClient.newHttpClient();
// 1) 取令牌
String basic = Base64.getEncoder()
.encodeToString((CLIENT_ID + ":" + CLIENT_SECRET).getBytes());
HttpRequest tokenReq = HttpRequest.newBuilder()
.uri(URI.create(GATEWAY + "/open-api/system/oauth2/token"))
.header("Authorization", "Basic " + basic)
.header("tenant-id", TENANT_ID)
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(
"grant_type=client_credentials&scope=goods:detail:read"))
.build();
HttpResponse<String> tokenResp = http.send(tokenReq, HttpResponse.BodyHandlers.ofString());
System.out.println(tokenResp.body());
// 实际项目中请用 JSON 库解析 data.access_token
String accessToken = "<从 tokenResp 解析 data.access_token>";
// 2) 调商品详情
HttpRequest bizReq = HttpRequest.newBuilder()
.uri(URI.create(GATEWAY + "/open-api/goods/detail"))
.header("Authorization", "Bearer " + accessToken)
.header("tenant-id", TENANT_ID)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(
"{\"goodsId\":\"123456\",\"source\":1}"))
.build();
HttpResponse<String> bizResp = http.send(bizReq, HttpResponse.BodyHandlers.ofString());
System.out.println(bizResp.body());
}
}
Python(requests)
import base64
import requests
GATEWAY = "https://api.rizzitgo.com"
TENANT_ID = "{租户编号}"
CLIENT_ID = "{clientId}"
CLIENT_SECRET = "{clientSecret}"
# 1) 取令牌
basic = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode()
token_resp = requests.post(
f"{GATEWAY}/open-api/system/oauth2/token",
headers={"Authorization": f"Basic {basic}", "tenant-id": TENANT_ID},
data={"grant_type": "client_credentials", "scope": "goods:detail:read"},
)
access_token = token_resp.json()["data"]["access_token"]
# 2) 调商品详情
biz_resp = requests.post(
f"{GATEWAY}/open-api/goods/detail",
headers={
"Authorization": f"Bearer {access_token}",
"tenant-id": TENANT_ID,
"Content-Type": "application/json",
},
json={"goodsId": "123456", "source": 1},
)
print(biz_resp.json())
12. 常见问题(FAQ)
- 频繁收到
401? 多半是令牌过期或未缓存。请在expires_in内复用令牌,并在过期前刷新;确认Authorization头为Bearer {access_token}格式。 - 收到
403? 说明当前应用未被授予该接口所需的scope,请联系运营开通。 - 收到
429? 触发了按clientId维度的 QPS 限流,请降低频率并退避重试,必要时申请提升配额。 - 跨租户访问被拒? 请确认请求头
tenant-id与换取令牌时一致。 clientSecret泄漏怎么办? 立即联系运营「重置密钥」,旧密钥即时失效,并更新你侧的密钥配置。
13. 在线接口文档
网关 Knife4j 聚合的 Swagger 中包含 /open-api/** 分组,可在线查看每个接口的入参/出参定义,便于联调。
14. 文档版本
| 版本 | 日期 | 变更说明 |
|---|---|---|
| v1.0 | 2026-06 | 首次发布:OAuth2 客户端模式接入、商品详情试点接口。 |
| v1.1 | 2026-06 | 新增商品质检接口:质检详情、质检图片、按时间分页查询质检数据(scope goods:qc:read);token_type 调整为 Bearer。 |
| v1.2 | 2026-06 | 新增图片识别搜索商品接口 /open-api/goods/search-by-images(scope goods:search:read)。 |
| v1.3 | 2026-06 | 新增优惠券随机兑换接口 /open-api/promotion/redeem-code/redeem(scope promotion:redeem-code:redeem),按邮箱为会员发券。 |
| v1.4 | 2026-06 | 新增商品链接解析接口 /open-api/goods/search-url-parse(scope goods:search:read),支持可选推广码 rno。 |