Files
webman-buildadmin-mall/docs/积分商城-PlayX对接实施方案.md

20 KiB
Raw Blame History

积分商城 PlayX 对接实施方案

基于《积分商城-内部对接与流程说明.md》和《PlayX-对接文档(积分商城).md》整理结合当前项目结构给出具体落地方案。


一、接口创建

1.1 商城需对外提供的接口PlayX 调用商城)

Daily Push API

接收 PlayX 每日 T+1 数据推送。

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

当配置了 playx.daily_push_secretDaily Push 签名校验)时,需要携带:

  • 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)}
  • expectedhash_hmac('sha256', canonical, daily_push_secret)
请求Body
字段 类型 必填 说明
request_id string 外部推送请求号(原样返回)
date string(YYYY-MM-DD) 业务日期(入库到 mall_playx_daily_push.date
user_id string PlayX 用户 ID幂等键组成部分
username string 展示冗余
yesterday_win_loss_net number 昨日净输赢(仅当 < 0 时计算新增保障金)
yesterday_total_deposit number 昨日总充值(用于计算今日可领取上限)
lifetime_total_deposit number 历史总充值
lifetime_total_withdraw number 历史总提现
幂等
  • 幂等键:user_id + date
  • 重复推送时不会重复入账,返回 data.deduped = true
返回Response

通用返回包结构:{ code, msg, time, data }

成功返回data

  • data.request_id:原样返回
  • data.acceptedtrue
  • data.deduped:是否幂等命中(false=首次入库,true=重复推送)
  • data.message:首次为 ok,重复为 duplicate input
示例

无签名校验(PLAYX_DAILY_PUSH_SECRET 为空):

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"
  }
}

1.2 商城需调用的 PlayX 接口(外部,由 PlayX 提供)

以下为商城侧调用(由 PlayX 提供)。

Token Verification API

  • 方法: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(必选)
  • username(可选)
  • token_expire_at(可选,能被 strtotime 解析)
示例
{
  "request_id": "mall_abc123",
  "token": "PLAYX_TOKEN_XXX"
}
{
  "user_id": "U123",
  "username": "demo_user",
  "token_expire_at": "2026-04-01T12:00:00Z"
}

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 订单幂等键: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
示例(请求)
{
  "request_id": "mall_bonus_abc123",
  "externalTransactionId": "BONUS_ORD2026....",
  "user_id": "U123",
  "amount": 10.0,
  "rewardName": "每日红利",
  "category": "daily",
  "categoryTitle": "每日",
  "multiplier": 1
}
返回(期望)

商城侧以 data.status 判断:

  • status = "accepted":读取 playx_transaction_id
  • 否则:读取 message 写入 fail_reason

示例accepted

{
  "status": "accepted",
  "playx_transaction_id": "PX_TX_001"
}

示例reject

{
  "status": "rejected",
  "message": "insufficient balance"
}

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 订单幂等键:external_transaction_id
user_id string PlayX 用户 ID
amount number 订单金额:MallPlayxOrder.amount
multiplier int MallPlayxOrder.multiplier
返回(期望)

与 Bonus Grant 一致:

  • 成功:status="accepted" + playx_transaction_id
  • 失败:message 写入 fail_reason
示例

请求(示意,由商城侧发起,实际 URL 以配置为准):

{
  "request_id": "mall_withdraw_abc123",
  "externalTransactionId": "WITHDRAW_ORD2026....",
  "user_id": "U123",
  "amount": 100.0,
  "multiplier": 1
}

响应accepted

{
  "status": "accepted",
  "playx_transaction_id": "PX_TX_002"
}

响应rejected

{
  "status": "rejected",
  "message": "insufficient balance"
}

Transaction Status Query API交易终态查询

  • 方法:GET
  • URL${playx.api.base_url}${playx.api.transaction_status_url}(默认 /api/v1/transaction/status
Query
  • externalTransactionId:订单幂等键
示例(请求)
curl -G '${playx.api.base_url}/api/v1/transaction/status' \
  --data-urlencode 'externalTransactionId=BONUS_ORD2026....'
返回(期望)

商城读取 data.status

  • COMPLETED:设置订单 status=COMPLETED
  • FAILEDREJECTED:设置订单 status=REJECTEDgrant_status=FAILED_FINAL,并退回积分;失败信息取 data.message

示例completed

{ "status": "COMPLETED" }

示例failed

{ "status": "FAILED", "message": "grant rejected by PlayX" }

1.3 商城内部 API供 H5 前端调用)

Token 验证

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

请求Body

  • token(必填,优先读取)
  • 兼容:session(当 token 为空时当作 token 使用)

成功返回data

  • session_id
  • user_id
  • username
  • token_expire_atISO 时间字符串)

示例:

