易览云全域旅游系统
渠道开放 API — 第三方对接开发文档

面向对象:渠道方 / 供应商自有网站或小程序的开发人员
版本:v1.3(2026-09-24)
模式:贵方收款 → 调本站锁单与支付确认 → 本站按结算价扣商家预充值或占授信并履约
在线阅读:https://www.elanyun.cn/doc/commerce-channel-api.html
覆盖品类:commerce 已接入的全部来源(mall / hotel / ticket / venue / travel / carrent / rent / pointsmall)


1. 业务说明(先读)

角色 做什么
贵方(渠道) 展示商品、向用户收款/退款、调用本站 API
本站(平台) 商品/库存/订单履约;按结算价扣商家预充值或记授信
商家(供应商) 在本站配置预充值或授信额度;不直接对接本 API(除非自己也是渠道)

钱怎么流

  1. 用户付钱给贵方(微信/支付宝等,贵方自己的商户号)
  2. 贵方调本站 order_pay_notify
  3. 本站从商家账户扣结算价(预充值余额减少,或授信已用增加)
  4. 本站负责发货/核销等履约

贵方零售价可自定;本站只认 channel_settle_amount(结算价)。

推荐主流程

products / product_detail(标价与基础信息)
        ↓
price_quote(日期类:日历 / 连住 / 场次询价)← hotel/ticket/venue/travel 必调
        ↓
merchant_quota(可选,下单前看额度)
        ↓
order_hold(锁库存,校验额度,不扣款)
        ↓
贵方向用户收款
        ↓
order_pay_notify(成功)──→ 本站履约
        ↓ 失败(如额度不足)
贵方立即退款给用户

退款:

贵方先退用户 → order_refund_notify → 本站回滚额度与库存

关于价格(重要)

字段 含义
product_detail.price 标价/底价,来自商品基础信息,不含日历日期价
product_detail.channel_settle_price 未带日期时多为估算;settle_is_estimate=true 时不能当成交价
price_quote.channel_settle_price 指定日期后的渠道结算价,与 hold 扣款一致
order_hold.channel_settle_amount 最终以锁单返回为准

日期类(hotel / ticket / venue / travel / carrent):详情看介绍 → price_quote 定价 → hold。
固定价(mall / rent / pointsmall)可直接用详情结算价。

限制(请遵守)

  • 单笔订单暂不支持多个供应商商品,请拆单
  • 锁单默认约 30 分钟超时,超时自动取消并恢复库存
  • hold 不扣商家额度;真正扣/占在 pay_notify
  • 结算价为 0 的商品不会出现在列表,也无法 hold(如纯积分商品)
  • 酒店/门票/场地等日期类商品,锁单须在 items[].extra 带齐业务字段(见 4.4 extra 总表)
  • 开放渠道仅能售预充值商家商品(见 2.1)

1.1 支持的商品来源(source)

source 说明 product_id 含义 sku_id 结算价规则 锁单 extra 要点
mall 商城 商品 ID SKU ID,无规格传 0 cost_price>0 用成本,否则用售价 一般无需
hotel 酒店房型 房型 ID 0 须 price_quote(check_in/check_out) 连住总价 必填 check_in/check_out;quantity=间数
ticket 门票 票种 ID price_quote 返回的日库存行 ID 按 booking_date 日报价 传询价得到的 sku_id
venue 场地 场地 ID 当日时段 ID price_quote(booking_date) 时段价 sku_id + extra.booking_date
travel 旅游套餐 套餐 ID 套餐 SKU price_quote 日期价 sku_id + extra.booking_date
carrent 租车 车辆 ID 0 按 rent_type 单价 可选 rent_type(日/周/月);quantity=租期数
rent 租赁商品 商品 ID 0 日租/套餐价 可选 use_plan
pointsmall 积分商城 商品 ID 0 仅现金价;纯积分不可渠道售 无

先调 sources 接口查看当前租户实际已开通(registered=true)的来源。


2. 接入准备

向平台运营索取:

