开放API接入文档 v1.0
面向租户自研系统(App / 小程序 / 自有系统)的标准化接入指南
简介
开放API允许租户通过标准 HTTP 接口驱动商城中台业务,实现商品查询、订单管理、售后处理、用户管理和营销功能,无需依赖中台托管的 H5/PC 商城前端。
接口基础信息
| 项目 | 说明 |
|---|---|
| Base URL | http://mall.hzkting.com:9988/open/api/v1 |
| 协议 | HTTP |
| 数据格式 | JSON(Content-Type: application/json) |
| 字符编码 | UTF-8 |
接入流程
- 联系平台运营,在管理后台创建开放应用,获取
AppKey和AppSecret - 根据业务需要申请对应的权限范围(PermScope)
- 在沙箱模式下完成接口联调测试
- 测试通过后,由运营人员审核升级为生产模式
权限范围(PermScope)
| 权限值 | 说明 | 覆盖接口 |
|---|---|---|
PRODUCT_READ | 商品查询 | 商品列表、详情、价格、库存、分类 |
ORDER_WRITE | 订单写入 | 创建订单、取消订单 |
ORDER_READ | 订单查询 | 订单详情、列表、物流 |
AFTERSALE | 售后管理 | 申请售后、查询、取消 |
USER | 用户管理 | 注册、登录、个人信息、收货地址 |
PROMOTION | 营销 | 优惠券列表、领取 |
FINANCE | 资金流 | 账户余额、资金流水、结算账单、提现 |
签名鉴权
所有接口请求必须携带以下三个请求头:
| 请求头 | 说明 | 示例 |
|---|---|---|
X-App-Key | 应用标识,由平台分配 | a1b2c3d4e5f6... |
X-Timestamp | Unix 时间戳(秒),与服务器时差不超过 300 秒 | 1745548800 |
X-Sign | 请求签名,见下方签名算法 | A3F2B1C4... |
签名算法
- 收集所有请求参数:URL Query 参数 + JSON Body 顶层字段(嵌套对象不参与签名,空值字段跳过)
- 将参数按 key 字母升序排列,拼接为
key1=value1&key2=value2&... - 末尾追加
&app_secret={AppSecret} - 对拼接字符串执行 MD5,结果转大写即为签名值
拼接:
page=1&product_id=123&app_secret=mysecret签名:
MD5("page=1&product_id=123&app_secret=mysecret").toUpperCase()Java 示例
public static String sign(Map<String, String> params, String appSecret) {
TreeMap<String, String> sorted = new TreeMap<>(params);
StringBuilder sb = new StringBuilder();
sorted.forEach((k, v) -> {
if (v != null && !v.isEmpty()) {
if (sb.length() > 0) sb.append("&");
sb.append(k).append("=").append(v);
}
});
sb.append("&").append("app_secret=").append(appSecret);
return DigestUtils.md5DigestAsHex(sb.toString().getBytes(StandardCharsets.UTF_8)).toUpperCase();
}
Python 示例
import hashlib
def sign(params: dict, app_secret: str) -> str:
items = sorted((k, v) for k, v in params.items() if v)
query = "&".join(f"{k}={v}" for k, v in items)
query += f"&app_secret={app_secret}"
return hashlib.md5(query.encode()).hexdigest().upper()
完整 curl 示例
# 1. 拼接签名字符串:pageNo=1&app_secret=mysecret
# 2. 计算签名:MD5("pageNo=1&app_secret=mysecret").toUpperCase()
# 3. 发起请求:
curl -X GET "http://mall.hzkting.com:9988/open/api/v1/products?pageNo=1" \
-H "X-App-Key: abc123" \
-H "X-Timestamp: 1745548800" \
-H "X-Sign: D41D8CD98F00B204E9800998ECF8427E"
消费者 Token(X-User-Token)
用户登录成功后,响应体中返回 user_token。后续调用需要消费者身份的接口时,需在请求头中携带:
| 请求头 | 说明 |
|---|---|
X-User-Token | 用户登录后获取的 Token,有效期 7 天 |
统一响应格式
所有接口均返回以下 JSON 结构:
{
"code": 0, // 0=成功,非0=失败
"message": "success",
"data": { ... }, // 业务数据,失败时为 null
"is_sandbox": false // true 表示沙箱模式响应
}
| 字段 | 类型 | 说明 |
|---|---|---|
code | int | 0 表示成功,非 0 表示失败,见错误码表 |
message | string | 成功时为 "success",失败时为错误描述 |
data | object/null | 业务数据 |
is_sandbox | boolean | 沙箱模式下写单操作返回 true |
错误码
| HTTP | code | 错误码 | 说明 |
|---|---|---|---|
| 400 | 1001 | MISSING_HEADER | 缺少必要请求头 X-App-Key / X-Timestamp / X-Sign |
| 401 | 1002 | SIGN_INVALID | 签名错误或应用不存在 |
| 401 | 1003 | TIMESTAMP_EXPIRED | 时间戳与服务器时差超过 5 分钟 |
| 401 | 1004 | USER_TOKEN_INVALID | X-User-Token 无效或已过期 |
| 403 | 1005 | IP_FORBIDDEN | 请求 IP 不在白名单 |
| 403 | 1006 | PERMISSION_DENIED | 应用无该接口的权限范围 |
| 403 | 1007 | APP_DISABLED | 应用已停用 |
| 403 | 1008 | TENANT_DISABLED | 租户已停用 |
| 403 | 1009 | PRODUCT_NOT_AUTHORIZED | 商品不在租户授权范围 |
| 403 | 1010 | ORDER_NOT_AUTHORIZED | 订单不属于当前租户 |
| 403 | 1011 | AFTERSALE_NOT_AUTHORIZED | 售后单不属于当前租户 |
| 409 | 1012 | PHONE_ALREADY_EXISTS | 手机号已注册 |
| 409 | 1013 | COUPON_ALREADY_CLAIMED | 优惠券已领取 |
| 400 | 1014 | COUPON_UNAVAILABLE | 优惠券不可用或已过期 |
| 429 | 1015 | RATE_LIMIT_EXCEEDED | 调用频率超限(默认 60 次/分钟) |
| 403 | 1016 | FINANCE_NOT_AUTHORIZED | 账单或提现记录不属于当前租户 |
| 502 | 2001 | UPSTREAM_ERROR | 上游平台调用失败 |
| 500 | 9999 | INTERNAL_ERROR | 系统内部错误 |
商品接口
所有商品接口需要 PRODUCT_READ 权限。
响应示例
{"code":0,"data":{"total":100,"pageNo":1,"pageSize":20,"products":[{"unifiedProductId":"123456","productName":"京东京造示例商品","brandName":"京东京造","brandTag":"JD_JINGZAO","salePrice":99.00,"mainImage":"https://...","status":"ON_SALE"}]}}
响应示例
{"code":0,"data":{"unifiedProductId":"123456","platformProductId":"SKU001","productName":"示例商品","brandName":"品牌A","category":"食品","salePrice":99.00,"costPrice":60.00,"mainImage":"https://...","status":"ON_SALE","stockStatus":"IN_STOCK","stockNum":500}}
Query 参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| areaCode | string | 可选 | 区域编码(省_市_区_镇),用于区域差价 |
响应示例
{"code":0,"data":{"productId":"123456","salePrice":99.00,"costPrice":60.00,"marketPrice":128.00}}
Query 参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| areaCode | string | 可选 | 区域编码 |
响应示例
{"code":0,"data":{"productId":"123456","hasStock":true,"stockNum":500,"areaCode":"1_72_2799_0"}}
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| parentId | string | 可选 | 父分类ID,不传则返回顶级分类 |
响应字段(data[])
| 字段 | 类型 | 说明 |
|---|---|---|
| categoryId | string | 分类ID |
| categoryName | string | 分类名称 |
| parentId | string | 父分类ID,顶级为 null |
| level | int | 层级(1=一级,2=二级...) |
响应示例
{"code":0,"data":[{"categoryId":"100","categoryName":"食品饮料","parentId":null,"level":1},{"categoryId":"101","categoryName":"休闲零食","parentId":"100","level":2}]}
返回当前可用于 /products 接口 brandTag 参数的所有品牌标签及其中文名。
建议:客户端不要硬编码品牌标签,应在每次启动或按需调用此接口获取最新列表。新接入的品牌渠道(如新增供应商)会自动出现在返回结果中,无需修改客户端代码。
响应示例
{
"code": 0,
"data": [
{"brandTag": "JD_JINGZAO", "brandLabel": "京东京造"},
{"brandTag": "YIHAO", "brandLabel": "1号店"},
{"brandTag": "JD_FRESH", "brandLabel": "京东生鲜"},
{"brandTag": "NETEASE_YX", "brandLabel": "网易严选"},
{"brandTag": "SAM", "brandLabel": "山姆超市"},
{"brandTag": "MINISO", "brandLabel": "名创优品"}
]
}
返回中台全部启用类目(status=ENABLED),按 parentId 组装为树形结构。用于客户端展示中台类目、与商品接口返回的 saasCategoryId 对应。
PRODUCT_READ 权限。可加 ?flat=true 返回平铺列表(不组树,children 恒为空)。此接口返回中台自建类目,与 /categories(平台商品分类)不同。响应示例
{
"code": 0,
"data": [
{
"id": 3,
"categoryCode": "food",
"categoryName": "食品",
"parentId": 0,
"sortOrder": 0,
"icon": null,
"children": [
{"id": 31, "categoryCode": "food_snack", "categoryName": "休闲零食", "parentId": 3, "sortOrder": 0, "icon": null, "children": []}
]
}
]
}
用于子品牌频道的分类钻取(如「京东精选 → 休闲食品 → 肉干小食」)。从已同步的本地商品库按 platformCode + brandTag 反查,按 category 路径切出指定层级聚合,返回 categoryId / categoryName / productCount。
Query 参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| brandTag | string | 必填 | 品牌标签(如 JD_JINGZAO),通过 /brand-tags 拿列表 |
| platformCode | string | 可选 | 平台编码,默认 JD_VOP。其他平台需联调确认 category 字段分隔符;当前 brandTag 渠道主要在京东 VOP 下 |
| parentCatId | string | 可选 | 父类目 ID。仅 level=2/3 时使用;level=0/1 时传入会返回 400。格式:数字字母下划线分号-,长度 1~32 |
| level | int | 可选 | 目标层级。1=一级(默认);2=二级;3=三级;0=返回三级树(一次拿全,避免多次往返) |
| sort | string | 可选 | 排序方式。count(按商品数倒序,默认)/ name(按名称升序) |
/products 一致)。同一品牌在不同租户下看到的类目和商品数可能不同。响应字段(data[])
| 字段 | 类型 | 说明 |
|---|---|---|
| categoryId | string | 类目 ID(来自 ext_platform_category.cat_id) |
| categoryName | string | 类目名称;类目表无对应记录时回退为 ID |
| level | int | 层级 1/2/3 |
| productCount | long | 该类目下商品数(已排除 OFF_SALE/DELETED 状态;level=0 时含子节点累计) |
| children | array | 子节点列表,仅在 level=0(三级树)模式下出现 |
示例 1:拿京东精选的一级类目
GET /open/api/v1/brand-categories?brandTag=JD_JINGZAO&level=1
{
"code": 0,
"data": [
{"categoryId": "670", "categoryName": "休闲食品", "level": 1, "productCount": 124},
{"categoryId": "1316", "categoryName": "粮油副食", "level": 1, "productCount": 87},
{"categoryId": "1320", "categoryName": "乳饮冲调", "level": 1, "productCount": 56},
{"categoryId": "1620", "categoryName": "护肤美妆", "level": 1, "productCount": 32},
{"categoryId": "737", "categoryName": "家电数码", "level": 1, "productCount": 41}
]
}
示例 2:拿"休闲食品"下的二级类目
GET /open/api/v1/brand-categories?brandTag=JD_JINGZAO&parentCatId=670&level=2
{
"code": 0,
"data": [
{"categoryId": "729", "categoryName": "肉干小食", "level": 2, "productCount": 45},
{"categoryId": "730", "categoryName": "膨化食品", "level": 2, "productCount": 31},
{"categoryId": "731", "categoryName": "辣条豆干", "level": 2, "productCount": 28},
{"categoryId": "732", "categoryName": "果干蜜饯", "level": 2, "productCount": 20}
]
}
示例 3:一次拿三级树(level=0)
GET /open/api/v1/brand-categories?brandTag=JD_JINGZAO&level=0&sort=name
{
"code": 0,
"data": [
{
"categoryId": "670",
"categoryName": "休闲食品",
"level": 1,
"productCount": 124,
"children": [
{
"categoryId": "729",
"categoryName": "肉干小食",
"level": 2,
"productCount": 45,
"children": [
{"categoryId": "4837", "categoryName": "猪肉脯", "level": 3, "productCount": 18},
{"categoryId": "4838", "categoryName": "牛肉干", "level": 3, "productCount": 27}
]
}
]
}
]
}
brandTag + platformCode + parentCatId + level + sort 维度做 5 分钟本地缓存。新增/下架商品后类目计数最长 5 分钟才会刷新;如需立即生效,联系平台运营触发刷新。GET /open/api/v1/products?brandTag=JD_JINGZAO&categoryFilter=肉干小食&pageNo=1&pageSize=20订单接口
创建/取消订单需要 ORDER_WRITE 权限;查询订单需要 ORDER_READ 权限。
请求示例
{
"products": [
{"platformProductId": "1008814639745", "skuId": "6010122344404", "quantity": 1, "price": 28.9}
],
"address": {
"receiverName": "张三",
"receiverMobile": "13800138000",
"province": "北京市",
"city": "北京市",
"district": "朝阳区",
"detailAddress": "xxx路xxx号",
"fullAddress": "北京市朝阳区xxx路xxx号"
},
"areaCode": "110105",
"paymentType": 4,
"buyerRemark": "请尽快发货",
"customerOrderNo": "CUSTOM-20260506-001"
}
响应示例
{"code":0,"data":{"orderSn":"SO20260425143000T001ABC123","orderId":1,"costAmount":60.00,"tenantAmount":66.00,"status":"CREATED"},"is_sandbox":false}
// 沙箱模式响应:
{"code":0,"data":{"orderSn":"SANDBOX_D0C35852DB854C05","orderId":1,"status":"CREATED","receiverName":"张三","receiverPhone":"13800138000","receiverAddress":"北京市朝阳区xxx路xxx号"},"is_sandbox":true}
{ "reason": "取消原因" }。响应示例
{"code":0,"data":{"orderSn":"SO20260425143000T001ABC123","status":"CANCELLED"}}
响应示例
{"code":0,"data":{"order":{"id":1,"orderSn":"SO20260425...","status":"CREATED","costAmount":60.00,"tenantAmount":66.00,"receiverName":"张三","orderTime":"2026-04-25T14:30:00"},"items":[{"productId":123,"productName":"示例商品","quantity":1,"costPrice":60.00}]}}
consumerId 为额外查询参数(非 DTO 字段),可按消费者ID筛选;订单状态见 status 枚举。响应示例
{"code":0,"data":{"total":50,"pageNo":1,"pageSize":20,"orders":[{"unifiedOrderId":"1","platformOrderId":"JD001","totalAmount":60.00,"status":"CREATED","orderTime":"2026-04-25T14:30:00"}]}}
响应示例
{"code":0,"data":{"logisticsCompany":"顺丰速运","waybillNo":"SF1234567890","status":"IN_TRANSIT","tracks":[{"time":"2026-04-25 10:00:00","content":"已揽收"}]}}
售后接口
所有售后接口需要 AFTERSALE 权限。
售后类型(type)可选值
| 枚举值 | 说明 |
|---|---|
REFUND_ONLY | 仅退款 |
RETURN_REFUND | 退货退款 |
EXCHANGE | 换货 |
REPAIR | 维修 |
申请原因编码(reasonCode)可选值
| 编码 | 含义 |
|---|---|
QUALITY_ISSUE | 质量问题 |
NOT_AS_DESCRIBED | 与描述不符 |
DAMAGED | 商品损坏 |
WRONG_ITEM | 发错商品 |
NO_LONGER_NEEDED | 不再需要 |
{afterSaleSn, type, status};下方响应字段为生产模式(AfterSaleCreateResult)。请求示例
{
"platformOrderId": "SANDBOX_CE3C017EF6FE4FB5",
"type": "REFUND_ONLY",
"reasonCode": "NO_LONGER_NEEDED",
"description": "不想要了,申请退款",
"refundAmount": 99.00,
"contactName": "张三",
"contactPhone": "13800138000",
"items": [
{"platformProductId": "123456", "quantity": 1, "productName": "测试商品"}
]
}
响应示例
// 沙箱模式
{"code":0,"data":{"afterSaleSn":"SANDBOX_AS_A1B2C3D4E5F6","type":"REFUND_ONLY","status":"CREATED"},"is_sandbox":true}
// 生产模式
{"code":0,"data":{"success":true,"platformAfterSaleId":"AS123456"},"is_sandbox":false}
响应示例
{"code":0,"data":{"afterSaleId":"AS001","orderSn":"SO20260425...","afterSaleType":"REFUND_ONLY","reason":"质量问题","status":"PENDING_AUDIT","refundAmount":60.00,"createTime":"2026-04-25T15:00:00"}}
响应示例
{"code":0,"data":[{"id":1,"orderSn":"SO20260425...","afterSaleType":"REFUND_ONLY","status":"PENDING_AUDIT","refundAmount":60.00,"createTime":"2026-04-25T15:00:00"}]}
无需请求体,直接 POST 即可。
响应字段(data)- 生产模式
| 字段 | 类型 | 说明 |
|---|---|---|
| data | boolean | true 表示取消成功 |
响应示例
// 生产模式
{"code":0,"data":true}
// 沙箱模式
{"code":0,"data":{"afterSaleSn":"SANDBOX_AS_A1B2C3D4E5F6","type":"REFUND_ONLY","status":"CANCELLED"},"is_sandbox":true}
仅适用于 RETURN_REFUND(退货退款)类型、且售后单已审核通过后,买家寄回商品时回填退货运单号。
请求体字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| logisticsCompany | string | 是 | 退货快递公司名称,如“中通快递” |
| logisticsNo | string | 是 | 退货运单号 |
| shipperCode | string | 否 | 快递公司编码,如 ZTO |
请求示例
{
"logisticsCompany": "中通快递",
"logisticsNo": "78474409893076",
"shipperCode": "ZTO"
}
响应示例
{"code":0,"data":true}
用户接口
所有用户接口需要 USER 权限。注册和登录接口无需 X-User-Token,其余接口需要。
响应示例
{"code":0,"data":{"userId":10001,"username":"testuser","mobile":"13800138000","nickName":"testuser","status":"ENABLED","createTime":"2026-04-25T16:00:00"}}
响应示例
{"code":0,"data":{"user_token":"a1b2c3d4e5f6...","user_id":10001}}
无需额外参数,从 X-User-Token 识别用户身份。
响应示例
{"code":0,"data":{"userId":10001,"username":"testuser","mobile":"13800138000","nickName":"小明","avatar":"https://...","sex":1,"status":"ENABLED"}}
响应示例
{"code":0,"data":{"userId":10001,"username":"testuser","nickName":"新昵称","avatar":"https://new-avatar.png","sex":1}}
返回当前用户的所有收货地址。
响应示例
{"code":0,"data":[{"addressId":1,"receiverName":"张三","receiverMobile":"13800138000","province":"浙江省","city":"杭州市","district":"西湖区","detailAddress":"文三路100号","isDefault":true}]}
响应示例
{"code":0,"data":{"addressId":2,"receiverName":"李四","receiverMobile":"13900139000","province":"广东省","city":"深圳市","district":"南山区","detailAddress":"科技园路1号","fullAddress":"广东省深圳市南山区科技园路1号","isDefault":false}}
营销接口
所有营销接口需要 PROMOTION 权限。
响应示例
{"code":0,"data":{"total":5,"coupons":[{"couponId":1,"couponName":"满100减10","couponType":"PRICE","price":10.00,"consumeThreshold":100.00,"status":"ACTIVE"}]}}
无需请求体,通过 X-User-Token 识别领取用户。
响应示例
{"code":0,"data":{"userCouponId":100,"couponId":1,"couponName":"满100减10","status":"UNUSED","startTime":"2026-04-25","endTime":"2026-05-25"}}
| 错误情况 | HTTP | 错误码 |
|---|---|---|
| 优惠券已领完或不在有效期 | 400 | COUPON_UNAVAILABLE |
| 消费者已领取过同一优惠券 | 409 | COUPON_ALREADY_CLAIMED |
资金流接口
所有资金流接口需要 FINANCE 权限。
响应字段(data)
| 字段 | 类型 | 说明 |
|---|---|---|
| data | decimal | 账户余额(元) |
响应示例
{"code":0,"data":1280.50}
storeId 由系统按当前租户自动覆盖,无需传入。flowType:ORDER_IN / REFUND_OUT / WITHDRAW / COMMISSION / SUBSIDY / ADJUSTMENT。响应示例
{"code":0,"data":[{"flowId":1,"flowSn":"F20260425...","flowType":"ORDER_IN","amount":60.00,"direction":"IN","balanceBefore":1220.50,"balanceAfter":1280.50,"description":"订单结算","createTime":"2026-04-25T14:30:00"}]}
storeId 由系统按当前租户自动覆盖,无需传入。响应示例
{"code":0,"data":{"total":3,"bills":[{"billId":1,"billSn":"B20260425...","orderAmount":1000.00,"commissionAmount":60.00,"finalAmount":940.00,"status":"PENDING","startTime":"2026-04-01","endTime":"2026-04-30"}]}}
返回指定账单的完整信息。
响应示例
{"code":0,"data":{"billId":1,"billSn":"B20260425...","orderAmount":1000.00,"refundAmount":0,"commissionAmount":60.00,"finalAmount":940.00,"status":"PENDING","startTime":"2026-04-01","endTime":"2026-04-30"}}
返回账单下的所有结算明细条目。
响应示例
{"code":0,"data":[{"detailId":1,"billId":1,"orderSn":"SO20260425...","orderAmount":100.00,"commissionAmount":6.00,"settlementAmount":94.00}]}
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| applyAmount | decimal | 必填 | 申请提现金额 |
| bankName | string | 必填 | 开户银行 |
| bankAccountName | string | 必填 | 开户名 |
| bankAccountNumber | string | 必填 | 银行账号 |
| remark | string | 可选 | 备注 |
响应字段(data)
| 字段 | 类型 | 说明 |
|---|---|---|
| data | long | 提现申请ID(withdrawalId) |
响应示例
{"code":0,"data":1}
返回当前租户的提现申请记录列表,支持分页。
响应示例
{"code":0,"data":{"total":3,"withdrawals":[{"withdrawalId":1,"withdrawalSn":"W20260425...","applyAmount":500.00,"status":"PENDING","applyTime":"2026-04-25T16:00:00"}]}}
返回指定提现申请的详细信息及当前审核状态。
响应示例
{"code":0,"data":{"withdrawalId":1,"withdrawalSn":"W20260425...","applyAmount":500.00,"actualAmount":500.00,"bankName":"工商银行","bankAccountName":"张三","status":"APPROVED","applyTime":"2026-04-25T16:00:00","approveTime":"2026-04-26T10:00:00"}}
沙箱环境
新创建的应用默认为沙箱模式(app_type=SANDBOX),用于安全地测试 API 集成,不影响真实业务。
沙箱与生产的区别
| 操作类型 | 沙箱模式 | 生产模式 |
|---|---|---|
| 创建订单 | 仅本地落库,生成 SANDBOX_ 前缀订单号 | 调用上游平台,真实下单 |
| 取消订单 | 仅本地更新状态 | 调用上游平台取消 |
| 申请售后 | 仅本地落库 | 调用上游平台 |
| 查询订单列表 | 查询沙箱订单表,返回 SANDBOX_ 前缀订单 | 查询生产订单表 |
| 查询订单详情/物流 | 返回沙箱数据 | 返回真实数据 |
| 商品/用户/优惠券 | 与生产一致 | 与生产一致 |
识别沙箱响应
{"code":0,"data":{"orderSn":"SANDBOX_A1B2C3D4E5F6G7H8","orderId":1,"status":"CREATED","receiverName":"张三","receiverPhone":"13800138000","receiverAddress":"北京市朝阳区xxx路xxx号"},"is_sandbox":true}
升级为生产模式
完成沙箱联调测试后,联系平台运营人员在管理后台审核升级为生产模式。
常见问题
- 确认参数排序是否按 key 字母升序(区分大小写)
- 确认空值字段是否已跳过(值为 null 或空字符串的字段不参与签名)
- 确认 JSON Body 中只有顶层字段参与签名,嵌套对象(如 address)整体不参与
- 确认 MD5 结果是否转为大写
- 确认使用的是 UTF-8 编码计算 MD5
- 使用秒级时间戳,不是毫秒(毫秒值会比服务器时间大 1000 倍)
- 服务器时钟与 NTP 同步,避免时钟漂移
- 每次请求重新生成时间戳,不要复用
- 默认限制为 60 次/分钟,收到 429 后等待 1 分钟再重试
- 如业务需要更高频率,联系平台运营调整 rate_limit 配置
Token 有效期 7 天,过期后重新调用 POST /users/login 获取新 Token。建议捕获 USER_TOKEN_INVALID 错误后自动跳转登录流程。
沙箱订单号以 SANDBOX_ 为前缀,如 SANDBOX_A1B2C3D4E5F6G7H8。生产模式下为上游平台的真实订单号格式。