房源接入 API

v2.0 · 2026-09

房源开放接口(商家版)· 房源创建 / 上下架 / 户型 / 房态

面向白名单运营商与持有方商家:自助完成创建、调价、户型维护、逐晚房态、上下架,并接住客户订单(查单 / 确认 / 拒单);C 端目录只展示 online 房源。鉴权复用平台商家开放接口同一套 HMAC-SHA256 机制(与家政开放接口同签名算法、同密钥体系)。

RESTful · JSON 统一 HMAC-SHA256 鉴权 14 个接口 · 全部 POST 房态日历 · 逐晚库存 owner 隔离 · 越权不可见

1 接入概览

房源开放接口是平台商家开放接口的房源分支:路径前缀 /api/juzhu/housing/vendor/*,全部为 POST,请求/响应均为 JSON。鉴权、密钥、错误码风格与家政开放接口(/api/juzhu/jiazheng/vendor/*)完全一致,一套密钥两个业务共用。

  • 提交即生效:房源与户型实时可查,逐晚房态即时更新;客户订单占用的晚自动锁定,任何接口都不可改。
  • 数据归属:房源创建时自动绑定签约商家,所有读写按归属隔离——查询、更新、上下架只能命中自己的房源,他人房源一律 404(不泄露存在性)。
  • 上架语义:status ∈ draft(草稿,默认)/ online(上架)/ offline(下架);C 端目录只出 status=online 且 rating_status=passed(双闸)。目录有 ≤ 15 秒缓存,上下架最迟 15 秒生效。
  • 审核语义:新建一律先落草稿(传 online 创建会被拒);上架前须通过平台评级审核(rating_status:draft → pending → passed / rejected),提审与查进度走开放接口 projects/rating/submit / projects/rating/status(见 4.11)。已上架房源的内容更新免审即时生效。

接入流程

1开通凭证平台为商家配置接入密钥,线下下发 vendor_id + hmac_key。
2沙箱联调用调试台或脚本签名调通 regions/list + projects/list(只读)。
3创建房源projects/create 建草稿(默认不入 C 端),补户型、图片与价格。
4评级提审projects/rating/submit 提交自评分进入平台复核,projects/rating/status 查进度与驳回原因。
5上架运营审核通过后 projects/status 上架;日常用房态接口做关房/夜价,随用随调。

环境

环境Base说明
联调/演示https://sytest.meizu.life(本页同源,调试台直接可用)演示库,数据可随时清理重建
生产以平台接入单下发的域名为准同一套接口与签名,仅换 Base
同源调用:本页与 API 同域部署,前端直接请求相对路径 /api/juzhu/housing/vendor/* 即可;跨域接入方按上表 Base 拼全路径。

2 HMAC 签名认证(与家政开放接口同机制)

2.1 凭证

每个商家一个 vendor_id + 一把 hmac_key,由平台统一配置并线下下发。hmac_key 等同密码:只放服务端或自己的联调机,不要写进前端代码、不要提交仓库。

2.2 签名算法

  1. 取业务参数对象 body,加上 vendor_id;剔除 sign 字段。
  2. 扁平化:嵌套对象用 父键.子键 点号展平;null、''(空串)的键不参与签名。
  3. 值序列化:字符串原样;数字/布尔转字符串(true→'True'、false→'False');数组 → ['a', 'b'](单引号、逗号空格分隔)。
  4. 按键名字典序排序,拼成 k1=v1&k2=v2&…(& 连接,无转义)。
  5. 追加 timestamp(毫秒时间戳)参与上述串;用 hmac_key 做 HMAC-SHA256,取 hex 小写 为 sign。
  6. 请求体 = 业务参数 + vendor_id + timestamp + sign,POST JSON 发送。时间戳与服务器偏差 ±5 分钟 内有效。
⚠️ 数值字段一律传整数,禁止浮点(如 price_night: 188,不要 188.0):Python 侧 str(188.0)='188.0' 而平台序列化为 '188',两边签名串不一致会直接 401 验签失败。价格单位为元、取整数。
// Node 签名示例
const crypto = require('crypto');

function pyStr(v) {
  if (v === true)  return 'True';
  if (v === false) return 'False';
  if (Array.isArray(v)) return '[' + v.map(x => (typeof x === 'string' ? "'" + x + "'" : pyStr(x))).join(', ') + ']';
  return String(v);
}
function flatten(data, prefix) {
  const flat = {};
  for (const [k, v] of Object.entries(data || {})) {
    if (v == null || v === '') continue;          // 空值不参与签名
    const key = prefix ? prefix + '.' + k : k;
    if (v && typeof v === 'object' && !Array.isArray(v)) Object.assign(flat, flatten(v, key));
    else flat[key] = pyStr(v);
  }
  return flat;
}
function sign(secretKey, body) {
  const payload = { ...body };
  delete payload.sign;
  const timestamp = Date.now();
  const flat = flatten(payload);
  flat.timestamp = String(timestamp);
  const stringToSign = Object.keys(flat).sort().map(k => k + '=' + flat[k]).join('&');
  payload.timestamp = timestamp;
  payload.sign = crypto.createHmac('sha256', secretKey).update(stringToSign, 'utf8').digest('hex');
  return payload;                        // 直接作为 POST body
}

const body = sign(HMAC_KEY, { vendor_id: VENDOR_ID });
await fetch(BASE + '/api/juzhu/housing/vendor/projects/list', {
  method: 'POST', headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(body),
});
易错点:① 数组参与签名用单引号形式(tags=['演示', '回归']),与 JSON 序列化不同;② 嵌套对象(如 units 数组里的对象)会被点号展平进签名,直接把最终 body 交给 sign 函数即可,不要手工拼;③ 重试时须重新签名(timestamp 会过期)。

响应统一为 JSON:成功 { "code": 0, "message": "success", … };失败 HTTP 状态码 + { "code": 4xx, "message": "原因" }(见 错误码)。

3 数据字典

字段与平台房源模型一致、即接口契约本身;带 ★ 为商家写入接口接受的字段,标 只读出参 的字段由平台计算下发、传入无效。

2026-09 口径调整(价格与住宿规则下放到户型): ① price_from 改为选填,上架闸改为逐户型校验「能算出默认夜价」; ② 户型 price_night 在 rental 频道同样生效(不再只认月租 ÷ 30); ③ 最短连住下放户型(户型 > 房源 > 频道默认),与取消政策同一层; ④ 新增只读出参 default_night_price(户型)与 price_from_display / price_unit / price_note(房源卡片价,见 3.4)。 三项改动均向后兼容:按原样传 price_from + rent_monthly 的房源行为逐字不变。

3.1 房源(projects)

房源/项目名城市 id(cities.id);须在该商家开放城市内。取值用 regions/list(4.1)
字段类型说明
idint平台生成,创建后返回;后续接口用 id 定位房源
namestring★必填
channelstring★ 业务频道:rental 租赁住宿(长租+旅居,监管同口径)/ minsu 惠居民宿 / newhouse 新房 / resale 二手 / trade 卖旧买新;缺省 rental,创建后不可改。「保租房」不是频道,用 tags 表达(["保租房"],平台侧专题)
city_idint★必填
district_idint★ 行政区 id(districts.id),须属于 city_id;可不传
addressstring★ 详细地址;缺省自动拼「城市 · 名称」。暂无经纬度字段(如需 POI 打点请向平台提需求)
slugstringURL 标识(C 端深链词),同 channel 内唯一。建议直接传贵方房源编号;不传则按名称自动生成。注意:传入已存在的 slug 不报错,会静默加 -2/-3 后缀——创建前请先 projects/list 核对防重;改 name 会同步重生成 slug
cover_imagestring★ 封面图 URL;室内图集走 photos/add(4.11),上架须 ≥ 7 张照片
tagsstring[]★ 标签数组(如 ["保租房","近地铁"])
price_fromint选填(2026-09 起)。起价,单位元:rental 为「元/月」,minsu 为「元/晚」;作为默认夜价的最后兜底(rental 折算 价/30)。不传也可以——此时由每个户型自己的价(price_night 或 rent_monthly)承担,上架闸按户型逐个校验(见 4.6)。平日/周末/节假日分档不走它,走房态日历逐晚覆盖(见 5.1)
price_from_display / price_unit / price_note—只读出参(2026-09):C 端卡片展示价三件套,页面直接渲染、不要自行折算。price_unit ∈ night(按晚起)/ month(按月起)/ null(该频道不适用,走 price_total);price_from_display 为数值,为空时取 price_note(暂无可订 / 价格面议)。单位口径:minsu 与带「旅居」tag 的 rental 按晚(取最低可售单夜价,见 3.4),其余 rental 按月(起价语义不变)
sort_order / is_featured / featured_rank / old_house_hint—★ 排序权重 / 平台精选开关 / 精选排序位(越小越靠前,与 is_featured 配合)/ 卖旧买新提示。is_featured、featured_rank 一般由平台运营设置,商家传了也会被接受
unit_count / managed_unit_countint★ 户型数 / 在管套数(C 端「在管房源 N 套」的展示口径,与户型条数不是一回事)。unit_count 由平台在增删户型时自动维护,商家传了会被覆盖
contact_phonestring★ 真实联系电话,11 位手机号(1 开头;400 号码不接受)。仅入库用于 C 端拨号的虚拟号实时绑定,任何查询接口不回显;可经 projects/update 更新
owner_vendor_idint数据归属商家(签名商家自动写入,不可改)。所有接口按它做行级隔离;订单佣金快照按它取商家档费率
statusstring★ draft 草稿(默认,新建强制)/ online 上架 / offline 下架
extobject★ 房源能力与差异属性:online_booking(在线预订)+ online_payment(在线支付,两项至少开启一项,见 5.3)+ insurance(保险标识数组,见 7)+ min_stay_nights(最短连住,2026-09 起降级为「房源默认值」,户型级优先,见 5.2)
rating_status / rating—审核/评级位:draft → pending → passed / rejected。passed 是上架前置;提审走 projects/rating/submit、进度与驳回原因走 projects/rating/status(4.12),列表/详情出参也随行返回

3.2 户型(units)

户型 = 房源下的实体售卖库存单元:预订、逐晚房态、免费取消政策都挂在户型维度;整栋 / 不限房型走项目级(unit_id=0)。同规格房型多间库存用 total_qty 表达(多间口径 2026-09-10:房型 × N 间,按晚扣减、售满即关,不区分具体房间号)。projects/create 可带 units[] 一次建全,后续 units/create 追加。

所属房源 id户型名(如「两居 68㎡」「整栋 · 6 室」)。可传 slug = 贵方房型编号(项目内唯一,冲突静默加后缀,同 3.1 slug 注意事项)
字段类型说明
project_idint★必填
namestring★必填
area_sqmnumber★ 面积(㎡)
layout_labelstring★ 户型标签(2室1厅)
rent_monthlyint★ 月租(元/月),照实传贵方月租即可;月租→夜价的 /30 折算发生在平台侧(C 端按晚售卖口径)。2026-09 起为可选:与 price_night 至少给一个(短租房源可直接给夜价,见下)
price_totalint★ 总价(元),newhouse/resale 频道用
promo_priceint★ 优惠价(元)。当前为展示位:C 端据此打「首月特惠」标签,不参与订单计价(营销价真实成交需另行排期)
total_qtyint★ 总间数(1-999,缺省 1)。该房型同规格可订单元数:每晚可订量 = total_qty − 当晚已订(可用 stay-calendar/set 的 qty 按晚覆盖放出间数,见 5.1);下单可选多间(rooms),订单金额按 逐晚价 × 间数 计。调低不得低于未来晚已订间数,否则 400
price_nightint★ 夜价(元/晚),写入 units.ext.price_night。2026-09 起两个频道都生效:默认夜价取值顺序 = price_night > rent_monthly/30 > price_from/30(rental)/ price_from(minsu)。房态日历的逐晚覆盖价仍优先于以上全部(见 5.1)
min_stay_nightsint★ 最短连住(晚),写入 units.ext.min_stay_nights。2026-09 起下放到户型:生效顺序 = 户型 > 房源 > 频道默认(rental/minsu 默认均 15 晚;下限 rental 1 / minsu 15,上限 365)。传 null 清除(回落房源级)。整栋单(不指定户型)按排序最前的户型执行,与取消政策同一套回退
default_night_price—只读出参(2026-09):该户型的默认单夜价(按上面的顺序算好)。C 端详情页/下单页直接读它,不要再用 rent_monthly/30 自行折算——否则会与成交价脱节
default_closedbool★ 默认关房(2026-09,写 units.ext.default_closed):开启后只有你们推送过放出量的晚才可订,没推过的晚一律不可订——多渠道商家防超售(见 3.6)。缺省关闭;null 清除
room_profileobject★ 房间档案(2026-09 开放,写 units.ext.room_profile):房源简介 / 面积口径 / 窗户 / 入住人数 / 床位 / 厨房 / 吸烟 等 21 个字段,C 端「房间档案」卡直接渲染(见 3.7)。传 null 清除
min_stay_nights_source—只读出参:最短连住这个生效值的来源,unit(户型级显式配置)/ project(房源级)/ default(频道默认)。后台表单靠它区分「显式配的」与「继承来的」,避免保存时把继承值固化
cancel_policyobject★ 免费取消政策(房型级),写入 units.ext.cancel_policy:{enabled, days_before, cutoff_time};免费取消窗口 = 入住日往前推 days_before(0-30,0=入住当天)天的 cutoff_time(HH:mm)时刻,窗口外 / 未开通一律不可取消不可退
tags / unit_spec / cover_image / sort_order—★ 标签 / 房型说明 / 封面 / 排序(sort_order 升序展示,缺省 99,用于主推房型排前)
amenitiesstring[]★ 配套标识数组,按下表 10 个 id 传(C 端按 id 渲染图标)
amenities 取值(C 端图鉴 10 项): ac 空调 · washer 洗衣机 · fridge 冰箱 · heater 热水器 · lock 智能锁 · wifi 宽带 · tv 电视 · hood 油烟机 · microwave 微波炉 · induction 电磁炉。 不传 = C 端回落展示全部 10 项;传未知 id 不渲染。枚举字典化(随接口下发)在平台排期内。

3.3 房态日历(逐晚库存 × 间数)

字段说明
project_id + unit_id库存归属;unit_id=0 或缺省表示项目级(整栋 / 不限房型,容量恒 1)。下单冲突口径:整栋单查全项目任一户型,指定户型单查「项目级 + 该户型」
stay_date晚(YYYY-MM-DD,入住当晚)。写入窗口:单次 ≤ 400 晚、可多次滚动写更远(存储无总上限);C 端日历可订窗口 = 当月起 12 个月,建议对账口径与其对齐
qty★ 放出间数覆盖(1-999;多间口径 2026-09-10)。该晚实际投放的可订间数;不传 / 传 null = 用房型 total_qty。项目级(unit_id=0)不收 qty。不得低于该晚已订间数(booked_qty),否则 400。与 available_qty 语义不同,同一次调用只能传一个
available_qty★ 净可售(2026-09 方案 B,推荐多渠道商家用):0-999,直接传「这一晚平台还能卖几间」——已经在你们那边扣掉其他渠道售出与暂不可售,可以按你们自己的口径算,不必关心平台卖了多少。平台记下推送时点的已订数作为基线,之后只有平台自己卖掉的才从这个数里扣,取消自动加回;同时以「物理余量(total_qty − 已订)」封顶,防对账漏项导致超售。详见 3.5
statusopen 可订(默认,未设置过的晚即可订,放出 = total_qty)/ blocked 关房(商家;已订晚 booked_qty>0 不可关房,须先取消订单)/booked 已订满——2026-09-10 起为派生态(剩余 remaining ≤ 0 时出参给出),由平台订单占用计数自动产生,取消自动释放,接口不可直接写
booked_qty / remaining出参随日历查询下发:该晚已订间数 / 剩余可订间数。remaining 按该晚的放出口径计算:qty(放出总量)= max(0, qty − booked_qty);available_qty(净可售)= clamp(qty − (booked_qty − 基线), 0, total_qty − booked_qty)(取消释放不会越过物理余量)。关房为 0。多间房型当晚 0 < remaining < qty 时状态仍为 open——部分可订,C 端展示「仅剩 X 间」
available_qty(出参)回显该晚的净可售口径值:null = 该晚按旧「放出总量」口径;有值 = 你们上次推的 available_qty。set 响应还会逐日返回 days[](qty / qty_base / booked_qty / remaining),推完即可对账,不必再查一次日历
price_night当晚价格覆盖(元),未覆盖回落默认夜价;户型级 > 项目级 > 默认。2026-09-10 起参与下单计价:订单金额 = 逐晚(覆盖价 || 默认夜价)合计 × 间数,与 C 端日历展示同口径——周末 / 节假日分档即按日期批量覆盖实现
status / 价格 / 间数三者相互独立,不须同时传:blocked=关房(可带价可不带;已订晚不可关);open + price_night 和/或 qty=覆盖价 / 放出间数(只改其一就只传其一,另一个保持原值);open 且价、间数均不传=恢复默认(清差异行;有占用的晚保留占用计数、仅清覆盖回默认)。一次调用一个 status 作用于整批 dates。
验收工具:GET /api/juzhu/projects/{id}/stay-calendar?month=YYYY-MM&unit_id=(公开只读,用于上下架 / 房态验收)返回逐晚三态 + 价格 + qty/booked_qty/remaining 间数 + 最短连住 + 保险标识,与 C 端日历渲染一致。

3.4 卡片展示价(最低可售单夜价)

C 端房源卡上的价格由平台计算并随接口下发(price_from_display / price_unit / price_note),贵方无需传、也不要在自己页面另算一套。单位口径:minsu 与带「旅居」tag 的 rental 按晚;其余 rental 按月(起价语义,price_from 缺省时回落户型默认夜价 × 30)。

按晚房源的取值规则:从今天起按自然月向后扫描(最多 12 个月,与 C 端可订窗口同口径),取第一个存在可售间夜的月份,该月内最低单夜价即展示价;扫满仍无可售 → 不展示价格,文案「暂无可订」。

某一晚的情况能不能卖单价取谁
没有房态记录(默认态,绝大多数晚属于这种)✅ 能默认夜价(price_night > 月租/30 > 起价/30)
有记录,blocked(关房)❌ 不能—
有记录,open + 有覆盖价✅ 能该覆盖价
有记录,open + 无覆盖价✅ 能默认夜价
该晚已订满(remaining ≤ 0)❌ 不能—
常见误解:「查房态 status=open 的记录」是不够的——平台库存模型是没有记录 = 默认可订,差异行只在关房 / 改价 / 改放出间数 / 被订时才产生。若只扫有记录的晚,会把绝大多数可订房源误判成「暂无可订」。

3.5 净可售库存(available_qty,多渠道商家)

如果同一批房间你们同时在别的渠道卖(OTA、直客、自己的小程序…),或者有房间临时不可售(未保洁、维修),用 available_qty 比 qty 更省事:直接把「这一晚平台还能卖几间」推上来,按你们自己的口径算就行。

字段语义平台怎么算 remaining
qty(旧)该晚对平台开放的总量,含平台已卖掉的max(0, qty − 已订)——所以你们必须先查一次平台已订多少,有竞态
available_qty(推荐)该晚净可售,按你们的口径(已扣他渠道与不可售)记下推送时点的已订数作基线,clamp(可用 − (已订 − 基线), 0, 物理余量)——平台之后卖掉的才扣,取消自动加回,无需你们再算

举个你们反馈里的例子:房型 total_qty=6,平台已售 2、其他渠道售 1、还有 1 间未保洁 → 你们算出「能卖 2 间」,直接推 available_qty: 2:

  1. 推送时平台已订 2 → 基线记 2 → 平台显示剩余 2(旧口径会二次扣成 0);
  2. 平台又卖掉 1 间 → 剩余 1;客户取消 → 自动回到 2;
  3. 其他渠道又卖掉 1 间 → 你们重推 available_qty: 1,基线刷新为当时的已订数 → 剩余 1。
防超售兜底:如果推的净数超过实际剩下的房间数(比如对账漏了他渠道的销量),平台按物理余量 total_qty − 已订 封顶,不会卖超。所以推大不会超售,推小才会少卖——对账宁可略保守。
两个口径别混:qty 与 available_qty 同一次调用只能传一个(同时传 400)。任一时刻该晚只属于一种口径;用 qty 写回会把基线清空,回到旧语义。

3.6 默认关房(default_closed,可选开关)

净可售解决的是「推过的晚怎么算」,默认关房解决的是「没推过的晚怎么办」。默认情况下平台是「没推过的晚 = 可订、放出量 = total_qty」——对多渠道商家这很危险:你们在别处卖掉、或临时不可售的晚,只要没推过,平台照样会放出去卖。

把房型的 default_closed 设为 true(写 units.ext,可经 units/update 或后台配置)后:

  • 只有你们显式推送过放出量的晚才可订(qty 或 available_qty 都算);
  • 没推过的晚一律不可订(remaining = 0,下单 400);只设过夜价、没设放出量的晚也算没推过;
  • 缺省关闭——不配置的房源行为与现在完全一致;只作用于指定户型的预订,整栋单(不指定户型)语义不变。

建议需要多渠道对账的房型开启,并按「未来 12 个月」滚动推送放出量(见 5.1)。

3.7 房间档案(room_profile)

房源细节统一放在户型级 units.ext.room_profile(来自入驻时的「房源字段」表),经 units/create / units/update 的 room_profile 对象整体写入,C 端详情页「房间档案」卡直接渲染。规则:

  • 整体合并:传入的键覆盖同名键,未传的键保持原值;传 null 清除整块。
  • 白名单:不在下表内的键直接丢弃(不报错)。空值(null / 空串)视为未传。
  • 越界报错:枚举取值非法、数值越界、文本超长、图片地址非法 → 400,message 里带字段中文名与可选值。
  • C 端渲染时对文本做转义,正文里可以正常使用中文标点与换行(多行文本用 \n)。
字段类型说明
introductionstring ≤1000房源简介:空间、布局、风格、适用人群、与本房型差异点
area_typeenum面积口径:building 建筑面积 / usable 使用面积
shared_spacesstring ≤300共享空间(如共享厨房、花园;无则填「无」)
window_typeenum窗户类型:exterior 外窗 / interior 内窗 / none 无窗
window_countint 0-20窗户数量
window_openablebool窗户是否可开启
max_guests / max_adults / max_childrenint最大入住人数 1-99 / 成人上限 1-99 / 儿童上限 0-99
child_age_policystring ≤300儿童适住年龄(如「0-12 岁;婴幼儿须由监护人陪同」)
extra_guest_policystring ≤500加人 / 加床政策:是否允许、最多人数、费用、是否含床品早餐
bedsstring ≤500卧室与床位明细(如「卧室1:1 张 1.8×2.0m 大床;客厅:1 张沙发床」)
bath_hot_waterstring ≤300洗浴与热水(如「淋浴 · 24 小时热水」)
smokingenum吸烟属性:no 全屋禁烟 / designated 仅指定区域 / allowed 可吸烟
kitchenstring ≤500厨房可用性:独立/共享/简易/不可用;厨具餐具、调料与做饭规则
climatestring ≤300空调 / 暖气覆盖(如「全屋空调 · 地暖」)
networkstring ≤200网络(如「免费 Wi-Fi」)
cleaning_frequencystring ≤200打扫频率(如「一客一扫」)
linen_frequencystring ≤200床品 / 毛巾更换频率(如「一客一换」)
feature_imagestring ≤1000房间示意图地址:http(s) URL 或站内路径(/、assets/、uploads/);禁换行与引号
feature_image_captionstring ≤120房间示意图说明
与图集的分工:room_profile 只放一条房间示意(feature_image)+ 一堆文字描述;多张实拍图走图集(4.11 photos/sync,带分类与排序)。两者互不影响。

4 房源接口

以下示例省略签名三件套(vendor_id / timestamp / sign),实际每个请求体都必须携带(见 §2,或直接用 §7 调试台)。

4.1 城市 / 行政区枚举

POST/api/juzhu/housing/vendor/regions/listcity_id / district_id 选值主数据(限商家开放城市;未配置城市约束则出全量)
{}
→ 200 {
  "code": 0, "list": [
    { "id": 4, "name": "贵阳", "slug": "guiyang",
      "districts": [ { "id": 41, "name": "观山湖区", "slug": "guanshanhu" } ] }
  ], "total": 1
}

4.2 房源列表

POST/api/juzhu/housing/vendor/projects/list本商家房源(最多 200 条)
{
  "channel": "rental",      // 可选过滤:channel / status / city_id / keyword
  "status": "online"
}

→ 200
{
  "code": 0, "message": "success",
  "list": [
    {
      "id": 1611, "name": "示例·观山遇见旅居", "channel": "rental",
      "status": "online", "price_from": 2400,
      "min_stay_nights": 15,
      "insurance": ["switch_rental", "property"],
      "insurance_types": [ { "key": "switch_rental", "label": "换租保险", "icon": "🔄" }, /*…*/ ]
    }
  ],
  "total": 1
}