配置项 说明
tenant_id 租户 ID(请求头 X-Tenant-Id)
app_key 渠道应用 Key
app_secret 渠道应用 Secret(仅线下交付,勿写进前端)
API 域名 如 https://www.example.com

可选:IP 白名单(后台配置后,非白名单 IP 将被拒绝)。

2.1 渠道类型与结算模式(必读)

后台创建「渠道应用」时有两类:

channel_type 名称 商家结算限制
1 签约合作 平台签约渠道 允许预充值与/或授信(以后台 allow_bill_modes 为准,默认 1,2)
2 开放接入 开放 API 仅允许预充值商家(channel_bill_mode=1);授信商家商品 lock 会失败

商家账户:

channel_bill_mode 含义 pay_notify 行为
1 预充值 扣减 prepaid_balance(ledger:consume)
2 授信 增加 credit_used(ledger:credit_occupy)

账户 status=0 冻结时不可 hold / pay。


3. 通用约定

3.0 金额与精度

  • 所有金额字段(请求与响应)一律为 两位小数字符串,如 "36.00"
  • 本站用字符串精确比较(bccomp);禁止用浮点直接比对
  • channel_settle_amount 必须与锁单返回值 完全一致(含两位小数)
  • 数量 quantity 为正整数;酒店=间数,租车=租期数

3.1 Base URL

https://{域名}/plugin.php/commerce/channel_api/{动作}.html

示例:

GET https://www.example.com/plugin.php/commerce/channel_api/products.html?source=mall&page=1&limit=20

3.2 请求头(必填)

Header 说明
X-Tenant-Id 租户 ID
X-App-Key AppKey
X-Timestamp 当前 Unix 时间戳(秒)
X-Nonce 随机字符串(建议 UUID)
X-Sign 签名(见下)
Content-Type POST 时用 application/json

也可用 Query 传 app_key / timestamp / nonce / sign(不推荐,优先 Header)。

3.3 签名算法

待签字符串 = app_key + timestamp + nonce + body
X-Sign = UPPERCASE( HEX( HMAC-SHA256( app_secret, 待签字符串 ) ) )

body 怎么取

方法 body
GET 业务 Query 参数按 key 字典序 拼成 a=1&b=2(不含 app_key/timestamp/nonce/sign/s)
POST HTTP 原始 Body(一般是 JSON 原文,须与发送内容完全一致)

时间戳与服务器相差超过约 300 秒(可按应用配置 notify_tol,最小 60)会判过期。

空业务参数的 GET(如 sources)

无业务 Query 时,签名用的 body 为 空字符串:

待签 = app_key + timestamp + nonce + ""

PHP:$body = ''; 再算 HMAC。不要传 null,也不要拼多余的 =。

3.4 统一响应

{
  "code": 1,
  "info": "ok",
  "data": { }
}
code 含义
1 成功
0 失败(看 info;data 可能带附加信息)

3.5 PHP 签名示例

function channelSign(string $secret, string $appKey, string $timestamp, string $nonce, string $body): string
{
    return strtoupper(hash_hmac('sha256', $appKey . $timestamp . $nonce . $body, $secret));
}

// GET
$query = ['source' => 'mall', 'page' => '1', 'limit' => '20'];
ksort($query);
$body = http_build_query($query); // a=1&b=2
$sign = channelSign($secret, $appKey, $ts, $nonce, $body);

// POST
$json = json_encode($payload, JSON_UNESCAPED_UNICODE);
$sign = channelSign($secret, $appKey, $ts, $nonce, $json);
// 请求时必须发送同一份 $json 作为 Body

3.6 Node.js 签名示例

const crypto = require('crypto');

function channelSign(secret, appKey, timestamp, nonce, body) {
  return crypto
    .createHmac('sha256', secret)
    .update(appKey + timestamp + nonce + body)
    .digest('hex')
    .toUpperCase();
}

4. 接口一览

