开放平台
把这家店的账,安全地交给别的软件和你的 AI 助手。
收银机记一本账、铺子帐房记另一本,月底对不上只能靠回忆——开放平台就是为这件事开的:店里的交易可以由收银软件推进来,收支明细与汇总可以由会计软件拉走,你自己不用天天抄。想更省事,手机里的 AI 助手也能直接查这些数,不用写一行代码。
钥匙在你手里:自己生成、自己停用,不需要平台审批;一家店最多 3 把,谁能干什么、哪天被谁改过,全都留痕。
- 店主请从 两枚写总闸 与 生成一把钥匙 看起;
- 对接方(收银软件、代账会计)直接看 能力清单 与 给对接方示例;
- 要接 AI 助手(MCP)的,看 MCP 接入:握手、列工具、调用形状、21 条工具对照表都在那儿;
- 想知道我们会不会把顾客手机号给出去,看 隐私边界。
两扇门,同一套纪律
| 第一扇门 · 开放接口 | 第二扇门 · MCP 助手接入 | |
|---|---|---|
| 给谁用 | 会发 HTTP 请求的软件(收银机、代账工具、Excel 插件) | 支持 MCP 的 AI 助手(Claude、Cursor、各家 agent) |
| 地址 | https://www.miandia.com/api/open/v1 | {{MCP 服务地址}}(路径固定 POST /api/mcp,挂在开放接口同一台服务上;完整域名随发版公布,见 MCP 接入) |
| 钥匙 | 同一把 AppKey + 密钥(不另发) | 同一把 AppKey + 密钥 |
| 能力 | 读 18 条 + 写 3 条 | 同样的 21 条,一一对应,不增不减 |
| 写入需要 | 钥匙勾了「上传交易」且「收银与软件写入」开着 | 钥匙勾了「上传交易」且「AI 助手写入」开着 |
| 频率额度 | 两扇门共用同一个桶——换门不涨额度 | 同左 |
这不是"同一套东西抄两遍"。鉴权、权限勾选、门店状态门禁、限流、金额与日期校验、字段出域检查,全部走同一段代码;助手拿到的字段和接口拿到的逐字相同,不存在"多看一眼"的数据。
生成一把钥匙
- App 里「我的门店 → 开放接口」(只有老板能看到这一段);
- 勾「这把钥匙要做什么」:
- 上传交易 —— 只给收银机这类要写流水的系统;
- 读取账务 —— 只给会计软件这类只看数的系统;
- 两枚都勾也行,但建议分开,谁出问题停谁;
- 点「生成一把」,弹窗里当场复制 AppKey 与完整密钥——这是唯一一次能看到完整密钥,关掉就再也读不回来;
- 把这两串交给对接方(收银软件通常填在「接口设置 / 门店对接」里);
- 之后列表只显示密钥尾号与最近用过的日期;要换权限没有"编辑"按钮,停用旧的、再生成一把。
密钥只能作废,不能找回
库里不存明文,只存哈希与末 4 位。忘了、外泄了、换了收银软件,都是同一个动作:停用,重发一把。停用即刻生效,不需要联系客服。
两枚写总闸
写入是唯一会改变店里数据动作,所以除了钥匙上的权限勾选,店级还各有一枚总闸。两枚各管各的门,互不影响:
| 开关 | 管哪扇门 | 出厂默认 | 关掉之后 |
|---|---|---|---|
| 收银与软件写入 | 开放接口(收银机、会计软件) | 开 | 收银软件传不进新流水(报 38016),读取与助手那扇门不受影响 |
| AI 助手写入 | MCP 助手接入 | 关 | 助手看不见 3 个写入工具,强行调用也被拒(报 38017),收银机不受影响 |
- 默认值是有意的:接口那侧出厂就是开的,已经装好收银软件的店上线日零感知;助手那侧出厂就是关的,没人亲手拨开之前,这条写入路径对外不存在。
- 每次翻转都记谁、什么时候、从什么改成什么,在同一页面可以往回读;改这两枚闸的入口只有老板账号,店员与平台都动不了(平台没有代管入口,和钥匙一样:自助)。
- 拒绝文案会点名是哪扇门,不会让你查半天发现是另一枚闸。
能力清单
一共 21 条:读 18 条 + 写 3 条。所有路径都在 https://www.miandia.com/api/open/v1 下,鉴权用请求头三件套;参数里不需要也不接受 storeId,能碰哪家店完全由这把钥匙决定。
写入(需要「上传交易」勾选 + 收银写闸开)
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /txns | 上传一笔交易流水 |
| POST | /txns/batch | 批量上传,单次 ≤100 条,逐条回执 |
| POST | /txns/{externalId}/void | 作废一笔(更正只有作废,没有改单) |
没有的写口:改价、库存调整、回款登记、卡券核销、会员与商品建档、支出侧写入。这条线是全系统共守的:记录不可改,只可作废;能给外部开的写入口子只能是不改已有账的那三种。
账务读取(需要「读取账务」勾选)
| 路径 | 内容 |
|---|---|
/txns、/txns/summary | 外部上传的流水明细与汇总 |
/orders | 销售单据头 |
/payments | 回款(挂账销账) |
/purchases | 进货单头 |
/supplier-payments | 供应商付款 |
/expenses | 费用记录 |
/payables | 应付「仍欠」 |
/summary | 收支两侧区间汇总 |
档案读取(需要「读取账务」勾选)
| 路径 | 内容 |
|---|---|
/store | 门店状态、有效期、行业与地区等基本信息 |
/clerks、/staff | 店员与技师 |
/products | 商品目录(不含进货成本价) |
/members | 脱敏会员视图 |
/cards | 卡与券余额 |
/shifts | 班次与交班汇总 |
/inventory-ledger | 库存台账 |
/dictionaries | 封闭枚举字典(支付方式、门店状态等的取值与中文含义) |
MCP 那侧的工具名
同样 21 条,一一映射成 21 个工具:ledger.* 管账目读、profile.* 管档案读、txn.* 管写入,与 REST 路径一一对应。每个工具都带三段式中文说明:用途 / 参数关键语义(含取值边界)/ 何时不该用。只勾「读取账务」的钥匙,列出来的就只有读工具;「AI 助手写入」关着时,写工具连清单都不出现。调用方法与工具对照表见 MCP 接入。
清单只是目录,不是通行证
工具列表缓存在助手侧,但每一次调用服务端都从头再验一遍:凭证、勾选、店闸、门店状态、频率与单次成本封顶。半路关掉闸,下一次调用立刻被拒。
数据口径:对接前必读的四件事
1)金额。 上传时是数字,最多两位小数——填三位直接拒(38010),系统不替你四舍五入。返回时统一是两位小数的定点字符串,不会出现浮点尾巴。一笔流水只有一列 amount,「应收 / 实收 / 现金」三个口径由它加支付方式派生:
| 口径 | 含义 | 不含什么 |
|---|---|---|
应收 receivable | 单据整笔金额(退货笔次取负) | — |
开单实收 paidOnBill | 开单时收到的钱,事后回款不改它 | 不含后续挂账回款 |
回款 repayment | 区间内登记的有效回款 | 不含已作废那笔 |
未收 outstanding | 应收 − 开单实收 − 有效冲抵(只派生,不可写) | — |
现金流入 cashReceived | 真进抽屉的钱 | 不含赊账(那是债)与卡内余额消费(那是负债减少) |
支付方式共六种:CASH 现金 / WECHAT 微信 / CREDIT 赊账 / CARD_BALANCE 卡内余额 / WECHAT_TRANSFER 微信转账 / ALIPAY_TRANSFER 支付宝转账。卡余额与待提券是店对顾客的负债,不会与当日收入相加。
2)日期。 三套日期各用各的,别混:
| 数据 | 归哪一天 |
|---|---|
| 你上传的流水 | 你声明的 businessDate(服务端受理时刻另存一列,永不参与归日) |
| 进货、费用 | 单据上的日期(补录上月是常态,不按创建时间归集) |
| 销售、回款、供应商付款 | 记录时刻按东八区折成日历日 |
日期入参只收 YYYY-MM-DD。带时刻或时区偏移的串(如 2026-11-07T00:00:00+08:00)会被拒——跨系统对接最常见的翻车点就是这种日界漂移,所以入口直接钉死。营业日不能晚于明天(38008,通常是收银设备时钟漂了),不能早于 90 天前(38009,太久以前的账请走人工,别让系统收下错账)。
3)幂等与更正。 (门店, 外部单号) 是库里的唯一键:
- 同单号同内容重复提交 → 告诉你「重复」不算失败(38011),不会记出两笔;
- 同单号不同内容 → 拒(38012),不改写原记录,请作废后换新单号重传;
- 更正只有作废,作废可重复提交且结果与首次一致;作废行仍留在读取结果里(靠标记识别)——为了让你第二次拉取永远不会"莫名少了几条";
- 批量逐条回执、绝不整批回滚:一条坏数据不该让收银机无限重推;
- 所以被限流后放心重试是安全的:写口全部幂等。
4)增量与汇总。 明细按创建时刻做游标分页;同一区间「逐页拉全再相加」与「一次汇总」必须逐分相等。对外不出现"报表"二字,区间数字叫汇总——库里没有历史快照,存量类只给当下值、不给环比。
频率与单次封顶
按钥匙计量,不按 IP(小店全店走一个出口,按 IP 限会让收银机、后厨平板、会计软件互相挤兑)。桶容量是配额的 2 倍,允许短时突发,长期均速等于配额。
| 桶 | 配额 | 用在 |
|---|---|---|
| 上传 / 作废 | 600 次/分钟 | POST /txns、/txns/{id}/void |
| 批量 | 60 次/分钟 | POST /txns/batch |
| 明细读取 | 300 次/分钟 | 各条读口 |
| 汇总 | 60 次/分钟 | 两个汇总接口 |
| 字典 | 200 次/分钟 | /dictionaries |
| 生成钥匙 | 3 次/小时 | 店主侧(防账号被盗后狂建钥匙) |
超限:429 + Retry-After,文案会写清还要等几秒(38006)。单次成本封顶:分页 ≤100 条、区间跨度 ≤31 天、批量 ≤100 条、请求体 ≤100KB(超了是 38007)。
门店状态:只有营业中的门店接受调用(38004);到期后读得出、写不进(38005),续费后可以按原营业日把到期期间的流水补推上来,不丢账。
隐私边界
外部只出单据头——不给的比给的多。明确不给的:商品行项与价格结构、会员完整手机号与微信标识、账号与密码字段、进货成本价与毛利下限、证照照片、券号全串(只给末 4 位)等。
还有一层兜底:响应序列化后按出域形态再检查一次(比如完整手机号那类形态),命中就整次拒发并指出是哪个字段(38015),而不是"抹掉那一列继续返回"——账目少一列会被读成假账,宁可报错。
日志侧的纪律:只出现 AppKey,永不出现密钥与请求体。
开启助手接入前请知情
MCP 意味着你店里的经营数据会进入外部大模型的上下文(这是这一功能唯一的代价,换来的是零对接成本)。上面这些出域红线原样穿过这扇门,不放宽;写入工具默认不可见,先把「AI 助手写入」保持在关,只用读工具试手感。
给对接方示例
请求头三件套:x-app-key、x-app-secret、x-timestamp(epoch 毫秒,与服务器相差不得超过 5 分钟,否则 38002)。未知字段一律拒绝(多传一个字段是报错,不是被忽略)。
bash
curl -X POST 'https://www.miandia.com/api/open/v1/txns' \
-H 'Content-Type: application/json' \
-H 'x-app-key: ak_1a2b3c4d' \
-H 'x-app-secret: 店主当场复制的那串完整密钥' \
-H 'x-timestamp: 1761696000000' \
-d '{
"externalId": "POS-20261108-0001",
"businessDate": "2026-11-08",
"amount": 39.90,
"method": "CASH",
"memo": "收银机 A"
}'返回信封与 App 内接口一致(code / data / message,code=0 为成功)。字段级定义、分页参数与枚举取值,以只含开放接口的在线文档为准:https://www.miandia.com/api/open-docs。
错误码速查
| 码 | 含义 | 对接方该做什么 |
|---|---|---|
| 38001 | 凭证无效(密钥不对、已停用、或不存在——三种同一种说法,不给你可枚举的探针) | 找店主重发一把 |
| 38002 | 时间戳超出允许范围 | 校对收银设备时钟 |
| 38003 | 这把钥匙没勾该项权限 | 让店主停用重发并勾上 |
| 38004 | 门店不是营业中 | 联系店主/代理 |
| 38005 | 门店已到期:读得出、写不进 | 续费后按原营业日补推 |
| 38006 | 调用过于频繁 | 按 Retry-After 等待后重试(幂等,安全) |
| 38007 | 单次成本越界(分页/区间/批量/请求体) | 拆小区间、减小分页 |
| 38008 | 营业日晚于允许日期 | 检查设备时钟 |
| 38009 | 营业日超出补推窗口(90 天) | 走人工 |
| 38010 | 金额不合法(负数 / 超两位小数 / 超上限) | 不要指望系统四舍五入 |
| 38011 | 重复提交(同单号同内容) | 不算失败,继续下一笔 |
| 38012 | 同单号内容不一致 | 作废原笔,换新单号重传 |
| 38013 | 本店没有这个单号 | 单号来自你自己的系统,别跨店找 |
| 38014 | 密钥已达上限(3 把) | 先停用一把 |
| 38015 | 返回内容含个人信息形态,整次拒发 | 检查是哪句备注/名称触发的 |
| 38016 | 本店已停用对外接口写入 | 店主在「我的门店 → 开放接口」重新开启 |
| 38017 | 本店未开启助手写入 | 店主拨开「AI 助手写入」 |
MCP 接入
这扇门的地址还没公布
协议、鉴权、工具清单都已定稿,但服务域名要等平台发版时公布(路径固定是 POST /api/mcp)。没拿到地址之前照着下面调用连不上是正常的——要接就往 联系我们 说一声,我们把你排进对接名单。
形态是 Streamable HTTP:一条 POST 地址上跑 JSON-RPC 2.0,鉴权用的还是那三头。这一侧是无会话(stateless)设计——每一帧 HTTP 请求都独立跑一遍完整鉴权与准入判定,服务端不颁发、也不引用任何 Mcp-Session-Id;GET 与 DELETE 直接 405(不提供服务器推送通道,也没有会话可终止)。版本协商接受 MCP 现行协商集(2025-11-25 / 2025-06-18 / 2025-03-26 / 2024-11-05 / 2024-10-07),客户端带的版本不被支持时不会进入可调用态。
1)每一帧都带三头(和开放接口同一套)
| 头 | 填什么 |
|---|---|
x-app-key | 这把钥匙的 AppKey |
x-app-secret | 完整密钥原文(全程 https;服务端只比哈希,日志永不记它) |
x-timestamp | 每帧新生成的 epoch 毫秒,与服务器相差 5 分钟内 |
静态 header 的填法撑不过 5 分钟
时间戳必须是每一帧重新生成的。如果你的客户端只能填一次固定头(多数图形化配置的默认行为),前几分钟能用,之后就会一直收到 38002。要长期接,请用支持"每请求动态注入请求头"的客户端,或在本地跑一层小代理来签时间戳——这也是我们建议先只读试手感的原因。
2)握手 initialize
json
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"clientInfo": { "name": "你的客户端", "version": "1.0.0" },
"capabilities": {}
}
}响应里的 serverInfo.name 是 puzi-openapi-mcp,能力只声明 tools(不提供 resources 与 prompts):
json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-06-18",
"serverInfo": { "name": "puzi-openapi-mcp", "version": "1.0.0" },
"capabilities": { "tools": {} }
}
}握手之后按协议补一帧 notifications/initialized,再往下才是列工具与调用。
3)tools/list —— 清单就是"这把钥匙此刻能用的一切"
json
{ "jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {} }返回的每个工具形如(截断):
json
{
"name": "ledger.orders",
"title": "销售单据头",
"description": "用途:查本店店内销售单据头(应收/开单实收/让利/未收四数并列,不含行项与会员标识)。参数:since 或 from/to、page/pageSize。何时不该用:这里只读店内账目的只读视图,不改动任何账。",
"inputSchema": { "type": "object", "additionalProperties": false, "properties": { "since": {}, "from": {}, "to": {}, "page": {}, "pageSize": {} } }
}可见集合 = 钥匙勾选 × 「AI 助手写入」闸:read 钥匙只见读工具;upload 钥匙还要那枚闸开着才见 txn.* 三条。什么都不该见时返回空清单,而不是"方法不存在"的错误——这样客户端的行为是可预期的。
4)tools/call —— 成功与拒绝都是协议内结果
json
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "txn.upload",
"arguments": {
"externalId": "POS-20261108-0001",
"businessDate": "2026-11-08",
"amount": 39.9,
"method": "CASH",
"memo": "收银机 A"
}
}
}成功:业务数据(与 REST 响应里 data 那份逐字同)直接放进 content[0].text,isError 为假——REST 那层 {code, data, message} 信封在这侧不需要,所以少一层壳:
json
{
"jsonrpc": "2.0",
"id": 3,
"result": { "content": [{ "type": "text", "text": "{…与 REST 的 data 字段同形…}" }], "isError": false }
}被拒(闸没开 / 没勾权限 / 门店到期 / 被限流 / 参数不合规 / 出域断言命中)一律是 isError: true + content[0].text 里一段 {code, message},错误码与开放接口同族、可区分:
json
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"isError": true,
"content": [{ "type": "text", "text": "{\"code\":38017,\"message\":\"该门店未开启助手写入,AI 助手不能代记流水(店主可在门店设置开启)\"}" }]
}
}只有协议本身出问题(帧不合法、方法未知、版本不接受)才走 JSON-RPC 的 error 对象;HTTP 错误页绝不会漏进协议流,而且这类拒绝零副作用——不落库、不计费、不建半截会话。限流那条文案会写明还要等几秒,重试安全(写工具全幂等)。
5)工具对照表(21 条)
| 工具名 | 对应 REST | 需要勾选 | 参数 |
|---|---|---|---|
ledger.txns | GET /txns | 读取账务 | since 或 from/to、page/pageSize |
ledger.txnSummary | GET /txns/summary | 读取账务 | from/to(必填)、page/pageSize |
ledger.orders | GET /orders | 读取账务 | since 或 from/to、分页 |
ledger.payments | GET /payments | 读取账务 | since 或 from/to、分页 |
ledger.purchases | GET /purchases | 读取账务 | from/to(必填)、分页 |
ledger.supplierPayments | GET /supplier-payments | 读取账务 | since 或 from/to、分页 |
ledger.expenses | GET /expenses | 读取账务 | from/to(必填)、分页 |
ledger.payables | GET /payables | 读取账务 | 无参(整店一份「仍欠」) |
ledger.summary | GET /summary | 读取账务 | from/to(必填) |
profile.store | GET /store | 读取账务 | 无参 |
profile.clerks | GET /clerks | 读取账务 | 无参 |
profile.staff | GET /staff | 读取账务 | 无参 |
profile.products | GET /products | 读取账务 | since 或 from/to、分页 |
profile.members | GET /members | 读取账务 | since 或 from/to、分页 |
profile.cards | GET /cards | 读取账务 | since 或 from/to、分页 |
profile.shifts | GET /shifts | 读取账务 | since 或 from/to、分页 |
profile.inventoryLedger | GET /inventory-ledger | 读取账务 | since 或 from/to、分页 |
profile.dictionaries | GET /dictionaries | 读取账务 | 无参(上传前先取字典校验 method 取值) |
txn.upload | POST /txns | 上传交易 + 助手写闸 | externalId、businessDate、amount、method,可选 occurredAt/memo |
txn.batch | POST /txns/batch | 上传交易 + 助手写闸 | items(1~100 条,条内字段同上) |
txn.void | POST /txns/{externalId}/void | 上传交易 + 助手写闸 | externalId |
调用体与 REST 是同一个执行点:入参用同一份 zod 校验、区间跨度与分页封顶在同一处判、字段出域断言也在同一位置(读工具挂、写工具不挂,与 REST 逐字镜像)。所以"助手看到的字段会不会更多"这个问题,答案由构造决定:不会。
6)客户端配置长什么样
json
{
"mcpServers": {
"puzi-zhangfang": {
"type": "http",
"url": "<向平台索取的 MCP 服务地址>",
"headers": {
"x-app-key": "ak_…",
"x-app-secret": "…(生成那一次复制走的完整密钥)",
"x-timestamp": "…(每帧新生成)"
}
}
}
}别把密钥写进会被同步走的地方
密钥明文只出现在生成那一次的弹窗里,之后任何地方读不回来。填进助手配置时,选本地配置文件而不是会云同步的笔记/仓库;怀疑外泄就在「我的门店 → 开放接口」里停用这把、重发一把。
常见疑问
会不会把店里的账改乱? 写口只有上传与作废,没有改单;重复上传被唯一键挡下;写进去的流水是独立一本簿,不进你的看板、交班、未结台账——界面数字与接入之前逐字相同,比对由对接方完成。
店员能看到钥匙吗? 这一段只有老板可见;店员既不能生成也不能停用。
我不想用了怎么办? 停用钥匙、或把对应写闸拨回关,即刻生效;历史流水留在库里可回读,不删。
顾客数据会因此外泄吗? 见 隐私边界。外部拿不到完整手机号,拿不到的东西也不会因为换了一扇门就变得能拿到。
对接中卡住了:客服电话 18175831106(请附店名、请求时间、返回的 code 与 message);功能建议见 联系我们,其它功能用法见 功能介绍。
