Files
webman-buildadmin-mall/docs/PlayX-接口文档.md
2026-04-21 16:00:02 +08:00

21 KiB
Raw Blame History

playX 接口文档(按调用方向拆分)

说明:本文档严格依据当前代码 app/api/controller/v1/Playx.phpapp/api/controller/v1/Auth.php(临时登录)、config/playx.php 与定时任务 app/process/PlayxJobs.php 整理。

按调用方向分为三类(避免与历史章节标题混淆):

方向 含义 本文位置
playX → 积分商城 playX或上游批处理主动 HTTP 调用商城开放接口 §1(如 Daily Push
积分商城 → playX 商城 Worker / 后台 主动 HTTP 调用 playX / Cash Market 提供的接口 不展开于本文;交付 playX 的说明见 docs/playX-接口待完善清单.md
积分商城 → H5 H5 / 内嵌页 调用商城 的会员与积分业务接口 §3

1. playX → 积分商城(外部系统调用商城开放接口)

1.1 Daily Push API

  • 方法:POST
  • 路径:/api/v1/mall/dailyPush

Header多语言可选

  • lang: zh/zh-cn 返回中文(默认);en 返回英文

Header签名校验HMAC 必填)

playX.daily_push_secret 配置非空时需要携带HMAC

  • X-Request-Id:请求 ID
  • X-Timestamp:时间戳
  • X-Signature签名HMAC_SHA256

服务端签名计算:

  • canonical = X-Timestamp + "\n" + X-Request-Id + "\nPOST\n/api/v1/mall/dailyPush\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_daily_push.date
user_id string playX 用户 ID用于幂等入库 mall_daily_push.user_id 等;服务端会按 user_id/username 确保存在 mall_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_id
  • member[].login -> username
  • member[].yesterday_total_w -> yesterday_win_loss_net
  • member[].yesterday_total_deposit -> yesterday_total_deposit
  • member[].lty_deposit -> lifetime_total_deposit
  • member[].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=0msg 为缺少字段错误
  • 当签名不正确HTTP 401code=0msg 为 INVALID_SIGNATURE

示例(未开启签名校验)

请求:

curl -X POST 'http://localhost:1818/api/v1/mall/dailyPush' \
  -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/mall/dailyPush' \
  -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贵方需提供的 HTTP 接口)

商城在验 Token、红利发放、交易轮询、Angpow 导入等场景会 主动请求 playX / Cash Market
完整 URL、请求/响应字段、成功判定、与 Angpush 双路径关系、联调待办 已单独整理,便于 直接转发给 playX 平台

  • docs/playX-接口待完善清单.md

本文 §1 仅描述「谁调用商城」;§3 描述「H5 调用商城」。


3. 积分商城 → H5服务端提供给 H5 的接口)

3.0 数据模型说明(与代码一致)

  • 积分商城用户资产主表mall_user_asset(账号、积分、playx_user_idH5 临时登录 temLogin 直接创建/复用该表行,不依赖独立会员 user 表)。
  • 会话缓存mall_session(字段含 session_iduser_id(此处存 playX 侧用户标识字符串,与 mall_user_asset.playx_user_id 一致)、expire_time 等)。
  • 统一订单mall_order(红利/实物/提现订单;user_id 字段为 playx_user_id 字符串)。
  • 对外业务 ID:订单与推送中的 user_id 多为 playX 用户 IDplayx_user_id)。临时登录场景下,资产表会生成占位 ID形如 mall_{mall_user_asset.id}(见 MallUserAsset::ensureForUsername)。

3.1 鉴权解析规则(resolvePlayxAssetIdFromRequest

以下接口在服务端最终都会解析出 mall_user_asset.id(整型,资产表主键),再按该 ID 加载资产与关联数据。

优先级(由高到低):

  1. session_idpost 优先,get 兼容)
    • mall_session 中存在且未过期:用会话里的 user_idplayx_user_id 字符串)在 mall_user_assetplayx_user_id 查找,得到资产主键。
    • 若会话无效:兼容把 session_id 参数误当作 商城 token 再试一次UUID 形态 token
  2. tokenpost / get 或请求头 ba-token / token
    • 校验 token 表:类型为会员 user 或商城临时 muser。未过期时,user_id 字段为 mall_user_asset.idmuser)或会员体系约定 IDuser)。
  3. user_idpost / get 兼容)
    • 纯数字:视为 mall_user_asset.id
    • 非纯数字:视为 playx_user_id,在 mall_user_asset 按该字段查找主键。

