# H5 积分商城接口文档（含流程说明）

> 面向：H5（活动页/积分商城前台）调用  
> 基础路径：`/api/v1`  
> 返回结构：BuildAdmin 通用 `code/msg/time/data`（成功 `code=1`）

---

## 1. 总体流程说明

### 1.1 流程 A：H5 临时登录（推荐）

适用场景：H5 只需要“用户名级别”的轻量登录，不依赖 playX 的 token。

1. H5 调用 `GET/POST /api/v1/temLogin?username=xxx` 获取 **商城 token**（类型 `muser`）
2. H5 后续请求统一携带该 token（推荐放在 Header：`token: <muser_token>`，也可用参数 `token`）
3. H5 调用：
   - `GET /api/v1/mall/assets` 获取资产
   - `POST /api/v1/mall/claim` 领取积分（幂等）
   - `GET /api/v1/mall/items` 获取商品
   - `POST /api/v1/mall/bonusRedeem` / `physicalRedeem` / `withdrawApply` 提交兑换/提现
   - `GET /api/v1/mall/orders` 查询订单列表
   - `GET /api/v1/mall/order` 按订单 ID 查询单笔订单（含状态）
   - `GET/POST /api/v1/mall/address*` 管理地址（addressList/addressAdd/addressEdit/addressDelete）

### 1.2 流程 B：playX token 换取 session（兼容）

适用场景：H5 已经拿到了 playX 下发的 token，希望换取商城侧 `session_id`。

1. H5 调用 `POST /api/v1/mall/verifyToken`（传 `token` 或 `session`）
2. 服务端返回 `data.session_id`
3. H5 后续请求携带 `session_id`（优先级高于 token）

---

## 2. 身份与鉴权（重要）

以下接口通过服务端解析「当前资产主体」，**仅**支持：

1. **`session_id`**（GET/POST）：对应表 `mall_session`，未过期则可映射到资产主体  
2. **`token`**（GET/POST 或 Header `token` / `ba-token`）：会员 token 或 H5 临时登录签发的 **`muser`** token  

不再支持单独传 `user_id` 作为鉴权参数（避免越权）。

推荐做法：
- H5 统一使用 **Header `token`**（`muser` 或 playX 换 session 后的凭证），避免 URL 泄露。

---

## 3. 接口列表（H5 常用）

### 3.1 H5 临时登录

**GET/POST** ` /api/v1/temLogin `

参数：
- `username`：必填，用户名（字符串）

成功返回 `data.userInfo`：
- `id`：资产主键（`mall_user_asset.id`）
- `username`：用户名
- `playx_user_id`：映射的 playX 用户标识（字符串）
- `token`：**muser token**（后续请求使用）
- `refresh_token`：刷新 token（当前前端未强依赖可不接）
- `expires_in`：秒

示例：

```bash
curl "https://{域名}/api/v1/temLogin?username=test001"
```

---

### 3.2 资产查询

**GET** ` /api/v1/mall/assets `

鉴权：携带 `token` 或 `session_id`

成功返回 `data`：
- `locked_points`：待领取积分
- `available_points`：可用积分
- `today_limit`：今日可领取上限
- `today_claimed`：今日已领取
- `withdrawable_cash`：可提现现金（积分按配置比例换算）

---

### 3.3 领取积分（幂等）

**POST** ` /api/v1/mall/claim `

参数：
- `claim_request_id`：必填，幂等键（建议：`{业务前缀}_{assetId}_{毫秒时间戳}`）
- 身份参数：`token` / `session_id`（推荐 Header `token`）

说明：
- 同一个 `claim_request_id` 重复提交会直接返回成功（不会重复入账）
- 会受 `today_limit/today_claimed/locked_points` 限制

---

### 3.4 商品列表

**GET** ` /api/v1/mall/items `

参数：
- `type`：可选，`BONUS | PHYSICAL | WITHDRAW`

---

### 3.5 红利兑换（提交订单）

**POST** ` /api/v1/mall/bonusRedeem `

参数：
- `item_id`：必填
- 身份参数：`token` / `session_id`

返回：
- `data.order_id`
- `data.status`（通常 `PENDING`）

---

### 3.6 实物兑换（提交订单）

**POST** ` /api/v1/mall/physicalRedeem `

参数：
- `item_id`：必填
- `address_id`：必填，收货地址主键（`mall_address.id`，须为当前用户资产下地址）
- 身份参数：`token` / `session_id`

说明：服务端会将该地址在下单时刻的 **收货人 / 电话 / 完整地址** 写入订单字段（快照），并写入 `mall_order.mall_address_id` 关联所选地址。

---

### 3.7 提现申请（提交订单）

**POST** ` /api/v1/mall/withdrawApply `

参数：
- `item_id`：必填
- 身份参数：`token` / `session_id`

---

### 3.8 订单列表

**GET** ` /api/v1/mall/orders `

鉴权：`token` / `session_id`

说明：
- 返回最多 100 条
- 订单里包含 `mallItem`（商品信息）

---

### 3.8.1 单笔订单查询（按 ID）

**GET** ` /api/v1/mall/order `

鉴权：`token` / `session_id`（同订单列表）

