# 格物开放 API · 消费者购物
# 由生成脚本输出，接口事实逐条取自 apps/api-hub/app/open 下各 route.ts 与 packages/commerce。
# 文档页：https://open.gowu.net/ （或 www.gowu.net/open）
openapi: 3.0.3
info:
  title: 格物开放 API · 消费者购物
  version: 1.0.0
  description: >-
    **场景：消费者购物**（交易模式：用户在线支付）。面向普通消费者：你把每个用户自己的 open-id 放进 `x-open-user`（首次访问自动建档，不需要注册）。适用于消费者商城、导购与内容电商、AI
    购物助手。只含该场景开通的接口，与审批默认给的权限一致（也是 MCP 工具集的范围）。


    **下单与付款**：钱由平台收。`POST /v1/lm/pay/web-prepare` 拿支付宝链接（或 `POST /v1/lm/checkout/sessions`
    拿托管结账页），用户在浏览器里付款，付款成功后平台自动下单、失败自动退款。没有直接下单接口。


    收货地址用行政区划码：先用 `GET /v1/divisions` 查出 `divisionCode`（必填）与 `townDivisionCode`（强烈建议）。金额单位为元。


    ## 鉴权与签名（v2）


    所有请求带 `x-app-key`。为 appKey 配置 `app_secret` 后，该 appKey 的**所有**请求都要签名：

    带 `x-timestamp`（Unix 秒，±300 秒内有效）、`x-nonce`（每次请求一个随机串）、`x-sign-version: 2` 与 `x-signature`。


    签名串为下面八段用 `\n` 连接：


    1. `OCP-SIGN-V2`（固定字符串）

    2. 时间戳（同 x-timestamp）

    3. nonce（同 x-nonce）

    4. 请求方法，大写

    5. 纯路径，不含域名与 query

    6. 规范化 query：解码后按 key、再按 value 升序，各自 encodeURIComponent，用 `&` 连接；没有 query 为空串

    7. 请求体原文的 sha256（十六进制）；无请求体时为空串的 sha256：`e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`

    8. `x-open-user` 的值；不带则为空串


    `x-signature = HMAC-SHA256(app_secret, 签名串)` 的十六进制。不带 `x-sign-version` 的请求暂按旧版（v1）校验，过渡期后只接受 v2。


    ⚠️ `app_secret` 只放你自己的服务端，不要打进小程序 / App / 网页。`x-open-user` 必须取自你服务端的登录态，不能透传前端传来的值。


    ## 错误


    错误统一返回 `{ "code": "...", "message": "..." }`（部分接口另带 `ok: false`），HTTP 状态码同时表意：

    `400 BAD_REQUEST` 参数不合法 · `401 UNAUTHORIZED` appKey 无效 / 签名错误 / 时间戳超窗 · `403 FORBIDDEN` 路径不在 appKey 权限白名单 ·

    `404 NOT_FOUND` 资源不存在或不归属你的 appKey · `429 RATE_LIMITED` 超出限流（按 Retry-After 重试）。


    ## 限流


    只读接口每个 appKey 每分钟 300 次；用户态（交易类）接口每分钟 60 次。


    ## 对外 ID


    订单号、售后单号、商品 ID、SKU ID 都是不透明字符串：原样保存、原样回传，不要解析格式。

    商品接口正在重构为统一商品层，届时商品 ID 会切换为平台 ID，会提前通知并提供对照。
servers:
  - url: https://api.gowu.net
    description: 生产
tags:
  - name: 商品
    description: 商品接口正在重构为统一商品层，上线前后会给出对照
  - name: 用户与收货地址
    description: x-open-user 直接传用户 open-id；地址用行政区划码
  - name: 结算与下单
    description: 结算预览与订单查询（下单与付款见「平台收银台」）
  - name: 平台收银台
    description: 平台收款方式
  - name: 售后
    description: 售后单列表 / 详情 / 撤销、退款预览（退款申请与退货回传见「平台收银台」）
  - name: 接入与账户
    description: 沙箱 key 与账户信息
security:
  - AppKey: []
