Files
webman-buildadmin-mall/docs/PlayX-调用积分商城接口说明.md
zhenhui ccdb58ea1d 1.优化相关文档
2.重新设置Token验证接口
2026-04-30 15:18:06 +08:00

644 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# playX 调用积分商城接口说明
本文档描述 **playX 平台(或 playX 侧脚本/服务)如何调用积分商城已开放的 HTTP 接口**:基础约定、推荐流程、鉴权方式、请求参数与返回结构。
实现依据:`config/route.php``app/api/controller/v1/Playx.php``config/playx.php`
---
## 1. 基础约定
### 1.1 Base URL
将下列路径拼在积分商城对外域名之后,例如:
`https://{商城域名}/api/v1/mall/dailyPush`
(联调时请向商城方索取正式环境与测试环境地址。)
### 1.2 通用响应结构JSON
所有接口成功或失败,响应体均为:
| 字段 | 类型 | 说明 |
|------|------|------|
| `code` | int | `1` 表示业务成功;`0` 表示业务失败 |
| `msg` | string | 提示信息(失败时为错误原因) |
| `time` | int | Unix 时间戳(秒) |
| `data` | object/array/null | 业务数据;失败时可能为 `null` |
部分错误场景会通过 HTTP 状态码区分(如 **401**),此时 `code` 仍为 `0`,请同时判断 HTTP 状态与 `code`
**成功示例:**
```json
{
"code": 1,
"msg": "",
"time": 1730000000,
"data": { }
}
```
**失败示例:**
```json
{
"code": 0,
"msg": "错误原因",
"time": 1730000000,
"data": null
}
```
### 1.3 Content-Type
- 本文档中 **POST** 且带 JSON Body 的接口,请使用:`Content-Type: application/json`
### 1.4 多语言(响应文案)
可通过请求头 `lang` 控制返回文案语言:
| Header | 值 | 说明 |
|--------|----|------|
| `lang` | `zh` / `zh-cn` | 返回中文(默认) |
| `lang` | `en` | 返回英文 |
---
## 2. 使用流程(推荐)
### 2.1 playX 服务端 → 商城:每日数据推送(主流程)
适用于 T+1 等业务数据由 **playX 服务端**主动推送到积分商城。
```mermaid
sequenceDiagram
participant PX as playX 服务端
participant M as 积分商城
Note over PX,M: 按约定配置 HMAC 密钥
PX->>M: POST /api/v1/mall/dailyPushJSON Body
M-->>PX: code=1, data.accepted / deduped
```
1. 与商城方约定 **商城 Base URL****HMAC**`X-Signature` 等)密钥。
2.**§3** 构造请求并推送。
3. 根据返回 `data.deduped` 判断是否为幂等重复推送。
### 2.2 用户侧H5 / 内嵌页)→ 商城:会话与业务接口
以下接口多由 **用户在浏览器内**打开积分商城 H5 后调用,通过 **`session_id`**(先调 `verifyToken` 获取)或 **`token`**(商城 `muser` 类 token标识用户**不一定由 playX 后端直接调用**
- `POST /api/v1/mall/verifyToken`:用 playX token 换商城 `session_id`
- `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`:订单列表
若 playX 后端需要代替用户调用上述接口,须同样携带有效的 `session_id``token`,并遵守同一用户身份规则(见 **§4 身份说明**)。
---
## 3. playX 服务端推送Daily Push
### 3.1 概要
| 项目 | 值 |
|------|-----|
| 方法 | `POST` |
| 路径 | `/api/v1/mall/dailyPush` |
### 3.2 鉴权(按商城部署配置,可组合)
#### 推荐方案:仅启用 HMAC当前对接采用
商城侧配置:设置环境变量 **`PLAYX_DAILY_PUSH_SECRET`** 为非空(启用 HMAC 校验)。
#### HMAC 签名(必填)
当商城配置 **`PLAYX_DAILY_PUSH_SECRET`** 非空时,需同时携带:
| Header | 说明 |
|--------|------|
| `X-Request-Id` | 请求 ID建议与 Body 内可追溯字段一致) |
| `X-Timestamp` | Unix 时间戳(秒,字符串) |
| `X-Signature` | 签名(十六进制小写或大写需与实现一致,以下为十六进制字符串) |
签名原文与计算:
```
canonical = X-Timestamp + "\n" + X-Request-Id + "\nPOST\n/api/v1/mall/dailyPush\n" + sha256(json_body)
expected = HMAC_SHA256( canonical , PLAYX_DAILY_PUSH_SECRET )
```
其中 `json_body`**实际发送的 JSON 原始字符串** 计算出的 SHA256十六进制与 PHP `hash('sha256', $rawBody)` 一致。
校验:`hash_equals(expected, X-Signature)`
#### Header 填写清单HMAC 模式)
- 必填:`X-Request-Id``X-Timestamp``X-Signature`
#### 重要注意:`json_body` 必须与实际发送一致
为了保证签名可验通过:用于计算 sha256 的 `json_body` 必须是**实际发送到 HTTP body 的原始 JSON 字符串**(字节级一致)。\
建议:在发送端先序列化 JSON 得到字符串 `rawBody`,用该 `rawBody` 做 sha256 与 HMAC再把同一个 `rawBody` 作为请求 body 发送。
### 3.3 Body 参数JSON
`/api/v1/mall/dailyPush` 支持 **两种入参格式**(按字段自动识别):
#### 格式 A旧版单条上报兼容
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `request_id` | string | 是 | 本次推送请求号;响应中原样返回 |
| `date` | string | 是 | 业务日期,格式 `YYYY-MM-DD` |
| `user_id` | string | 是 | playX 用户 ID幂等键之一 |
| `username` | string | 否 | 展示名;用于同步/创建商城侧用户资产展示信息 |
| `yesterday_win_loss_net` | number | 否 | 昨日净输赢;**小于 0** 时按配置比例计入待领取保障金(`locked_points` |
| `yesterday_total_deposit` | number | 否 | 昨日总充值;用于计算当日可领取上限等 |
| `lifetime_total_deposit` | number | 否 | 历史总充值(冗余入库) |
| `lifetime_total_withdraw` | number | 否 | 历史总提现(冗余入库) |
#### 格式 B新版批量上报你图中格式
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `report_date` | string/number | 是 | 报表日期;可以为 Unix 秒时间戳(如 `1700000000`)或 `YYYY-MM-DD` |
| `member` | array | 是 | 成员列表,每个成员包含一名 playX 用户数据 |
成员元素字段:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `member_id` | string | 是 | playX 用户 ID幂等键之一 |
| `login` | string | 否 | 用户展示名 |
| `yesterday_total_w` | number | 否 | 昨日净输赢;小于 0 才会累加到 `locked_points` |
| `yesterday_total_deposit` | number | 否 | 昨日总充值;用于计算 `today_limit` |
| `lty_deposit` | number | 否 | 历史总充值(冗余入库) |
| `lty_withdrawal` | number | 否 | 历史总提现(冗余入库) |
#### Body 填写要求(批量模式)
- **必须有**`report_date``member`(数组且至少 1 个元素)、`member[].member_id`
- **允许缺省**:成员的 `login/yesterday_total_w/yesterday_total_deposit/lty_deposit/lty_withdrawal`;缺省时按 `0` 或空字符串处理。
- **日期**`report_date` 传 Unix 秒会自动转换成 `YYYY-MM-DD`;如果直接传 `YYYY-MM-DD` 也支持。
### 3.4 幂等
- 幂等键:**`user_id` + `date`**
- 重复推送:不重复入账,返回 `data.deduped = true`
### 3.5 返回 `data` 字段
| 字段 | 类型 | 说明 |
|------|------|------|
| `request_id` | string | 与请求一致 |
| `accepted` | bool | 是否受理成功 |
| `deduped` | bool | 是否为重复推送(幂等命中) |
| `message` | string | 说明文案 |
#### 格式 B批量上报的返回补充
批量模式会在 `data` 中增加:`results`
`data.results` 为数组,元素字段如下:
| 字段 | 类型 | 说明 |
|------|------|------|
| `user_id` | string | 对应成员的 `member_id` |
| `accepted` | bool | 是否受理成功 |
| `deduped` | bool | 该成员是否为重复推送 |
| `message` | string | `ok``duplicate input` |
**HTTP 401**HMAC 不通过(签名缺失/不完整/校验失败)。
### 3.6 请求示例
```bash
curl -X POST 'https://{商城域名}/api/v1/mall/dailyPush' \
-H 'Content-Type: application/json' \
-H 'X-Request-Id: req_1700000000_123456' \
-H 'X-Timestamp: 1700000000' \
-H 'X-Signature: <按本文档 canonical 计算出的 HMAC_SHA256>' \
-d '{
"report_date": "1700000000",
"member": [
{
"member_id": "123456",
"login": "john",
"lty_deposit": 15230.75,
"lty_withdrawal": 12400.50,
"yesterday_total_w": -320.25,
"yesterday_total_deposit": 500.00
}
]
}'
```
响应示例(首次写入,至少有一个成员非重复):
```json
{
"code": 1,
"msg": "",
"time": 0,
"data": {
"request_id": "report_2023-11-14",
"accepted": true,
"deduped": false,
"message": "Ok",
"results": [
{
"user_id": "123456",
"accepted": true,
"deduped": false,
"message": "Ok"
}
]
}
}
```
---
## 4. 身份说明(`session_id` / `token` / `user_id`
以下接口通过 **`resolvePlayxAssetIdFromRequest`** 解析当前用户,优先级如下:
1. **`session_id`**POST/GET对应商城表 `mall_playx_session`,未过期则映射到 `mall_playx_user_asset`
2.`session_id` 实际是 **`muser` 类型 token**(历史兼容),也会按 token 解析。
3. **`token`**POST/GET 或标准鉴权头):商城 token 表内类型为会员或 **`muser`** 且未过期时,`user_id`**`mall_playx_user_asset.id`**(资产表主键)。
4. **`user_id`**POST/GET
- 若**纯数字**:视为 **`mall_playx_user_asset.id`**
- 否则:按 **`playx_user_id`** 查找资产行。
无法解析身份时,通常返回 **401** 或参数错误提示。
---
## 5. 其他接口一览(前端联调版)
> 下列接口统一返回 `code/msg/time/data`;成功通常为 `code=1`。
> 除 `verifyToken` 外,其余用户接口均需携带 `session_id` 或 `token`(见 §4
### 5.0 `POST /api/v1/mall/dailyPush`(后端对后端)
**用途**playX 按日推送用户资产基础数据到商城(前端一般不直接调用)。
**请求示例:**
```json
{
"request_id": "report_20260430",
"rows": [
{
"user_id": "U10001",
"username": "demo_user",
"yesterday_total_deposit": 1000,
"yesterday_win_loss_net": -500
}
]
}
```
### 5.1 `POST /api/v1/mall/verifyToken`
**用途**:把 playX token 换成商城 `session_id`
**前端入参约定(重要)**
- 前端/客户端调用本接口时,**只需要传 `token`**(或兼容传 `session`)。
- `merchant_code``request_date``request_id``X-Request-Signature` 由商城后端生成并带给 playX前端无需参与签名。
- 若仅传 `token` 仍校验失败,通常是 token 本身无效/过期或对方网关路径未放通,并非前端少传字段。
**请求示例:**
```json
{
"token": "eyJhbGciOi..."
}
```
**成功响应示例:**
```json
{
"code": 1,
"msg": "Success",
"time": 1777533000,
"data": {
"session_id": "fc7f3e3f0d0f4cb29f66e4c8fbab4f66",
"user_id": "U10001",
"username": "demo_user",
"token_expire_at": "2026-04-30T16:20:00+08:00"
}
}
```
### 5.2 `GET /api/v1/mall/assets`
**用途**:查询当前用户积分资产。
**请求示例:**
```http
GET /api/v1/mall/assets?session_id=fc7f3e3f0d0f4cb29f66e4c8fbab4f66
```
**成功响应示例:**
```json
{
"code": 1,
"msg": "Success",
"time": 1777533000,
"data": {
"locked_points": 120,
"available_points": 350,
"today_limit": 200,
"today_claimed": 80,
"withdrawable_cash": 35
}
}
```
### 5.3 `POST /api/v1/mall/claim`
**用途**:领取积分(幂等)。
**请求示例:**
```json
{
"session_id": "fc7f3e3f0d0f4cb29f66e4c8fbab4f66",
"claim_request_id": "claim_20260430_0001"
}
```
**成功响应示例:**
```json
{
"code": 1,
"msg": "Success",
"time": 1777533000,
"data": {
"locked_points": 60,
"available_points": 410,
"today_limit": 200,
"today_claimed": 140,
"withdrawable_cash": 41
}
}
```
### 5.4 `GET /api/v1/mall/items`
**用途**:获取商城商品列表(可按类型筛选)。
**请求示例:**
```http
GET /api/v1/mall/items?type=BONUS
```
**成功响应示例:**
```json
{
"code": 1,
"msg": "Success",
"time": 1777533000,
"data": {
"list": [
{
"id": 101,
"title": "10 MYR Bonus",
"type": "BONUS",
"points_cost": 1000
}
]
}
}
```
### 5.5 `POST /api/v1/mall/bonusRedeem`
**用途**:兑换红利商品。
**请求示例:**
```json
{
"session_id": "fc7f3e3f0d0f4cb29f66e4c8fbab4f66",
"item_id": 101
}
```
**成功响应示例:**
```json
{
"code": 1,
"msg": "Success",
"time": 1777533000,
"data": {
"order_id": "ORD202604300001",
"status": "PENDING"
}
}
```
### 5.6 `POST /api/v1/mall/physicalRedeem`
**用途**:兑换实物商品(需要地址)。
**请求示例:**
```json
{
"session_id": "fc7f3e3f0d0f4cb29f66e4c8fbab4f66",
"item_id": 202,
"address_id": 12
}
```
**成功响应示例:**
```json
{
"code": 1,
"msg": "Success",
"time": 1777533000,
"data": {
"order_id": "ORD202604300002",
"status": "PENDING"
}
}
```
### 5.7 `POST /api/v1/mall/withdrawApply`
**用途**:发起提现档位兑换申请。
**请求示例:**
```json
{
"session_id": "fc7f3e3f0d0f4cb29f66e4c8fbab4f66",
"item_id": 303
}
```
**成功响应示例:**
```json
{
"code": 1,
"msg": "Success",
"time": 1777533000,
"data": {
"order_id": "ORD202604300003",
"status": "PENDING"
}
}
```
### 5.8 `GET /api/v1/mall/orders`
**用途**:查询当前用户订单列表。
**请求示例:**
```http
GET /api/v1/mall/orders?session_id=fc7f3e3f0d0f4cb29f66e4c8fbab4f66
```
**成功响应示例:**
```json
{
"code": 1,
"msg": "Success",
"time": 1777533000,
"data": {
"list": [
{
"order_id": "ORD202604300001",
"status": "PENDING",
"item_title": "10 MYR Bonus"
}
]
}
}
```
### 5.9 `GET /api/v1/mall/pointsLogs`
**用途**:查询积分变动日志。
**请求示例:**
```http
GET /api/v1/mall/pointsLogs?session_id=fc7f3e3f0d0f4cb29f66e4c8fbab4f66
```
**成功响应示例:**
```json
{
"code": 1,
"msg": "Success",
"time": 1777533000,
"data": {
"list": [
{
"id": 9001,
"change_points": 50,
"type": "CLAIM",
"remark": "daily claim"
}
]
}
}
```
### 5.10 收货地址(`mall_address`
**用途**:用户收货地址 CRUD。
#### 5.10.1 `GET /api/v1/mall/addressList`
**请求示例:**
```http
GET /api/v1/mall/addressList?session_id=fc7f3e3f0d0f4cb29f66e4c8fbab4f66
```
**成功响应示例:**
```json
{
"code": 1,
"msg": "Success",
"time": 1777533000,
"data": {
"list": [
{
"id": 12,
"receiver_name": "Tom",
"phone": "0123456789",
"detail_address": "KLCC",
"default_setting": 1
}
]
}
}
```
#### 5.10.2 `POST /api/v1/mall/addressAdd`
**请求示例:**
```json
{
"session_id": "fc7f3e3f0d0f4cb29f66e4c8fbab4f66",
"receiver_name": "Tom",
"phone": "0123456789",
"region": "Kuala Lumpur,KLCC",
"detail_address": "Tower A, 8F",
"default_setting": 1
}
```
**成功响应示例:**
```json
{
"code": 1,
"msg": "Success",
"time": 1777533000,
"data": {
"id": 13
}
}
```
#### 5.10.3 `POST /api/v1/mall/addressEdit`
**请求示例:**
```json
{
"session_id": "fc7f3e3f0d0f4cb29f66e4c8fbab4f66",
"id": 13,
"detail_address": "Tower B, 10F",
"default_setting": 1
}
```
**成功响应示例:**
```json
{
"code": 1,
"msg": "Success",
"time": 1777533000,
"data": null
}
```
#### 5.10.4 `POST /api/v1/mall/addressDelete`
**请求示例:**
```json
{
"session_id": "fc7f3e3f0d0f4cb29f66e4c8fbab4f66",
"id": 13
}
```
**成功响应示例:**
```json
{
"code": 1,
"msg": "Success",
"time": 1777533000,
"data": null
}
```
---
## 6. 配置项(供运维/对方技术对照)
| 环境变量 / 配置 | 作用 |
|-----------------|------|
| `PLAYX_DAILY_PUSH_SECRET` | 非空则 Daily Push 必须带合法 HMAC 头 |
| `PLAYX_VERIFY_TOKEN_LOCAL_ONLY` | 为 true 时 verifyToken 不请求 playX 远程 |
| `PLAYX_ANGPOW_IMPORT_BASE_URL` | 远程 `verify-token` 基础地址(当前联调可含站点前缀路径) |
| `PLAYX_TOKEN_VERIFY_URL` | 远程 `verify-token` 路径(默认 `/api/v1/auth/verify-token` |
| `PLAYX_ANGPOW_MERCHANT_CODE` | `verify-token` 请求体 `merchant_code` |
| `PLAYX_ANGPOW_IMPORT_AUTH_KEY` | `verify-token` 签名密钥HMAC-SHA1Base64 输出) |
---
## 7. 版本与变更
- 文档与仓库代码同步维护;接口路径以 `config/route.php` 为准。
- 若后续升级鉴权策略(例如叠加 JWT以部署环境变量与最新文档为准。