第三方开放接口 API 文档
适用对象:抖店 / 闲鱼等第三方平台接入方 基础地址:
https://leitu-api.hsy.ac.cn传输协议:HTTPS + POST +application/json; charset=UTF-8
1. 概述
第三方以"渠道商代充"身份接入:平台在后台为每个接入方分配一组 appKey / appSecret,并绑定一个渠道商组织。开放接口的所有数据(商品价格、订单归属、余额扣减)均以该绑定渠道商为维度隔离。
- 下单从该渠道商账户余额中按渠道商进货价扣款,订单直接置为已支付 / 已完成,随后进入充值流程。
- 下单来源固定落库为:
douyin→ 1004(抖店),xianyu→ 1005(闲鱼)。 - 充值状态变化、撤单审核结果,平台会异步回调通知第三方(见第 6 节)。
所有接口均为 POST,请求与响应体均为 JSON。业务错误同样返回 HTTP 200,请以响应体
code字段判断成功与否。
2. 鉴权与签名
2.1 公共请求参数
鉴权参数与业务参数放在同一个 JSON 请求体内(不是 Header,也不是 Query)。每个请求体都必须包含:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
appKey |
string | 是 | 接入方应用 key,由平台分配 |
timestamp |
string/int | 是 | 10 位秒级 Unix 时间戳,与服务器时间偏差须在 ±300 秒内 |
sign |
string | 是 | 签名,32 位大写 MD5,算法见 2.2 |
| 业务参数 | — | 是 | 见各接口定义 |
2.2 签名算法
- 取请求体中除
sign外的全部字段; - 每个字段的值转为字符串:
null按空串""处理;数字/布尔按字面量处理(如1→"1"); - 按字段名 ASCII 升序排序;
- 依次以
key=value拼接,字段之间用&连接,末尾不加&:
appKey=xxx&buyNum=1&outOrderNo=xxx&productNo=xxx&...×tamp=xxx
- 在拼接串前后各拼接一次 appSecret,做 MD5,输出 32 位大写:
sign = MD5(appSecret + 排序拼接串 + appSecret).toUpperCase()
注意:参与签名的值以"解析后的值"为准,而非原始 JSON 文本。因此字段顺序、空白、
"1"与1不影响签名结果;但数字请勿使用科学计数法,并保证转成字符串后与示例一致。
2.3 签名示例
appKey = ak_demo_001,appSecret = sk_demo_8f3a2b1c9d4e5f6a
请求体(sign 之前):
{
"appKey": "ak_demo_001",
"timestamp": "1763000000",
"outOrderNo": "DOUYIN-20260624-001",
"source": "douyin",
"productNo": "PD-20260624001",
"buyNum": "1",
"rechargeName": "张三",
"rechargePhone": "13800000000",
"remark": ""
}
待签串(ASCII 升序):
appKey=ak_demo_001&buyNum=1&outOrderNo=DOUYIN-20260624-001&productNo=PD-20260624001&rechargeName=张三&rechargePhone=13800000000&remark=&source=douyin×tamp=1763000000
sign = 3A189715EB24BF099BD206AAE45E26E1
最终请求体:
{
"appKey": "ak_demo_001",
"timestamp": "1763000000",
"sign": "3A189715EB24BF099BD206AAE45E26E1",
"outOrderNo": "DOUYIN-20260624-001",
"source": "douyin",
"productNo": "PD-20260624001",
"buyNum": "1",
"rechargeName": "张三",
"rechargePhone": "13800000000",
"remark": ""
}
2.4 鉴权失败返回
| code | message | 说明 |
|---|---|---|
| 40101 | appKey不存在或已禁用 | appKey 缺失/不存在/被禁用 |
| 40102 | 签名错误 / 请求体不是合法 JSON | 验签失败或 body 非法 JSON |
| 40103 | 时间戳过期 | 时间戳缺失、非法或超出 ±300 秒 |
3. 公共响应结构与错误码
3.1 响应结构
{
"code": 200,
"message": "success",
"data": { }
}
| 字段 | 类型 | 说明 |
|---|---|---|
code |
int | 200=成功;非 200 为错误码 |
message |
string | 结果描述 |
data |
object/array | 业务数据,失败时为 null |
3.2 公共错误码
| code | 说明 |
|---|---|
| 200 | 成功 |
| 400 | 参数校验失败,message 为具体缺失/非法字段 |
| 40101 | appKey 不存在或已禁用 |
| 40102 | 签名错误 / 请求体不是合法 JSON |
| 40103 | 时间戳过期 |
| 40104 | 不支持的订单来源(source 不是 douyin/xianyu) |
| 40105 | 当前仅支持购买数量 1(buyNum ≠ 1) |
| 40106 | 订单不属于当前渠道商 |
| 1020 | 商品不存在 |
| 1040 | 订单不存在 |
| 1073 | 渠道商账户余额不足 |
| 1122 | 订单未支付,无法申请退款 |
| 1125 | 该订单已存在在途退款申请,请勿重复提交 |
| 1132 | 充值尚未完成,暂不支持申请退款 |
| 1133 | 订单已退款,无法再次申请 |
| 500 | 系统内部错误 |
平台后台管理类错误码(40107 appKey已存在、40108 应用不存在等)不会出现在第三方接口中。
4. 接口明细
下列"请求参数"均需叠加第 2.1 节公共参数(
appKey/timestamp/sign)。示例中为便于阅读已省略公共参数,实际调用必须携带。
4.1 商品列表
POST/open/product/list
productNo由平台生成(格式PD-yyyyMMdd序号,如PD-20260624001),是后续商品详情、下单接口的唯一凭据。第三方请从本接口或商品详情接口获取后原样使用,不可自行编造。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
pageNum |
int | 否 | 页码,默认 1 |
pageSize |
int | 否 | 每页条数,默认 10 |
keyword |
string | 否 | 关键词,模糊匹配商品名称或商品编号 |
cityId |
long | 否 | 城市 id |
brandId |
long | 否 | 品牌 id |
categoryId |
long | 否 | 商品分类 id(当前实现未生效,见第 9 节) |
响应参数(data)
| 字段 | 类型 | 说明 |
|---|---|---|
total |
long | 总条数 |
pageNum |
long | 当前页 |
pageSize |
long | 每页条数 |
records |
array | 商品列表,元素见下 |
records[] 元素(商品信息):
| 字段 | 类型 | 说明 |
|---|---|---|
productNo |
string | 商品编号 |
productName |
string | 商品名称 |
coverImage |
string | 商品首图 URL |
brandId |
long | 品牌 id |
brandName |
string | 品牌名称 |
categoryId |
long | 商品分类 id |
categoryName |
string | 商品分类名称 |
platformSupplyPrice |
number | 平台供货价(渠道商进货价,含渠道等级折扣与加价规则),下单即按此价扣款 |
channelProfit |
number | 渠道商利润 |
salePrice |
number | 渠道商销售价 = platformSupplyPrice + channelProfit |
lowestSalePrice |
number | 商品最低销售价 |
shelfStatus |
int | 当前渠道商上架状态:0 未上架,1 已上架 |
响应示例
{
"code": 200,
"message": "success",
"data": {
"total": 9,
"pageNum": 1,
"pageSize": 1,
"records": [
{
"productNo": "PD-20260624001",
"productName": "测试模版 / 北京市 / 1天",
"coverImage": null,
"brandId": 1,
"brandName": "铁塔",
"categoryId": null,
"categoryName": null,
"platformSupplyPrice": 0.01,
"channelProfit": null,
"salePrice": 0.01,
"lowestSalePrice": 0.01,
"shelfStatus": 0
}
]
}
}
4.2 商品详情
POST/open/product/detail
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
productNo |
string | 是 | 商品编号 |
响应参数(data):同 4.1 的商品信息结构。
请求示例
{
"appKey": "ak_demo_001",
"timestamp": "1763000000",
"sign": "……",
"productNo": "PD-20260624001"
}
响应示例
{
"code": 200,
"message": "success",
"data": {
"productNo": "PD-20260624001",
"productName": "测试模版 / 北京市 / 1天",
"coverImage": null,
"brandId": 1,
"brandName": "铁塔",
"categoryId": null,
"categoryName": null,
"platformSupplyPrice": 0.01,
"channelProfit": null,
"salePrice": 0.01,
"lowestSalePrice": 0.01,
"shelfStatus": 0
}
}
4.3 下单(渠道商代充)
POST/open/order/create
productNo由平台生成,须从 4.1 商品列表 / 4.2 商品详情返回的原样传入;buyNum当前固定为 1,传其他值返回 40105。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
outOrderNo |
string | 是 | 第三方订单号,原样回传,用于对账与回调关联;长度 ≤ 64 |
source |
string | 是 | 来源:douyin=抖店,xianyu=闲鱼 |
productNo |
string | 是 | 商品编号,平台生成,取自 4.1 商品列表 / 4.2 商品详情,原样传入 |
buyNum |
int | 是 | 购买数量,当前固定为 1,传其他值返回 40105 |
rechargeName |
string | 是 | 被充值人名称,长度 ≤ 50 |
rechargePhone |
string | 是 | 被充值人手机号,长度 ≤ 20 |
remark |
string | 否 | 订单备注,长度 ≤ 1024 |
业务规则
source仅支持douyin/xianyu,否则返回 40104;落库来源 1004 / 1005。buyNum必须为 1,否则返回 40105。productNo由平台生成,必须存在且平台已上架,否则返回 1020。- 下单即从 appKey 绑定渠道商余额中按
platformSupplyPrice扣款;余额不足返回 1073。 - 扣款成功即生成订单,
payStatus=1002(已支付)、orderStatus=1001(已完成),无需再次支付。 - 请勿在未确认结果前重复提交同一笔订单;网络超时后请先调用订单查询或按
outOrderNo去重(见第 9 节)。
响应参数(data)
| 字段 | 类型 | 说明 |
|---|---|---|
orderNo |
string | 平台订单编号 |
outOrderNo |
string | 第三方订单号(原样回传) |
source |
string | 来源(douyin/xianyu) |
totalPrice |
number | 订单总价 |
payPrice |
number | 支付价 |
payStatus |
int | 支付状态,见第 5 节 |
orderStatus |
int | 订单状态,见第 5 节 |
rechargeStatus |
int | 充值状态,见第 5 节 |
createdTime |
string | 创建时间 |
响应示例
{
"code": 200,
"message": "success",
"data": {
"orderNo": "PO-20260624001",
"outOrderNo": "DOUYIN-20260624-001",
"source": "douyin",
"totalPrice": 0.01,
"payPrice": 0.01,
"payStatus": 1002,
"orderStatus": 1001,
"rechargeStatus": 1000,
"createdTime": "2026-06-24 12:30:00"
}
}
4.4 订单查询
POST/open/order/query
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
orderNo |
string | 是 | 平台订单编号(下单响应中的 orderNo) |
业务规则:只能查询本 appKey 绑定渠道商、且来源为 1004/1005 的订单;其他订单返回 40106,订单不存在返回 1040。
响应参数(data)
| 字段 | 类型 | 说明 |
|---|---|---|
orderNo |
string | 平台订单编号 |
outOrderNo |
string | 第三方订单号 |
source |
string | 来源(douyin/xianyu) |
totalPrice |
number | 订单总价 |
payPrice |
number | 支付价 |
payStatus |
int | 支付状态 |
orderStatus |
int | 订单状态 |
rechargeUserName |
string | 被充值人名称 |
rechargePhone |
string | 被充值人电话 |
createdTime |
string | 创建时间 |
items |
array | 订单商品列表 |
refundApplyDetail |
object | 最新撤单申请,无则为 null |
items[]:
| 字段 | 类型 | 说明 |
|---|---|---|
productNo |
string | 商品编号 |
productName |
string | 商品名称 |
quantity |
int | 数量 |
salePrice |
number | 销售价 |
rechargeStatus |
int | 充值状态 |
refundApplyDetail:
| 字段 | 类型 | 说明 |
|---|---|---|
applyNo |
string | 撤单申请单号 |
status |
int | 申请状态,见第 5 节 |
响应示例
{
"code": 200,
"message": "success",
"data": {
"orderNo": "PO-20260624001",
"outOrderNo": "DOUYIN-20260624-001",
"source": "douyin",
"totalPrice": 0.01,
"payPrice": 0.01,
"payStatus": 1002,
"orderStatus": 1001,
"rechargeUserName": "张三",
"rechargePhone": "13800000000",
"createdTime": "2026-06-24 12:30:00",
"items": [
{
"productNo": "PD-20260624001",
"productName": "测试模版 / 北京市 / 1天",
"quantity": 1,
"salePrice": 0.01,
"rechargeStatus": 2001
}
],
"refundApplyDetail": null
}
}
4.5 撤单
POST/open/order/cancel
撤单即发起退款申请,提交后进入平台审核,审核结果通过异步回调通知(见 6.2)。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
orderNo |
string | 是 | 平台订单编号 |
refundReason |
string | 否 | 撤单原因 |
业务规则
- 仅能撤销本渠道商、来源 1004/1005 的订单;不属于本渠道商返回 40106。
- 订单必须为已支付(payStatus=1002)且无退款(refundStatus=1000),否则返回 1122 / 1133。
- 同一订单存在在途申请(1000 待审核 / 1001 审核中)时,重复提交返回 1125。
- 退款金额按充值状态计算: - 充值失败(2000)或充值中(1001):全额退款(refundRatio=1); - 充值成功(2001):按 未使用天数 / 总天数 比例退款; - 未充值(1000):不允许撤单,返回 1132。
- 申请创建后状态为 1001(审核中),需平台审核。
响应参数(data)
| 字段 | 类型 | 说明 |
|---|---|---|
applyNo |
string | 撤单申请单号 |
orderNo |
string | 平台订单编号 |
applyPrice |
number | 申请退款金额 |
refundRatio |
number | 退款比例(未使用天数/总天数) |
status |
int | 申请状态,见第 5 节 |
响应示例
{
"code": 200,
"message": "success",
"data": {
"applyNo": "ORA-20260624001",
"orderNo": "PO-20260624001",
"applyPrice": 0.01,
"refundRatio": 1,
"status": 1001
}
}
5. 枚举字典
| 枚举 | 取值 |
|---|---|
source |
douyin 抖店 / xianyu 闲鱼 |
payStatus 支付状态 |
1000 未支付 / 1001 支付中 / 1002 已支付 / 1003 已退款 |
orderStatus 订单状态 |
1000 未完成 / 1001 已完成 / 1002 已取消 |
rechargeStatus 充值状态 |
1000 未充值 / 1001 充值中 / 2000 充值失败 / 2001 充值成功 |
shelfStatus 渠道上架状态 |
0 未上架 / 1 已上架 |
| 撤单申请状态 | 1000 待审核 / 1001 审核中 / 1002 已通过 / 1003 已拒绝 / 1004 已取消 |
| 撤单回调结果 | 1002 通过 / 1003 拒绝 |
6. 异步通知(平台 → 第三方)
平台在事件发生时向接入方预留的回调地址发起 POST JSON 通知。回调地址由平台在后台配置:
| 事件 | 配置项 | 触发时机 |
|---|---|---|
| 充值状态通知 | notifyUrl |
订单充值状态变为 充值中 / 充值失败 / 充值成功 |
| 撤单结果回调 | cancelNotifyUrl |
平台退款审核完成(通过 / 拒绝) |
6.1 回调报文
回调报文在业务字段之外,附带 timestamp 与 sign,签名算法与 2.2 节完全一致(签名时排除 sign)。
充值状态通知
{
"orderNo": "PO-20260624001",
"outOrderNo": "DOUYIN-20260624-001",
"rechargeStatus": 2001,
"rechargeTime": "2026-06-24 12:31:20",
"failReason": "充值失败原因(失败时才有)",
"timestamp": 1763000000,
"sign": "……"
}
| 字段 | 类型 | 说明 |
|---|---|---|
orderNo |
string | 平台订单编号 |
outOrderNo |
string | 第三方订单号 |
rechargeStatus |
int | 充值状态 |
rechargeTime |
string | 充值时间,无则为空(不返回) |
failReason |
string | 失败原因,成功时无(不返回) |
timestamp |
long | 秒级时间戳 |
sign |
string | 签名 |
撤单结果回调
{
"orderNo": "PO-20260624001",
"outOrderNo": "DOUYIN-20260624-001",
"applyNo": "ORA-20260624001",
"status": 1002,
"refundPrice": 0.01,
"auditRemark": "审核备注(可选)",
"timestamp": 1763000000,
"sign": "……"
}
| 字段 | 类型 | 说明 |
|---|---|---|
orderNo |
string | 平台订单编号 |
outOrderNo |
string | 第三方订单号 |
applyNo |
string | 撤单申请单号 |
status |
int | 1002 通过 / 1003 拒绝 |
refundPrice |
number | 退款金额,可选 |
auditRemark |
string | 审核备注,可选 |
timestamp |
long | 秒级时间戳 |
sign |
string | 签名 |
6.2 回调应答与重试
- 第三方收到回调后,必须返回 HTTP 200 且响应体含
"code": 200,平台才视为成功。 - 返回非 200,或响应体
code不为 200,视为失败并进入重试。 - 重试计划:首次立即发送,失败后依次间隔 10s / 30s / 1m / 5m / 10m / 30m / 1h 重试,最多 8 次(含首次)。
- 请第三方按
outOrderNo+rechargeStatus/applyNo+status做幂等处理。
7. 接入流程与联调清单
- 平台为接入方创建开放接口应用,分配
appKey、appSecret,绑定渠道商组织。 - 接入方获取商品列表 / 详情,确认可下单商品与价格。
- 按 2.2 节实现签名,调用 4.3 下单。
- 配置回调地址(充值 / 撤单),实现回调验签与幂等应答。
- 联调自检:
- [ ] 所有请求体都带
appKey/timestamp/sign- [ ] 待签串为 ASCII 升序、以&连接、无末尾&,前后各拼一次 appSecret - [ ] 时间戳与服务器同步(±300 秒) - [ ] 回调返回{"code":200}- [ ] 处理 HTTP 200 但code != 200的业务错误 - [ ] 订单回调、下单重试均按outOrderNo去重
8. 附录
8.1 cURL 示例
curl -X POST 'https://leitu-api.hsy.ac.cn/open/order/create' \
-H 'Content-Type: application/json' \
-d '{
"appKey": "ak_demo_001",
"timestamp": "1763000000",
"sign": "3A189715EB24BF099BD206AAE45E26E1",
"outOrderNo": "DOUYIN-20260624-001",
"source": "douyin",
"productNo": "PD-20260624001",
"buyNum": "1",
"rechargeName": "张三",
"rechargePhone": "13800000000",
"remark": ""
}'
8.2 Java 签名示例
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import java.util.Map;
import java.util.TreeMap;
public class OpenSignDemo {
public static String sign(String body, String appSecret) {
JSONObject json = JSON.parseObject(body);
TreeMap<String, String> sorted = new TreeMap<>();
for (Map.Entry<String, Object> e : json.entrySet()) {
if ("sign".equals(e.getKey())) {
continue;
}
sorted.put(e.getKey(), e.getValue() == null ? "" : String.valueOf(e.getValue()));
}
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> e : sorted.entrySet()) {
if (sb.length() > 0) {
sb.append('&');
}
sb.append(e.getKey()).append('=').append(e.getValue());
}
return SecureUtil.md5(appSecret + sb + appSecret).toUpperCase();
}
}
文档结束