# 中国道开放平台 · H5 单产品外放接口详细接入文档

版本：v1.2（2026-07-20）  
适用环境：正式环境  
基础域名：`https://lukeydao.com`

---

## 1. 接口说明

H5 单产品外放接口用于商户服务端按中国道 OpenAPI 签名规则获取某个 H5 产品的服务访问地址。

典型产品：话费充值 H5、水电燃 H5、美团外卖 H5、叮咚买菜 H5 等。

标准链路：

```text
商户服务端签名请求 /openapi/v1/h5/entry
→ 中国道校验 AppID/AppSecret、商户状态、产品授权
→ 中国道请求服务通道并返回 h5Url
→ 商户前端/APP/WebView 跳转 h5Url
→ 用户在 H5 页面内下单
→ 服务通道通过 createUrl 回调中国道创建 WAIT_PAY 订单
→ 商户服务端确认用户支付成功后，签名请求 /openapi/v1/h5/payment/notify
→ 中国道校验订单归属、锁价金额、quoteId 与幂等状态
→ 中国道按订单冻结配置完成资金处理并触发服务通道履约
→ 中国道将最终订单结果异步通知商户 notifyUrl
```

注意：`/h5/entry` 只返回 H5 访问地址，不代表订单已创建，也不代表支付成功。`/h5/payment/notify` 只能由商户服务端在确认用户支付成功后调用，严禁由浏览器、App、小程序或 H5 页面直接调用。

---

## 2. 接口一：获取 H5 访问地址

### 2.1 请求地址

```http
POST https://lukeydao.com/prod-api/openapi/v1/h5/entry
Content-Type: application/json
```

签名时使用的 `URI_PATH` 必须是不带 `/prod-api` 的后端路径：

```text
/openapi/v1/h5/entry
```

### 2.2 请求 Header

| Header | 必填 | 类型 | 示例 | 说明 |
|---|---:|---|---|---|
| `app_id` | 是 | string | `app_xxxxxxxxx` | 中国道开放平台应用 ID |
| `timestamp` | 是 | string | `1783510000` | 秒级 Unix 时间戳；允许约 5 分钟误差 |
| `nonce` | 是 | string | `9f3a7c21b8e64d02` | 随机字符串；5 分钟内不可重复 |
| `sign` | 是 | string | `AbCd...` | HMAC-SHA256 签名 |
| `Content-Type` | 是 | string | `application/json` | 请求体必须是 JSON |

### 2.3 请求 Body 字段

| 字段 | 必填 | 类型 | 示例 | 说明 |
|---|---:|---|---|---|
| `typeCode` | 是 | string | `Weg` | 中国道产品编码，由平台提供 |
| `interfaceTypeCode` | 是 | string | `weg_interface` | 中国道接口产品编码，由平台提供 |
| `deliveryMode` | 否 | string | `h5_page` | 建议固定传 `h5_page` |
| `userId` | 是 | string | `merchant-user-001` | 商户侧用户唯一标识；建议会员 ID，不要随机变 |
| `phone` | 是 | string | `13800138000` | 用户手机号；多数 H5 产品需要 |
| `callbackUrl` | 否 | string | `https://merchant.example.com/return` | 用户流程完成后的业务返回地址；具体是否使用取决于产品 |
| `notifyUrl` | 否 | string | `https://merchant.example.com/notify` | 商户接收订单结果通知地址；若开放平台后台已配置默认地址，可不传 |
| `outRequestNo` | 否 | string | `REQ202607080001` | 商户请求号；不传则中国道自动生成。建议商户传入并保存，便于 `/h5/query` 查询 |

### 2.4 请求示例

```json
{
  "typeCode": "Weg",
  "interfaceTypeCode": "weg_interface",
  "deliveryMode": "h5_page",
  "userId": "merchant-user-001",
  "phone": "13800138000",
  "callbackUrl": "https://merchant.example.com/return",
  "notifyUrl": "https://merchant.example.com/notify",
  "outRequestNo": "REQ202607080001"
}
```

### 2.5 成功响应

