# Dicey Fun 第三方接入文档（基于现有项目接口）

## 1. 基础信息

- provider: `Dicey Fun`
- provider_code: `DF`
- game_type: `slot`
- game_list: `["dafuwen"]`
- provider_logo: ``
- 获取大厅地址: `https://dice-v3-lobby.h55555game.top`
- 后台管理地址: `https://dice-v3.h55555game.top/`
- agent_token: `[后台菜单查看：系统管理-用户管理-代理ID]`
- agent_id: `5ef059938ba799aaa845e1c2e8a762bd`

## 2. 接口域名与协议

- 协议: `HTTP/HTTPS`（建议生产使用 `HTTPS`）
- Base URL: `https://{your-domain}`（由部署方提供）
- 数据格式: `application/json`
- 字符编码: `UTF-8`

### 2.1 调用规范（建议第三方按此统一实现）

- `/api/v1/authToken` 使用 `GET + Query 参数`
- 其他 `/api/v1/*` 接口使用 `POST + JSON Body`（本项目路由为 `Route::any`，但建议第三方统一按 `POST` 接入）
- 请求头统一：
  - `Content-Type: application/json`
  - `Accept: application/json`
  - `api-key: {api_key}`（**所有 `/api/v1/*` 必传**，与服务端 `.env` 中 `API_KEY` 一致）
  - `auth-token: {authtoken}`（除 `/api/v1/authToken` 外必传）
- `api-key` 携带方式（任选其一，按优先级读取，先命中即采用）：
  1. 请求头 `api-key`（**推荐**）
  2. URL 查询参数 `api_key`（或 `api-key`）
  3. body 表单/JSON 字段 `api_key`（或 `api-key`）
- 时间相关参数统一使用 Unix 时间戳（秒）
- 建议所有请求设置超时：连接超时 `3s`，读取超时 `10s`
- 生产环境建议增加调用方 IP 白名单和重试退避机制（避免瞬时重试风暴）

---

## 3. 通用返回格式

所有接口统一返回：

```json
{
  "code": 200,
  "message": "success",
  "data": {}
}
```

说明：

- `code=200` 表示成功
- 失败时通常返回：`400/401/402/403/404/422/500`

常见错误码：

- `400` 参数错误
- `401` 未携带 `api-key`、`auth-token` 或 `token`
- `402` token 无效或过期
- `403` `api-key` 无效、签名或鉴权失败
- `404` 资源不存在
- `422` 业务错误（如余额不足）
- `500` 服务端异常

---

## 4. 鉴权流程（平台级）

平台级凭证分两层：

- **`api-key`**：所有 `/api/v1/*` 接口必传，与服务端 `.env` 中 `API_KEY` 一致；可通过请求头、query、body 任一方式携带（详见 §2.1）。
- **`auth-token`**：业务接口（除 `/api/v1/authToken` 外）必传，由 `/api/v1/authToken` 颁发。

`/api/v1/*` 接口调用前，先获取 `auth-token`。

### 4.1 获取 auth-token

- 路径: `GET /api/v1/authToken`
- Header：
  - `api-key: {api_key}`（必传，与服务端 `.env` 中 `API_KEY` 一致）
- 鉴权参数（Query）：
  - `agent_id`：代理标识（商户标识）
  - `secret`：双方约定密钥
  - `time`：Unix 时间戳（秒）
  - `signature`：签名

签名规则：

```text
signature = md5(agent_id + secret + time)
```

签名示例（原串与结果）：

```text
agent_id = 9f86d081884c7d659a2feaa0c55ad015
secret   = xF75oK91TQj13s0UmNIr1NBWMWGfflNO
time     = 1713753600
sign_src = 9f86d081884c7d659a2feaa0c55ad015xF75oK91TQj13s0UmNIr1NBWMWGfflNO1713753600
signature= md5(sign_src)
```

PHP 生成签名示例：

```php
$agentId = '9f86d081884c7d659a2feaa0c55ad015';
$secret = 'xF75oK91TQj13s0UmNIr1NBWMWGfflNO';
$time = (string) time();
$signature = md5($agentId . $secret . $time);
```

JavaScript 生成签名示例（Node.js）：

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

