第三方开放接口 · API 文档
对外版本

第三方开放接口 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 签名算法

  1. 取请求体中除 sign 外的全部字段;
  2. 每个字段的值转为字符串:null 按空串 "" 处理;数字/布尔按字面量处理(如 1 → "1");
  3. 按字段名 ASCII 升序排序;
  4. 依次以 key=value 拼接,字段之间用 & 连接,末尾不加 &:

appKey=xxx&buyNum=1&outOrderNo=xxx&productNo=xxx&...&timestamp=xxx

  1. 在拼接串前后各拼接一次 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&timestamp=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

业务规则

  1. source 仅支持 douyin / xianyu,否则返回 40104;落库来源 1004 / 1005。
  2. buyNum 必须为 1,否则返回 40105。
  3. productNo 由平台生成,必须存在且平台已上架,否则返回 1020。
  4. 下单即从 appKey 绑定渠道商余额中按 platformSupplyPrice 扣款;余额不足返回 1073。
  5. 扣款成功即生成订单,payStatus=1002(已支付)、orderStatus=1001(已完成),无需再次支付。
  6. 请勿在未确认结果前重复提交同一笔订单;网络超时后请先调用订单查询或按 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 否 撤单原因

业务规则

  1. 仅能撤销本渠道商、来源 1004/1005 的订单;不属于本渠道商返回 40106。
  2. 订单必须为已支付(payStatus=1002)且无退款(refundStatus=1000),否则返回 1122 / 1133。
  3. 同一订单存在在途申请(1000 待审核 / 1001 审核中)时,重复提交返回 1125。
  4. 退款金额按充值状态计算: - 充值失败(2000)或充值中(1001):全额退款(refundRatio=1); - 充值成功(2001):按 未使用天数 / 总天数 比例退款; - 未充值(1000):不允许撤单,返回 1132。
  5. 申请创建后状态为 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. 接入流程与联调清单

  1. 平台为接入方创建开放接口应用,分配 appKey、appSecret,绑定渠道商组织。
  2. 接入方获取商品列表 / 详情,确认可下单商品与价格。
  3. 按 2.2 节实现签名,调用 4.3 下单。
  4. 配置回调地址(充值 / 撤单),实现回调验签与幂等应答。
  5. 联调自检: - [ ] 所有请求体都带 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();
    }
}


文档结束