4.3 房源详情

POST/api/juzhu/housing/vendor/projects/detail含户型明细
{ "id": 1611 }
→ 200 { "code": 0, "project": { /* 同 4.1,另含 address/tags/ext 等 */ }, "units": [ /* 户型数组 */ ] }

4.4 创建房源(默认草稿,不入 C 端)

POST/api/juzhu/housing/vendor/projects/create可带 units 一次建全
{
  "name": "观山湖·遇见旅居",
  "channel": "rental",
  "city_id": 4,
  "district_id": 41,
  "address": "观山湖区 · 示例大道 88 号",
  "tags": ["旅居", "近地铁"],
  "price_from": 2400,             // 选填(2026-09);不传则各户型自带价
  "contact_phone": "13800001234",       // 仅入库(虚拟号绑定),永不回显
  "min_stay_nights": 15,               // 房源默认值;户型级优先(见 3.2 / §5.2)
  "insurance": ["switch_rental", "property"],   // 保险标识(见 §6)
  "units": [
    { "name": "一居 45㎡", "layout_label": "1室1厅", "area_sqm": 45, "rent_monthly": 2400 },
    // 短租房源可只给夜价(rental 同样生效,2026-09):
    { "name": "庭院房", "area_sqm": 30, "price_night": 268, "min_stay_nights": 3 }
  ],
  "status": "draft"                      // 缺省 draft;draft/offline 不出 C 端
}

