咖莱翼开放平台 · 开放 API 对接文档

面向第三方系统的 M2M 对接接口(OAuth2 client_credentials / PAT 双凭证) · 版本 v1 · 最后更新 2026-10-11

1. 概述与基础地址

开放 API 用于让您的自有系统(ERP、BI、财务中台、连锁总部系统等)以机器对机器(M2M)方式安全读取或写入咖莱翼各业务数据。所有接口均为 HTTPS,返回统一 JSON 信封。

生产基础地址(Base URL):

https://open.calay.com.cn/api/open/v1

说明:独立域名 open.calay.com.cn 即接入入口,所有开放接口均挂在 /api/open/v1 前缀下。测试/演示环境路径带 /v050 前缀(如 https://test.calay.com.cn/v050/api/open/v1),字段与行为完全一致。

能力范围

利润中心(读/写) 人事中心(读) 报表中心(读) 渠道订单(读) 共享商业(读)

红线:开放 API 不含「出金 / 真实发票开具 / 系统配置」类写操作;此类敏感动作仅限控制台人工操作,不通过开放接口暴露。密钥创建/吊销属于控制台操作,需 owner/admin 登录后调用。

2. 对接准备与步骤

调用接口前请先完成以下步骤,与通用约定一起阅读。

1申请密钥

由租户 owner/admin 在控制台「开放平台」创建密钥,勾选所需 scope(如 profit:read、report:read)。

2领取凭证

创建后一次性获得 client_id(calay_ 前缀)与 client_secret(sk_ 前缀)。secret 仅展示一次,请妥善保存。

3阅读约定

阅读下方「通用数据格式约定」「鉴权方式」「响应格式」,了解金额 fen / 比率 bp 规则。

4调接口

用 client_id+secret 换取 JWT,或用 sk_ 直连,按接口目录调用业务接口。

最小权限建议:为每个对接系统创建独立密钥,仅授予所需 scope;系统下线时及时在控制台吊销。密钥等同密码,切勿写入前端代码或公开仓库。

3. 通用数据格式约定

为确保跨语言(Java / Python / Node / PHP 等)解析一致,平台对数据格式做如下统一约定:

3.1 编码与内容类型

  • 请求与响应统一使用 UTF-8 编码。
  • 请求体固定为 application/json(除 GET 查询外),请勿使用表单提交。
  • 时间字段:会计月用 yyyy-MM(如 2026-09);订单支付时间等时间戳用 yyyy-MM-dd HH:mm:ss,不带毫秒。

3.2 金额单位:一律以「分」整数返回 _fen

重要:涉及金额的所有字段,均以「分」(人民币最小单位)的整数返回,字段名以 _fen 结尾,例如 revenue_fen、cost_fen、profit_fen、amount_fen、total_profit_fen。
换算:元 = fen / 100。例如 revenue_fen = 123456 表示 1234.56 元。
写入时同样传「分」整数(如营收录入传 amount_fen: 123456)。请勿使用浮点直接运算或 toFixed 做金额换算,避免精度丢失(如 1.005 元会被误算为 1.00 元)。

3.3 比率单位:一律以「基点 bp」整数返回

重要:毛利率、分红比例等比率,均以「基点」(万分之一)的整数返回,字段名以 _bp 结尾,例如 gross_margin_bp、dividend_bp、payback_multiple_bp。
换算:百分比 = bp / 100。例如 gross_margin_bp = 3500 表示毛利率 35.00%;dividend_bp = 4000 表示分红比例 40.00%。payback_multiple_bp = 10000 表示回本倍数 1.00 倍。

3.4 标识型字段(ID)与精度

  • id / store_id / org_id / tenant_id 等标识字段为整型或字符串,请原样透传,不要在前端用浮点解析(如 JavaScript 的 Number() 对超长整型会丢失精度,建议以字符串处理或保持原类型)。
  • 枚举/状态字段(如 status、doc_type、channel)为小写字符串,取值见各接口说明。

3.5 分页约定(当前版本)

为降低接入复杂度,当前版本多数接口返回全量匹配数据;部分高频写后查询接口采用「最近 N 条」截断:

接口返回策略
GET /channel/orders返回该租户最近 200 条渠道订单(可按 /:channel 过滤单一渠道),无游标分页
GET /sb/bills(不带 store_id/month)返回最近 100 条结算单
其余列表/详情接口返回全部匹配行(数据量可控,一般无需分页)
后续若引入 page/pageSize 游标分页,将以响应中新增 page/pageSize/total 字段为准,本文档同步更新。

4. 鉴权方式

每个请求必须在 Authorization 头携带 Bearer 令牌。支持两种获取/携带方式:

方式 A:OAuth2 client_credentials(推荐服务端调用)

用 client_id + client_secret 调用令牌接口换取短期 JWT(默认 1 小时有效),再以 JWT 调业务。JWT 可服务端缓存复用,避免每次请求都换取。

# 1) 换取令牌
curl -X POST https://open.calay.com.cn/api/open/v1/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "calay_a6856eec8010e5fa87d93084",
    "client_secret": "sk_336a257b366abb9a8410c502099f1761fe38fb3d61d4e297"
  }'