动作 Method 说明
sources GET 查询已支持/已注册来源
products GET 商品列表(可全源聚合)
product_detail GET 商品详情 + 标价(日期价见下)
price_quote GET 日期/日历询价(酒店连住、门票日价、场地时段等)
merchant_quota GET 查询可用额度
order_hold POST 锁库存、创建待支付订单
order_pay_notify POST 用户已付款,确认成交
order_query GET 查单
order_refund_notify POST 退款回滚
— — 另见 幂等与状态机、常见错误

4.0 商品来源 sources

GET .../sources.html

无业务 Query。用于发现当前租户开通了哪些 commerce 品类。

成功 data

{
  "list": [
    {"source": "mall", "name": "商城实物/虚拟", "registered": true},
    {"source": "hotel", "name": "酒店房型", "registered": true},
    {"source": "ticket", "name": "门票票种", "registered": true},
    {"source": "venue", "name": "场地预订", "registered": true},
    {"source": "travel", "name": "旅游套餐", "registered": true},
    {"source": "carrent", "name": "租车车辆", "registered": true},
    {"source": "rent", "name": "租赁商品", "registered": false},
    {"source": "pointsmall", "name": "积分商城", "registered": true}
  ]
}

仅 registered=true 的来源可拉商品与下单。


4.1 商品列表 products

GET .../products.html

Query

参数 类型 必填 说明
source string 否 留空=聚合全部已注册来源;指定则只查该源(如 mall/hotel)
keyword string 否 关键词
page int 否 默认 1
limit int 否 默认 20,最大 50

成功 data 示例

{
  "list": [
    {
      "source": "mall",
      "product_id": 1001,
      "title": "海南椰子糖",
      "cover": "https://cdn.example.com/p.jpg",
      "price": "29.90",
      "channel_settle_price": "18.00",
      "stock": 200,
      "supplier_id": 12,
      "channel_bill_mode": 2,
      "quota_available": "50000.00",
      "need_shipping": true,
      "need_verify": false,
      "sort": 100
    },
    {
      "source": "hotel",
      "product_id": 88,
      "title": "海景大床房",
      "cover": "https://cdn.example.com/room.jpg",
      "price": "388.00",
      "channel_settle_price": "388.00",
      "stock": 0,
      "supplier_id": 5,
      "channel_bill_mode": 2,
      "quota_available": "80000.00",
      "need_shipping": false,
      "need_verify": true,
      "sort": 10
    }
  ],
  "total": 2,
  "page": 1,
  "limit": 20
}
字段 说明
source 来源编码,下单必须原样回传
price 本站参考标价;贵方可另设售价
channel_settle_price 结算单价(成交按此×数量扣商家)
channel_bill_mode 1 预充值 / 2 授信
quota_available 所属商家当前可用额度

列表价注意

  • 结算价为 0 的商品会被过滤,不出现在列表中。
  • 日期类(hotel / ticket / venue / travel 等)列表里的 price / channel_settle_price 仅为标价估算,与真实成交价可能不一致;下单前必须再调 price_quote。
  • 固定价品类(mall / rent / pointsmall)列表结算价一般可直接用于 hold。

4.2 商品详情 product_detail

GET .../product_detail.html

注意:本接口复用各品类基础 getProductInfo,返回的是标价/底价。
酒店/门票/场地/旅游等有日历价的品类,请用 price_quote 询价后再 hold,不要直接用这里的 channel_settle_price 当成交价。

Query

参数 必填 说明
source 是 来源编码
product_id 是 商品/房型/票种等 ID
check_in / check_out 否 酒店:若传入则 channel_settle_price 按连住估算(仍推荐专用询价)
booking_date 否 旅游:日期价估算
rent_type 否 租车:租期类型

成功 data(节选)

{
  "source": "hotel",
  "source_name": "酒店房型",
  "product_id": 88,
  "title": "海景大床房",
  "cover": "https://cdn.example.com/room.jpg",
  "price": "388.00",
  "price_mode": "list_only",
  "channel_settle_price": "388.00",
  "settle_is_estimate": true,
  "supplier_id": 5,
  "channel_bill_mode": 2,
  "need_shipping": false,
  "need_verify": true,
  "extra_hint": {
    "required": ["check_in", "check_out"],
    "note": "详情 price 仅为标价。请先调 price_quote(check_in/check_out) 再 hold;quantity=间数"
  },
  "specs": [],
  "skus": [],
  "stores": [],
  "quota_available": "80000.00"
}
字段 说明
price 标价/底价
price_mode list_only=未带日期的日期商品;dated=已带日期;fixed=固定价品类
settle_is_estimate true 时结算价仅为估算,须再调 price_quote