→ 200 { "code": 0, "message": "success", "project": { "id": 1611, "status": "draft", /*…*/ } }

4.5 更新房源

POST/api/juzhu/housing/vendor/projects/update按 id 增量更新,仅传变更字段
{
  "id": 1611,
  "price_from": 2680,
  "tags": ["旅居", "近地铁", "含早"],
  "insurance": ["hotel_cancel", "property"],
  "min_stay_nights": 20,
  "online_booking": true,
  "online_payment": true
}
→ 200 { "code": 0, "project": { /* 更新后全量 */ } }

不可改:city_id、channel、owner_vendor_id;改 name 会同步重生成唯一 slug。

4.6 上下架

POST/api/juzhu/housing/vendor/projects/status上架 / 下架 / 转草稿
{ "id": 1611, "status": "online" }     // online 上架 / offline 下架 / draft 转草稿
→ 200 { "code": 0, "message": "success", "id": 1611, "status": "online" }
上架前置检查:① 至少有 1 个户型;② 每个户型都能算出一个大于 0 的默认夜价(price_night 或 rent_monthly,也可由房源 price_from 兜底);③ 图集 ≥ 7 张 + 封面;④ 评级 passed。任一不满足返回 400 并指明是哪个户型缺价。C 端目录有 ≤ 15 秒缓存,上架后最迟 15 秒可见、下架后最迟 15 秒消失。