const agentId = '9f86d081884c7d659a2feaa0c55ad015';
const secret = 'xF75oK91TQj13s0UmNIr1NBWMWGfflNO';
const time = Math.floor(Date.now() / 1000).toString();
const signature = crypto.createHash('md5').update(agentId + secret + time).digest('hex');
```

服务端校验逻辑（关键点）：

- `api-key` 缺失即失败（`401`），与 `.env` 中 `API_KEY` 不一致即失败（`403`）
- `agent_id/secret/time/signature` 任一缺失即失败（`400`）
- `secret` 不匹配即失败（`403`）
- `time` 超出容差窗口即失败（`403`，默认容差 `300s`）
- `signature` 校验失败即失败（`403`）
- 校验通过后颁发 `authtoken`，后续请求必须放在 Header `auth-token`（同时仍需带 `api-key`）

防重放与时间同步建议：

- 调用方服务器必须启用 NTP 时间同步
- `time` 使用发起请求时的当前秒级时间戳，不要复用旧值
- 遇到 “Timestamp expired or invalid” 时优先检查服务器时间偏差

成功返回示例：

```json
{
  "code": 200,
  "message": "success",
  "data": {
    "authtoken": "xxx.yyy.zzz"
  }
}
```

后续调用 `/api/v1/*` 时，请在 Header 携带：

```text
api-key: {api_key}
auth-token: {authtoken}
```

### 4.2 完整调用链（推荐）

1. 计算 `signature = md5(agent_id + secret + time)`
2. 调用 `GET /api/v1/authToken`（Header 携带 `api-key`）获取 `authtoken`
3. 在 Header 添加 `api-key: {api_key}` 与 `auth-token: {authtoken}`
4. 调用业务接口（如 `getPlayerInfo`、`setPlayerWallet`、`getGameUrl`、`getPlayerGameRecord`、`getPlayerWalletRecord`、`getPlayerTicketRecord`）
5. 若返回 `402`，重新获取 `authtoken` 后重试一次

---

## 5. 游戏相关接口

以下接口均需 Header：`api-key` + `auth-token`（`api-key` 也可放 query/body，参见 §2.1）。

## 5.1 获取游戏列表（已支持）

- 路径: `POST /api/v1/getGameList`
- Header:
  - `api-key: {api_key}`
  - `auth-token: {authtoken}`
- Body 参数：
  - `lang`（可选）：`zh`/`en`，默认 `zh`
- 返回说明：
  - 从 `dice_game` 表读取启用状态（`status=1`）的游戏
  - 当前默认仅返回一个游戏：`dafuwen`
  - 按 `lang` 返回中英文 `game_name`
  - 已做字段脱敏：不会返回敏感配置（如 `sensitive_config_json`）

返回字段（`data.game_list[]`）：

- `provider`：供应商名称
- `provider_code`：供应商编码
- `game_code`：游戏编号
- `game_key`：游戏唯一值
- `game_type`：游戏类型
- `logo`：游戏 Logo 地址
- `game_url`：游戏地址
- `hall_url`：大厅地址
- `status`：状态（`1` 启用）
- `sort`：排序值
- `game_name`：游戏名称（跟随 `lang` 返回中文或英文）

成功返回示例（`lang=zh`）：

```json
{
  "code": 200,
  "message": "success",
  "data": {
    "game_list": [
      {
        "provider": "Dicey Fun",
        "provider_code": "DF",
        "game_code": "dafuwen",
        "game_key": "dafuwen",
        "game_type": "slot",
        "logo": "",
        "game_url": "https://dice-v3-game.h55555game.top/",
        "hall_url": "https://dice-v3-game.h55555game.top/",
        "status": 1,
        "sort": 1,
        "game_name": "大富翁"
      }
    ]
  }
}
```

成功返回示例（`lang=en`）：

```json
{
  "code": 200,
  "message": "success",
  "data": {
    "game_list": [
      {
        "provider": "Dicey Fun",
        "provider_code": "DF",
        "game_code": "dafuwen",
        "game_key": "dafuwen",
        "game_type": "slot",
        "logo": "",
        "game_url": "https://dice-v3-game.h55555game.top/",
        "hall_url": "https://dice-v3-game.h55555game.top/",
        "status": 1,
        "sort": 1,
        "game_name": "Dafuweng"
      }
    ]
  }
}
```

## 5.2 获取游戏大厅信息（已支持，脱敏）

- 路径: `POST /api/v1/getGameHall`
- Header:
  - `api-key: {api_key}`
  - `auth-token: {authtoken}`
- Body 参数：
  - `lang`（可选）：`zh`/`en`，默认 `zh`
- 返回说明：
  - 返回大厅地址 `hall_url`
  - 返回游戏列表（来自 `dice_game`）
  - 不返回敏感信息字段

返回字段：

- `data.provider`：供应商名称
- `data.provider_code`：供应商编码
- `data.hall_url`：大厅地址
- `data.game_list`：游戏列表（字段结构同 `/api/v1/getGameList`）

成功返回示例：

```json
{
  "code": 200,
  "message": "success",
  "data": {
    "provider": "Dicey Fun",
    "provider_code": "DF",
    "hall_url": "https://dice-v3-game.h55555game.top/",
    "game_list": [
      {
        "provider": "Dicey Fun",
        "provider_code": "DF",
        "game_code": "dafuwen",
        "game_key": "dafuwen",
        "game_type": "slot",
        "logo": "",
        "game_url": "https://dice-v3-game.h55555game.top/",
        "hall_url": "https://dice-v3-game.h55555game.top/",
        "status": 1,
        "sort": 1,
        "game_name": "大富翁"
      }
    ]
  }
}
```

成功返回示例（`lang=en`）：

```json
{
  "code": 200,
  "message": "success",
  "data": {
    "provider": "Dicey Fun",
    "provider_code": "DF",
    "hall_url": "https://dice-v3-game.h55555game.top/",
    "game_list": [
      {
        "provider": "Dicey Fun",
        "provider_code": "DF",
        "game_code": "dafuwen",
        "game_key": "dafuwen",
        "game_type": "slot",
        "logo": "",
        "game_url": "https://dice-v3-game.h55555game.top/",
        "hall_url": "https://dice-v3-game.h55555game.top/",
        "status": 1,
        "sort": 1,
        "game_name": "Dafuweng"
      }
    ]
  }
}
```

## 5.3 获取某个游戏地址（已支持）

- 路径: `POST /api/v1/getGameUrl`
- Header:
  - `api-key: {api_key}`
  - `auth-token: {authtoken}`
- Body 参数：
  - `username`（必填）：玩家账号（不存在会自动创建）
  - `time`（可选）：不传则服务端取当前时间戳
  - `lang`（可选）：`zh`/`en`，默认 `zh`

成功返回示例：

```json
{
  "code": 200,
  "message": "success",
  "data": {
    "url": "https://{game-domain}/?token=...&lang=zh"
  }
}
```

### 5.4 获取大厅地址（当前项目建议）

当前项目以 `getGameUrl` 作为主要入场方式，`game_list` 仅有 `dafuwen`。

- 若第三方需要独立大厅地址，可使用你方配置值：`[代填]`
- 若第三方可直接跳游戏，可直接调用 `getGameUrl`

### 5.5 获取游戏列表 API（当前项目状态）

已提供独立接口：`POST /api/v1/getGameList`，支持中英文格式，且数据来自 `dice_game` 表。

## 5.6 获取玩家游戏记录（已支持）

- 路径: `POST /api/v1/getPlayerGameRecord`
- Header:
  - `api-key: {api_key}`
  - `auth-token: {authtoken}`
- Body 参数：
  - `username`（可选）：玩家账号；不传则**不按玩家筛选**（返回库内符合条件的记录，请谨慎使用）
  - `start_create_time`（可选）、`end_create_time`（可选）：与表字段 `create_time` 比较；**仅允许落在「当前时间起向前 7 天」内**，且两者跨度**不得超过 7 天**；均不传时服务端默认查询该 7 天窗口。`end_create_time` 晚于当前时间时按当前时间截断；不满足规则时返回参数错误。
  - `limit`（可选）：返回条数上限，默认 `20`；若小于 `1` 或大于 `2000` 则按 `20` 处理
- 返回说明：
  - 成功时 `data` 为**数组**（按 `id` 倒序，最多 `limit` 条），每条为 `dice_play_record` 行数据，并附带 `dice_player`：`{ id, username, phone }`（若批量关联不到则为 `null`）
  - 若传了 `username` 且玩家不存在：`data` 为空数组 `[]`

记录主要字段（与库表一致，节选）：`id`、`player_id`、`admin_id`、`lottery_config_id`、`lottery_type`、`ante`、`paid_amount`、`is_win`、`win_coin`、`super_win_coin`、`reward_win_coin`、`use_coins`、`direction`、`reward_tier`、`lottery_id`、`start_index`、`target_index`、`roll_array`、`roll_number`、`lottery_name`、`status`、`create_time`、`update_time` 等。

---

## 6. 游戏管理后台（新增）

本次已新增游戏管理数据表与菜单，用于统一管理游戏基础信息。

### 6.1 数据表

- 建表 SQL：`server/db/dice_game.sql`
- 表名：`dice_game`
- 关键字段：
  - `logo`：游戏 logo
  - `game_url`：游戏地址
  - `hall_url`：大厅地址
  - `game_code`：游戏编号
  - `game_key`：游戏唯一值
  - `status`：状态（1 启用 / 0 禁用）
  - `game_type`：游戏类型
  - `merchant_config_json`：商户可见扩展
  - `sensitive_config_json`：敏感配置（大厅接口不返回）

### 6.2 菜单与权限

- 菜单 SQL：`server/db/dice_game_menu.sql`
- 菜单路径：`/dice/game/index`
- 权限标识：
  - `dice:game:index:index`
  - `dice:game:index:read`
  - `dice:game:index:save`
  - `dice:game:index:update`
  - `dice:game:index:destroy`

---

## 7. 钱包相关接口

以下接口均需 Header：`api-key` + `auth-token`（`api-key` 也可放 query/body，参见 §2.1）。

### 7.1 查询余额（已支持）

方式 1（推荐）：`POST /api/v1/getPlayerInfo`

- 请求参数：
  - `username`（必填）
- 余额字段：
  - `data.coin`

### 7.2 额度转入 / 额度转出（已支持）

接口：`POST /api/v1/setPlayerWallet`

- 请求参数：
  - `username`（必填）
  - `coin`（必填）
    - `coin > 0`：额度转入（充值）
    - `coin < 0`：额度转出（提现）
    - `coin = 0`：非法

成功返回 `wallet record`，包含转账前后余额等信息。

### 7.3 获取大厅地址（钱包侧）

如接入方钱包流程要求“转账后返回大厅地址”，可在完成 `setPlayerWallet` 后再调用：

- `POST /api/v1/getGameUrl`

### 7.4 获取钱包流水（已支持）

- 路径: `POST /api/v1/getPlayerWalletRecord`
- Header:
  - `api-key: {api_key}`
  - `auth-token: {authtoken}`
- Body 参数：
  - `username`（可选）：玩家账号；不传则**不按玩家筛选**
  - `start_create_time`、`end_create_time`（可选）：同 `getPlayerGameRecord`，作用于 `create_time`
  - `limit`（可选）：条数规则同 `getPlayerGameRecord`
- 返回说明：
  - 成功时 `data` 为数组；每条为 `dice_player_wallet_record` 数据，并含关联 `dice_player`（`id, username, phone`）
  - 若传了 `username` 且玩家不存在：`data` 为空数组 `[]`

`type` 含义：`0` 充值、`1` 提现、`2` 购买抽奖次数（与业务写入一致）。

### 7.5 获取抽奖券获取记录（已支持）

- 路径: `POST /api/v1/getPlayerTicketRecord`
- Header:
  - `api-key: {api_key}`
  - `auth-token: {authtoken}`
- Body 参数：与 **7.4** 相同（`username`、`start_create_time`、`end_create_time`、`limit`）
- 返回说明：
  - 成功时 `data` 为 `dice_player_ticket_record` 列表，含关联 `dice_player`
  - 若传了 `username` 且玩家不存在：`data` 为空数组 `[]`

主要字段（节选）：`id`、`player_id`、`admin_id`、`use_coins`、`ante`、`total_ticket_count`、`paid_ticket_count`、`free_ticket_count`、`remark`、`create_time`、`update_time` 等。

---

## 8. 商户（代理）可配置字段

建议在对接参数表中配置以下字段：

- `provider`：`Dicey Fun`
- `provider_code`：`DF`
- `agent_id`：`5ef059938ba799aaa845e1c2e8a762bd`
- `secret`：签名密钥（双方约定，对应服务端 `.env` 中 `API_AUTH_TOKEN_SECRET`）
- `api_key`：所有 `/api/v1/*` 请求必传的 `api-key`（对应服务端 `.env` 中 `API_KEY`）
- `agent_token`：`[我来填]`（如需额外业务层 token）
- `game_url`：游戏前端域名/地址
- `lobby_url`：大厅地址（可选）
- `lang`：默认语言（`zh`/`en`）
- `callback_url`：业务回调地址（如后续扩展）
- `ip_whitelist`：调用白名单（建议）

---

## 9. 后台管理信息

- 后台管理地址：`https://dice-v3.h55555game.top/`
- 后台账号：`zhuguan`
- 后台密码：`qwer1234`

---

## 10. 对接时序（建议）

1. 平台分配 `agent_id`、`secret`、`api_key`
2. 第三方调用 `/api/v1/authToken`（Header 携带 `api-key`）获取 `authtoken`
3. 第三方调用 `/api/v1/getGameHall` 或 `/api/v1/getGameList` 获取大厅/游戏信息
4. 第三方调用 `/api/v1/getPlayerInfo`（可选，检查用户与余额）
5. 第三方调用 `/api/v1/setPlayerWallet` 进行额度转入（如有）
6. 第三方调用 `/api/v1/getGameUrl` 获取游戏地址并跳转
7. 结束后调用 `/api/v1/setPlayerWallet` 执行额度转出（如有）
8. （可选）对账或客服：调用 `/api/v1/getPlayerWalletRecord`、`/api/v1/getPlayerGameRecord`、`/api/v1/getPlayerTicketRecord` 拉取流水

---

## 11. Postman 调用示例

### 11.1 获取 auth-token

```bash
curl --location --request GET 'https://{your-domain}/api/v1/authToken?agent_id={agent_id}&secret={secret}&time={time}&signature={signature}' \
--header 'api-key: {api_key}'
```

建议在接入测试时，先本地打印以下值再发请求，便于排查：

- `agent_id`
- `time`
- `sign_src`（拼接前字符串）
- `signature`
- 最终请求 URL

### 11.2 获取游戏地址

```bash
curl --location --request POST 'https://{your-domain}/api/v1/getGameUrl' \
--header 'Content-Type: application/json' \
--header 'api-key: {api_key}' \
--header 'auth-token: {authtoken}' \
--data-raw '{
  "username":"test_player_001",
  "lang":"zh"
}'
```

### 11.3 获取游戏列表（中文）

```bash
curl --location --request POST 'https://{your-domain}/api/v1/getGameList' \
--header 'Content-Type: application/json' \
--header 'api-key: {api_key}' \
--header 'auth-token: {authtoken}' \
--data-raw '{
  "lang":"zh"
}'
```

### 11.4 获取游戏列表（英文）

```bash
curl --location --request POST 'https://{your-domain}/api/v1/getGameList' \
--header 'Content-Type: application/json' \
--header 'api-key: {api_key}' \
--header 'auth-token: {authtoken}' \
--data-raw '{
  "lang":"en"
}'
```

### 11.5 获取游戏大厅（中文）

```bash
curl --location --request POST 'https://{your-domain}/api/v1/getGameHall' \
--header 'Content-Type: application/json' \
--header 'api-key: {api_key}' \
--header 'auth-token: {authtoken}' \
--data-raw '{
  "lang":"zh"
}'
```

### 11.6 额度转入

```bash
curl --location --request POST 'https://{your-domain}/api/v1/setPlayerWallet' \
--header 'Content-Type: application/json' \
--header 'api-key: {api_key}' \
--header 'auth-token: {authtoken}' \
--data-raw '{
  "username":"test_player_001",
  "coin":100
}'
```

### 11.7 查询余额

```bash
curl --location --request POST 'https://{your-domain}/api/v1/getPlayerInfo' \
--header 'Content-Type: application/json' \
--header 'api-key: {api_key}' \
--header 'auth-token: {authtoken}' \
--data-raw '{
  "username":"test_player_001"
}'
```

### 11.8 获取玩家游戏记录

```bash
curl --location --request POST 'https://{your-domain}/api/v1/getPlayerGameRecord' \
--header 'Content-Type: application/json' \
--header 'api-key: {api_key}' \
--header 'auth-token: {authtoken}' \
--data-raw '{
  "username":"test_player_001",
  "limit":20
}'
```

### 11.9 获取钱包流水

```bash
curl --location --request POST 'https://{your-domain}/api/v1/getPlayerWalletRecord' \
--header 'Content-Type: application/json' \
--header 'api-key: {api_key}' \
--header 'auth-token: {authtoken}' \
--data-raw '{
  "username":"test_player_001",
  "limit":20
}'
```

### 11.10 获取抽奖券获取记录

```bash
curl --location --request POST 'https://{your-domain}/api/v1/getPlayerTicketRecord' \
--header 'Content-Type: application/json' \
--header 'api-key: {api_key}' \
--header 'auth-token: {authtoken}' \
--data-raw '{
  "username":"test_player_001",
  "limit":20
}'
```