请阅读返回的 extra_hint。有 skus 时下单传对应 sku_id,无规格传 0。


4.2.1 日期询价 price_quote

GET .../price_quote.html

日期类商品的正式成交结算价与可售库存由此接口给出。

酒店 source=hotel

模式 Query 返回要点
连住询价 product_id + check_in + check_out channel_settle_price(1 间整段)、nights、available、daily_prices、extra_for_hold
月日历 product_id + month=YYYY-MM daily_prices 每晚价
区间日历 product_id + start + end 同上(跨度≤93 天)
{
  "source": "hotel",
  "product_id": 88,
  "mode": "range",
  "check_in": "2026-10-01",
  "check_out": "2026-10-03",
  "nights": 2,
  "available": 3,
  "retail_total": "880.00",
  "channel_settle_price": "776.00",
  "daily_prices": [
    {"date": "2026-10-01", "price": "388.00", "available": 3},
    {"date": "2026-10-02", "price": "492.00", "available": 3}
  ],
  "extra_for_hold": {"check_in": "2026-10-01", "check_out": "2026-10-03"}
}

门票 source=ticket

模式 Query 返回要点
单日 product_id + booking_date(或 date) sku_id(日库存行,可直接 hold)、结算价、余量
日历 product_id + start + end daily_prices;其中 sku_id=0 表示尚未物化,不可直接 hold;选定日期后必须再调单日询价拿正式 sku_id

旅游 source=travel

模式 Query 返回要点
单日 product_id + sku_id + booking_date 当日结算价
日历 product_id + sku_id + start + end daily_prices

场地 source=venue

product_id + booking_date → slots[](含各时段 sku_id 与结算价)。

租车 source=carrent

product_id + rent_type(1 日 / 2 周 / 3 月)→ 单价结算价。

固定价

mall / rent / pointsmall 也可调,返回 mode=fixed 与结算价(与详情一致)。


4.3 商家额度 merchant_quota

GET .../merchant_quota.html

Query

参数 必填 说明
supplier_id 否 供应商 ID;0 或不传表示平台自营账户

成功 data

{
  "subject_type": "supplier",
  "subject_id": 12,
  "channel_bill_mode": 2,
  "channel_bill_mode_text": "credit",
  "prepaid_balance": "0.00",
  "credit_limit": "100000.00",
  "credit_used": "50000.00",
  "quota_available": "50000.00",
  "status": 1,
  "status_text": "normal",
  "warnings": [],
  "low_balance": false
}

status=0 表示账户冻结,不可成交。warnings 可能含「余额偏低」「额度将尽」等提示。


4.4 锁单 order_hold

POST .../order_hold.html
Content-Type: application/json

Body

{
  "out_order_no": "CH202609240001",
  "contact_name": "张三",
  "contact_phone": "13800138000",
  "address": {
    "province": "海南省",
    "city": "海口市",
    "district": "龙华区",
    "detail": "某某路1号"
  },
  "items": [
    {
      "source": "mall",
      "product_id": 1001,
      "sku_id": 2001,
      "quantity": 2
    }
  ],
  "remark": "渠道锁单"
}

酒店示例 item

{
  "source": "hotel",
  "product_id": 88,
  "sku_id": 0,
  "quantity": 1,
  "extra": {
    "check_in": "2026-10-01",
    "check_out": "2026-10-03",
    "guest_name": "张三"
  }
}

门票示例 item(sku_id 来自 price_quote 单日询价)

{
  "source": "ticket",
  "product_id": 15,
  "sku_id": 9001,
  "quantity": 2,
  "extra": {
    "booking_date": "2026-10-01"
  }
}