# 2) 用返回的 access_token 调业务
curl https://open.calay.com.cn/api/open/v1/profit/dashboard \
  -H "Authorization: Bearer <access_token>"

方式 B:PAT 个人访问令牌(sk_ 直连)

直接用密钥本身(sk_ 前缀)作为 Bearer 令牌调用业务,无需先换 JWT,适合简单脚本与快速联调。

curl https://open.calay.com.cn/api/open/v1/profit/dashboard \
  -H "Authorization: Bearer sk_336a257b366abb9a8410c502099f1761fe38fb3d61d4e297"
密钥由租户 owner/admin 在控制台「开放平台」中创建,可精细勾选 scope(利润读/写、人事读、报表读、渠道读、共享商业读),并可随时吊销。令牌接口 IP 限流 20 次/分钟。

5. 请求头与公共参数

请求头必填说明
Authorization是Bearer <token>,token 为方式 A 换取的 access_token 或方式 B 的 sk_ 明文密钥
Content-Type是(POST)固定 application/json; charset=UTF-8
Accept否建议 application/json

公共路径/查询参数

  • :month / month:会计月份,格式 YYYY-MM(如 2026-09)。
  • :store_id / store_id:门店 ID(整型,原样透传)。
  • :org_id:组织编码,用于按组织定位门店利润。
  • :months:趋势回溯月数(整型,上限 24)。
平台不要求调用方在请求头中传递租户 ID;租户身份由密钥/JWT 自动解析并强制行级隔离,调用方只能访问自身租户数据。

6. 响应格式(统一信封)

所有接口返回统一结构:

字段类型说明
codeint0 = 业务成功;1 = 业务失败
bizCodestring业务码,如 SUCCESS / APIKEY_INVALID / SCOPE_DENIED
messagestring人类可读描述
dataobject/array业务数据载体(各接口字段见下文)
tenant_idstring数据所属租户
// 成功
{
  "code": 0,
  "bizCode": "SUCCESS",
  "message": "ok",
  "data": { ... },
  "tenant_id": "tenant_xxxx"
}

// 失败
{
  "code": 1,
  "bizCode": "APIKEY_INVALID",
  "message": "密钥凭证错误或已吊销",
  "data": null,
  "tenant_id": null
}
HTTP 状态码与 code 配合判断:鉴权/授权类失败通常返回 401/403,参数错误返回 400;请同时检查 HTTP 状态码与 bizCode。

7. 错误码(bizCode)

业务失败时在响应 bizCode 返回,HTTP 状态码见下表:

bizCodeHTTP含义处理建议
PARAM_INVALID400必需参数缺失或格式错误检查请求体字段是否完整、类型是否正确
UNSUPPORTED_GRANT400grant_type 非 client_credentials固定传 client_credentials
AUTH_REQUIRED401未携带 Authorization 头补充 Authorization: Bearer <token>
APIKEY_INVALID401client_id / client_secret 不匹配核对密钥,或重新创建
APIKEY_REVOKED401密钥已被吊销在控制台重新生成密钥
APIKEY_EXPIRED401密钥已过期在控制台延长有效期或重新生成
SCOPE_DENIED403当前密钥 scope 不包含该资源在控制台为密钥追加对应 scope
SB_PERMISSION_DENIED403角色不满足接口所需权限(如写接口需 write)使用具备 write 角色的密钥,或检查 role
NOT_FOUND404资源不存在(如门店/月份无数据)确认参数 org_id/store_id/month 正确
STORE_NOT_FOUND404门店不存在或无权限确认 store_id 属于本租户
STORE_PROFIT_NOT_FOUND404该门店该月无利润数据先录入营收/成本再查询
CHANNEL_UNSUPPORTED400渠道不支持(仅 meituan/eleme/jd/taobao)使用受支持渠道枚举
PAYLOAD_TOO_LARGE413请求体超出最大允许大小拆分批量请求
REQUEST_TIMEOUT408请求体读取超时(慢速)提升网络或减小包体

8. 权限 Scope 矩阵

密钥创建时按以下 scope 子集授权,调用对应接口时由网关强制校验:

Scope说明覆盖接口
profit:read利润中心只读dashboard / stores / store profit / profit report / 趋势
profit:write利润中心写入创建门店 / 营收录入 / 成本录入
hr:read人事中心只读hr dashboard / employees
report:read报表中心只读利润趋势 / 月度利润 / 人事薪资 / 账单发票
channel:read渠道订单只读channel orders
sb:read共享商业只读sb agreements / bills / snapshots
密钥管理接口(/keys 系列)不走 scope 体系,需控制台登录且具备 config 权限(owner/admin)。

9.1 令牌换取

POST /api/open/v1/oauth/token
公开接口(无需令牌)· IP 限流 20 次/分钟

请求体参数

参数类型必填说明
grant_typestring是固定 client_credentials
client_idstring是控制台生成的客户端 ID(calay_ 前缀)
client_secretstring是密钥明文(sk_ 前缀)
api_keystring否*也可用 sk_ 明文直接换令牌(*与 client_secret 二选一)

响应 data 字段

字段类型说明
access_tokenstringJWT,用于后续业务调用的 Bearer 令牌
token_typestring固定 Bearer
expires_inint有效期(秒),默认 3600
scopestring该密钥被授予的 scope(逗号分隔字符串)
{
  "code": 0,
  "bizCode": "SUCCESS",
  "message": "ok",
  "data": {
    "access_token": "eyJ...",
    "token_type": "Bearer",
    "expires_in": 3600,
    "scope": "profit:read,report:read"
  },
  "tenant_id": "tenant_xxxx"
}

9.2 密钥管理(控制台内操作)

以下接口需控制台会话 + config 权限(owner/admin),不走开放 scope 体系。密钥明文仅创建时返回一次。
POST /api/open/v1/keys
需控制台会话 + config 权限 · 创建密钥
参数类型必填说明
namestring否密钥备注名(≤60 字)
accessstring否read(默认,仅读)/ readwrite(读写数据,不含 config)
scopesarray是scope 子集,如 ["profit:read","report:read"]

响应 data:client_id、client_secret(仅创建时明文返回一次)、role、scopes、expires_in。

GET /api/open/v1/keys
需控制台会话 + config 权限 · 列出本租户密钥(不含 secret)

响应 data:items(数组,含 id/client_id/name/role/scopes/status/created_at/last_used_at/expires_at)、total。

POST /api/open/v1/keys/:id/revoke
需控制台会话 + config 权限 · 吊销密钥(:id 为内部 id,非 client_id)

响应 data:revoked: true。

9.3 利润中心(scope: profit:read / profit:write)

GET /api/open/v1/profit/dashboard
profit:read

响应 data 字段

字段类型说明
monthstring统计会计月 YYYY-MM
storesarray各门店汇总,元素见下
total_profit_fenint全部门店净利润合计(分)
total_gross_margin_bpint整体毛利率(基点,/100=%)