4.7 新增户型

POST/api/juzhu/housing/vendor/units/create向已有房源追加户型
{ "project_id": 1611, "name": "两居 68㎡", "layout_label": "2室1厅",
  "area_sqm": 68, "rent_monthly": 3200, "total_qty": 3,
  "room_profile": { "max_guests": 4, "beds": "卧室1:1.8×2.0m 大床" } }   // 房间档案(见 3.7)
→ 200 { "code": 0, "unit": { "id": 3101, /*…含 room_profile 已解析对象…*/ } }

4.8 更新户型(调价 / 间数 / 取消政策等)

POST/api/juzhu/housing/vendor/units/update按户型 id 增量更新
{ "id": 3101, "rent_monthly": 3380, "price_night": 128, "total_qty": 5,
  "min_stay_nights": 3,                     // 户型级最短连住(2026-09);null 清除回落房源级
  "default_closed": false,                   // 默认关房(见 3.6);null 清除
  "room_profile": { "window_type": "exterior", "smoking": "no" },   // 房间档案(见 3.7),只合并传入的键
  "cancel_policy": { "enabled": true, "days_before": 1, "cutoff_time": "18:00" } }
→ 200 { "code": 0, "unit": { /*…含 default_night_price / min_stay_nights / room_profile 生效值…*/ } }
  • 最短连住:传入 min_stay_nights(rental 1-365 / minsu 15-365,越界 400);传 null 或空串清除,回落房源级 → 频道默认。整栋单(不指定户型)按排序最前的户型执行。出参附 min_stay_nights_source(unit/project/default)。
  • 房间档案:传入 room_profile(见 3.7),只合并传入的键、未传的保持原值;传 null 清除整块;未知键静默丢弃,取值/长度非法 400。
  • 取消政策:传入 cancel_policy(非法 days_before/cutoff_time 返回 400);传 null 清除(= 未开通,客户不可取消)。免费取消窗口 = 入住日往前推 days_before 天的 cutoff_time 时刻;客户在窗口内取消自动退款并释放房态。
  • 总间数(多间口径 2026-09-10):total_qty 须为 1-999;调低到低于未来晚已订间数会返回 400(先处理订单再缩容)。

4.9 删除户型

POST/api/juzhu/housing/vendor/units/delete有关联订单或被占用晚时拒绝
{ "id": 3101 }
→ 200 { "code": 0, "message": "success" }

4.10 房态查询(商家视角)

POST/api/juzhu/housing/vendor/stay-calendar/query逐晚三态 + 间数(qty/booked_qty/remaining) + 占用来源 + 最短连住/保险
{ "project_id": 1611, "unit_id": 0, "month": "2026-12" }
→ 200 {
  "code": 0, "month": "2026-12", "base_price_night": 80,
  "min_stay_nights": 15, "insurance": ["switch_rental", "property"],
  "days": [
    // 多间口径:房型 total_qty=3,19 晚被订 2 间(部分可订),20 晚已关房
    { "date": "2026-12-19", "status": "open", "price": 80, "qty": 3, "booked_qty": 2, "remaining": 1 },
    { "date": "2026-12-20", "status": "blocked", "price": 80, "qty": 3, "booked_qty": 0, "remaining": 0 },
    { "date": "2026-12-21", "status": "open", "price": 80, "qty": 3, "booked_qty": 0, "remaining": 3 }
  ]
}