旅游示例 item

{
  "source": "travel",
  "product_id": 20,
  "sku_id": 301,
  "quantity": 2,
  "extra": {
    "booking_date": "2026-10-05",
    "guest_name": "张三",
    "guest_phone": "13800138000"
  }
}

场地示例 item

{
  "source": "venue",
  "product_id": 7,
  "sku_id": 5501,
  "quantity": 1,
  "extra": {
    "booking_date": "2026-10-02"
  }
}

租车示例 item

{
  "source": "carrent",
  "product_id": 3,
  "sku_id": 0,
  "quantity": 2,
  "extra": {
    "rent_type": 1,
    "customer_name": "张三",
    "customer_phone": "13800138000",
    "pickup_time": "2026-10-01 10:00:00",
    "expect_return_time": "2026-10-03 10:00:00"
  }
}

锁单 items[].extra 字段总表

source 必填 extra / 关键字段 建议 extra 说明
mall 无 — 有规格必传 sku_id
hotel check_in、check_out(YYYY-MM-DD) guest_name、guest_phone、guest_count quantity=间数;结算价=连住单价×间数
ticket sku_id=询价返回的日库存行 booking_date(建议与询价日一致) 日历接口里 sku_id=0 不可 hold
venue sku_id=询价 slots[].sku_id booking_date 时段物化行 ID,不是模板 slot_id
travel sku_id + booking_date guest_name、guest_phone 有日期价时必须带 booking_date
carrent 建议 rent_type(1 日/2 周/3 月) customer_name、customer_phone、customer_id_card、pickup_time、expect_return_time、pickup_store_id quantity=租期数
rent 无 use_plan、rent_type、start_at、store_id、customer_name、customer_phone 套餐租可设 use_plan=1
pointsmall 无 — 仅现金结算价>0 可售

另:顶层 contact_name / contact_phone 建议始终填写(写入渠道虚拟会员与取货联系人)。需要快递时传 address;纯核销类可不传。

可选顶层字段:distributor_id(绑定分销员;默认可用渠道应用上的绑定分销员)。

字段 必填 说明
items 是 商品行,同单须同一供应商;可混合同一供应商下不同 source(不推荐)
items[].source 是 来源编码
items[].product_id 是 见第 1.1 节
items[].sku_id 视品类 无规格传 0;门票/场地/旅游见上表
items[].quantity 是 数量;酒店=间数;租车=租期数
items[].extra 视品类 见上表
out_order_no 建议 贵方单号,防重复锁单
contact_name / contact_phone 建议 联系人
address 否 有则按快递地址写入;虚拟/核销类可不传

成功 data(节选)

{
  "order_id": 90001,
  "order_no": "C20260924153000001",
  "out_order_no": "CH202609240001",
  "status": 0,
  "status_text": "待支付",
  "retail_amount": "59.80",
  "channel_settle_amount": "36.00",
  "channel_bill_mode": 2,
  "hold_expire_at": "2026-09-24 16:00:00",
  "quota_available_after_hold": "49964.00",
  "items": [
    {
      "product_id": 1001,
      "sku_id": 2001,
      "quantity": 2,
      "price": "29.90",
      "channel_settle_price": "18.00",
      "channel_settle_amount": "36.00"
    }
  ]
}

请保存 order_no 与 channel_settle_amount,支付通知必须一致。hold 只校验额度、不扣额度;quota_available_after_hold 为校验后可用额度快照。

常见失败

  • 商家可用额度不足
  • 库存不足 / 未配置结算价
  • 多供应商混单
  • out_order_no 已存在(同渠道下待支付/已支付/已发货/已完成)
  • 开放渠道遇到授信商家:开放渠道仅支持预充值模式商家商品

4.5 支付通知 order_pay_notify

POST .../order_pay_notify.html

在贵方确认用户支付成功后调用。