注意:请求参数的取值方式是 post() 优先,get() 兼容(即同字段既可传 post 也可传 get


3.2 临时登录(获取商城 token

  • 方法:GET(推荐)或 POST
  • 路径:/api/v1/temLogin
  • 开关:config/buildadmin.phpagent_auth.temp_login_enabletrue;有效期 agent_auth.temp_login_expire(秒)。

请求参数

字段 类型 必填 说明
username string 登录名(唯一);不存在则自动创建 mall_user_asset

行为说明

  • 若用户名不存在:MallUserAsset::ensureForUsername 创建资产行(随机密码等),并将 playx_user_id 更新为 mall_{id} 形式(与真实 playX ID 区分)。
  • 签发 商城 token(类型 musertoken 表内 user_id = mall_user_asset.id),并签发 muser-refresh 刷新令牌。

返回(成功 data.userInfo

字段 类型 说明
id int mall_user_asset.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(推荐 GETtoken 亦可)
  • 路径:/api/v1/mall/verifyToken

配置:本地验证 vs 远程 playX

  • 配置项:config/playx.phpverify_token_local_only(环境变量 PLAYX_VERIFY_TOKEN_LOCAL_ONLY,未设置时默认为 **1 / 开启本地验证)。
  • verify_token_local_only = true(默认)
    • 不请求 playX HTTP。
    • 仅接受商城临时登录 token类型 muser),校验 token 表后写入 mall_session
    • 返回的 data.user_idplayx_user_id(与资产表一致)。
  • verify_token_local_only = false(生产对接 playX
    • 需配置 playX.api.base_url,由商城向 playX 发起 POST 校验(请求/响应约定见 docs/playX-接口待完善清单.md 第一部分 §1
    • 若未配置 base_url,返回 playX API not configured

请求参数

必填其一:

  • tokenBody 优先;session 兼容字段Query 也可传 token

返回(成功 data

字段 类型 说明
session_id string 写入 mall_session
user_id string playX 用户 IDplayx_user_id,会话内与订单/推送一致)
username string 用户名
token_expire_at string ISO 字符串(服务端 date('c', expireAt)

失败:

  • token 为空HTTP 401msg=INVALID_TOKEN
  • 远程模式且 playX 未配置:msg=playX API not configured

示例(本地验证)

curl -X POST 'http://localhost:1818/api/v1/mall/verifyToken' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'token=上一步TemLogin返回的token'

3.x 收货地址(mall_address

下面接口用于 H5 维护收货地址。鉴权同本章其他接口:携带 session_idtokenuser_id

3.x.1 地址列表

  • 方法:GET
  • 路径:/api/v1/mall/addressList

返回:data.list 为地址数组。

3.x.2 添加地址

  • 方法:POST
  • 路径:/api/v1/mall/addressAdd

Body

字段 必填 说明
receiver_name 收货人
phone 电话
region 地区(数组或逗号分隔字符串)
detail_address 详细地址
default_setting 1 设为默认地址

3.x.3 修改地址(含设为默认)

  • 方法:POST
  • 路径:/api/v1/mall/addressEdit

Bodyid 必填,其余字段按需传入更新。

3.x.4 删除地址

  • 方法:POST
  • 路径:/api/v1/mall/addressDelete

Bodyid 必填。若删除默认地址,服务端会自动挑选一条剩余地址设为默认(如存在)。


3.4 用户资产Assets

  • 方法:GET
  • 路径:/api/v1/mall/assets

请求参数(鉴权)

以下任选其一即可(与 3.1 鉴权解析规则 一致):

  • session_id
  • token(或请求头 ba-token / token
  • user_id(纯数字为 mall_user_asset.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/mall/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/mall/claim

请求 Body

必填:

  • claim_request_id幂等键string唯一

鉴权:同 3.1session_id / token / user_id

返回(成功 data

用户资产 返回字段一致(资产快照)。

幂等:

  • claim_request_id 已存在:不会重复入账,直接返回当前资产快照

示例

curl -X POST 'http://localhost:1818/api/v1/mall/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/mall/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/mall/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/mall/bonusRedeem

请求 Body

必填:

  • item_id:商品 ID要求 mall_item.type=BONUSstatus=1 鉴权:同 3.1session_id / token / user_id

返回(成功)

  • msgRedeem submitted, please wait about 10 minutes
  • data.order_id:订单 ID
  • data.statusPENDING

示例

curl -X POST 'http://localhost:1818/api/v1/mall/bonusRedeem' \
  -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/mall/physicalRedeem

请求 Body

必填:

  • item_id:商品 ID要求 mall_item.type=PHYSICALstatus=1
  • address_id:收货地址 IDmall_address.id,须属于当前用户资产;下单时写入 mall_order.mall_address_id,并将该地址快照写入 receiver_name / receiver_phone / receiver_address 鉴权:同 3.1session_id / token / user_id

返回(成功)

  • msgRedeem success
  • datanull

示例

curl -X POST 'http://localhost:1818/api/v1/mall/physicalRedeem' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'item_id=200' \
  --data-urlencode 'address_id=10' \
  --data-urlencode 'session_id=7b1c....'

响应(示例):

{
  "code": 1,
  "msg": "Redeem success",
  "time": 1712345687,
  "data": null
}

3.9 提现申请Withdraw Apply

  • 方法:POST
  • 路径:/api/v1/mall/withdrawApply

请求 Body

必填:

  • item_id:商品 ID要求 mall_item.type=WITHDRAWstatus=1 鉴权:同 3.1session_id / token / user_id

返回(成功)

  • msgWithdraw submitted, please wait about 10 minutes
  • data.order_id:订单 ID
  • data.statusPENDING

示例

curl -X POST 'http://localhost:1818/api/v1/mall/withdrawApply' \
  -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/mall/orders

请求参数(鉴权)

3.1session_id / token / user_id)。

返回(成功 data

  • list:订单列表(最多 100 条),并包含关联的 mallItem(关系对象)
  • 列表项中的 user_idplayX 侧 playx_user_id(字符串),与 mall_order.user_id 一致

示例

请求:

curl -G 'http://localhost:1818/api/v1/mall/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 积分流水Points Logs

  • 方法:GET
  • 路径:/api/v1/mall/pointsLogs

请求参数(鉴权)

3.1session_id / token / user_id)。

Query 参数

字段 类型 必填 说明
limit int 每页条数,默认 20最大 100
cursor string 游标(上一页返回 next_cursor
direction string IN 仅入账 / OUT 仅扣减;不传返回全部

返回(成功 data

字段 类型 说明
data.list array 流水数组(时间倒序)
data.next_cursor string|null 下一页游标(本页最后一条记录的游标)

list 每一项字段:

字段 类型 说明
biz_type string CLAIM/REDEEM_BONUS/REDEEM_PHYSICAL/REDEEM_WITHDRAW/REFUND
direction string IN/OUT
points int 积分变动值(正数,方向由 direction 表示)
ts int Unix 秒
ref_id string 领取为 claim_request_id;订单为 external_transaction_id
order_no string 订单号(非订单类为空)
order_status string 订单状态(非订单类为空)
mallItem object|null 商品信息(非订单类为 null
item_id int 兼容字段商品ID非订单类为 0
item_title string 兼容字段:商品标题(非订单类为空)
item_type int 兼容字段:商品类型(非订单类为 01=BONUS 2=PHYSICAL 3=WITHDRAW
item_score int 兼容字段:商品所需积分(非订单类为 0
cursor string 当前记录游标

mallItem 字段(非隐私):

字段 类型 说明
id int mall_item.id
title string 商品名
type int 商品类型
score int 所需积分
amount number 现金面值
multiplier int 流水倍数
category string 红利业务类别
category_title string 类别展示名

示例

curl -G 'http://localhost:1818/api/v1/mall/pointsLogs' \
  --data-urlencode 'token=上一步temLogin返回的token' \
  --data-urlencode 'limit=20'

3.12 同步额度(可选)

当前代码未实现并未注册路由:/api/v1/mall/syncLimit