4.11 房源图片(图集)

4.11.1 全量覆盖图集(推荐,2026-09)

POST/api/juzhu/housing/vendor/photos/sync传入该实体当前完整的图集,平台对比存量:保留列表内的、移除不在列表内的,整体一个事务
{
  "project_id": 1611,
  "unit_id": 3101,                     // 可选;缺省 = 房源级图集(只动房源级,不会碰户型级)
  "photos": [
    { "url": "https://cdn.example.com/p1.jpg", "category": "bedroom", "sort": 0, "is_cover": true,
      "external_id": "IMG-1001" },      // 你们那边的稳定图 id(URL 带签名会变,靠它认同一张图)
    { "url": "https://cdn.example.com/p2.jpg", "category": "living", "sort": 1,
      "width": 1920, "height": 1080, "size_bytes": 480000 }   // 声明值可选,见下「校验」
  ]
}
→ 200 {
  "code": 0, "applied": 8, "added": 5, "updated": 3, "removed": 2,
  "cover": "https://cdn.example.com/p1.jpg", "truncated": false,
  "gallery_total": 12, "warnings": [],
  "photos": [ { /* 覆盖后的完整图集(含 category_label),可直接用来核对 */ } ]
}
字段说明
url★ 必填,http/https 绝对地址,≤500 字符;同一实体内不得重复
category★ 图片分类,可选(缺省 other):bedroom 卧室 / kitchen 厨房 / bathroom 卫生间 / living 客厅 / nearby 周边 / other 其他。未知值 400
sort★ 展示顺序,从小到大;不传按数组顺序。平台归一化为 0..n-1 落库,C 端据此排序
is_cover★ 封面;多张标记时取 sort 最靠前的那张。列表里一张都没标 → 取排序第一张。覆盖后自动同步 cover_image
external_id★ 你们侧的稳定图 id(≤120 字符)。匹配键:external_id 优先、URL 兜底——匹配上的原地更新(图片 id 不变、C 端引用不抖),否则插入/删除。URL 带签名会变,建议始终带上
width / height / size_bytes★ 可选声明值:推送时即时校验(分辨率 ≥800×600、单图 ≤10MB,越界 400,无网络开销);真伪由上架抽检核对
规则与语义: ① 按实体覆盖——房源级只动房源级、户型级只动该户型,不会误删另一层(上架闸按两层合计算); ② 整体事务——任一校验不过整批不动,不留半拉图集; ③ 上限 100 张——超出按 sort/数组顺序截取前 100 张,响应回 truncated: true + warnings(不静默丢图); ④ 覆盖后若该房源图集合计不足 7 张,响应会带一条 warnings 提示(不阻断——「≥8 张」是上架闸,不是每次覆盖的闸)。
上架抽检(2026-09):projects/status online 时会真拉取封面 + 排序靠前的图(默认 2 张)核对大小与分辨率。 确定性不合格(超 10MB / 小于 800×600 / 非图 / 地址非法)→ 400 拒绝上架; 仅网络不可达(超时、DNS、非 200)→ 放行并在响应里回 warnings(CDN 抖动不卡上架)。 抽检会读到你们的图片,平台只读必要字节、不落盘、不转存。

4.11.2 单张追加 / 更新

POST/api/juzhu/housing/vendor/photos/add追加室内图 / 户型图;同 URL 或同 external_id 重推 = 原地更新(2026-09 起不再堆重复行)
{
  "project_id": 1611,
  "unit_id": 3101,                      // 可选;缺省 = 房源级图集
  "file_path": "https://cdn.example.com/p1.jpg",  // 必填,URL ≤ 500 字符
  "is_cover": true,                       // 设为封面(同实体原封面自动让位)
  "category": "bedroom",                 // 图片分类(可选,缺省 other)
  "external_id": "IMG-1001",             // 稳定图 id(可选):幂等匹配键
  "sort_order": 0                         // 不传 = 追加到末尾(2026-09 起传入即生效)
}
→ 200 { "code": 0, "updated": true, "photo": { /* 命中已有图时原地更新(updated=true) */ } }

图集元素字段:id / entity_type / entity_id / file_path / category / category_label / external_id / is_cover / sort_order。category_label 为中文名,C 端直接渲染。

户型出参字段(units/create / units/update / projects/detail 的 units[] 同口径): id / name / slug / area_sqm / layout_label / rent_monthly / price_total / promo_price / total_qty / sort_order / cover_image / tags / amenities / unit_spec / ext, 外加平台算好的生效值:default_night_price(默认夜价)、min_stay_nights + min_stay_nights_source(连住下限与来源)、cancel_policy / cancel_policy_text(取消政策及中文文案)、room_profile(已解析的对象,不用自己 parse ext 字符串)。
上架素材硬性前置:房源(含其户型)图集合计 ≥ 7 张且至少 1 张封面(photos/sync 单次覆盖不卡这个下限——它是上架闸,便于先建后补),否则 projects/status online 返回 400「上架前须至少上传 8 张房源照片」。

4.12 评级提审 / 审核状态

上架闸之一是平台评级审核(rating_status:draft → pending → passed / rejected;rental=好房子 4 维,minsu=旅居彩贝 5 维)。提审与查进度直接走开放接口,不必线下催单:

POST/api/juzhu/housing/vendor/projects/rating/submit提交自评分,进入平台复核队列(pending 中不可重复提)
{
  "id": 1611,
  "dims": { "comfort": 4.5, "green": 4, "tech": 4.6, "safety": 4.4 }
                  // minsu 频道为 scenery / facilities / service / location / culture
}
→ 200 { "code": 0, "rating_status": "pending", "rating_code": "SY-RENT-1611", "dims": { /*…*/ } }
POST/api/juzhu/housing/vendor/projects/rating/status查审核状态 / 驳回原因 / 维度缺口
{ "id": 1611 }
→ 200 {
  "rating_status": "rejected", "rating_code": "SY-RENT-1611",
  "dims": { /* 已提交自评分 */ }, "missing_dims": [],
  "dims_meta": [ { "key": "comfort", "label": "舒适", "icon": "🛋" } /*…*/ ],
  "note": "安全维度证明材料不足",   // 驳回原因;passed/rejected 的复核时间见 reviewed_at
  "submitted_at": "2026-09-10T03:20:11Z", "reviewed_at": "2026-09-10T07:41:00Z"
}
  • dims 须覆盖该频道全部维度、数值 0–5;缺维度 / 越界 / 未知维度返回 400 并指明字段。
  • 被驳回(rejected)修订后可重新提审;提审会清除上一轮 note。平台复核通过(passed)后 projects/status online 才会放行。

5 订单履约(商家接单)

客户在 C 端提交预订单后,商家经开放接口完成接单闭环:定时拉取订单 → 确认生效或拒单 → 拒单自动释放房态。订单按签约商家隔离,联系人手机号只回掩码(139****5678)。

订单金额 = 逐晚合计(2026-09 口径):每晚按「房态日历覆盖价(户型级 > 项目级)→ 默认夜价」取值后求和,与 C 端日历展示同口径。默认夜价(两个频道一致):房型 price_night > round(月租/30) > round(起价/30)(minsu 最后一档为 price_from 原值)。整栋单(不指定户型)价格基准 = 房源起价,起价缺失时回落排序最前户型。金额再 × 间数;算不出价的订单直接拒绝(400),不会 0 元成单。佣金按订单金额快照锁定(规则 20)。
POST/api/juzhu/housing/vendor/bookings/list本商家订单(≤200 条)
{
  "status": "pending",        // 可选过滤:status / pay_status / project_id
  "project_id": 1611
}
→ 200 { "code": 0, "list": [ {
  "id": 36, "order_no": "BKG-RENTAL-00036", "project_name": "观山湖·遇见旅居",
  "checkin": "2026-12-19", "checkout": "2027-01-08", "nights": 20,
  "price_total": 1600, "commission_rate": 8.00, "commission_fee": 128.00,
  "status": "pending", "pay_status": null,
  "contact_name": "张**", "contact_phone": "139****5678"
} ] }
  • 佣金(规则 20):commission_rate / commission_fee 为下单时锁定的费率与佣金快照(元),结算按快照执行、商家后续费率调整不追溯;未差异化商家按全局基准(缺省 10%)。