```json
{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "outRequestNo": "REQ202607080001",
    "merchantRequestNo": "REQ202607080001",
    "platformRequestNo": "H5E202607081901408796",
    "typeCode": "Weg",
    "interfaceTypeCode": "weg_interface",
    "deliveryMode": "h5_page",
    "h5Url": "https://service.example.com/entry?...",
    "url": "https://service.example.com/entry?...",
    "createUrl": "https://lukeydao.com/prod-api/api/v1/order/create?...",
    "orderCreated": false,
    "expireTime": "2026-07-08 21:01:40"
  }
}
```

### 2.6 响应字段说明

| 字段 | 说明 |
|---|---|
| `h5Url` / `url` | 用户需要跳转访问的 H5 服务地址 |
| `outRequestNo` | 商户请求号；商户传入则原样返回 |
| `merchantRequestNo` | 商户请求号兼容字段 |
| `platformRequestNo` | 中国道平台请求号 |
| `createUrl` | 中国道回调建单地址，通常已由中国道传给服务通道；商户一般不需要处理 |
| `orderCreated` | 是否已经创建中国道本地订单；刚获取 H5 地址通常是 `false` |
| `expireTime` | H5 访问地址建议有效时间 |

---

## 3. 接口二：查询 H5 后续订单状态

### 3.1 请求地址

```http
POST https://lukeydao.com/prod-api/openapi/v1/h5/query
Content-Type: application/json
```

签名时使用的 `URI_PATH`：

```text
/openapi/v1/h5/query
```

### 3.2 请求 Header

同 `/openapi/v1/h5/entry`。

### 3.3 请求 Body

按商户请求号查询：

```json
{
  "outRequestNo": "REQ202607080001"
}
```

或按中国道订单号查询：

```json
{
  "orderNo": "H520260708xxxxxx"
}
```

### 3.4 尚未创建订单响应

```json
{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "outRequestNo": "REQ202607080001",
    "orderCreated": false
  }
}
```

`orderCreated=false` 表示用户尚未在 H5 页面内完成下单回调，这是正常状态。

### 3.5 已创建订单响应

```json
{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "outRequestNo": "REQ202607080001",
    "orderCreated": true,
    "orderNo": "H520260708xxxxxx",
    "typeCode": "Weg",
    "orderStatus": "WAIT_PAY",
    "upstreamOrderNo": "xxx",
    "upstreamStatus": "xxx",
    "payAmount": 10.00,
    "totalAmount": 10.00,
    "createTime": "2026-07-08T20:00:00",
    "cashierUrl": "https://merchant.example.com/cashier?..."
  }
}
```

---

## 4. 接口三：下游支付成功通知

### 4.1 使用场景

用户在商户自有收银台支付成功后，由**商户服务端**通知中国道。中国道收到并校验成功后，复用统一支付确认链路处理商户资金、订单状态和服务通道履约。

- `callbackUrl`：用户浏览器同步回跳地址，不是支付通知接口。
- `notifyUrl`：中国道向商户推送最终订单结果的服务端地址，不是本接口地址。
- 本接口：商户服务端向中国道上报“用户已支付成功”。

### 4.2 请求地址

```http
POST https://lukeydao.com/prod-api/openapi/v1/h5/payment/notify
Content-Type: application/json
```

签名时使用的 `URI_PATH`：

```text
/openapi/v1/h5/payment/notify
```

### 4.3 请求 Header

与 `/openapi/v1/h5/entry` 相同：必须携带 `app_id`、`timestamp`、`nonce`、`sign` 和 `Content-Type: application/json`。

平台还会校验开放应用状态、应用所属商户、IP 白名单、时间窗、nonce 防重放及调用频率。IP 白名单配置的是**商户服务端真实出口 IP**。

### 4.4 请求 Body

| 字段 | 必填 | 类型 | 示例 | 说明 |
|---|---:|---|---|---|
| `orderNo` | 是 | string | `H520260711xxxxxx` | 中国道订单号；订单必须属于当前 `app_id` 绑定的商户 |
| `amount` | 是 | decimal | `26.40` | 用户实际支付金额；也兼容字段名 `payAmount`、`userPayAmount`、`totalAmount`，建议统一传 `amount` |
| `quoteId` | 条件可选 | string | `quote_xxx` | 仅特殊锁价订单使用：如果订单创建时已生成并保存 `quoteId`，须原样回传；普通 H5 订单不传。`/h5/entry`、`/h5/query` 不返回该字段，下游不得自行生成或猜测 |