paths:
  /v1/lm/products/search:
    post:
      tags:
        - 商品
      summary: 搜索商品
      description: >-
        请求体带 version: 2（响应同样带 version: 2）；不带 version 为旧版接口，返回格式不同，保留一个版本周期。按关键词 / 编码找商品，或按条件浏览。每条结果是「商品 +
        推荐规格」，可附上符合程度与每个条件的判定（include: ["match"]）。mode 控制多严：缺省 balanced（完全符合的在前、一件都没有才放宽）；AI 助手推荐、清单解析建议
        strict（只要完全符合的）。翻页用 nextCursor。匹配与排序规则、各商品池可用的筛选字段见开发者文档 §11。未带 x-open-user 时带 query、筛选或排序都返回 401
        NEED_LOGIN，纯浏览只给第一页。
      security:
        - AppKey: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              required:
                - version
              description: >-
                必须带 version: 2，其余全部可选；不传 query = 按条件浏览。不认识的参数一律 400 并在 field 里指明（带了旧参数会提示新写法）。不带 version
                的请求按旧版接口处理（keyword / category / brand / priceMin / priceMax / inStockOnly / sortBy / page /
                pageSize，返回旧的 { items, page, pageSize, total }），保留一个版本周期
              properties:
                version:
                  type: integer
                  enum:
                    - 2
                  description: 接口版本，固定传 2。不传 = 旧版接口（返回格式不同）
                query:
                  type: object
                  description: 找什么：text 与 code 二选一
                  properties:
                    text:
                      type: string
                      maxLength: 100
                      description: 关键词。多个条件用空格分开，必须条件全部满足才算完全符合
                    optional:
                      type: array
                      items:
                        type: string
                      maxItems: 10
                      description: text 里「有更好、没有也行」的词（如用户说颜色无所谓）：只参与排序，不作为必须条件。必须是 text 里出现过的词，否则 400
                    code:
                      type: string
                      maxLength: 64
                      description: 按 SKU 编码 / 型号 / 条码 / 上游商品 ID 精确查，不容错、不改写
                filter:
                  type: object
                  description: 硬条件：不满足就不返回
                  properties:
                    category:
                      oneOf:
                        - type: string
                        - type: array
                          items:
                            type: string
                          maxItems: 20
                      description: 叶子类目名，精确匹配；数组 = 任一
                    brand:
                      oneOf:
                        - type: string
                        - type: array
                          items:
                            type: string
                          maxItems: 20
                      description: 品牌名，精确匹配；数组 = 任一
                    price:
                      type: object
                      description: 价格区间（元，含边界），按规格价：任一可售规格落在区间内即命中，此时推荐的就是区间内的规格。传反了自动对调
                      properties:
                        gte:
                          type: number
                          minimum: 0
                        lte:
                          type: number
                          minimum: 0
                    inStock:
                      type: boolean
                      description: 只要有货的
                unknown:
                  type: string
                  enum:
                    - include
                    - exclude
                  default: include
                  description: 筛选字段没有值的商品怎么处理（当前可用字段都有值，预留）
                mode:
                  type: string
                  enum:
                    - balanced
                    - strict
                    - broad
                  default: balanced
                  description: >-
                    多严。balanced：完全符合的在前、部分符合的随后，一件都没有时才放宽（返回 relaxation）；strict：只要完全符合的，宁缺毋滥（AI
                    助手推荐、清单解析用）；broad：全部给，含放宽得到的
                sort:
                  type: string
                  enum:
                    - relevance
                    - price_asc
                    - price_desc
                    - sales_desc
                  default: relevance
                  description: 顺序。价格按推荐规格的价格；任何排序下不符合的（related）都排在最后
                profile:
                  type: string
                  enum:
                    - agent
                    - mall
                  default: agent
                  description: 排序预设：agent 推荐给人看的少量结果（符合程度优先，其次有货）；mall 搜索结果页
                prefer:
                  type: array
                  maxItems: 5
                  description: >-
                    软偏好：只影响同一符合程度内的先后，不影响返回哪些；多条按先后递减。sort 不是 relevance 时忽略。每条是以下三种写法之一：`{ "field": "price",
                    "direction": "low" | "high" }` 便宜 / 贵的优先；`{ "field": "brand", "value": "膳魔师" }` 这个品牌优先；`{ "text":
                    "大容量" }` 这个词命中的优先
                  items:
                    oneOf:
                      - type: object
                        required:
                          - field
                          - direction
                        properties:
                          field:
                            type: string
                            enum:
                              - price
                          direction:
                            type: string
                            enum:
                              - low
                              - high
                      - type: object
                        required:
                          - field
                          - value
                        properties:
                          field:
                            type: string
                            enum:
                              - brand
                          value:
                            type: string
                      - type: object
                        required:
                          - text
                        properties:
                          text:
                            type: string
                            maxLength: 50
                            description: 这个词命中的优先（可以不在 query.text 里）
                excludeFields:
                  type: array
                  items:
                    type: string
                    enum:
                      - title
                      - titleGrams
                      - aliases
                      - subtitle
                      - brand
                      - category
                      - categoryPath
                      - tags
                      - skuSpecValues
                      - skuTitles
                      - specsText
                      - qualityPerformance
                      - useCases
                      - descriptionSellingPoints
                      - audience
                      - exactCodes
                      - supplierName
                  description: >-
                    这次查询不用这些字段（只对 query.text 起作用）：既不在里面找商品，也不拿它判断符合与否。用来对比某个字段的作用，如去掉卖点
                    sellingPoints。不能全部去掉。可选字段：title（标题）、titleGrams（标题按每两个字切开（中文分词切错时的兜底））、aliases（买家叫法（别名））、subtitle（副标题）、brand（品牌）、category（叶子类目名）、categoryPath（类目整条路径）、tags（平台类目路径）、skuSpecValues（各规格的规格值（颜色、尺码、容量、型号…）与材质）、skuTitles（各规格的规格名）、specsText（商品参数（前
                    600 字））、qualityPerformance（品质性能（前 300 字））、useCases（适用场景）、descriptionSellingPoints（商品描述与卖点（前 300
                    字））、audience（适用人群）、exactCodes（编码：上游商品 ID、SKU 编码、型号、条码）、supplierName（供应商名）
                limit:
                  type: integer
                  minimum: 1
                  maximum: 50
                  default: 20
                cursor:
                  type: string
                  description: 翻页：上次返回的 nextCursor。与本次请求的条件绑定（换了条件 400），1 小时有效
                include:
                  type: array
                  items:
                    type: string
                    enum:
                      - match
                  description: match：每条结果附上条件判定（符合程度、每个条件满足与否及凭据）
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchV2Page'
        '400':
          description: '`BAD_REQUEST`：参数不合法（field 指明哪个参数；带了旧参数会提示新写法），version 不是 2，或 cursor 无效 / 过期 / 与本次条件不符'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: '`NEED_LOGIN`：未带 x-open-user 时只能取第一页和精选位。响应带 `blocked`：`search`（带了搜索条件）/ `category`（按类目浏览）/ `paging`（翻页）'
          content:
            application/json:
              schema:
                type: object
                properties:
                  code:
                    type: string
                    example: NEED_LOGIN
                  message:
                    type: string
                  blocked:
                    type: string
                    enum:
                      - search
                      - category
                      - paging
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: postLmProductsSearch
  /v1/lm/products/{id}:
    get:
      tags:
        - 商品
      summary: 商品详情
      security:
        - AppKey: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductDetail'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: '`NOT_FOUND`：商品不存在或已下架'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: getLmProductsByid
  /v1/lm/categories:
    get:
      tags:
        - 商品
      summary: 类目列表
      description: 按商品数降序的类目名列表（不是树），用作搜索的 category 参数。
      security:
        - AppKey: []
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 30
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      type: object
                      properties:
                        category:
                          type: string
                        count:
                          type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: getLmCategories
  /v1/divisions:
    get:
      tags:
        - 用户与收货地址
      summary: 行政区划级联
      description: >-
        从 parent=1（全国）开始逐级下钻：省 → 市 → 区县（divisionCode）→ 乡镇街道（townDivisionCode）。与福利GO 小程序、网页商城的地址选择同一份数据。老路径
        `/v1/lm/divisions` 仍可用，返回相同。
      security:
        - AppKey: []
      parameters:
        - name: parent
          in: query
          required: false
          description: 上一级区划码
          schema:
            type: string
            default: '1'
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  items:
                    type: array
                    items:
                      type: object
                      properties:
                        code:
                          type: string
                        name:
                          type: string
                        parentCode:
                          type: string
                        hasChildren:
                          type: boolean
                        level:
                          type: integer
                          description: 2 省 / 3 市 / 4 区县 / 5 乡镇街道
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: getDivisions
  /v1/buyer/addresses:
    get:
      tags:
        - 用户与收货地址
      summary: 地址簿
      description: 可选：也可以不用地址簿，下单时直接传 receiver。默认地址在前。
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/Address'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: getBuyerAddresses
    post:
      tags:
        - 用户与收货地址
      summary: 新增地址
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddressInput'
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  address:
                    $ref: '#/components/schemas/Address'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: postBuyerAddresses
  /v1/buyer/addresses/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    put:
      tags:
        - 用户与收货地址
      summary: 修改地址
      description: 整体覆盖：没传的字段会被清空。
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddressInput'
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  address:
                    $ref: '#/components/schemas/Address'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: putBuyerAddressesByid
    post:
      tags:
        - 用户与收货地址
      summary: 设为默认地址
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      parameters:
        - name: action
          in: query
          required: true
          schema:
            type: string
            enum:
              - default
          description: 固定为 default
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Ok'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: postBuyerAddressesByid
    delete:
      tags:
        - 用户与收货地址
      summary: 删除地址
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Ok'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: deleteBuyerAddressesByid
  /v1/lm/checkout/render:
    post:
      tags:
        - 结算与下单
      summary: 结算预览
      description: 按本地价计算，不需要地址。
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - items
              properties:
                items:
                  type: array
                  minItems: 1
                  items:
                    type: object
                    required:
                      - productId
                    properties:
                      productId:
                        type: string
                        description: 商品接口返回的 id
                      skuId:
                        type: string
                        description: 商品详情 skus[].skuId
                      qty:
                        type: integer
                        minimum: 1
                        default: 1
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  render:
                    type: object
                    properties:
                      totalFee:
                        type: number
                      postFee:
                        type: number
                      payable:
                        type: number
                      lines:
                        type: array
                        items:
                          type: object
                          properties:
                            productId:
                              type: string
                            skuId:
                              type: string
                            itemName:
                              type: string
                            itemPic:
                              type: string
                            skuText:
                              type: string
                            itemPrice:
                              type: number
                            quantity:
                              type: integer
        '400':
          description: '`EMPTY` / `PRODUCT_NOT_FOUND` / `SKU_UNAVAILABLE`'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: postLmCheckoutRender
  /v1/lm/orders:
    get:
      tags:
        - 结算与下单
      summary: 订单列表
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/Order'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: getLmOrders
  /v1/lm/orders/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
        description: 采购单 id、上游采购单号或你的 idempotencyKey 都可以
    get:
      tags:
        - 结算与下单
      summary: 订单详情
      description: 返回子订单 orders。
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: getLmOrdersByid
    post:
      tags:
        - 结算与下单
      summary: 确认收货
      description: '按子订单确认收货：`action=receive`，`orderId` 传子订单号（query 或 body）。失败返回 400 `{ ok: false, message }`。'
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      parameters:
        - name: action
          in: query
          required: false
          schema:
            type: string
            enum:
              - receive
        - name: orderId
          in: query
          required: false
          schema:
            type: string
          description: 子订单号
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                action:
                  type: string
                  enum:
                    - receive
                orderId:
                  type: string
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  message:
                    type: string
        '400':
          description: 见 message
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: postLmOrdersByid
  /v1/lm/orders/{id}/logistics:
    get:
      tags:
        - 结算与下单
      summary: 物流轨迹
      description: '路径参数为**子订单号**。上游查询失败时仍返回 200，`success: false`。'
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: 子订单号 orderId
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                  packages:
                    type: array
                    items:
                      type: object
                      properties:
                        mailNo:
                          type: string
                          description: 运单号
                        companyName:
                          type: string
                        goods:
                          type: array
                          items:
                            type: object
                            properties:
                              name:
                                type: string
                              skuTitle:
                                type: string
                              quantity:
                                type: integer
                        traces:
                          type: array
                          items:
                            type: object
                            properties:
                              time:
                                type: string
                              desc:
                                type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: getLmOrdersByidLogistics
  /v1/lm/pay/web-prepare:
    post:
      tags:
        - 平台收银台
      summary: 网页支付宝：直接返回支付链接
      description: >-
        平台收款方式（手上有完整收货地址时）：返回支付短链 `payUrl` 与它的二维码 `qrImageUrl`——在电脑上或对话里优先展示二维码，用户用手机支付宝扫码付款（手机上已登录），否则把 `payUrl`
        交给用户打开付款；付款成功后平台自动下单，失败自动退款。同一 `idempotencyKey` 重放返回同一张收款单（`outTradeNo` 不变），内容不同则 409。链接自首次生成起约 23
        小时内可付。付款结果以收款单查询为准。
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - items
                - idempotencyKey
              properties:
                items:
                  type: array
                  minItems: 1
                  items:
                    type: object
                    required:
                      - productId
                      - skuId
                    properties:
                      productId:
                        type: string
                        description: 商品接口返回的 id
                      skuId:
                        type: string
                        description: 商品详情 skus[].skuId，必填
                      qty:
                        type: integer
                        minimum: 1
                        default: 1
                receiver:
                  $ref: '#/components/schemas/Receiver'
                addressId:
                  type: string
                  description: 与 receiver 二选一，必须给其一
                idempotencyKey:
                  type: string
                  maxLength: 128
                  description: 你方订单唯一号（同一用户内唯一）
                device:
                  type: string
                  enum:
                    - pc
                    - mobile
                  description: 收银台类型。payUrl 为短链时打开它的设备会自动选择（手机 / 电脑），此参数可不传；未启用短链时决定网关地址的类型（缺省 pc）
                  default: pc
                returnUrl:
                  type: string
                  description: https，域名须为登记的 callback_url 域名或其子域；不传则停在支付宝成功页
                remark:
                  type: string
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  outTradeNo:
                    type: string
                  money:
                    type: string
                  payUrl:
                    type: string
                    description: 支付短链（约 60 字符）。打开时按设备生成支付宝收银台（手机 / 电脑），付完回跳 returnUrl；已付 / 已关 / 超时会给出提示页。手机上直接打开即可付款
                  qrImageUrl:
                    type: string
                    description: payUrl 的二维码图片（PNG，几百字节），可直接放进对话或网页让用户用手机支付宝扫码；未配置公网地址时不返回，payUrl 也回落为支付宝网关地址
        '400':
          description: >-
            `EMPTY` / `SKU_REQUIRED` / `PRODUCT_NOT_FOUND` / `SKU_UNAVAILABLE` / `BAD_RECEIVER` / `ADDRESS_NOT_FOUND` /
            `BAD_IDEMPOTENCY_KEY` / `BAD_DEVICE` / `BAD_RETURN_URL` / `PAY_EXPIRED`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          description: '`IDEMPOTENCY_CONFLICT` / `ALREADY_PAID` / `PAY_CLOSED` / `IN_PROGRESS`'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          description: '`PAY_NOT_CONFIGURED`'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      operationId: postLmPayWebPrepare
  /v1/lm/checkout/sessions:
    post:
      tags:
        - 平台收银台
      summary: 托管结账页（网页支付宝）
      description: >-
        平台收款方式（手上没有完整收货地址时）：建结账会话，把 `checkoutUrl` 交给用户，用户在平台结账页核对商品、补地址，跳浏览器支付宝付款。付款成功后平台自动下单，失败自动退款。`checkoutUrl`
        即付款凭据，只发给对应用户。地址可不传，由用户在页面填写。
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - items
              properties:
                items:
                  type: array
                  minItems: 1
                  items:
                    type: object
                    required:
                      - productId
                      - skuId
                    properties:
                      productId:
                        type: string
                        description: 商品接口返回的 id
                      skuId:
                        type: string
                        description: 商品详情 skus[].skuId，必填
                      qty:
                        type: integer
                        minimum: 1
                        default: 1
                receiver:
                  $ref: '#/components/schemas/Receiver'
                idempotencyKey:
                  type: string
                  maxLength: 128
                  description: >-
                    幂等键（建议必传，用你的订单号）。同键重试返回同一个结账页（新的 checkoutUrl，旧链接失效），不会出现两个都能付的页面；同键不同内容返回 409
                    IDEMPOTENCY_CONFLICT，已过期且未付返回 409 SESSION_EXPIRED
                addressId:
                  type: string
                returnUrl:
                  type: string
                  description: 付完「返回应用」的地址；https，域名须为登记的 callback_url 域名或其子域
                remark:
                  type: string
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  sessionId:
                    type: string
                  checkoutUrl:
                    type: string
                  expiresAt:
                    type: string
                    format: date-time
                  money:
                    type: string
        '400':
          description: >-
            `EMPTY` / `SKU_REQUIRED` / `PRODUCT_NOT_FOUND` / `SKU_UNAVAILABLE` / `BAD_RECEIVER` / `ADDRESS_NOT_FOUND` /
            `BAD_RETURN_URL`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          description: '`NOT_CONFIGURED`'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      operationId: postLmCheckoutSessions
  /v1/lm/checkout/sessions/{sessionId}:
    get:
      tags:
        - 平台收银台
      summary: 托管结账会话进度
      description: '`state`：open 待支付 / expired 未支付已过期 / paying 已发起支付（再看 `payStatus`，fulfilled 即已下单）。'
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      parameters:
        - name: sessionId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  sessionId:
                    type: string
                  state:
                    type: string
                    enum:
                      - open
                      - expired
                      - paying
                  outTradeNo:
                    type: string
                  payStatus:
                    type: string
                  purchaseOrderId:
                    type: string
                  money:
                    type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: getLmCheckoutSessionsBysessionId
  /v1/lm/pay/orders:
    get:
      tags:
        - 平台收银台
      summary: 收款单列表
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/PayOrder'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: getLmPayOrders
  /v1/lm/pay/orders/{outTradeNo}:
    get:
      tags:
        - 平台收银台
      summary: 收款单详情
      description: 付款后轮询它：`status` 变为 fulfilled 且有 `upstreamOrderId` 即已下单。也用来看退款进度（refundItems）和退货信息（returnInfo）。
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      parameters:
        - name: outTradeNo
          in: path
          required: true
          schema:
            type: string
        - name: subOrderId
          in: query
          required: false
          schema:
            type: string
          description: 退货阶段指定订单行
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PayOrder'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: getLmPayOrdersByoutTradeNo
  /v1/lm/pay/refund:
    post:
      tags:
        - 平台收银台
      summary: 申请退款
      description: 按订单行退款，一单多行时 `subOrderId` 必填。未发货原路退款；已发货需要退货的，状态会变为 awaiting_return，寄回后回传运单。
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - outTradeNo
              properties:
                outTradeNo:
                  type: string
                subOrderId:
                  type: string
                  description: 订单行 id（orderLineId）
                reasonId:
                  type: string
                  description: 可选原因 id；不传取第一个可选原因
                reason:
                  type: string
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  status:
                    type: string
                    enum:
                      - refund_requested
                      - awaiting_return
                      - refund_pending
                  message:
                    type: string
        '400':
          description: >-
            `NOT_PAID` / `CLOSED` / `FULFILLING` / `SUB_ORDER_REQUIRED` / `SUB_ORDER_INVALID` / `ALREADY_SHIPPED` /
            `NO_REMAINING` / `SUBMIT_FAILED` 等
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: postLmPayRefund
  /v1/lm/pay/refund/return:
    post:
      tags:
        - 平台收银台
      summary: 回传退货运单
      description: 寄回商品后调用。快递公司编码取自收款单详情的 `returnInfo.carriers`。
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - outTradeNo
                - logisticsCompany
                - invoiceNo
              properties:
                outTradeNo:
                  type: string
                logisticsCompany:
                  type: string
                  description: returnInfo.carriers[].code
                invoiceNo:
                  type: string
                  description: 退货运单号
                subOrderId:
                  type: string
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  status:
                    type: string
                    example: returning
                  message:
                    type: string
        '400':
          description: '`NOT_AWAITING_RETURN` / `BAD_CARRIER` / `RETURN_SUBMIT_FAILED` 等'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: postLmPayRefundReturn
  /v1/lm/refunds:
    get:
      tags:
        - 售后
      summary: 售后列表 / 退款预览（可退金额与可选原因）
      description: >-
        带 `render=1&orderId=&orderLineId=`：返回这一行的可退金额和可选原因，把选中的 `reasonTextId` 作为 `reasonId` 传给 `POST
        /v1/lm/pay/refund`（原因随订单状态变化，不要写死；不传时平台取第一条）。不带 render 时返回这个用户的上游售后单列表（可用 orderId 筛选）。上游失败时 200 且 `success:
        false`。
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      parameters:
        - name: render
          in: query
          required: false
          schema:
            type: string
            enum:
              - '1'
        - name: orderId
          in: query
          required: false
          schema:
            type: string
          description: 子订单号
        - name: orderLineId
          in: query
          required: false
          schema:
            type: string
          description: 订单行 id，render 时必填
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                description: 列表时返回 items；render 时返回 success / maxRefundFee / reasons
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/Refund'
                  success:
                    type: boolean
                  message:
                    type: string
                  maxRefundFee:
                    type: number
                    description: 最多可退（元）
                  reasons:
                    type: array
                    items:
                      type: object
                      properties:
                        reasonTextId:
                          type: string
                        reasonTips:
                          type: string
                        proofRequired:
                          type: boolean
                        refundDescRequired:
                          type: boolean
        '400':
          description: render 缺少 orderId / orderLineId
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: getLmRefunds
  /v1/lm/refunds/{disputeId}:
    parameters:
      - name: disputeId
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - 售后
      summary: 售后详情
      description: 读取时会同步一次上游状态。钱是否已退到用户手里以收款单 status / refundItems 为准。
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Refund'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: getLmRefundsBydisputeId
    post:
      tags:
        - 售后
      summary: 撤销售后
      description: 用户不想退了时撤销；撤销后收款单回到原状态，钱不退。
      security:
        - AppKey: []
          Signature: []
          OpenUser: []
      parameters:
        - name: action
          in: query
          required: false
          schema:
            type: string
            enum:
              - cancel
            default: cancel
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  message:
                    type: string
        '400':
          description: 见 message
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: postLmRefundsBydisputeId
  /v1/access-clients/me:
    get:
      tags:
        - 接入与账户
      summary: 当前 appKey 信息
      security:
        - AppKey: []
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  appKey:
                    type: string
                  remark:
                    type: string
                    nullable: true
                  createdAt:
                    type: string
                    nullable: true
                  apiCallCount:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
      operationId: getAccessClientsMe
  /v1/health:
    get:
      tags:
        - 接入与账户
      summary: 健康检查
      security: []
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
                  db:
                    type: string
                    example: ok
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          description: 依赖不可用
      operationId: getHealth