POST/api/juzhu/housing/vendor/bookings/detail按 id 查单(owner 校验)
{ "id": 36 } → 200 { "code": 0, "booking": { /* 同上,单条 */ } }
POST/api/juzhu/housing/vendor/bookings/confirm确认生效
{ "id": 36 } → 200 { "code": 0, "status": "confirmed" }
POST/api/juzhu/housing/vendor/bookings/cancel拒单 / 取消(自动释放房态)
{ "id": 36 }
→ 200 { "code": 0, "status": "cancelled", "pay_status": null }

订单状态与支付口径

字段取值说明
statuspending → confirmed / cancelled确认后订单生效;取消为终态,不可再变更
pay_statusunpaid / paid / refunded 或 空由订单交易方式决定,与频道无关:在线支付单为 unpaid 起步;在线预订单为空,商家确认后线下收款
确认前置—unpaid 的订单不可确认(400),支付完成后可确认;cancelled 不可再变更
拒单/取消—自动释放占用的房态晚;已支付订单 pay_status 标记 refunded

Webhook 事件通知(平台 → 商家)

客户动作触发的订单变更,平台会主动推送签名事件到你为商家配置的通知地址(接入单里提供 URL 即可),不必轮询等单:

event触发时机商家应做什么
booking.created客户提交预订单备房;预付单等待支付到账
booking.paid预付单支付完成(民宿民宿)此时方可确认订单
booking.cancelled客户取消(商家自己拒单不会推送)释放已备房源

推送为 POST JSON,body 即开放接口同款结构:event + vendor_id + order(订单对象,手机号不外传)+ timestamp + sign。验签与开放接口同一算法(对去掉 sign/timestamp 的参数扁平排序后 HMAC-SHA256),收到后先验签再处理:

// Node 验签示例(与平台 hmac_auth.cjs 同算法)
app.post('/hooks/booking', (req, res) => {
  const body = req.body;
  const payload = { ...body };
  delete payload.sign;
  const flat = flatten(payload);              // 与请求签名同一套 flatten
  flat.timestamp = String(body.timestamp);
  const expect = crypto.createHmac('sha256', HMAC_KEY)
    .update(Object.keys(flat).sort().map(k => k + '=' + flat[k]).join('&')).digest('hex');
  if (expect !== String(body.sign).toLowerCase()) return res.status(401).end();
  // 处理 body.event / body.order …
  res.status(200).end('ok');                    // 2xx 即视为送达
});
送达语义:非 2xx 或超时(5s)视为失败,按 5s / 30s / 120s 重试 3 次后放弃——Webhook 是「尽力通知」,不担保必达;请以 bookings/list 定时对账兜底(推荐粒度:每分钟拉一次 status='pending')。订单明细里不含客户明文手机号。
完整业务流(商家视角):创建房源(草稿)→ 补价与户型 → 上架 → 房态/夜价日常维护 → 收 Webhook(或拉单对账)→ 确认或拒单 → 入住 / 离店;任一环节失败都会返回明确的 400/404 与原因(见 错误码)。

6 房态日历与短住规则

5.1 批量设置房态 / 夜价 / 放出间数

POST/api/juzhu/housing/vendor/stay-calendar/set关房 / 开房 / 夜价覆盖 / 放出间数覆盖(多间口径 2026-09-10),单次 ≤ 400 晚
{
  "project_id": 1611,
  "unit_id": 0,                        // 0 或缺省 = 项目级(整栋 / 不限房型,容量恒 1)
  "dates": ["2026-12-10", "2026-12-11"],
  "status": "blocked",                 // 可选:blocked 关房 / open 开房;缺省 = 保持各晚现状(只改价/净可售)
  "price_night": null,                 // blocked 可同时设价;open + 价格 = 夜价覆盖;open + null = 不改价
  "qty": null,                         // ★ 放出总量覆盖(1-999,仅房型级):open + qty = 该晚只放 N 间;不传 = 用 total_qty
  "available_qty": null                  // ★ 净可售(0-999,推荐,见 3.5):与该晚的 qty 二选一;写回 qty 会把基线清空
}
→ 200 {
  "code": 0, "affected": 2, "dates": 2,
  "days": [                          // 推完即可对账(不必再查一次日历)
    { "date": "2026-12-10", "status": "open", "qty": 2, "qty_base": 2,
      "available_qty": 2, "booked_qty": 2, "remaining": 2 }
  ]
}
  • status 可选(2026-09-22):只推价格 / 净可售时可以不传 status——对应间夜保持原状态(blocked 晚保持关房、open 晚保持可订),无差异行的晚按默认 open 建行。status: open 且价、放出量均不传 = 显式「恢复默认」。
  • dates 支持对象数组(2026-09-22):逐晚独立值,一次请求混合推送不同房态 / 价格——
    [{ "stay_date": "2026-12-10", "available_qty": 0, "status": "open" },
     { "stay_date": "2026-12-11", "price_night": 188 }]
    字段缺省含义与顶层一致(status 缺省 = 保持现状)。平铺字符串数组 + 顶层字段形态继续可用(所有晚同值)。
  • 统一用 available_qty 管理房态(推荐):available_qty: 0 + status: open 即「该晚 0 间可售」——剩余派生为 0、C 端显示售罄,效果等同关房,且随时可推一个正数恢复;不必在 open / blocked 之间切换语义。
  • 已订晚(booked_qty>0)不可关房:返回 400 并列出日期,须先取消订单;改价 / 调放出量不受限。
  • 未设置过的晚天然可订、放出 = 房型 total_qty(除非该房型开了「默认关房」,见 3.6);「恢复默认」= status=open 且价、放出量均不传:清除差异行,有占用的晚保留占用计数、仅清覆盖回默认。
  • qty 与已订(多间口径 2026-09-10):qty 低于该晚已订间数返回 400(先取消订单或调高);qty 改变只影响当晚可订余量,已订单不受影响。剩余 = max(0, qty||total_qty − booked_qty)。
  • available_qty(2026-09 方案 B):不受「不得低于已订」限制(它本身就是净数,推送时点的已订自动进基线);qty 与 available_qty 同时传返回 400。详见 3.5。
  • 覆盖价参与下单计价(2026-09-10 起):订单金额 = 逐晚「覆盖价 || 默认夜价」合计 × 间数。周末 / 节假日分档 = 把对应日期批量 open + price_night;平日不传即回落默认。订单占用不清除当晚覆盖价,取消释放后商家差异行(价 / qty / 关房)原样保留。
  • 可订窗口:单次 ≤ 400 晚、可滚动写更远;C 端日历展示当月起 12 个月,建议按「未来 12 个月」滚动维护。

5.2 最短连住(min_stay_nights)

旅居/短住按连续入住售卖:平台缺省口径为 rental=15 晚、minsu=15 晚。商家可覆盖,2026-09 起生效顺序为「户型 > 房源 > 频道默认」:户型级写 units.ext.min_stay_nights(units/create / units/update 直接传 min_stay_nights),房源级 projects.ext.min_stay_nights 降级为房源默认值。取值范围:rental 1–365 晚,minsu 15–365 晚、不得低于 15 晚(越界返回 400)。