curl -X POST 'http://localhost:1818/api/v1/mall/verifyToken' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'token=PLAYX_TOKEN_XXX'

用户资产

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

请求参数(二选一):

  • session_id(优先):从 mall_playx_session 查 user_id并校验过期
  • user_id:直接使用(兼容)

成功返回data

  • locked_points
  • available_points
  • today_limit
  • today_claimed
  • withdrawable_cashavailable_points * points_to_cash_ratio,保留 2 位)
示例
curl -G 'http://localhost:1818/api/v1/mall/assets' --data-urlencode 'session_id=7b1c....'
{
  "code": 1,
  "msg": "",
  "data": {
    "locked_points": 100,
    "available_points": 50,
    "today_limit": 200,
    "today_claimed": 80,
    "withdrawable_cash": 5.2
  }
}

领取Claim

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

请求:

  • claim_request_id幂等键string必填且唯一
  • 鉴权:session_iduser_id

成功返回data与资产接口一致locked_points/available_points/today_limit/today_claimed/withdrawable_cash

示例

(首次领取成功,可能返回 msg=Claim success;若幂等重复,msg 可能为空)

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 'session_id=7b1c....'
{
  "code": 1,
  "msg": "Claim success",
  "data": {
    "locked_points": 60,
    "available_points": 90,
    "today_limit": 200,
    "today_claimed": 120,
    "withdrawable_cash": 9.0
  }
}

商品列表

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

请求(可选):

  • type=BONUS|PHYSICAL|WITHDRAW

成功返回data

  • listmall_item 列表(包含 amount/multiplier/category/category_title 等字段)
示例
curl -G 'http://localhost:1818/api/v1/mall/items' --data-urlencode 'type=BONUS'
{
  "code": 1,
  "msg": "",
  "data": {
    "list": [
      {
        "id": 123,
        "title": "每日红利",
        "type": 1,
        "score": 100,
        "amount": 10.0,
        "multiplier": 1,
        "category": "daily",
        "category_title": "每日"
      }
    ]
  }
}

红利兑换Bonus Redeem

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

请求:

  • item_id:商品 IDBONUS
  • 鉴权:session_iduser_id

成功返回data

  • order_id
  • 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",
  "data": {
    "order_id": 456,
    "status": "PENDING"
  }
}

实物兑换Physical Redeem

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

请求:

  • item_id:商品 IDPHYSICAL
  • address_idmall_address.id(当前用户资产下地址;订单写入 mall_address_id 与收货快照)
  • 鉴权:session_iduser_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",
  "data": null
}

提现申请Withdraw Apply

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

请求:

  • item_id:商品 IDWITHDRAW
  • 鉴权:session_iduser_id

成功返回data

  • order_id
  • 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",
  "data": {
    "order_id": 789,
    "status": "PENDING"
  }
}

订单列表

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

请求:

  • session_iduser_id