stores[] 元素:id(int)、name(string)、org_id(string|null)、revenue_fen(int)、cost_fen(int)、profit_fen(int)、gross_margin_bp(int)。

GET /api/open/v1/profit/stores
profit:read

响应 data 字段

字段类型说明
totalint门店总数
listarray门店数组

list[] 元素:id(int)、name(string)、org_id(string|null)、bound_months(int,已绑定利润的月数)。

GET /api/open/v1/profit/store/profit/:org_id/:month
profit:read

路径参数

参数说明示例
org_id组织/门店编码org_1001
month会计月份 YYYY-MM2026-09

响应 data 字段(单对象)

字段类型说明
store_idint门店 ID
revenue_fenint营收(分)
cost_fenint成本(分)
profit_fenint净利润(分)
gross_margin_bpint毛利率(基点)
GET /api/open/v1/profit/report/:store_id/:month
profit:read

路径参数

参数说明示例
store_id门店 ID1
month会计月份 YYYY-MM2026-09

响应 data 字段

字段类型说明
storeobject门店主记录(含 id/name/org_id 等)
profitobject{revenue_fen,cost_fen,profit_fen,gross_margin_bp}
revenuearray按渠道营收汇总,元素 {channel, s}(s 为分)
costItemsarray成本明细,元素 {cost_type, source, s}(s 为分)
POST /api/open/v1/profit/stores
profit:write · 限流 600 次/分钟/密钥

请求体参数

参数类型必填说明
namestring是门店名称
org_idstring否组织编码

响应 data 字段

字段类型说明
idint新建门店 ID
namestring门店名称
org_idstring|null组织编码
POST /api/open/v1/profit/revenue
profit:write · 限流 600 次/分钟/密钥

请求体参数

参数类型必填说明
store_idint是门店 ID
monthstring是会计月份 YYYY-MM
channelstring否渠道标识,默认 offline
amount_fenint是营收金额(分,整数)

响应 data 字段(重算后门店利润)

字段类型说明
revenue_fenint累计营收(分)
cost_fenint累计成本(分)
profit_fenint净利润(分)
gross_margin_bpint毛利率(基点)
POST /api/open/v1/profit/cost
profit:write · 限流 600 次/分钟/密钥

请求体参数

参数类型必填说明
store_idint是门店 ID
monthstring是会计月份 YYYY-MM
cost_typestring是成本类型(如 LABOR/RENT/MATERIAL 等)
amount_fenint是成本金额(分,整数)
sourcestring否来源,默认 manual

响应 data 同营收接口(重算后门店利润四字段)。

9.4 人事中心(scope: hr:read)

GET /api/open/v1/hr/dashboard
hr:read

响应 data 字段

字段类型说明
employee_countint员工总数
monthly_payroll_fenint当月薪资合计(分)
GET /api/open/v1/hr/employees
hr:read

响应 data(数组,无外层包装)

字段类型说明
idint员工 ID
org_idstring|null所属组织
namestring姓名
post_idint|null岗位 ID
statusstring状态,如 active
响应 data 直接为数组(非 {list:[]} 包裹),请勿按对象解析。

9.5 报表中心(scope: report:read)

GET /api/open/v1/report/profit-trend/:store_id/:months
report:read
参数说明示例
store_id门店 ID1
months回溯月数(≤24)12

响应 data 字段

字段类型说明
store_idint门店 ID
monthsint实际回溯月数
listarray按月升序序列,元素 {month, revenue_fen, cost_fen, profit_fen, gross_margin_bp}
GET /api/open/v1/report/profit-trend-all/:months
report:read
参数说明示例
months回溯月数(≤24)12

响应 data 字段

字段类型说明
monthsint实际回溯月数
listarray全部门店合并净利润趋势,元素 {month, total_profit_fen}
GET /api/open/v1/report/profit-month/:month
report:read
参数说明示例
month会计月份 YYYY-MM2026-09

响应 data 字段(二维表)