整栋单(下单不指定户型)按排序最前的户型取值——与取消政策同一套回退,因此「最短连住」和「取消政策」两条住宿规则都可以只在户型上配,不必记住哪个在房源、哪个在户型。

三处同口径校验:C 端日历选段、下单页、平台下单接口 POST /api/juzhu/booking(不足返回 400「该房源须连住至少 N 晚」)。下单成功即占用这些晚,取消自动释放。户型数组与房态日历出参均随行返回生效值 min_stay_nights,页面直接读,不要回落到房源级或自设默认值。

5.3 房源交易能力(online_booking / online_payment)

projects/create / projects/update 按房源配置两个独立布尔能力:online_booking = 客户在线选房并提交订单、商家确认后线下收款;online_payment = 客户在线选房后进入收银台、支付完成再由商家确认。两项可同时开启,但至少开启一项,与 rental/minsu 频道无关;新建时两项均省略则默认仅开启 online_booking。两项都开时,C 端由客户选择;接口下单用 transaction_mode: "booking" | "payment" 指定,缺省优先 booking。catalog / 详情 / 房态日历出参均返回两个能力位,并保留 bookable 作为“至少一项可用”的兼容汇总字段。

7 保险标识

商家为房源配置的「入住保障」,随 catalog / 详情 / 房态日历下发 C 端展示。枚举由平台统一维护,接口只接受以下 key,未知 key 返回 400:

key名称保障口径
switch_rental换租保险租期到期换租衔接期内的房租损失与搬迁费用
hotel_cancel酒店取消险行程变更按规则退改,未入住部分房费可退
property财产保险入住期间屋内财产与房屋主体意外损坏

配置入口:projects/create / projects/update 传 insurance 数组;清空传 []。出参同时给出 insurance(key 数组)与 insurance_types(含 label/icon,可直接渲染)。

Webhook 通知与审核自动上架(2026-09-22)

审核通过自动上架(auto_publish)

默认行为:评级复核通过后,商家仍需自行调 projects/status 推 online。若希望复核通过即自动上架,在 projects/create 或 projects/update 传 auto_publish: true(存 ext.auto_publish,随时可传 false 关闭)。自动上架复用与商家推送完全相同的上架闸(评级通过 / 逐户型默认夜价 > 0 / 图片抽检)与交易方式校验:闸全过 → 直接 online;任一不过 → 保持 draft,具体原因通过 rating.reviewed webhook 的 auto_publish.reason 带回,商家补齐后自行推送上架即可。

评级审核结果 Webhook(rating.reviewed)

评级复核(通过 / 驳回)完成时,平台向商家 webhook_url POST 一条签名通知,商家无需再轮询 rating/status。webhook_url 由商家自助配置:B 端后台「房源管理」(b-listing-mgmt.html)顶部「🔔 Webhook 通知」卡填写并保存,支持一键发送 webhook.test 连通性测试(同步回传送达结果);推送与测试统一经平台出网防护层(内网 / 环回 / 云元数据地址拒绝)。

字段说明
eventrating.reviewed
vendor_id商家 id
data.project_id / data.code / data.channel房源 id / 评级编号(SY-RENT-… / MZ-…)/ 频道
data.rating_statuspassed / rejected
data.rating_note / data.reviewed_at复核备注 / 复核时间(ISO)
data.auto_publish{enabled, attempted, result: published|blocked|skipped, reason?, warnings?};未开启 auto_publish 时 result=skipped
timestamp / sign毫秒时间戳与 HMAC-SHA256 签名,对 {event, vendor_id, data} 按第 2 章同算法计算,商家按同规则验签

推送 5 秒超时,失败重试 3 次(5s/30s/120s)后放弃——webhook 只通知不担保必达,商家以 projects/rating/status 拉取对账兜底。响应返回任意 2xx 即视为送达。

附 · 常见接入疑问(答疑纪要 2026-09-10)

以下为首批接入方答疑结论,与线上实现同步校准;与本页正文冲突处以正文为准。

问题结论
channel 怎么传?保租房是不是 channel?字符串枚举 rental / minsu / newhouse / resale / trade,缺省 rental,创建后不可改。长租 + 旅居统一 rental;「保租房」是平台专题(topic)不是频道,用 tags 表达(["保租房"])。支持在线预订的只有 rental / minsu。
city_id / district_id 有枚举接口吗?有:regions/list(4.1),只出该商家开放城市及其行政区;未配置城市约束则出全量。
有经纬度字段吗?暂无,只有 address 文本。房源级 POI 打点属新需求,请书面提给平台评估。
slug 可以传我方房源 / 房型编号吗?可以(同频道内唯一,未做字符集限制)。传入已存在的 slug 不报错、会静默加 -2 后缀,创建前先 projects/list 核对防重;接口暂无按 slug 幂等 upsert,已列入平台排期。
只有封面图吗?不止:图集走 photos/add(4.11),上架硬性要求 ≥ 7 张 + 封面。
price_from 是基础价吗?平日 / 周末 / 节假日价怎么表达?price_from 是选填起价(rental=元/月、minsu=元/晚)+ 默认夜价的最后兜底,不分档。分档 = 房态日历逐晚 price_night 覆盖(5.1),订单金额按逐晚覆盖合计。不传起价时由各户型的价承担(见下条)。
我不按月租卖,只按每晚定价,可以吗?可以(2026-09 起)。给户型的 price_night 即可,rental 频道同样生效;rent_monthly 与 price_from 都可以不传。默认夜价顺序 = price_night > 月租/30 > 起价/30。
不传 price_from,C 端卡片价格显示什么?按展示价三件套(3.4)自动算:带「旅居」tag 的 rental 与 minsu 按晚,取最低可售单夜价(从今天起按自然月向后扫 12 个月,取第一个有可售间夜的月份内的最低价);其余 rental 按月,起价缺失时用户型默认夜价 × 30。全窗口无可售时卡片显示「暂无可订」。贵方无需做任何事。
月租 /30 折算发生在哪侧?平台侧(C 端按晚售卖口径):默认夜价 = round(月租/30)。2026-09 起户型 price_night 优先于它(两个频道一致);详情页/下单页直接读接口下发的 default_night_price,不要自己折。
我们同时在别的渠道卖,怎么让平台显示正确的可售数?用 available_qty(净可售,2026-09)直接推「这一晚平台还能卖几间」,按你们自己的口径算(已扣他渠道 / 不可售);平台记基线,之后只扣平台自己卖掉的,取消自动加回,并按物理余量封顶防超售。详见 3.5。
没推过的日期,怎么让它默认不可订?把房型 default_closed 设为 true(2026-09,见 3.6)。之后只有你们推过放出量的晚才可订,避免别处卖掉/临时不可售的晚被平台放出去。缺省关闭,不影响存量房源。
换图 / 撤图怎么做?现在只能一张张加。用 photos/sync(2026-09,见 4.11.1):把该实体当前完整的图集传上来,平台自动移除不在列表里的旧图、保留并更新列表内的。单张的 photos/add 也保留,且同 URL / 同 external_id 重推 = 原地更新,不再堆重复图。
图片 URL 带签名、每次都不一样,怎么让平台认出是同一张图?传 external_id(你们侧的稳定图 id)。匹配键是 external_id 优先、URL 兜底——带上它,换签名 URL 也只是原地更新那张图,图片 id 不变、C 端引用不抖。
图集要几张?大小和分辨率有要求吗?上架需合计 ≥ 7 张(房源级 + 户型级)且至少 1 张封面;单次 photos/sync 上限 100 张(超出按 sort 截取前 100 并回 truncated + 告警)。单图 ≤10MB、分辨率 ≥800×600:推送时可带 width/height/size_bytes 声明值即时校验;平台在上架时会抽检(默认封面 + 排序靠前的 2 张)真拉取核对。
房间档案(简介 / 窗户 / 床位 / 入住人数…)怎么传?走户型级的 room_profile 对象(2026-09 开放,见 3.7):units/create / units/update 传 room_profile: {...} 即可,只合并传入的键、未传的保持原值,传 null 清除整块。字段清单与取值范围见 3.7;越界会 400 并报出字段中文名与可选值。
抽检拉图会不会拖慢上架?拉不到图怎么办?只拉 2 张、只读必要字节、不落盘。仅网络不可达(超时 / DNS / 非 200)时放行并在响应里回 warnings——你们的 CDN 抖动不会卡住上架;只有确定性不合格(超 10MB / 小于 800×600 / 非图)才拒绝。
remaining 是怎么算的?我能自己传吗?一律只读,平台算好下发,接口不接受传入。算法随该晚的放出口径:qty(放出总量)= max(0, qty − 已订);available_qty(净可售)= clamp(可用 − (已订 − 基线), 0, 物理余量)。关房 / 未放出(默认关房)= 0。
promo_price 会影响成交价吗?不会,当前是「首月特惠」展示标签位;营销价真实成交需另行排期。
amenities 没有枚举怎么映射?按下表 10 个 id 传(3.2):ac / washer / fridge / heater / lock / wifi / tv / hood / microwave / induction。不传 = C 端回落展示全部;未知 id 不渲染。枚举字典化在平台排期。
联系电话必须是 400 吗?后续能改吗?相反:须为 11 位手机号(1 开头),400 号码会被拒。号码仅入库用于 C 端虚拟号实时绑定、任何接口不回显;可用 projects/update 更新。
owner_vendor_id 是什么?签名商家自动写入的数据归属标记,不可传不可改;一切接口按它行级隔离,订单佣金按它取商家档费率。
首推后是什么状态?有审核吗?不通过呢?新建一律草稿(传 online 创建直接 400)。上架前须过平台评级审核:rating/submit 提审 → pending → 平台复核 passed / rejected(附驳回原因 note,修订后可重新提审)。进度用 rating/status 自查(4.12)。
上架后更新房源信息要重新提审吗?外网会下架吗?不须提审、不下架:内容更新免审即时生效(目录 ≤ 15 秒缓存),外网持续可售。当前契约即「免审直更」,如业务要求重大变更走审核请向平台提出。
房态 / 房价日历最长设多久?C 端展示多久?单次 ≤ 400 晚、可滚动写更远;C 端可订窗口 = 当月起 12 个月,建议按 12 个月滚动维护。
某间夜的房态和价格必须同时传吗?不必,二者独立:blocked 关房;open + price 覆盖价;open + price:null 恢复默认价。一次调用一个 status 作用于整批 dates。

