Skip to content

开放平台 ​

把这家店的账,安全地交给别的软件和你的 AI 助手。

收银机记一本账、铺子帐房记另一本,月底对不上只能靠回忆——开放平台就是为这件事开的:店里的交易可以由收银软件推进来,收支明细与汇总可以由会计软件拉走,你自己不用天天抄。想更省事,手机里的 AI 助手也能直接查这些数,不用写一行代码。

钥匙在你手里:自己生成、自己停用,不需要平台审批;一家店最多 3 把,谁能干什么、哪天被谁改过,全都留痕。

两扇门,同一套纪律 ​

第一扇门 · 开放接口第二扇门 · 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 助手写入」开着
频率额度两扇门共用同一个桶——换门不涨额度同左

这不是"同一套东西抄两遍"。鉴权、权限勾选、门店状态门禁、限流、金额与日期校验、字段出域检查,全部走同一段代码;助手拿到的字段和接口拿到的逐字相同,不存在"多看一眼"的数据。

生成一把钥匙 ​

  1. App 里「我的门店 → 开放接口」(只有老板能看到这一段);
  2. 勾「这把钥匙要做什么」:
    • 上传交易 —— 只给收银机这类要写流水的系统;
    • 读取账务 —— 只给会计软件这类只看数的系统;
    • 两枚都勾也行,但建议分开,谁出问题停谁;
  3. 点「生成一把」,弹窗里当场复制 AppKey 与完整密钥——这是唯一一次能看到完整密钥,关掉就再也读不回来;
  4. 把这两串交给对接方(收银软件通常填在「接口设置 / 门店对接」里);
  5. 之后列表只显示密钥尾号与最近用过的日期;要换权限没有"编辑"按钮,停用旧的、再生成一把。

密钥只能作废,不能找回

库里不存明文,只存哈希与末 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.txnsGET /txns读取账务since 或 from/to、page/pageSize
ledger.txnSummaryGET /txns/summary读取账务from/to(必填)、page/pageSize
ledger.ordersGET /orders读取账务since 或 from/to、分页
ledger.paymentsGET /payments读取账务since 或 from/to、分页
ledger.purchasesGET /purchases读取账务from/to(必填)、分页
ledger.supplierPaymentsGET /supplier-payments读取账务since 或 from/to、分页
ledger.expensesGET /expenses读取账务from/to(必填)、分页
ledger.payablesGET /payables读取账务无参(整店一份「仍欠」)
ledger.summaryGET /summary读取账务from/to(必填)
profile.storeGET /store读取账务无参
profile.clerksGET /clerks读取账务无参
profile.staffGET /staff读取账务无参
profile.productsGET /products读取账务since 或 from/to、分页
profile.membersGET /members读取账务since 或 from/to、分页
profile.cardsGET /cards读取账务since 或 from/to、分页
profile.shiftsGET /shifts读取账务since 或 from/to、分页
profile.inventoryLedgerGET /inventory-ledger读取账务since 或 from/to、分页
profile.dictionariesGET /dictionaries读取账务无参(上传前先取字典校验 method 取值)
txn.uploadPOST /txns上传交易 + 助手写闸externalId、businessDate、amount、method,可选 occurredAt/memo
txn.batchPOST /txns/batch上传交易 + 助手写闸items(1~100 条,条内字段同上)
txn.voidPOST /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);功能建议见 联系我们,其它功能用法见 功能介绍。

用 铺子帐房,把每家店经营明白。