22 KiB
PlayX 接口文档(按调用方向拆分)
说明:本文档严格依据当前代码 app/api/controller/v1/Playx.php、app/api/controller/v1/Auth.php(临时登录)、config/playx.php 与定时任务 app/process/PlayxJobs.php 整理。
三类接口分别为:
积分商城 -> PlayX(PlayX 调用商城)PlayX -> 积分商城(商城调用 PlayX)积分商城 -> H5(H5 调用商城)
1. 积分商城 -> PlayX(PlayX 调用商城)
1.1 Daily Push API
- 方法:
POST - 路径:
/api/v1/playx/daily-push
Header(多语言,可选)
lang:zh/zh-cn返回中文(默认);en返回英文
Header(签名校验:HMAC 必填)
当 playx.daily_push_secret 配置非空时,需要携带(HMAC):
X-Request-Id:请求 IDX-Timestamp:时间戳X-Signature:签名(HMAC_SHA256)
服务端签名计算:
canonical = X-Timestamp + "\n" + X-Request-Id + "\nPOST\n/api/v1/playx/daily-push\n" + sha256(json_body)expected = hash_hmac('sha256', canonical, daily_push_secret)- 校验:
hash_equals(expected, X-Signature)
说明:
- 本项目对接方案为 仅启用 HMAC,不使用
Authorization头做校验。
Body
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
request_id |
string | 是 | 外部推送请求号(原样返回) |
date |
string(YYYY-MM-DD) | 是 | 业务日期(入库到 mall_playx_daily_push.date) |
user_id |
string | 是 | PlayX 用户 ID(用于幂等;入库 mall_playx_daily_push.user_id 等;服务端会映射/创建 mall_user 与 mall_playx_user_asset) |
username |
string | 否 | 展示冗余(同步到商城用户侧逻辑时使用) |
yesterday_win_loss_net |
number | 否 | 昨日净输赢(仅当 < 0 时计算新增保障金) |
yesterday_total_deposit |
number | 否 | 昨日总充值(用于计算今日可领取上限) |
lifetime_total_deposit |
number | 否 | 历史总充值 |
lifetime_total_withdraw |
number | 否 | 历史总提现 |
格式 B:新版批量上报(兼容你截图)
新版 body 形如:
{
"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
}
]
}
字段映射(服务端内部会转换成旧字段再计算):
report_date->date(若为 Unix 秒则转为YYYY-MM-DD)member[].member_id->user_idmember[].login->usernamemember[].yesterday_total_w->yesterday_win_loss_netmember[].yesterday_total_deposit->yesterday_total_depositmember[].lty_deposit->lifetime_total_depositmember[].lty_withdrawal->lifetime_total_withdraw
返回补充:
- 批量模式会在
data里增加results[],每个成员一条结果(是否deduped)。
幂等规则
- 幂等键:
user_id + date - 重复推送:不会重复入账,返回
data.deduped=true
返回(Response)
外层通用返回结构:{ code, msg, time, data }
成功(首次入库):
| 字段 | 类型 | 说明 |
|---|---|---|
data.request_id |
string | 原样返回 |
data.accepted |
boolean | true |
data.deduped |
boolean | false |
data.message |
string | ok |
成功(重复推送):
| 字段 | 类型 | 说明 |
|---|---|---|
data.request_id |
string | 原样返回 |
data.accepted |
boolean | true |
data.deduped |
boolean | true |
data.message |
string | duplicate input |
失败:
- 当缺少必填字段:code=0,msg 为缺少字段错误
- 当签名不正确:HTTP 401,code=0,msg 为
INVALID_SIGNATURE
示例(未开启签名校验)
请求:
curl -X POST 'http://localhost:1818/api/v1/playx/daily-push' \
-H 'Content-Type: application/json' \
-d '{
"request_id":"req_1001",
"date":"2026-03-18",
"user_id":"U123",
"username":"demo_user",
"yesterday_win_loss_net":-120.5,
"yesterday_total_deposit":50,
"lifetime_total_deposit":5000,
"lifetime_total_withdraw":2000
}'
响应(首次):
{
"code": 1,
"msg": "",
"data": {
"request_id": "req_1001",
"accepted": true,
"deduped": false,
"message": "ok"
},
"time": 0
}
示例(新版批量上报)
请求:
curl -X POST 'http://localhost:1818/api/v1/playx/daily-push' \
-H 'Content-Type: application/json' \
-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
}
]
}'
返回(首次写入至少一个成员时的示例):
{
"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"
}
]
}
}
2. PlayX -> 积分商城(商城调用 PlayX)
下面这些接口由 PlayX 提供。商城侧仅按“请求参数 + 期望返回判定条件”发起调用与处理结果。
说明:H5 调商城的/api/v1/playx/verify-token在配置playx.verify_token_local_only=true(默认)时不会请求本节接口,而是在商城内校验musertoken;远程对接 PlayX 时见 3.3 与下文 2.1。
2.1 Token Verification API(PlayX 侧实现,远程验证时使用)
- 方法:
POST - URL:
${playx.api.base_url}${playx.api.token_verify_url}- 默认:
/api/v1/auth/verify-token
- 默认:
请求 Body(商城侧发送)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
request_id |
string | 是 | 形如 mall_{uniqid} |
token |
string | 是 | 前端传入的 PlayX token |
返回(期望)
商城侧校验:
- HTTP 状态码必须为
200 - 且响应体中必须包含
user_id
期望字段(示例):
| 字段 | 类型 | 说明 |
|---|---|---|
user_id |
string | 必选 |
username |
string | 可选 |
token_expire_at |
string | 可选(能被 strtotime 解析) |
示例(成功):
{
"user_id": "U123",
"username": "demo_user",
"token_expire_at": "2026-04-01T12:00:00Z"
}
示例(失败):
{
"message": "invalid token"
}
2.2 Bonus Grant API
- 方法:
POST - URL:
${playx.api.base_url}${playx.api.bonus_grant_url}- 默认:
/api/v1/bonus/grant
- 默认:
请求 Body(商城侧发送)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
request_id |
string | 是 | 形如 mall_bonus_{uniqid} |
externalTransactionId |
string | 是 | MallPlayxOrder.external_transaction_id |
user_id |
string | 是 | PlayX 用户 ID |
amount |
number | 是 | MallPlayxOrder.amount |
rewardName |
string | 是 | mall_item.title |
category |
string | 是 | mall_item.category(默认 daily) |
categoryTitle |
string | 是 | mall_item.category_title |
multiplier |
int | 是 | MallPlayxOrder.multiplier |
返回(期望)
商城侧判定:
- HTTP 状态码
200 - 且
data.status === "accepted"
成功时读取:
data.playx_transaction_id
失败时读取:
data.message写入订单fail_reason
示例(accepted):
{
"status": "accepted",
"playx_transaction_id": "PX_TX_001"
}
示例(rejected):
{
"status": "rejected",
"message": "insufficient balance"
}
2.3 Balance Credit API
- 方法:
POST - URL:
${playx.api.base_url}${playx.api.balance_credit_url}- 默认:
/api/v1/balance/credit
- 默认:
请求 Body(商城侧发送)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
request_id |
string | 是 | 形如 mall_withdraw_{uniqid} |
externalTransactionId |
string | 是 | MallPlayxOrder.external_transaction_id |
user_id |
string | 是 | PlayX 用户 ID |
amount |
number | 是 | MallPlayxOrder.amount |
multiplier |
int | 是 | MallPlayxOrder.multiplier |
返回(期望)
与 Bonus Grant 一致:
data.status === "accepted"-> 读取playx_transaction_id- 否则 -> 读取
message写入fail_reason
示例(accepted):
{
"status": "accepted",
"playx_transaction_id": "PX_TX_002"
}
示例(rejected):
{
"status": "rejected",
"message": "insufficient balance"
}
2.4 Transaction Status Query API(交易终态查询)
- 方法:
GET - URL:
${playx.api.base_url}${playx.api.transaction_status_url}- 默认:
/api/v1/transaction/status
- 默认:
Query 参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
externalTransactionId |
string | 是 | 订单幂等键 external_transaction_id |
返回(期望)
定时任务读取 data.status:
COMPLETED:商城将订单status更新为COMPLETEDFAILED或REJECTED:商城将订单status=REJECTED、grant_status=FAILED_FINAL,并退回积分- 失败信息取
data.message写入订单fail_reason
示例(completed):
{ "status": "COMPLETED" }
示例(failed):
{ "status": "FAILED", "message": "grant rejected by PlayX" }
3. 积分商城 -> H5(服务端提供给 H5 的接口)
3.0 数据模型说明(与代码一致)
- 商城用户:表
mall_user(主键id)。 - PlayX 资产扩展:表
mall_playx_user_asset,与mall_user一对一(mall_user_id唯一,playx_user_id唯一)。 - 对外业务 ID:接口里返回或订单里使用的
user_id字符串多为 PlayX 侧用户 ID(playx_user_id);H5 临时登录场景若尚无真实 PlayX ID,会生成形如mall_{mall_user.id}的占位 ID(见temLogin)。 - 服务端内部:
Playx控制器内部用mall_user.id(整型)解析资产;session_id/token/user_id会映射到该mall_user。
3.1 鉴权解析规则(resolveMallUserIdFromRequest)
以下接口在服务端最终都会解析出 商城用户 mall_user.id,再按该用户查询 mall_playx_user_asset 等。
优先级(由高到低):
session_id(post优先,get兼容)- 在
mall_playx_session中存在且未过期:用会话里的user_id(即playx_user_id)在mall_playx_user_asset反查mall_user_id。 - 若会话无效:兼容把
session_id参数误当作 商城 token 再试一次(UUID 形态 token)。
- 在
token(post/get或请求头ba-token/token)- 校验
token表:类型为会员user或商城临时muser(mall_user登录),未过期则user_id字段即为mall_user.id。
- 校验
user_id(post/get兼容)- 纯数字:视为
mall_user.id。 - 非纯数字:视为
playx_user_id,在mall_playx_user_asset查找对应mall_user_id。
- 纯数字:视为
注意:请求参数的取值方式是
post()优先,get()兼容(即同字段既可传 post 也可传 get)。
3.2 临时登录(获取商城 token)
- 方法:
GET(推荐)或POST - 路径:
/api/v1/temLogin - 开关:
config/buildadmin.php→agent_auth.temp_login_enable为true;有效期agent_auth.temp_login_expire(秒)。
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
username |
string | 是 | 商城用户名(唯一);不存在则自动创建 mall_user |
行为说明
- 若
mall_user不存在:创建用户(随机占位手机号、随机密码等,与后台「商城用户」一致)。 - 无论是否新用户:保证存在
mall_playx_user_asset一条记录(MallPlayxUserAsset::ensureForMallUser),playx_user_id默认mall_{mall_user.id}(与 PlayX 真实 ID 冲突概率低)。 - 签发 商城 token(类型
muser,非会员表user),并签发muser-refresh刷新令牌。
返回(成功 data.userInfo)
| 字段 | 类型 | 说明 |
|---|---|---|
id |
int | mall_user.id |
username |
string | 用户名 |
nickname |
string | 同 username |
playx_user_id |
string | 资产表中的 playx_user_id(如 mall_12) |
token |
string | 访问 H5 接口时携带 |
refresh_token |
string | 调用 /api/common/refreshToken 时使用(类型 muser-refresh) |
expires_in |
int | token 有效秒数 |
示例
curl -G 'http://localhost:1818/api/v1/temLogin' --data-urlencode 'username=demo_h5'
用户名含 + 等号时需 URL 编码(如 %2B60123456789)。
3.3 Token 验证(换 session)
- 方法:
POST(推荐GET传token亦可) - 路径:
/api/v1/playx/verify-token
配置:本地验证 vs 远程 PlayX
- 配置项:
config/playx.php→verify_token_local_only(环境变量PLAYX_VERIFY_TOKEN_LOCAL_ONLY,未设置时默认为 **1/ 开启本地验证)。 verify_token_local_only = true(默认)- 不请求 PlayX HTTP。
- 仅接受商城临时登录 token(类型
muser),校验token表后,根据mall_user与mall_playx_user_asset写入mall_playx_session。 - 返回的
data.user_id为playx_user_id(无资产记录时回退为mall_user.id字符串,一般 temLogin 后已有资产)。
verify_token_local_only = false(生产对接 PlayX)- 需配置
playx.api.base_url,由商城向 PlayX 发起POST校验(见下文「远程模式」)。 - 若未配置
base_url,返回PlayX API not configured。
- 需配置
请求参数
必填其一:
token(Body 优先;session兼容字段;Query 也可传token)
返回(成功 data)
| 字段 | 类型 | 说明 |
|---|---|---|
session_id |
string | 写入 mall_playx_session |
user_id |
string | PlayX 用户 ID(即 playx_user_id,会话内与订单/推送一致) |
username |
string | 用户名 |
token_expire_at |
string | ISO 字符串(服务端 date('c', expireAt)) |
失败:
- token 为空:HTTP 401,msg=
INVALID_TOKEN - 远程模式且 PlayX 未配置:
msg=PlayX API not configured
示例(本地验证)
curl -X POST 'http://localhost:1818/api/v1/playx/verify-token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'token=上一步TemLogin返回的token'
远程模式(verify_token_local_only=false + 已配置 base_url)
商城侧请求 URL:${playx.api.base_url}${playx.api.token_verify_url}(默认路径 /api/v1/auth/verify-token)。
请求 Body(商城侧发送)——仅远程模式
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
request_id |
string | 是 | 形如 mall_{uniqid} |
token |
string | 是 | 前端传入的 PlayX token |
返回(期望)——仅远程模式
- HTTP 状态码必须为
200 - 且响应体中必须包含
user_id
响应(成功示例):
{
"code": 1,
"msg": "",
"data": {
"session_id": "7b1c....",
"user_id": "U123",
"username": "demo_user",
"token_expire_at": "2026-04-01T12:00:00+00:00"
}
}
3.4 用户资产(Assets)
- 方法:
GET - 路径:
/api/v1/playx/assets
请求参数(鉴权)
以下任选其一即可(与 3.1 鉴权解析规则 一致):
session_idtoken(或请求头ba-token/token)user_id(纯数字为mall_user.id,否则为playx_user_id)
返回(成功 data)
若未找到资产:返回 0。
| 字段 | 类型 | 说明 |
|---|---|---|
locked_points |
int | 待领取积分 |
available_points |
int | 可用积分 |
today_limit |
int | 今日可领取上限 |
today_claimed |
int | 今日已领取 |
withdrawable_cash |
number(2) | available_points * points_to_cash_ratio(保留 2 位) |
示例
curl -G 'http://localhost:1818/api/v1/playx/assets' --data-urlencode 'token=上一步temLogin返回的token'
响应(示例):
{
"code": 1,
"msg": "",
"time": 1712345678,
"data": {
"locked_points": 100,
"available_points": 50,
"today_limit": 200,
"today_claimed": 80,
"withdrawable_cash": 5.2
}
}
3.5 领取(Claim)
- 方法:
POST - 路径:
/api/v1/playx/claim
请求 Body
必填:
claim_request_id:幂等键(string,唯一)
鉴权:同 3.1(session_id / token / user_id)
返回(成功 data)
与 用户资产 返回字段一致(资产快照)。
幂等:
claim_request_id已存在:不会重复入账,直接返回当前资产快照
示例
curl -X POST 'http://localhost:1818/api/v1/playx/claim' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'claim_request_id=claim_001' \
--data-urlencode 'token=上一步temLogin返回的token'
响应(首次领取,示例):
{
"code": 1,
"msg": "Claim success",
"time": 1712345679,
"data": {
"locked_points": 60,
"available_points": 90,
"today_limit": 200,
"today_claimed": 120,
"withdrawable_cash": 9.0
}
}
响应(幂等重复,示例:可能 msg 为空):
{
"code": 1,
"msg": "",
"time": 1712345680,
"data": {
"locked_points": 60,
"available_points": 90,
"today_limit": 200,
"today_claimed": 120,
"withdrawable_cash": 9.0
}
}
3.6 商品列表
- 方法:
GET - 路径:
/api/v1/playx/items
请求参数
type(可选):BONUS|PHYSICAL|WITHDRAW
不传或空:返回 mall_item.status=1 且不过滤 type 的商品列表。
返回(成功 data)
list:商品列表(直接返回MallItem的字段数组;包含扩展字段:amount/multiplier/category/category_title等)
示例
请求:
curl -G 'http://localhost:1818/api/v1/playx/items' --data-urlencode 'type=WITHDRAW'
响应(示例):
{
"code": 1,
"msg": "",
"time": 1712345685,
"data": {
"list": [
{
"id": 321,
"title": "提现档位A",
"type": 3,
"score": 1000,
"amount": 100.0,
"multiplier": 1,
"category": "withdraw",
"category_title": "提现"
}
]
}
}
3.7 红利兑换(Bonus Redeem)
- 方法:
POST - 路径:
/api/v1/playx/bonus/redeem
请求 Body
必填:
item_id:商品 ID(要求mall_item.type=BONUS且status=1) 鉴权:同 3.1(session_id/token/user_id)
返回(成功)
msg:Redeem submitted, please wait about 10 minutesdata.order_id:订单 IDdata.status:PENDING
示例
curl -X POST 'http://localhost:1818/api/v1/playx/bonus/redeem' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'item_id=123' \
--data-urlencode 'session_id=7b1c....'
响应(示例):
{
"code": 1,
"msg": "Redeem submitted, please wait about 10 minutes",
"time": 1712345686,
"data": {
"order_id": 456,
"status": "PENDING"
}
}
3.8 实物兑换(Physical Redeem)
- 方法:
POST - 路径:
/api/v1/playx/physical/redeem
请求 Body
必填:
item_id:商品 ID(要求mall_item.type=PHYSICAL且status=1)receiver_name:收货人receiver_phone:收货电话receiver_address:收货地址 鉴权:同 3.1(session_id/token/user_id)
返回(成功)
msg:Redeem successdata:null
示例
curl -X POST 'http://localhost:1818/api/v1/playx/physical/redeem' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'item_id=200' \
--data-urlencode 'receiver_name=张三' \
--data-urlencode 'receiver_phone=18800001111' \
--data-urlencode 'receiver_address=北京市朝阳区XX路XX号' \
--data-urlencode 'session_id=7b1c....'
响应(示例):
{
"code": 1,
"msg": "Redeem success",
"time": 1712345687,
"data": null
}
3.9 提现申请(Withdraw Apply)
- 方法:
POST - 路径:
/api/v1/playx/withdraw/apply
请求 Body
必填:
item_id:商品 ID(要求mall_item.type=WITHDRAW且status=1) 鉴权:同 3.1(session_id/token/user_id)
返回(成功)
msg:Withdraw submitted, please wait about 10 minutesdata.order_id:订单 IDdata.status:PENDING
示例
curl -X POST 'http://localhost:1818/api/v1/playx/withdraw/apply' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'item_id=321' \
--data-urlencode 'session_id=7b1c....'
响应(示例):
{
"code": 1,
"msg": "Withdraw submitted, please wait about 10 minutes",
"time": 1712345688,
"data": {
"order_id": 789,
"status": "PENDING"
}
}
3.10 订单列表
- 方法:
GET - 路径:
/api/v1/playx/orders
请求参数(鉴权)
同 3.1(session_id / token / user_id)。
返回(成功 data)
list:订单列表(最多 100 条),并包含关联的mallItem(关系对象)- 列表项中的
user_id为 PlayX 侧playx_user_id(字符串),与mall_playx_order.user_id一致
示例
请求:
curl -G 'http://localhost:1818/api/v1/playx/orders' --data-urlencode 'token=上一步temLogin返回的token'
响应(示例,简化):
{
"code": 1,
"msg": "",
"time": 1712345689,
"data": {
"list": [
{
"id": 456,
"user_id": "U123",
"type": "BONUS",
"status": "PENDING",
"mall_item_id": 123,
"points_cost": 100,
"amount": 10.0,
"external_transaction_id": "BONUS_ORD2026....",
"grant_status": "NOT_SENT",
"mallItem": {
"id": 123,
"title": "每日红利",
"type": 1
}
}
]
}
}
3.11 同步额度(可选)
当前代码未实现并未注册路由:/api/v1/playx/sync-limit。