8 在线调试台

在本页直接签名并调用真实接口:选择接口 → 填业务参数 JSON(无需自己写 vendor_id/timestamp/sign)→ 发送。签名在你的浏览器本地完成,hmac_key 只留在本机(可选「记住」,存 localStorage,换机不带)。

密钥安全:调试台等同用你的商家身份操作房源。演示环境请使用测试商家密钥;生产密钥不要在公共电脑上使用或勾选「记住」。
stringToSign 预览会显示在这里(联调对签用)

常用模板:上架 {"id":123,"status":"online"} · 关房 {"project_id":123,"dates":["2026-12-10"],"status":"blocked"} · 建房源见 §4.3 示例 · 接单 {"id":36}(confirm/cancel 同参,id 取 bookings/list 返回值)。

等待发送…

9 错误码

HTTPcode含义处置
401—缺失签名(sign)或时间戳(timestamp)参数检查签名三件套是否都在 body 里
401—请求已过期(±5 分钟窗口)校准服务器时间后重签
401—签名校验失败 / vendor_id 密钥未配置核对 hmac_key 与签名算法(数组、空值、嵌套展平)
400—name / city_id / project_id 等必填缺失按 message 补齐
400—channel 非法 / city_id 不属于商家开放城市 / district_id 不属于城市先用 projects/list 或平台侧确认主数据
400—上架被拒:有户型算不出默认夜价(缺 price_night/rent_monthly 且无 price_from,message 会点名户型)/ 无户型 / 照片不足 7 张 / 无封面 / 评级未通过 / 商家未过审按 message 补齐;评级走 rating/submit(4.12),图片走 photos/add(4.11)
400—下单被拒:该房源未配置价格,暂不可预订(2026-09 新增:算不出价不再 0 元成单)给户型补 price_night 或 rent_monthly(或房源起价)
400—评级:dims 缺维度 / 越界(0-5)/ 未知维度;或已在复核队列中重复提审按 4.12 维度表修正;进度用 rating/status 查
400—insurance 含未知标识 / min_stay_nights 超出频道范围(rental 1–365 / minsu 15–365)按 §6 / §5 枚举修正
400—qty 与 available_qty 同时传(语义不同,只能传一个)/ available_qty 超出 0-999 / 项目级传放出量见 3.5;项目级(unit_id=0)容量恒 1,不接受放出量
400—default_closed 非布尔传 true / false,null 清除(3.6)
400—room_profile 取值非法:未知枚举值 / 数值越界 / 文本超长 / feature_image 非法地址按 message 里的字段中文名与可选值修正(见 3.7)
400—photos/sync 入参问题:photos 空数组 / 未知 category / 同实体内 url 或 external_id 重复 / 非 http(s) 地址 / 声明分辨率低于 800×600 或超过 10MB见 4.11.1;错误信息会指明第几项(photos[i])
400—上架被拒:图集抽检确定性不合格(超 10MB / 小于 800×600 / 非图)按 message 里的图片地址整改后重推;仅网络不可达不会拒绝(走 warnings)
400—房态:以下日期已有预订占用先取消订单再改房态
400—无可更新字段 / city_id 不支持修改按 message 调整
400—订单未支付不可确认 / 订单已取消不可变更支付走收银台后重试确认;取消为终态
404404订单不存在或不属于该商家(房源 / 户型同理)统一 404,不区分「不存在」与「越权」,防探测
404404未知 vendor 路由核对路径(本页调试台清单)
405—非 POST 请求全部接口仅支持 POST

10 联调与回归

平台提供端到端回归脚本(Node,走真实 HTTP 签名链路),接入方可用它自检签名实现是否正确:

# 覆盖:regions 枚举→创建→评级提审/状态→上架→C 端可见→保险/连住更新→房态/夜价→
#       下单逐晚计价→订单闭环(webhook)→下架→越权负例→清理
node scripts/housing_vendor_hmac_regression.cjs [base_url]   # 默认 http://127.0.0.1:8766
node scripts/vendor_hmac_regression.cjs                       # 家政开放接口同机制回归

版本:v2.2(2026-09-18)· 文档与线上实现同步校准。
v2.2 变更(按商家反馈落地):⓪ 上架图集门槛由 ≥8 张下调为 ≥7 张(含封面,按商家确认的图集标准;房源图 + 户型图合计);① 价格与住宿规则下放到户型——户型 price_night 在 rental 同样生效、price_from 改选填、最短连住下放到户型(见 3.2 / 5.2);② 卡片展示价三件套 price_from_display / price_unit / price_note 与取数规则(3.4);③ 房态净可售 available_qty + set 响应回 days[] 对账、默认关房 default_closed(3.5 / 3.6);④ 图集全量覆盖 photos/sync + 分类/排序 + 上架抽检(4.11);⑤ 房间档案 room_profile 21 个字段对商家开放(3.7)。
v2.1(2026-09-10):新增 regions/list、photos/add、rating/submit|status 端点;夜价覆盖价参与下单计价;amenities 10-id 映射与上架前置清单补全;新增「常见接入疑问」节。

密钥与凭证:由平台统一配置并线下下发;遗失须走平台重置,不要在多个业务间共享 hmac_key。

试点阶段接口字段以本页为准,扩展字段只增不破坏;重大变更会提前在「接入单」里同步。发布节奏随政策与试点进展而定。