禁止传入或依赖 `merchantId`、`testMode`、`mock`、`testResult` 等字段切换商户或测试模式。商户归属来自已验签的开放应用；测试模式只认订单创建时冻结的数据。

普通 H5 订单请求示例（标准用法）：

```json
{
  "orderNo": "H520260711xxxxxx",
  "amount": 26.40
}
```

特殊锁价订单：仅当建单流程已经返回并保存 `quoteId` 时，在上述 Body 中额外原样传入 `"quoteId": "quote_xxx"`。

### 4.5 签名示例

签名原文仍为：

```text
POST\n/openapi/v1/h5/payment/notify\n{timestamp}\n{nonce}\n{原始JSON Body}
```

签名算法与第 5 节一致。签名路径不带 `/prod-api`，参与签名的 JSON 字符串必须与实际发送的 Body 完全一致。

### 4.6 成功与幂等响应

首次受理成功时，响应 `code=200`，`data` 返回订单号及支付/履约处理结果。平台随后按产品链路推进：

```text
WAIT_PAY → PAID → PROCESSING → SUCCESS
```

如服务通道暂时失败，平台按订单和补偿规则处理，不得由商户重复扣款或自行修改中国道订单状态。

同一 `app_id + orderNo` 重复通知不会重复扣款、重复冻结或重复履约；平台返回成功并标记幂等，例如：

```json
{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "orderNo": "H520260711xxxxxx",
    "orderStatus": "SUCCESS",
    "idempotent": true,
    "message": "重复支付通知已忽略"
  }
}
```

### 4.7 失败处理

| 错误 | 原因 | 商户处理 |
|---|---|---|
| `缺少订单号 orderNo` | Body 未传订单号 | 补齐中国道订单号 |
| `订单不存在` | 订单不存在或不属于当前开放应用商户 | 核对 AppID 与订单归属，禁止跨商户重试 |
| `支付金额异常` | 金额为空、格式错误或不大于 0 | 使用支付系统确认的实际金额 |
| `支付金额与订单锁价不一致` | 通知金额与中国道订单冻结金额不一致 | 停止重试并人工核对，禁止篡改金额 |
| `quoteId与订单不一致` | 锁价订单缺少或传错 quoteId | 使用建单时保存的 quoteId |
| `订单状态不允许支付确认` | 订单已关闭或状态不是待支付 | 不得补扣历史/关闭订单，查询订单后人工处理 |

只有收到 `code=200` 才表示中国道已受理本次支付通知；HTTP 送达、浏览器回跳或商户本地支付成功都不能代替本接口成功响应。

---

## 5. 签名规则

### 5.1 签名明文

```text
METHOD + "\n" + URI_PATH + "\n" + timestamp + "\n" + nonce + "\n" + body
```

字段说明：

| 项 | 示例 | 说明 |
|---|---|---|
| `METHOD` | `POST` | HTTP 方法大写 |
| `URI_PATH` | `/openapi/v1/h5/entry` | 后端路径，不包含域名，不包含 `/prod-api` |
| `timestamp` | `1783510000` | 与 Header 完全一致 |
| `nonce` | `9f3a7c21b8e64d02` | 与 Header 完全一致 |
| `body` | `{"typeCode":"Weg",...}` | HTTP 请求体原始 JSON 字符串 |

### 5.2 签名算法

```text
HmacSHA256(appSecret, 签名明文)
→ URL-safe Base64
→ 去掉末尾 padding 等号
```

### 5.3 重要注意事项

1. `body` 参与签名，发送给服务器的 JSON 字符串必须与签名时的字符串完全一致。
2. 签名路径不要带 `/prod-api`。例如公网请求 `/prod-api/openapi/v1/h5/payment/notify` 的签名路径是 `/openapi/v1/h5/payment/notify`。
3. `timestamp` 是秒级时间戳，不是毫秒。
4. `nonce` 5 分钟内不能重复，否则会触发防重放。
5. `appSecret` 只能保存在商户服务端，不能放在前端、App、小程序、H5 页面中。