字段类型说明
monthstring会计月
headerarray列标题:["门店ID","门店名称","营收(元)","成本(元)","净利润(元)","毛利率(%)"]
rowsarray数据行(数组的数组),末行为「合计」
此接口金额已换算为「元」字符串(便于直接展示),与开放 API 其余接口的「分」整数约定不同,请注意区分。
GET /api/open/v1/report/hr-payroll/:month
report:read
参数说明示例
month会计月份 YYYY-MM2026-09

响应 data 字段

字段类型说明
monthstring会计月
headerarray["员工姓名","岗位","月份","底薪(元)","奖金(元)","合计(元)"]
rowsarray数据行(数组的数组)
GET /api/open/v1/report/billing-invoice
report:read

响应 data 字段

字段类型说明
headerarray["发票ID","套餐","金额(元)","状态","到期","支付时间","创建时间"]
rowsarray数据行(数组的数组),金额已为「元」字符串

9.6 渠道订单(scope: channel:read)

GET /api/open/v1/channel/orders
channel:read

渠道(外卖/第三方平台)订单列表。支持按渠道过滤:/api/open/v1/channel/orders/meituan。返回该租户最近 200 条(单一渠道同样上限 200)。

响应 data 字段

字段类型说明
totalint返回条数(≤200)
listarray订单数组

list[] 元素关键字段:id(int)、store_id(int)、channel(string,如 meituan/eleme/jd/taobao)、order_id_ext(string,平台订单号)、paid_at(string|null,支付时间)、amount_fen(int,用户实付,分)、platform_fee_fen(int,平台佣金,分)、item_json(string,原始订单 JSON,体积较大建议按需解析)、created_at(string)。

订单写入由控制台「渠道订单导入」完成(开放 API 当前仅只读,不提供导入写接口,避免越权触碰平台商户凭证闸口)。

9.7 共享商业(scope: sb:read)

GET /api/open/v1/sb/agreements
sb:read

共享商业投资协议列表(data 直接为数组)。

响应 data[] 元素字段

字段类型说明
idint协议 ID
store_idint关联门店 ID
investor_namestring投资人/协议名称
principal_fenint本金(分)
dividend_bpint分红比例(基点)
payback_multiple_bpint回本倍数(基点,10000=1 倍)
region_fenint区域分润(分)
statusstringenabled / disabled
created_atstring创建时间
GET /api/open/v1/sb/bills
sb:read

共享商业分红结算单列表。可按门店/月份过滤:/sb/bills/:store_id、/sb/bills/:store_id/:month;不带参数返回最近 100 条。

响应 data[] 元素字段

字段类型说明
idint结算单 ID
store_idint门店 ID
monthstring会计月
doc_typestringNORMAL 正常单 / REVERSE 冲正单
versionint版本号(冲正后递增)
net_profit_fenint净利润池(分)
dividendsstring各投资人分红明细 JSON 字符串(需解析),元素含 agreement_id/name/amount_fen/region_fen/payback_remaining_fen
statusstring如 generated
created_atstring创建时间
GET /api/open/v1/sb/snapshots
sb:read

分红测算快照列表(data 直接为数组)。

响应 data[] 元素字段

字段类型说明
idint快照 ID
namestring快照名称
created_atstring创建时间

10. 多语言对接示例

以下演示从「换取令牌 → 读取利润总览」的完整链路。示例中的 calay_... / sk_... 为占位,请替换为控制台实际密钥。

10.1 cURL

# 步骤 1:换取访问令牌
TOKEN=$(curl -s -X POST https://open.calay.com.cn/api/open/v1/oauth/token \
  -H "Content-Type: application/json" \
  -d '{"grant_type":"client_credentials","client_id":"calay_替换为你的ID","client_secret":"sk_替换为你的密钥"}' \
  | python -c "import sys,json;print(json.load(sys.stdin)['data']['access_token'])")

# 步骤 2:读取利润总览
curl https://open.calay.com.cn/api/open/v1/profit/dashboard \
  -H "Authorization: Bearer $TOKEN"

# 步骤 3(可选):用 PAT 直连,跳过换令牌
curl https://open.calay.com.cn/api/open/v1/profit/stores \
  -H "Authorization: Bearer sk_替换为你的密钥"

10.2 Python(requests)

import requests, json