components:
  securitySchemes:
    AppKey:
      type: apiKey
      in: header
      name: x-app-key
    Signature:
      type: apiKey
      in: header
      name: x-signature
      description: 'HMAC-SHA256 签名（v2），另带 x-timestamp / x-nonce / x-sign-version: 2。算法见文档开头。'
    OpenUser:
      type: apiKey
      in: header
      name: x-open-user
      description: 终端用户标识：你系统里的用户 ID，必须取自你服务端的登录态。参与签名第 8 段。
  parameters:
    Page:
      name: page
      in: query
      schema:
        type: integer
        minimum: 1
        default: 1
  responses:
    BadRequest:
      description: 参数不合法
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: appKey 无效或已停用、签名错误、时间戳超出 ±300 秒
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: 路径不在该 appKey 的权限白名单，或业务上不允许（见 code）
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: 资源不存在，或不归属你的 appKey / 当前用户
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: 超出限流，按 Retry-After 头重试
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    Error:
      type: object
      required:
        - code
        - message
      properties:
        ok:
          type: boolean
          example: false
        code:
          type: string
          example: BAD_REQUEST
        message:
          type: string
    Ok:
      type: object
      properties:
        ok:
          type: boolean
          example: true
    Product:
      type: object
      properties:
        id:
          type: string
          description: 商品 ID，下单时原样放进 items[].productId
        externalItemId:
          type: string
        title:
          type: string
        subtitle:
          type: string
        imageUrl:
          type: string
        imageUrls:
          type: array
          items:
            type: string
        category:
          type: string
        brand:
          type: string
        price:
          type: number
          description: 售价（元）；多规格商品为最低规格价
        marketPrice:
          type: number
        shopName:
          type: string
        sales30d:
          type: number
        availability:
          type: string
          enum:
            - in_stock
            - out_of_stock
        featured:
          type: boolean
          description: 是否运营精选
        matchedSku:
          $ref: '#/components/schemas/MatchedSku'
    MatchedSku:
      type: object
      description: >-
        关键词搜索命中的规格：多规格商品、且搜索词能对应到某个规格时才有（「黑色 1000ml 保温杯」→ 黑色 / 1000ml）。带价格区间时给区间内的规格。建议卡片直接展示该规格，点进详情时用 skuId 预先选中。没有 =
        按商品展示
      properties:
        skuId:
          type: string
        specText:
          type: string
          description: 规格值，按「 / 」连接，如「黑色 / 1000ml」
        price:
          type: number
          description: 该规格的对外售价（元），与详情接口的规格价同口径；缺省 = 用商品价
        imageUrl:
          type: string
          description: 该规格的图片；缺省 = 用商品主图
    ProductPage:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/Product'
        page:
          type: integer
        pageSize:
          type: integer
        total:
          type: integer
          description: 命中总数（带关键词时为估算值）。结果按渠道可见范围再过滤，一页可能少于 pageSize；翻页以返回为空或 page × pageSize ≥ total 判断结束
        searchId:
          type: string
          description: 带关键词搜索时返回；上报点击 / 加购 / 下单时带回
        correctedQuery:
          type: string
          description: 做了错别字纠正时实际搜的词，可提示「已为您搜索 xx」
        relaxed:
          type: boolean
          description: true = 没有同时满足全部关键词的商品，返回的是放宽后的相关商品；可提示「没有找到同时满足『…』的商品，以下是相关商品」
    SearchBody:
      type: object
      description: 全部可选
      properties:
        keyword:
          type: string
          maxLength: 100
          description: 关键词。多个条件用空格分开，每个词都要命中（标题、买家叫法、品牌、类目、规格值 / 规格名、商品参数、场景、卖点、编码等）；一件都没有时才放宽并返回 relaxed=true。4 个字及以上的词允许错一个字
        category:
          type: string
          maxLength: 100
          description: 叶子类目名，精确匹配
        brand:
          type: string
          maxLength: 100
          description: 品牌名，精确匹配
        priceMin:
          type: number
          minimum: 0
          description: 元，含。多规格商品任一可售规格的价格落在区间内即命中
        priceMax:
          type: number
          minimum: 0
          description: 元，含。与 priceMin 传反会自动对调
        inStockOnly:
          type: boolean
          description: 只要有货的；缺省不过滤
        sortBy:
          type: string
          enum:
            - price_asc
            - price_desc
            - sales_desc
          description: 缺省按相关性（同一规格满足全部条件的排前），无关键词时按更新时间。价格升序取最低规格价、降序取最高规格价
        page:
          type: integer
          minimum: 1
          default: 1
        pageSize:
          type: integer
          minimum: 1
          maximum: 60
          default: 20
    SearchRequirement:
      type: object
      description: 从 query.text 理解出来的一个条件。只是我们的理解，供参考（会随理解能力变化），不是约定
      properties:
        text:
          type: string
        role:
          type: string
          enum:
            - product
            - attribute
            - brand
            - other
          description: product 品类 / attribute 属性 / brand 品牌 / other 其他（编码）
        enforced:
          type: boolean
          description: 是否作为必须条件（决定完全符合与否）。只有有把握的条件才是 true
        why:
          type: string
          enum:
            - optional
            - vague
            - unrecognized
          description: 不作为必须条件的原因：调用方标了可选 / 模糊词（大容量、好用）/ 认不出
    SearchV2Sku:
      type: object
      description: 推荐的规格（卡片直接展示它，进详情时用 id 预选）
      properties:
        id:
          type: string
          description: 规格 ID，即详情接口的 skus[].skuId；单规格商品（详情里没有规格选择）不给
        specText:
          type: string
          description: 规格值，如「曜石黑 / 1000ml」
        specs:
          type: object
          additionalProperties:
            type: string
          description: 规格名 → 规格值
        price:
          type: number
          description: 该规格的对外售价（元），与详情接口的规格价同口径
        marketPrice:
          type: number
        imageUrl:
          type: string
          description: 规格图；没有时用商品主图
        stock:
          type: integer
        available:
          type: boolean
        chosenBy:
          type: string
          enum:
            - match
            - cheapest
            - only
          description: match 按搜索条件选出 / cheapest 条件在规格间没有区分，给价格区间内最便宜的 / only 只有一个规格
    SearchV2Hit:
      type: object
      properties:
        product:
          allOf:
            - $ref: '#/components/schemas/Product'
            - type: object
              properties:
                categoryId:
                  type: string
                  description: 叶子类目 id（仅企业购）
        sku:
          allOf:
            - $ref: '#/components/schemas/SearchV2Sku'
          nullable: true
          description: 推荐的规格；商品没有可售规格时为 null
        skuCount:
          type: integer
          description: 可售规格数
        match:
          type: object
          description: include 含 match 时返回
          properties:
            level:
              type: string
              enum:
                - exact
                - partial
                - related
              description: exact 推荐的这个规格满足全部必须条件 / partial 没有不满足的，但有判断不了或分散在不同规格上的 / related 有不满足的，或是放宽得到的
            requirements:
              type: array
              description: 每个条件在推荐规格上的判定，顺序同 interpretation.requirements
              items:
                type: object
                properties:
                  text:
                    type: string
                  state:
                    type: string
                    enum:
                      - met
                      - unmet
                      - unknown
                    description: met 满足 / unmet 不满足（如类目明确是别的品类）/ unknown 判断不了（字面上没找到不算不满足）
                  by:
                    type: string
                    description: 满足的凭据：命中的规格值原文，如「曜石黑」
    SearchV2Page:
      type: object
      properties:
        version:
          type: integer
          enum:
            - 2
          description: 响应版本，固定 2。解析前可以校验它，防止拿旧格式硬解析
        searchId:
          type: string
          description: 带 query 时返回；上报点击 / 加购 / 下单时带回
        interpretation:
          type: object
          description: 带 query.text 时返回：我们怎么理解这次查询（供参考，不是约定）
          properties:
            searched:
              type: string
              description: 纠错 / 改写后实际搜的词；与输入不同时可提示「已为您搜索 xx」
            requirements:
              type: array
              items:
                $ref: '#/components/schemas/SearchRequirement'
        hits:
          type: array
          items:
            $ref: '#/components/schemas/SearchV2Hit'
        relaxation:
          type: object
          description: 返回了不符合（related）的结果时才有：哪些必须条件没有满足。可提示「没有找到同时满足…的商品，以下是相关商品」
          properties:
            dropped:
              type: array
              items:
                type: string
        relaxHints:
          type: array
          description: 完全符合的不足 limit 时：去掉某个必须条件能有几件完全符合的。AI 助手可据此问用户要不要放宽，再把那个词放进 query.optional 重搜
          items:
            type: object
            properties:
              drop:
                type: string
              exact:
                type: integer
        total:
          type: object
          properties:
            value:
              type: integer
            exact:
              type: boolean
              description: false = 估算值
        nextCursor:
          type: string
          description: 下一页的游标；没有 = 没有下一页。未登录访客不返回
        degraded:
          type: boolean
          description: 搜索服务暂时不可用、退回查库：只按标题拆词匹配，没有 match 与推荐规格的条件判定
    ProductDetail:
      allOf:
        - $ref: '#/components/schemas/Product'
        - type: object
          properties:
            detailImages:
              type: array
              items:
                type: string
            specs:
              type: object
              additionalProperties:
                type: string
            skus:
              type: array
              items:
                type: object
                properties:
                  skuId:
                    type: string
                    description: 下单每行必带
                  specs:
                    type: object
                    additionalProperties:
                      type: string
                  price:
                    type: number
                  stock:
                    type: number
                    description: 0 = 售罄；-1 = 上游不提供库存
                  image:
                    type: string
                  canSell:
                    type: boolean
                    description: false = 不可售
    Receiver:
      type: object
      description: 收货人。地区用行政区划码，先用 GET /v1/divisions 级联查出
      required:
        - receiver
        - receiverPhone
        - addressDetail
        - divisionCode
      properties:
        receiver:
          type: string
          description: 收货人姓名
        receiverPhone:
          type: string
        divisionCode:
          type: string
          description: 区县级（四级）区划码，必填
          example: '330106'
        townDivisionCode:
          type: string
          description: 乡镇级（五级）区划码，强烈建议带上，缺了上游可能拒单
          example: '330106001'
        addressDetail:
          type: string
          minLength: 5
    Address:
      type: object
      properties:
        id:
          type: string
        receiver:
          type: string
        receiverPhone:
          type: string
        province:
          type: string
        city:
          type: string
        district:
          type: string
        street:
          type: string
        divisionCode:
          type: string
        townDivisionCode:
          type: string
        addressDetail:
          type: string
        isDefault:
          type: boolean
        complete:
          type: boolean
          description: 能否直接用于下单
        missingFields:
          type: array
          items:
            type: string
          description: 缺哪些项（如 divisionCode、townDivisionCode）
        createdAt:
          type: string
    AddressInput:
      type: object
      required:
        - receiver
        - receiverPhone
        - addressDetail
      properties:
        receiver:
          type: string
        receiverPhone:
          type: string
        addressDetail:
          type: string
        province:
          type: string
        city:
          type: string
        district:
          type: string
        street:
          type: string
        divisionCode:
          type: string
          description: 传了会自动补齐省市区名称
        townDivisionCode:
          type: string
        isDefault:
          type: boolean
    Order:
      type: object
      description: 采购单。一张采购单可能拆成多张子订单（orders）
      properties:
        id:
          type: string
        outerPurchaseOrderId:
          type: string
          description: '= 你传的 idempotencyKey'
        purchaseOrderId:
          type: string
          description: 上游采购单号；还在创建中时为空
        lmStatus:
          type: string
          enum:
            - PENDING
            - CREATING
            - CREATED
            - FAILED
        displayStatus:
          type: string
        totalFee:
          type: number
        postFee:
          type: number
        payable:
          type: number
        items:
          type: array
          items:
            type: object
            properties:
              productId:
                type: string
              skuId:
                type: string
              title:
                type: string
              skuText:
                type: string
              imageUrl:
                type: string
              price:
                type: number
              qty:
                type: integer
              lineTotal:
                type: number
        receiver:
          type: object
        remark:
          type: string
        message:
          type: string
        createdAt:
          type: string
        orders:
          type: array
          description: 子订单（详情接口返回）。确认收货、物流、售后都按子订单号 orderId
          items:
            type: object
            properties:
              orderId:
                type: string
                description: 子订单号
              orderStatus:
                type: string
                description: 1 待支付 / 2 已支付 / 4 已退款关闭 / 6 交易成功 / 8 已关闭
              logisticsStatus:
                type: string
              refundStatus:
                type: string
              displayStatus:
                type: string
              orderAmount:
                type: number
              lines:
                type: array
                items:
                  type: object
                  properties:
                    orderLineId:
                      type: string
                      description: 订单行 id，售后用
                    productId:
                      type: string
                    productTitle:
                      type: string
                    skuId:
                      type: string
                    skuTitle:
                      type: string
                    quantity:
                      type: integer
                    payFee:
                      type: number
              closedReason:
                type: string
              createdAt:
                type: string
    PayOrder:
      type: object
      description: 平台收款单
      properties:
        outTradeNo:
          type: string
          description: 收款单号
        status:
          type: string
          enum:
            - pending_payment
            - paid
            - fulfilling
            - fulfilled
            - refund_requested
            - awaiting_return
            - returning
            - refund_pending
            - manual_review
            - refunded
            - partially_refunded
            - refund_failed
            - closed
        gateway:
          type: string
          enum:
            - alipay
            - wxpay
        tradeNo:
          type: string
        money:
          type: string
          description: 金额（元）
        name:
          type: string
        items:
          type: array
          items:
            type: object
        receiver:
          type: object
        upstreamOrderId:
          type: string
          description: 付款后平台自动下单得到的采购单号
        createdAt:
          type: string
        paidAt:
          type: string
          nullable: true
        fulfilledAt:
          type: string
          nullable: true
        refundedAt:
          type: string
          nullable: true
        refundItems:
          type: array
          description: 详情接口返回：按订单行的退款进度
          items:
            type: object
            properties:
              subOrderId:
                type: string
              amount:
                type: number
              status:
                type: string
              createdAt:
                type: string
        returnInfo:
          type: object
          description: 需要退货时返回：寄回地址与可选快递公司
          properties:
            subOrderId:
              type: string
            refunderName:
              type: string
            refunderTel:
              type: string
            refunderAddress:
              type: string
            carriers:
              type: array
              items:
                type: object
                properties:
                  code:
                    type: string
                  name:
                    type: string
            ready:
              type: boolean
    Refund:
      type: object
      properties:
        disputeId:
          type: string
          description: 售后单号
        orderId:
          type: string
        orderLineId:
          type: string
        status:
          type: integer
          description: 1 退货待处理 / 2 待买家退货 / 3 待商家收货 / 4 退款关闭 / 5 退款成功 / 6 已拒绝 / 17 取消中
        displayStatus:
          type: string
        bizClaimType:
          type: integer
          description: 1 仅退款 / 3 退货退款
        amount:
          type: number
        reason:
          type: string
        refunderName:
          type: string
        refunderTel:
          type: string
        refunderAddress:
          type: string
        canSubmitReturn:
          type: boolean
        createdAt:
          type: string