---

## 5. Node.js 签名示例

```js
const crypto = require('crypto');

function sign(appSecret, method, uriPath, timestamp, nonce, bodyObject) {
  const body = JSON.stringify(bodyObject);
  const content = [method.toUpperCase(), uriPath, timestamp, nonce, body].join('\n');
  return crypto.createHmac('sha256', appSecret).update(content).digest('base64url');
}

const appId = 'app_xxxxxxxxx';
const appSecret = '你的AppSecret';
const body = {
  typeCode: 'Weg',
  interfaceTypeCode: 'weg_interface',
  deliveryMode: 'h5_page',
  userId: 'merchant-user-001',
  phone: '13800138000',
  outRequestNo: 'REQ202607080001'
};
const timestamp = Math.floor(Date.now() / 1000).toString();
const nonce = crypto.randomBytes(8).toString('hex');
const signValue = sign(appSecret, 'POST', '/openapi/v1/h5/entry', timestamp, nonce, body);
console.log({ app_id: appId, timestamp, nonce, sign: signValue, body });
```

---

## 6. Java 签名示例

```java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class OpenApiSign {
    public static String sign(String appSecret, String method, String uriPath,
                              String timestamp, String nonce, String body) throws Exception {
        String content = method.toUpperCase() + "\n" + uriPath + "\n" + timestamp + "\n" + nonce + "\n" + body;
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(appSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
        byte[] raw = mac.doFinal(content.getBytes(StandardCharsets.UTF_8));
        return Base64.getUrlEncoder().withoutPadding().encodeToString(raw);
    }
}
```

---

## 7. PHP 签名示例

```php
<?php
function openapi_sign($appSecret, $method, $uriPath, $timestamp, $nonce, $body) {
    $content = strtoupper($method) . "\n" . $uriPath . "\n" . $timestamp . "\n" . $nonce . "\n" . $body;
    $raw = hash_hmac('sha256', $content, $appSecret, true);
    return rtrim(strtr(base64_encode($raw), '+/', '-_'), '=');
}
?>
```

---

## 8. curl 调用示例

```bash
curl -X POST 'https://lukeydao.com/prod-api/openapi/v1/h5/entry' \
  -H 'Content-Type: application/json' \
  -H 'app_id: app_xxxxxxxxx' \
  -H 'timestamp: 1783510000' \
  -H 'nonce: 9f3a7c21b8e64d02' \
  -H 'sign: 由服务端生成的签名' \
  -d '{"typeCode":"Weg","interfaceTypeCode":"weg_interface","deliveryMode":"h5_page","userId":"merchant-user-001","phone":"13800138000","outRequestNo":"REQ202607080001"}'
```

---

## 9. 当前可申请的 H5 产品编码

具体商户可用产品以平台实际授权为准。`typeCode` 与 `interfaceTypeCode` 必须成对使用，不得替换为同业务的 API 产品编码。

| 产品 | `typeCode` | `interfaceTypeCode` | 说明 |
|---|---|---|---|
| 打车 H5 | `Taxi` | `taxi_interface` | H5 服务访问入口 |
| 叮咚买菜 H5 | `MarketDD` | `ding_dong_interface` | H5 服务访问入口 |
| 图书 H5 | `Book` | `book_v2_interface` | H5 服务访问入口；实际开放以平台授权和服务通道状态为准 |
| 话费充值 H5 | `PrepaidRefill` | `directcharge_interface` | H5 服务访问入口 |
| 水电燃 H5 | `Weg` | `weg_interface` | H5 服务访问入口 |
| 美团外卖 H5 | `MeituanWaimai` | `mt_waimai_interface` | H5 服务访问入口 |
| 美团团购 H5 | `MeituanTuan` | `mt_canyin_interface` | H5 服务访问入口 |
| 小象超市 H5 | `FreshMart` | `mt_maicai_interface` | H5 服务访问入口 |
| 电影票 H5 | `MovieThree` | `movie_three_interface` | H5 服务访问入口 |
| 飞机票 H5 | `AirTicketH5` | `domestic_air_tickets_h5_interface` | 独立 H5 产品编码 |
| 火车票 H5 | `TrainH5` | `train_h5_interface` | 独立 H5 产品编码 |
| 京东 H5 | `MarketJDH5` | `jd_mall_h5_interface` | 独立 H5 产品编码，不得使用京东 API 编码代替 |
| 酒店 H5 | `HotelH5` | `hotel_h5_interface` | 独立 H5 产品编码 |