BASE = "https://open.calay.com.cn/api/open/v1"
CLIENT_ID = "calay_替换为你的ID"
CLIENT_SECRET = "sk_替换为你的密钥"

# 1) 换取令牌(建议缓存,到期前复用)
r = requests.post(f"{BASE}/oauth/token", json={
    "grant_type": "client_credentials",
    "client_id": CLIENT_ID,
    "client_secret": CLIENT_SECRET,
})
token = r.json()["data"]["access_token"]
headers = {"Authorization": f"Bearer {token}"}

# 2) 读取利润总览
dash = requests.get(f"{BASE}/profit/dashboard", headers=headers).json()
# 金额单位为「分」,换算成元:
for s in dash["data"]["stores"]:
    print(s["name"], "净利润(元)=", s["profit_fen"] / 100,
          "毛利率(%)=", s["gross_margin_bp"] / 100)

10.3 Node.js(fetch,Node 18+)

const BASE = "https://open.calay.com.cn/api/open/v1";
const CLIENT_ID = "calay_替换为你的ID";
const CLIENT_SECRET = "sk_替换为你的密钥";

// 1) 换取令牌
const t = await fetch(`${BASE}/oauth/token`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ grant_type: "client_credentials", client_id: CLIENT_ID, client_secret: CLIENT_SECRET }),
}).then(r => r.json());
const token = t.data.access_token;

// 2) 读取门店列表
const stores = await fetch(`${BASE}/profit/stores`, {
  headers: { Authorization: `Bearer ${token}` },
}).then(r => r.json());
console.log(stores.data.list);
密钥仅在创建时明文展示一次,请妥善保存。泄露立即在控制台吊销。

11. 限流与配额矩阵

为保障服务稳定,开放接口按下列规则限流(超出返回 HTTP 429,建议指数退避重试):

接口 / 类别限流规则维度
POST /oauth/token20 次 / 分钟按客户端 IP
写接口(profit:write 三类)600 次 / 分钟按密钥 clientId
读接口(其余全部)默认不限流(共享集群保护)—
请求体大小单请求上限见 PAYLOAD_TOO_LARGE(413)按请求
  • 令牌缓存:JWT 默认 1 小时有效,请在服务端缓存并重用,避免每次请求都换取(换取接口限流 20 次/分钟/IP)。
  • 最小权限:为每个对接系统创建独立密钥,仅授予所需 scope;下线时及时吊销。
  • 密钥保管:client_secret 等同密码,请勿写入前端代码或公开仓库;泄露立即吊销。
  • 错误重试:遇 401/403 先检查 scope 与令牌有效性;遇网络错误采用指数退避重试。
  • 时间参数:所有月份参数统一 YYYY-MM 格式。

12. 常见问题与红线

Q1:金额字段为什么是「分」而不是「元」?

为避免浮点精度问题(如 1.005 元被误算为 1.00 元),平台内部与开放接口统一以最小货币单位「分」整数传递。展示时除以 100 即可。仅「报表中心」的月报/薪资/发票三类接口为便于直接展示,已将金额格式化为「元」字符串(字段名不含 _fen)。

Q2:比率字段为什么是「基点 bp」?

同理,gross_margin_bp(毛利率)、dividend_bp(分红比例)等以「万分之一」整数传递,除以 100 即百分比。避免浮点比较误差。

Q3:支持回调 / Webhook 推送吗?

当前开放 API 为拉取(pull)模式,暂不支持事件回调推送。如需近实时数据,请按业务节奏轮询对应读接口。

Q4:为什么渠道订单没有分页参数?

当前版本渠道订单返回最近 200 条(按渠道过滤同理),未做游标分页。如需全量历史,请在控制台导出或联系技术支持。

红线提醒

开放 API 不暴露:出金、真实发票开具、系统配置写操作;密钥创建/吊销需控制台 owner/admin 操作。任何以开放接口名义索取明文密码、要求关闭 scope 校验的请求均属异常,请通过官方渠道核实。
如在对接中遇到接口行为与本文档不符,请以接口实际返回的 JSON 字段为准,并联系咖莱翼技术支持核对。