参数（Query）：
- `order_id`：必填，商城订单主键 `mall_order.id`（兼容参数名 `id`）

说明：
- 仅能查询当前鉴权用户本人名下的订单；订单不存在或不属于当前用户时返回「记录不存在」，不泄露他人订单信息。

返回（成功 `data`）：
- `order_id`：订单 ID
- `status`：订单状态（`PENDING` / `COMPLETED` / `SHIPPED` / `REJECTED`）
- `order`：订单详情（字段与订单列表单条一致，含 `reject_reason`、`grant_status`、`mallItem` 等）

示例：

```bash
curl -G "https://{域名}/api/v1/mall/order" \
  -H "token: <muser_token>" \
  --data-urlencode "order_id=123"
```

---

### 3.9 积分流水（领取/兑换/退回）

**GET** ` /api/v1/mall/pointsLogs `

鉴权：携带 `token` 或 `session_id`

参数（Query）：
- `limit`：可选，每页条数（默认 20，最大 100）
- `cursor`：可选，游标（上一页响应返回的 `next_cursor`，传入后获取下一页）
- `direction`：可选，`IN`（入账）/`OUT`（扣减），不传返回全部

返回（成功 `data`）：
- `list[]`：流水数组（按时间倒序）
  - `biz_type`：`CLAIM`（领取入账）/ `REDEEM_BONUS|REDEEM_PHYSICAL|REDEEM_WITHDRAW`（兑换扣分）/ `REFUND`（订单驳回或终态失败退回）
  - `direction`：`IN` / `OUT`
  - `points`：积分变动值（正数，方向由 `direction` 表示）
  - `ts`：时间戳（Unix 秒）
  - `ref_id`：关联业务号（领取为 `claim_request_id`；订单为 `external_transaction_id`）
  - `order_no`：订单号（非订单类为空）
  - `order_status`：订单状态（非订单类为空）
  - `mallItem`：商品信息（非订单类为 null）
    - `id`：商品ID
    - `title`：商品名
    - `type`：商品类型（`1=BONUS 2=PHYSICAL 3=WITHDRAW`）
    - `score`：所需积分
    - `amount`：现金面值（红利/提现档位）
    - `multiplier`：流水倍数
    - `category`：红利业务类别
    - `category_title`：类别展示名
  - `item_id/item_title/item_type/item_score`：兼容字段（建议优先使用 `mallItem`）
  - `cursor`：当前记录游标（用于分页）
- `next_cursor`：下一页游标（为本页最后一条的 `cursor`；若本页为空则为 null）

示例：

```bash
curl -G "https://{域名}/api/v1/mall/pointsLogs" \
  -H "token: <muser_token>" \
  --data-urlencode "limit=20"
```

## 4. 地址管理（H5）

> 地址与资产主体通过 `playx_user_asset_id` 关联（即 `mall_user_asset.id`）。

### 4.1 地址列表
**GET** ` /api/v1/mall/addressList `

### 4.2 新增地址
**POST** ` /api/v1/mall/addressAdd `

Body 含 `receiver_name`、`phone`、`detail_address`（完整收货地址，必填）；实物兑换下单快照使用上述字段。

### 4.3 编辑地址
**POST** ` /api/v1/mall/addressEdit `

### 4.4 删除地址
**POST** ` /api/v1/mall/addressDelete `

---

## 5. session 换取（可选）

### 5.1 token 换 session

**POST** ` /api/v1/mall/verifyToken `

参数（二选一）：
- `token`
- `session`

可选 **`lang`**：`zh` / `ZH` / `zh-cn` 返回中文 **`msg`**（默认中文），`en` / `EN` 返回英文；可通过请求头、Query 或与本接口 Body 同传的表单字段传入。

建议 **`Content-Type: application/json`**，Body 示例：`{"token":"..."}`。使用 `multipart/form-data` 时同样只传 `token`（及可选 `lang`）即可。

成功返回：
- `data.session_id`
- `data.user_id`
- `data.username`
- `data.token_expire_at`

**本地联调（不请求 PlayX）**：在 `.env` 中设置 `PLAYX_VERIFY_TOKEN_LOCAL_ONLY=true`，并配置：

- `PLAYX_VERIFY_TOKEN_LOCAL_DEFAULT_USER_ID`：写入 `mall_session.user_id` / 资产 `playx_user_id`
- `PLAYX_VERIFY_TOKEN_LOCAL_DEFAULT_USERNAME`：写入 `mall_session.username` 与资产展示名

此时任意（或空）`token` 均视为验证通过，返回的 `user_id` / `username` 以上述环境变量为准。生产环境请保持 `PLAYX_VERIFY_TOKEN_LOCAL_ONLY=false` 并配置 `PLAYX_TOKEN_VERIFY_URL`。

---

## 6. 常见错误与排查

- **401 登录态过期**：token/session 过期或不匹配；请重新 `temLogin` 或重新 `verifyToken`
- **提示缺少必填字段**：按各接口参数补齐（如 `claim_request_id`、`item_id`、`address_id`（实物）、地址中收货人/电话/完整地址等）
- **积分不足/无可领取积分**：`locked_points<=0` 或已达 `today_limit`