成功返回data

  • list:订单列表(最多 100 条,包含关联的 mallItem
示例
curl -G 'http://localhost:1818/api/v1/mall/orders' --data-urlencode 'session_id=7b1c....'
{
  "code": 1,
  "msg": "",
  "data": {
    "list": [
      {
        "id": 456,
        "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 }
      }
    ]
  }
}

同步额度

当前代码未实现并未注册路由:/api/v1/mall/syncLimit。 如需补齐,请在接口设计阶段新增对应实现与 PlayX API 对接。


二、后台修改

2.1 商品管理mall_item

  • 类型:需与文档一致
    • 1 = BONUS红利
    • 2 = PHYSICAL实物
    • 3 = WITHDRAW提现档位
  • 新增字段(红利/提现档位):
    • amount:现金面值(元)
    • multiplier:流水倍数
    • category:红利业务类别(如 daily
    • category_title:类别展示名
  • 库存:实物需 stock;红利/提现可不限制或按业务配置

2.2 订单管理(统一订单表)

  • 订单类型BONUS / PHYSICAL / WITHDRAW
  • 订单状态PENDING / COMPLETED / SHIPPED / REJECTED
  • 实物订单
    • 发货:录入物流公司、单号 → SHIPPED
    • 驳回:录入驳回原因 → REJECTED,自动退回积分
  • 红利/提现订单
    • 展示 external_transaction_idplayx_transaction_id、发放子状态
    • 手动重试:仅对 FAILED_RETRYABLE 状态,记录 retry_request_id、操作者、原因

2.3 用户资产与人工调账

  • 用户资产:按 user_id 展示 locked_pointsavailable_pointstoday_limittoday_claimed
  • 人工调账:针对 T+1 推送异常或客诉,支持对 locked_pointsavailable_points 手动加减,并记录审计日志

2.4 每日推送数据

  • 后台可查看 mall_playx_daily_push 表数据,按 user_iddate 查询,便于排查异常

三、数据库修改

3.1 用户资产表(改造 mall_player 或新建)

方案 A改造 mall_player

  • user_id 作为主键PlayX 的 user_id或与现有 id 并存
  • 新增字段:
    • locked_points(待领取积分)
    • available_points(可用积分)
    • today_limit(今日可领取上限)
    • today_claimed(今日已领取)
    • today_limit_date(今日上限所属日期,用于每日重置)

方案 B新建 mall_playx_user_asset

  • user_idPlayX 用户 ID主键或唯一
  • username(冗余展示)
  • locked_pointsavailable_pointstoday_limittoday_claimedtoday_limit_date
  • create_timeupdate_time

3.2 每日推送数据表(新建)

表名mall_playx_daily_push

字段 类型 说明
id int 主键
user_id varchar(64) 玩家 ID
date date 业务日期
username varchar(100) 展示名
yesterday_win_loss_net decimal(15,2) 昨日净输赢
yesterday_total_deposit decimal(15,2) 昨日总充值
lifetime_total_deposit decimal(15,2) 历史总充值
lifetime_total_withdraw decimal(15,2) 历史总提现
create_time bigint 创建时间

唯一索引(user_id, date) 幂等去重


3.3 领取记录表(幂等)

表名mall_playx_claim_log

字段 类型 说明
id int 主键
claim_request_id varchar(64) 幂等键,唯一
user_id varchar(64) 用户 ID
claimed_amount int 领取积分
create_time bigint 创建时间

唯一索引claim_request_id


3.4 统一订单表(改造或新建)

表名mall_playx_order(或统一改造 mall_pints_order / mall_redemption_order

字段 类型 说明
id int 主键
user_id varchar(64) 用户 ID
type enum BONUS / PHYSICAL / WITHDRAW
status enum PENDING / COMPLETED / SHIPPED / REJECTED
mall_item_id int 商品 ID
points_cost int 消耗积分
amount decimal(15,2) 现金面值(红利/提现)
multiplier int 流水倍数
external_transaction_id varchar(64) 订单号
playx_transaction_id varchar(64) PlayX 流水号
grant_status enum NOT_SENT / SENT_PENDING / ACCEPTED / FAILED_RETRYABLE / FAILED_FINAL
fail_reason text 失败原因
retry_count int 重试次数
reject_reason varchar(255) 驳回原因(实物)
shipping_company varchar(50) 物流公司
shipping_no varchar(64) 物流单号
mall_address_id int unsigned, NULL 实物兑换所选 mall_address.id(快照仍见 receiver_*
receiver_name varchar(50) 收货人
receiver_phone varchar(20) 收货电话
receiver_address text 收货地址
create_time bigint 创建时间
update_time bigint 更新时间

索引user_idexternal_transaction_idtypestatus


3.5 商品表mall_item扩展

新增字段(若不存在):

字段 类型 说明
amount decimal(15,2) 现金面值(红利/提现档位)
multiplier int 流水倍数
category varchar(32) 红利业务类别
category_title varchar(64) 类别展示名

四、业务规则

4.1 计算规则(需配置)

  • 返还比例新增保障金 = ABS(yesterday_win_loss_net) * 返还比例(仅 yesterday_win_loss_net < 0 时)
  • 解锁比例今日可领取上限 = yesterday_total_deposit * 解锁比例
  • 提现折算:积分 → 现金(如 10 分 = 1 元),用于前端展示

4.2 每日重置

  • today_claimedtoday_limit 按业务日重置(today_limit_date 变化时)

4.3 发放重试

  • 自动重试1min / 5min / 15min最多 3 次,使用同一 externalTransactionId
  • 仅对 NOT_SENTFAILED_RETRYABLE 重试
  • 收到 accepted 后不再重试,改轮询交易终态查询 API

五、实施顺序建议

  1. 数据库:新增迁移(mall_playx_daily_pushmall_playx_claim_logmall_playx_order),扩展 mall_itemmall_player(或新建资产表)
  2. 模型MallPlayxDailyPushMallPlayxClaimLogMallPlayxOrder、扩展 MallItemMallPlayer
  3. 接口Daily Push API含签名校验→ Token 验证 → 资产/领取 → 商品列表 → 红利/实物/提现 → 订单列表
  4. 后台:商品扩展、订单管理(含发货/驳回/重试)、人工调账、每日推送数据查看
  5. 定时任务:轮询交易终态、自动重试失败发放

六、待确认事项

  • PlayX 提供的 Token Verification APIBonus Grant APIBalance Credit API交易终态查询 API 的 URL、鉴权方式、字段最终表
  • date 的时区定义(如 UTC+8
  • 返还比例、解锁比例、提现折算的具体数值
  • 是否启用「同步额度」功能(需 PlayX 提供对应 API