1. 接口请求模型
所有开放接口走同一套鉴权:请求头携带 AppKey、时间戳、随机串与签名。业务参数按动作不同,放在 Query(GET)或 JSON Body(POST)。
统一鉴权
每个请求都要带 X-App-Key / X-Timestamp / X-Nonce / X-Sign,服务端校验通过后才进入业务。
按动作路由
URL 形如 /web/index.php?r=api/open/{模块}/{动作},模块为 erp、errand、worker。
租户范围
AppKey 绑定店铺。可选 mch_id 指定该店下的入驻商家;不传则操作平台自营(mch_id=0)。
1.1 接口地址
| 环境 | 基础地址 |
| 当前站点 | {站点域名}/web/index.php?r=api/open/{模块}/{动作} |
| 示例 | https://gw.kuaiyibu.net/web/index.php?r=api/open/erp/ping |
请把域名换成你们实际部署域名。模块名:erp(仓储)、errand(跑腿)、worker(技师)。
1.2 系统级请求头(每个接口都必须)
| 请求头 | 类型 | 必须 | 说明 |
X-App-Key | 字符串 | 是 | 店铺 AppKey,用于识别租户 |
X-Timestamp | 数字字符串 | 是 | Unix 秒级时间戳,与服务器相差不超过 5 分钟 |
X-Nonce | 字符串 | 是 | 8~64 位字母或数字;同一 AppKey 下 5 分钟内不可重复 |
X-Sign | 字符串 | 是 | HMAC-SHA256 十六进制小写签名 |
Content-Type | 字符串 | POST 时 | 固定 application/json; charset=UTF-8 |
兼容写法:也可用 Query/Form 传 app_key、timestamp、nonce、sign(优先读请求头)。
2. 应用 KEY 与密钥管理
| 位置 | 做什么 |
| 平台店铺列表 | 店铺行「开放接口」:生成或重置 AppKey、AppSecret |
| 商家后台仓储 | user/goods/stock/index 页「对外开放」:查看同一把店铺密钥 |
AppKey:可出现在请求里,用来认出是哪家店。
AppSecret:只留在对接方服务器,用来算签名。禁止写入前端、小程序、日志明文或传给第三方。
3. 签名算法
3.1 待签名字符串
三行文本,用换行符 \n 连接(最后一行也要有,GET 无 Body 时第三行为空字符串):
timestamp + "\n" + nonce + "\n" + rawBody
timestamp:与请求头 X-Timestamp 完全一致
nonce:与请求头 X-Nonce 完全一致
rawBody:HTTP 原始请求体字节原文。GET 无 Body → 空字符串;POST 必须是发出去的那份 JSON 原文(空格、字段顺序都要一致)
3.2 计算公式
X-Sign = hex_lowercase( HMAC_SHA256( key=AppSecret, message=待签名字符串 ) )
3.3 手算示例(GET)
假设 AppSecret=skdemo,Timestamp=1730000000,Nonce=Ab12Cd34,GET 无 Body:
待签名 =
1730000000
Ab12Cd34
(第三行为空)
X-Sign = HMAC-SHA256("skdemo", "1730000000\nAb12Cd34\n")
→ 小写十六进制
3.4 手算示例(POST)
Body 原文为(注意一字不差):
{"mch_id":0,"bill_type":"in","post_now":"1","lines":[{"goods_id":12,"attr_id_list":[3],"qty":10}]}
待签名 =
1730000000
Ab12Cd34
{"mch_id":0,"bill_type":"in","post_now":"1","lines":[{"goods_id":12,"attr_id_list":[3],"qty":10}]}
常见失败原因:Body 先 JSON.stringify 再改字段、多空格、用 Form 却按 JSON 签名、时间戳用了毫秒、Sign 写成了大写。
4. 公共约定
4.1 商家范围 mch_id
| 传参方式 | 说明 |
Query mch_id | GET / POST 均可 |
JSON Body mch_id | POST 时;Query 未传则读 Body |
| 都不传 | 默认 0 = 平台自营商品/单据 |
传入的 mch_id 必须属于该 AppKey 对应店铺,否则返回「商家不属于该店铺」。
4.2 统一返回结构
{
"code": 0,
"msg": "ok",
"data": { }
}
| 字段 | 说明 |
code | 0 成功;非 0 失败(多数为 1) |
msg | 说明文案;失败时为原因 |
data | 业务数据;部分接口成功时另有 bill_id 等顶层字段 |
5. 接口一览
| 模块 | 动作 | 方法 | 作用 |
ERP
api/open/erp/ | ping | GET | 核对密钥与 ERP 开关 |
goods | GET | 商品列表(最多 50) |
skus | GET | 某商品规格与库存 |
bills | GET | 最近库存单据 |
stock | POST | 提交入库 / 出库 / 盘点 |
跑腿
api/open/errand/ | types | GET | 跑腿场景列表 |
order | POST | 发单进抢单池 |
query | GET | 查订单状态 |
cancel | POST | 未接单时取消 |
技师
api/open/worker/ | goods | GET | 服务项目列表 |
workers | GET | 技师列表 |
order | POST | 发单待接 |
query | GET | 查订单状态 |
cancel | POST | 未接单时取消 |
跑腿、技师完整字段说明见:跑腿发单、技师发单。下文详细展开 ERP。
6. ERP 仓储接口
基础路径:/web/index.php?r=api/open/erp/{动作}。须先在对应商家或平台自营商品上开启「ERP 仓储」,库存单据才会写入;过账后小程序可售库存同步变化。
6.1 连通性检测 ping
GET
api/open/erp/ping
需签名
应用场景
对接联调时核对 AppKey/签名是否正确,并确认当前 mch 下 ERP 是否已开启。
应用级输入参数
| 名称 | 位置 | 必须 | 说明 |
mch_id | Query | 否 | 商家 ID,默认 0 |
返回 data
| 字段 | 说明 |
store_id | 店铺 ID |
store_name | 店铺名称 |
mch_id | 本次生效的商家 ID |
erp_enabled | 是否已开启 ERP 仓储 |
返回示例
{
"code": 0,
"msg": "ok",
"data": {
"store_id": 1,
"store_name": "示例店铺",
"mch_id": 0,
"erp_enabled": true
}
}
6.2 商品列表 goods
GET
api/open/erp/goods
需签名
应用场景
拉取当前 mch 下普通商品,用于对接方对照 goods_id。
应用级输入参数
| 名称 | 位置 | 必须 | 说明 |
mch_id | Query | 否 | 商家 ID,默认 0 |
q | Query | 否 | 按商品名称模糊搜索 |
返回 data(数组,最多 50 条)
| 字段 | 说明 |
id | 商品 ID(后续 skus / stock 用) |
name | 商品名称 |
cover_pic | 封面图 |
price | 售价 |
goods_num | 总库存汇总 |
6.3 规格库存 skus
GET
api/open/erp/skus
需签名
应用场景
查询某商品各规格当前可售数量,得到提交库存单据所需的 attr_id_list。
应用级输入参数
| 名称 | 位置 | 必须 | 说明 |
goods_id | Query | 是 | 商品 ID |
mch_id | Query | 否 | 须与商品所属商家一致 |
返回 data 元素
| 字段 | 说明 |
attr_id_list | 规格属性 ID 数组(已排序),提交 stock 时原样回传 |
label | 规格展示名,如「红色 / L」 |
num | 当前库存 |
pic | 规格图(可能为空) |
返回示例
{
"code": 0,
"msg": "ok",
"data": [
{ "attr_id_list": [3, 8], "label": "红色 / L", "num": 20, "pic": "" }
]
}
6.4 单据列表 bills
GET
api/open/erp/bills
需签名
应用场景
查看最近库存单据(最多 30 条)。
应用级输入参数
| 名称 | 位置 | 必须 | 说明 |
type | Query | 否 | in 入库 / out 出库 / check 盘点;不传返回全部 |
mch_id | Query | 否 | 商家 ID |
返回 data 元素
| 字段 | 说明 |
id | 单据 ID |
bill_no | 单号 |
bill_type | in / out / check |
type_label | 类型中文 |
status / status_label | 状态与文案(草稿 / 已过账等) |
remark | 备注 |
addtime | 创建时间 Unix 秒 |
6.5 提交库存单据 stock
POST
api/open/erp/stock
需签名 · JSON Body
应用场景
外部 ERP / WMS 把入库、出库、盘点写入本店,可选立即过账以变更小程序可售库存。
提交方式
POST,Body 为 JSON。签名必须针对原始 Body 字符串计算。
应用级输入参数(JSON)
| 名称 | 类型 | 必须 | 说明 |
mch_id | int | 否 | 商家 ID,默认 0 |
bill_type | string | 是 | in 入库;out 出库;check 盘点(qty=实盘数量) |
remark | string | 否 | 单据备注 |
post_now | string | 否 | 1 或缺省:立即过账;0:只存草稿,不改可售库存 |
warehouse_id | int | 否 | 仓库 ID,未启用多仓可传 0 |
lines | array | 是 | 明细行,至少 1 行;同一商品同一规格只能一行 |
lines[].goods_id | int | 是 | 商品 ID |
lines[].attr_id_list | array | 是 | 规格属性 ID 列表(与 skus 返回一致) |
lines[].qty | int | 是 | 数量;入库/出库须 >0;盘点为实盘数且 ≥0 |
lines[].unit_cost | number | 否 | 成本单价 |
lines[].remark | string | 否 | 行备注 |
请求体示例
{
"mch_id": 0,
"bill_type": "in",
"remark": "采购到货",
"post_now": "1",
"lines": [
{
"goods_id": 12,
"attr_id_list": [3, 8],
"qty": 10,
"unit_cost": 0,
"remark": ""
}
]
}
返回结果
| 情形 | 示例 |
| 立即过账成功 | {"code":0,"msg":"OK","bill_id":101} |
| 仅存草稿 | {"code":0,"msg":"草稿已保存","bill_id":102} |
| 未开 ERP | {"code":1,"msg":"请先在商品列表开启「ERP 仓储模式」"} |
| 参数错误 | {"code":1,"msg":"第 1 行:请选择规格"} 等 |
7. 跑腿发单(摘要)
完整参数与字段说明见 跑腿发单文档。基础路径:api/open/errand/{动作},鉴权同第 3 节。
| 动作 | 方法 | 要点 |
types | GET | 可选 biz_scene(默认 1=跑腿),返回场景 id / name |
order | POST | 必填 out_order_no、purchasing_type_id、fee、content、pickup、dropoff;同一 out_order_no 幂等 |
query | GET | out_order_no 或 order_no |
cancel | POST | 骑手未接单可取消;Body 传 out_order_no 或 order_no |
发单 Body 示例
{
"out_order_no": "WB20261026001",
"purchasing_type_id": 3,
"fee": 12.5,
"rider_fee": 8,
"expect_minutes": 90,
"content": "文件一份,请当面签收",
"remark": "门禁密码 1208",
"pickup": {
"name": "张三", "mobile": "13800000000",
"address": "深圳市南山区某某路 1 号",
"lng": "113.93", "lat": "22.54"
},
"dropoff": {
"name": "李四", "mobile": "13900000000",
"address": "深圳市南山区某某大厦",
"lng": "113.95", "lat": "22.53"
}
}
8. 技师发单(摘要)
完整说明见 技师发单文档。基础路径:api/open/worker/{动作}。
| 动作 | 方法 | 要点 |
goods | GET | 已上架服务项目 |
workers | GET | 可选 goods_id 过滤会做该项目的技师 |
order | POST | 必填 out_order_no、goods_id、fee、service_time、customer;worker_id=0 进抢单 |
query / cancel | GET / POST | 同跑腿,技师已接单不可取消 |
发单 Body 示例
{
"out_order_no": "HS20261026001",
"goods_id": 18,
"worker_id": 0,
"fee": 128,
"service_time": "2026-10-26 14:00:00",
"content": "空调清洗一台",
"customer": {
"name": "王女士",
"mobile": "13800000000",
"address": "深圳市南山区某某路 1 号",
"lng": "113.93",
"lat": "22.54"
}
}
9. 错误码与常见失败
| code | msg 示例 | 处理建议 |
| 1 | 缺少 AppKey、时间戳、随机串或签名 | 检查四个请求头是否齐全 |
| 1 | 时间戳无效或已过期 | 用 Unix 秒,与服务器时差 ≤5 分钟 |
| 1 | 随机串格式无效 / 已使用 | 8~64 位字母数字;每次请求换新 nonce |
| 1 | AppKey 无效 / 尚未发放 AppSecret | 平台后台重新生成密钥 |
| 1 | 签名不正确 | 核对 AppSecret、Body 原文、换行拼接 |
| 1 | 商家不属于该店铺 | mch_id 必须是本店商家 |
| 1 | 请使用 POST / 请求体须为 JSON | stock、发单、取消须 POST JSON |
| 0 | ok / OK / 草稿已保存 | 成功 |
10. 调用示例
10.1 PHP(推荐服务端)
<?php
$appKey = 'akxxxxxxxx';
$appSecret = 'skxxxxxxxxxxxxxxxx';
$timestamp = (string) time();
$nonce = bin2hex(random_bytes(8));
$body = ''; // GET 为空;POST 时填 JSON 原文
$sign = hash_hmac('sha256', $timestamp . "\n" . $nonce . "\n" . $body, $appSecret);
$url = 'https://你的域名/web/index.php?r=api/open/erp/ping';
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'X-App-Key: ' . $appKey,
'X-Timestamp: ' . $timestamp,
'X-Nonce: ' . $nonce,
'X-Sign: ' . $sign,
],
]);
echo curl_exec($ch);
10.2 Node.js
const crypto = require('crypto');
const appKey = 'akxxxxxxxx';
const appSecret = 'skxxxxxxxxxxxxxxxx';
const timestamp = String(Math.floor(Date.now() / 1000));
const nonce = crypto.randomBytes(8).toString('hex');
const rawBody = ''; // POST 时用发出去的 JSON 字符串
const sign = crypto.createHmac('sha256', appSecret)
.update(`${timestamp}\n${nonce}\n${rawBody}`).digest('hex');
fetch('https://你的域名/web/index.php?r=api/open/erp/ping', {
headers: {
'X-App-Key': appKey,
'X-Timestamp': timestamp,
'X-Nonce': nonce,
'X-Sign': sign,
}
}).then(r => r.json()).then(console.log);
10.3 curl(POST 库存)
# 先在脚本里算出 SIGN,BODY 必须与签名用的字符串一致
BODY='{"mch_id":0,"bill_type":"in","post_now":"1","lines":[{"goods_id":12,"attr_id_list":[3],"qty":10}]}'
curl -X POST "https://你的域名/web/index.php?r=api/open/erp/stock" \
-H "Content-Type: application/json; charset=UTF-8" \
-H "X-App-Key: akxxxxxxxx" \
-H "X-Timestamp: 1730000000" \
-H "X-Nonce: Ab12Cd34Ef56" \
-H "X-Sign: 算好的小写签名" \
-d "$BODY"
相关文档