{
  "order_no": "C20260924153000001",
  "out_order_no": "CH202609240001",
  "partner_trade_no": "P20260924WX998877",
  "paid_amount": "59.80",
  "channel_settle_amount": "36.00",
  "paid_at": "2026-09-24 15:35:10"
}
字段 必填 说明
order_no 或 out_order_no 二选一 定位订单
partner_trade_no 是 贵方支付流水号(幂等键)
channel_settle_amount 是 必须等于锁单返回值(两位小数字符串)
paid_amount 建议 贵方实收用户金额(仅快照)

成功 data(节选)

{
  "order_id": 90001,
  "order_no": "C20260924153000001",
  "status": 1,
  "status_text": "已支付",
  "channel_settle_amount": "36.00",
  "partner_trade_no": "P20260924WX998877",
  "payment_time": "2026-09-24 15:35:12",
  "hold_expire_at": "2026-09-24 16:00:00",
  "items": [ ],
  "ledger": {
    "change_type": "consume",
    "amount": "36.00",
    "quota_available": "49964.00"
  }
}
ledger.change_type 含义
consume 预充值扣减
credit_occupy 授信占用

重复通知:同一订单已支付时幂等返回成功(ledger 可能带历史扣款信息)。

失败且用户已付款时(重要)

若返回如「商家可用额度不足」,订单不会变成已支付。贵方必须:

  1. 立即退款给用户
  2. 联系平台给商家充值/提高授信后,可重新 hold 或协商处理

若履约回调失败,本站会尝试回滚已扣额度,订单保持待支付,可用新 partner_trade_no 重试或取消后重新 hold。

响应可能带:

{
  "code": 0,
  "info": "商家可用额度不足,请退款给用户后联系平台充值或回款",
  "data": { "suggest": "refund_user" }
}

4.6 订单查询 order_query

GET .../order_query.html

Query(三选一)

参数 说明
order_no 本站订单号
out_order_no 贵方订单号
partner_trade_no 贵方支付单号

成功 data 示例

{
  "order_id": 90001,
  "order_no": "C20260924153000001",
  "out_order_no": "CH202609240001",
  "partner_trade_no": "P20260924WX998877",
  "status": 1,
  "status_text": "已支付",
  "retail_amount": "59.80",
  "pay_amount": "59.80",
  "channel_settle_amount": "36.00",
  "channel_bill_mode": 1,
  "payment_type": "channel_partner",
  "payment_time": "2026-09-24 15:35:12",
  "hold_expire_at": "2026-09-24 16:00:00",
  "items": [
    {
      "source": "mall",
      "product_id": 1001,
      "sku_id": 2001,
      "title": "海南椰子糖",
      "quantity": 2,
      "price": "29.90",
      "channel_settle_price": "18.00",
      "channel_settle_amount": "36.00"
    }
  ]
}

订单状态 status

值 含义 渠道侧建议动作
0 待支付(锁单中) 可 pay_notify;超时后会变取消,需重新 hold
1 已支付 可对账;退款走 refund_notify
2 已发货 履约中
3 已完成 终态(仍可能支持退款视业务)
4 已退款 终态;refund 幂等
5 已取消 锁单超时或取消;不可再 pay
6 退款申请中 处理中

本查询不返回核销码、物流轨迹等履约明细;如需展示请联系平台另议或走运营后台。
详情/列表也不提供富文本 content / 多图相册;当前开放接口以交易字段为主。


4.7 退款通知 order_refund_notify

POST .../order_refund_notify.html

请先完成对用户的退款,再调本接口。

{
  "order_no": "C20260924153000001",
  "partner_refund_no": "R20260924WX001",
  "refund_settle_amount": "36.00",
  "refund_retail_amount": "59.80",
  "reason": "用户取消",
  "refunded_at": "2026-09-24 18:00:00"
}
字段 必填 说明
order_no 或 out_order_no 二选一 定位订单
partner_refund_no 是 贵方退款单号(幂等)
refund_settle_amount 否 默认按整单结算价回滚;不可大于锁单结算价

成功 data(节选)