获取 `h5Url` 只证明入口地址生成成功。完整商用链路还须分别验收服务通道回调 `createUrl` 创建 `WAIT_PAY`、商户收银台、`/h5/payment/notify`、履约终态、异步通知及售后。

---

## 10. 常见错误

| code/msg | 原因 | 处理方式 |
|---|---|---|
| `401 缺少必要参数: app_id, timestamp, nonce, sign` | Header 不完整 | 补齐签名 Header |
| `401 签名验证失败` | 签名明文、路径、body 或 appSecret 不一致 | 检查 URI_PATH 是否去掉 `/prod-api`，body 是否与发送内容完全一致 |
| `400 timestamp已过期` | 时间戳超过允许窗口 | 使用服务器当前秒级时间戳 |
| `400 nonce已使用` | nonce 重复 | 每次请求生成新的 nonce |
| `未授权该产品` | 商户未开通对应产品 | 联系平台开通授权 |
| `orderCreated=false` | 用户尚未在 H5 页面内完成下单回调 | 正常状态，可稍后查询 |
| `订单状态不允许支付确认` | 订单已经关闭、成功或不处于待支付状态 | 不得补扣；查询订单并人工核对 |
| `支付金额与订单锁价不一致` | 支付通知金额与冻结金额不同 | 停止重试并核对支付单和中国道订单 |
| `quoteId与订单不一致` | 锁价订单通知缺少或传错 quoteId | 使用建单时保存的 quoteId |

---

## 12. 中国道向商户发送最终结果通知

订单状态变化后，中国道向商户配置的 `notifyUrl` 发送原始 JSON Body。常用 Header：

```http
X-LYP-Event: ORDER_SUCCESS
X-LYP-Sign: {小写十六进制签名}
Content-Type: application/json
```

验签规则：

```text
X-LYP-Sign = hex_lowercase(HMAC-SHA256(SDK apiSecret, 原始 JSON Body))
```

此出站签名使用商户 SDK `apiSecret`；不要使用开放应用 `appSecret`。商户必须按 `orderNo + event` 幂等处理，重复通知不得重复发货或重复退款。中国道会记录通知地址、事件、完整 payload、签名、发送状态、次数、响应和错误，并对失败通知执行重试。

`callbackUrl` 仅用于浏览器同步回跳；商户后端订单终态必须以中国道异步通知和订单查询结果为准。

---

## 13. 商户技术接入检查清单

- [ ] 已在商户后台“开放平台”生成 AppID / AppSecret。
- [ ] AppSecret 只保存在服务端。
- [ ] 已确认商户开通的 `typeCode/interfaceTypeCode`。
- [ ] 服务端能生成 HmacSHA256 URL-safe Base64 签名。
- [ ] 签名路径使用 `/openapi/v1/h5/entry`，不带 `/prod-api`。
- [ ] 请求 body 字符串与签名 body 完全一致。
- [ ] 前端只跳转 `h5Url`，不参与签名。
- [ ] 保存 `outRequestNo/platformRequestNo`，便于后续查询。
- [ ] 使用 `/openapi/v1/h5/query` 查询是否已生成中国道订单。
- [ ] 用户在商户侧支付成功后，由商户服务端调用 `/openapi/v1/h5/payment/notify`。
- [ ] 支付通知金额与中国道订单锁价一致；锁价订单同时传入原 `quoteId`。
- [ ] 支付通知使用新的 nonce，并按 `app_id + orderNo` 做幂等，不重复扣款。
- [ ] `notifyUrl` 可从公网接收中国道通知，并使用 SDK `apiSecret` 校验 `X-LYP-Sign`。
- [ ] 按中国道订单状态和异步通知做幂等处理。
