Skip to content
登录
Veridien Docs

优惠与促销码

商品目录接口回答的是「每个房型里我能订什么」。这里的接口回答的是「这段住宿适用哪些优惠」,也正是预订引擎做销售展示所需要的:对外名称、描述、图片、套餐包含什么,以及针对这批客人的价格。

房价方案、套餐和促销,本质上是同一类对象。它们的区别在于 kind,以及价格如何从父方案推导而来,所以一个套餐的分发和定价方式,与任何其他房价完全一样。


GET /rate-plans

权限范围: availability:read

返回酒店为预订引擎发布的每一项优惠,并按所请求的住宿与人数完成定价。

参数必填说明
check_inYYYY-MM-DD
check_outYYYY-MM-DD,不含当天。
adults默认为 2。
children默认为 0。
room_type_id限定在一个房型内。
curl "$VRDN_BASE/rate-plans?check_in=2026-08-01&check_out=2026-08-04&adults=2" \
  -H "Authorization: Bearer $VRDN_KEY"
{
  "check_in": "2026-08-01",
  "check_out": "2026-08-04",
  "data": [
    {
      "rate_plan_id": "5c1e8f4a-2b7d-4a9c-8e6f-1d3b9a5c7e02",
      "room_type_id": "9b2f6c3e-8d1a-4f5b-a7c9-4e0d2b8f1a63",
      "room_type_name": "Ocean Suite",
      "public_name": "Half Board Escape",
      "kind": "package",
      "description": "Breakfast and dinner included daily.",
      "image_url": "https://cdn.example.com/half-board.jpg",
      "inclusions": [
        { "label": "Breakfast", "frequency": "perGuestPerNight", "included_quantity": 1 },
        { "label": "Dinner", "frequency": "perGuestPerNight", "included_quantity": 1 }
      ],
      "min_los": 2,
      "max_los": null,
      "currency": "USD",
      "total": "1341.60",
      "room_subtotal": "1020.00",
      "taxes_total": "321.60",
      "base_occupancy": 2,
      "nightly_rates": [{ "date": "2026-08-01", "day_of_week": 6, "rate": "340.00", "source": "interval" }]
    }
  ]
}

只有以下条件全部成立时,一项优惠才会出现。任何一条不成立,它就直接不在响应里,没有部分展示,也没有「不可用」的条目。

  • 方案处于启用状态,且酒店已为预订引擎开启它。
  • 这段住宿落在该优惠的可预订窗口与可入住窗口之内。
  • 住宿天数满足该优惠的最短与最长要求,且每一晚都落在允许的星期上。
  • 该房型装得下这批客人。
  • 该方案没有被促销码锁住。

被促销码锁住的优惠绝不会出现在这里。只有通过下面介绍的促销码校验成功,才能拿到它们。

inclusions 列出一个套餐打包了什么,以及每一项按什么频次重复:perStayperNightperGuestperGuestPerNight。它们只是描述性的。套餐的售价就是那一个 total;酒店会在内部把这个总额分摊到各个包含项上,让每个组成部分适用正确的税务处理,而客人只付一个数字。


POST /promo-codes/validate

权限范围: availability:read

字段必填说明
code忽略大小写和首尾空白。
check_inYYYY-MM-DD
check_outYYYY-MM-DD,不含当天。
room_type_id如果传了它,而该码对应的优惠属于别的房型,响应就是那个统一的 valid: false
adults默认为 2。
children默认为 0。
child_ages默认为 []。非空时,它的长度就是儿童人数,并覆盖 children

人数会与房型的最大可住人数比对,因此一个报少了的人数可能在这里校验通过,却在 POST /holds 时失败。

促销码解锁的是一个房价方案。它本身不施加折扣:优惠已经内建在它揭示出来的那个方案里,所以你拿到的价格就是最终价格,不需要再算什么。

curl -X POST "$VRDN_BASE/promo-codes/validate" \
  -H "Authorization: Bearer $VRDN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "SUMMER26",
    "check_in": "2026-08-01",
    "check_out": "2026-08-04",
    "adults": 2
  }'

一个适用的促销码会返回它解锁的优惠,结构与 /rate-plans 的条目一致。

{
  "valid": true,
  "code": "SUMMER26",
  "rate_plan": {
    "rate_plan_id": "e2a9d7c4-3f6b-4c1e-9a8d-7b5f0c2e4a19",
    "room_type_id": "9b2f6c3e-8d1a-4f5b-a7c9-4e0d2b8f1a63",
    "room_type_name": "Ocean Suite",
    "public_name": "Early Bird",
    "kind": "promotion",
    "description": "Book 60 days ahead and save.",
    "image_url": "https://cdn.example.com/early-bird.jpg",
    "inclusions": [],
    "min_los": 3,
    "max_los": null,
    "currency": "USD",
    "total": "1140.36",
    "room_subtotal": "867.00",
    "taxes_total": "273.36",
    "nightly_rates": [{ "date": "2026-08-01", "day_of_week": 6, "rate": "289.00", "source": "interval" }]
  }
}

一个不适用的促销码,无论原因是什么,都返回同一个响应。

{
  "valid": false,
  "reason": "That code is not valid for these dates."
}

无论是不存在的码、已过期的码、已被用完的码,还是对应优惠不覆盖所选日期的码,失败响应都是刻意做成一模一样的。区分它们会让调用方能够探出哪些码是真实存在的,因此具体原因绝不对外披露。

促销码匹配忽略大小写和首尾空白,所以 summer26SUMMER26 是同一个码。

促销码校验的限额比 API 其余部分更严,并叠加在身份认证中说明的按密钥和按 IP 的限额之上。反复失败会返回 429。请在客人提交促销码时校验,而不是每敲一个键就校验一次。


把促销码传给下单调用。服务端会从头重新解析它,并重新为这段住宿定价。

curl -X POST "$VRDN_BASE/holds" \
  -H "Authorization: Bearer $VRDN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "guest_id": "c4e7b9d2-6a1f-4e3c-9b8d-2f5a7c0e1d46",
    "room_type_id": "9b2f6c3e-8d1a-4f5b-a7c9-4e0d2b8f1a63",
    "rate_plan_id": "e2a9d7c4-3f6b-4c1e-9a8d-7b5f0c2e4a19",
    "check_in": "2026-08-01",
    "check_out": "2026-08-04",
    "adults": 2,
    "promo_code": "SUMMER26"
  }'

有两点后果值得在设计时考虑:

  • 校验结果只是参考。 它反映的是签发那一刻的情况。如果促销码在客人付完款之前过期、被停用或用满了兑换次数,那么下单会失败,而不是按之前的报价成交。请在你的结账流程里处理这种失败。
  • 绝不要把价格传过来。 服务端会自己为每一次下单定价,并忽略客户端提交的任何金额。报价只用于展示。

兑换次数是在预订被确认时计数的,不是在校验促销码时;预订取消后会释放。一个限定兑换次数的促销码,不会因为多人同时下单而被超卖。