{
  "order_id": 90001,
  "order_no": "C20260924153000001",
  "status": 4,
  "status_text": "已退款",
  "channel_settle_amount": "36.00",
  "ledger": {
    "change_type": "refund_prepaid",
    "amount": "36.00",
    "quota_available": "50000.00"
  }
}
ledger.change_type 含义
refund_prepaid 预充值退回
refund_credit 授信占用释放

(具体文案以接口返回为准;同一 partner_refund_no 幂等。)

本站会:回滚商家额度、恢复库存、订单置已退款。
不会给用户打款(用户款由贵方处理)。


4.8 幂等与状态机

hold(status=0) ──超时──→ 取消(5)  (须重新 hold)
       │
       └── pay_notify 成功 ──→ 已支付(1) → 发货(2) → 完成(3)
                │                     │
                │ 失败(额度不足等)    └── refund_notify ──→ 已退款(4)
                └── 订单仍为 0;用户已付款则贵方立即退用户
场景 行为
同一 out_order_no 再次 hold 若已有有效单 → 报「已存在」
同一 partner_trade_no 重复 pay 已支付则幂等成功
锁单过期后再 pay 失败「锁单已过期,请重新 hold」
已取消/已退款再 pay 失败「订单状态不可支付」
同一 partner_refund_no 重复退 已退款则幂等成功
pay 履约失败 额度回滚,订单保持待支付,可换 trade_no 重试

4.9 常见错误 info(节选)

info(或包含) 阶段 处理建议
缺少渠道鉴权参数 鉴权 检查 Header
渠道应用无效或已禁用 鉴权 联系平台开通
请求已过期,请校准时间戳 鉴权 NTP;核对 notify_tol
IP 不在白名单 鉴权 加白或去掉白名单
签名校验失败 鉴权 核对 body 原文/GET 排序/空 body
商家可用额度不足 hold/pay 充值或授信;pay 失败则退用户
商家渠道账户已冻结 hold/pay 联系平台解冻
开放渠道仅支持预充值模式商家商品 hold 换预充值商家,或改渠道类型
单笔渠道单暂不支持多个供应商 hold 拆单
out_order_no 已存在 hold 改单号或 order_query
channel_settle_amount 与锁单不一致 pay 用 hold 返回值,勿重算
锁单已过期,请重新 hold pay 重新 hold → 收款 → notify
partner_trade_no / partner_refund_no 必填 pay/refund 补流水号
订单不存在 查/付/退 核对租户与渠道 App
退款结算金额不能超过锁单结算价 refund 调低金额

失败统一:{ "code": 0, "info": "…", "data": … }。data.suggest=refund_user 时表示用户侧应退款。


5. 联调检查清单(给开发)

  1. 先调 sources,只对接 registered=true 的品类
  2. 确认本渠道是「签约」还是「开放」;开放渠道勿选授信商家商品
  3. 日期类商品:必须先 price_quote 再 order_hold;门票日历里 sku_id=0 不可 hold
  4. 签名:用同一份 POST JSON 算签并发送;sources 等无参 GET 的 body 为空串
  5. GET 参数按 key 字典序拼串再签
  6. 时钟同步(NTP),避免 timestamp 过期
  7. 金额一律两位小数字符串;pay_notify.channel_settle_amount 必须等于 hold 返回值
  8. hold → 再收款 → pay_notify;不要未 hold 就 notify
  9. pay_notify 失败且用户已付:立即退用户;见 data.suggest=refund_user
  10. partner_trade_no / partner_refund_no 全局唯一;用 order_query 对账
  11. Secret 只放服务端,勿进小程序/前端包
  12. 本 API 不返回富文本详情与核销码/物流;展示页字段以交易字段为准

6. 责任边界(合同建议引用)

事项 责任方
对用户收款、退款、客诉(支付相关) 渠道
商品信息、库存、履约、核销 平台
商家预充值/授信额度 平台 + 商家
AppSecret 保管 渠道(泄露须立即联系平台重置)

7. 联系与变更

  • 密钥重置、额度调整、IP 白名单:联系平台运营后台处理
  • 接口变更以本文档版本号为准;重大变更会另行通知