03-core-business-flows.md 15 KB

api 核心业务流程

1. SDK 请求协议流程

适用范围:多数继承 app\api\controller\Api 的接口。

flowchart TD
    A["客户端提交密文 body"] --> B["Api::_initialize"]
    B --> C["读取 header/body device"]
    C --> D["AES-128-ECB 解密 body"]
    D --> E{"qmapidebug 或解密失败?"}
    E -->|debug 明文| F["input() 参数"]
    E -->|正常密文| G["JSON decode"]
    F --> H["校验 appid"]
    G --> H
    H --> I["查询 App: id/appkey/client_appkey/gameid"]
    I --> J["MD5 签名校验"]
    J --> K["补默认 channel_id"]
    K --> L["checkLogin"]
    L --> M["业务 handler"]
    M --> N["jsonResult"]
    N --> O{"debug?"}
    O -->|是| P["明文 JSON"]
    O -->|否| Q["AES 加密 JSON"]

关键兼容点:

  • qmapidebug 是 header bool 值,开启后不需要密文。
  • 生产响应默认是加密字符串,不是 JSON。
  • jsonResult 会把业务 code 映射为 code=1/0,原始错误码保留在 err_code

2. 登录态校验流程

代码入口:Api::checkLogin

白名单逻辑:

  • 控制器白名单:registersendSmsCheckUpdatecomplexLoginstartupforgetprivacyconfigThird
  • 方法白名单由每个控制器的 $noNeedLogin 定义。

校验步骤:

  1. 校验 imeil 必填。
  2. 从参数或 header 读取 token
  3. Redis 查询 token => imeil,必须存在且与当前 imeil 一致。
  4. 使用 auth_code(token, DECODE, auth_key) 解出:
    • userid
    • username
    • gameid
    • sub_username
    • token_random
    • type
  5. Redis 查询 token|{type}|{userid}|{gameid},必须等于 token_random
  6. 注入运行时输入:
    • userid
    • gameid
    • username
    • token
    • sub_username
    • member_channel_id = getChannelId(userid, gameid)
  7. v2.user/personalIdentity 进行请求频率限制。

Go 迁移建议:

  • 用 request context 保存 AuthUser,不要继续改写请求参数 map。
  • Redis key 兼容期必须保持原样。
  • auth_code 算法需要先移植并做 golden test,否则旧 token 全部失效。

3. 登录流程

主要代码:v1\Login.php

账号密码登录

入口:Login::index

核心步骤:

  1. 校验用户名、密码、设备、游戏、渠道、IMEI。
  2. 海外游戏会使用 IpLimit 限制国内 IP。
  3. 校验渠道状态:
    • 渠道存在。
    • 渠道 level=3
    • 渠道未冻结登录。
  4. 校验游戏状态:
    • 游戏存在。
    • 游戏未下架。
    • 游戏未禁止登录,或玩家在白名单。
  5. 密码使用 auth_code(password, ENCODE, auth_key) 后匹配 cy_members
  6. 检查账号冻结、子账号冻结、风控冻结、IP/IMEI 封禁。
  7. 进入 loginProcess

loginProcess

职责:

  • 检查 IP/IMEI 封禁。
  • 检查渠道禁止登录。
  • 执行防沉迷登录时间限制:
    • 未成年只允许周五、周六、周日和法定节假日 20:00 到 21:00。
    • 命中时返回 offline_time
  • 建立或更新玩家游戏渠道关系 cy_member_channel_game_rel
  • 建立或更新子账号 nw_subaccount
  • 记录登录日志 cy_logininfo
  • 保存设备信息。
  • 生成 token 并写 Redis。

Token 生成格式:

userid|username|gameid|sub_username|token_random|sdk

返回字段通常包含:

  • token
  • username
  • sub_username
  • userid
  • gameid
  • imeil
  • offline_time

第三方登录

入口:

  • Login::loginQq
  • Login::third

第三方登录会按不同类型查找或创建用户:

  • 微信:wx_uid
  • QQ:qq_uid
  • 抖音:dy_uid

如果第三方用户不存在,会生成随机账号、写入主账号和相关扩展表,然后复用 loginProcess

4. 注册流程

主要代码:v1\Register.php

入口:Register::index

核心步骤:

  1. 收集注册参数:
    • username
    • password
    • gameid
    • imeil
    • channel_id
    • dy_channel_id
    • type
  2. 校验用户名格式、密码长度、游戏和渠道。
  3. 海外游戏 IP 限制。
  4. 校验渠道:
    • 渠道存在。
    • 渠道 level=3
    • 渠道注册未被冻结。
  5. 校验游戏:
    • 游戏存在并上线。
    • 游戏未禁止注册。
    • 特殊游戏必须手机号注册。
  6. 游客注册逻辑:
    • 如关闭游客模式,直接拒绝。
    • imeil 查询是否已有游客账号,有则直接返回登录信息。
  7. 手机注册逻辑:
    • 校验短信验证码。
    • 手机号写入 mobile
  8. 校验重复账号。
  9. 校验空渠道、分包配置、IP/IMEI 封禁。
  10. 执行注册频控:
    • 每月同 IP 注册上限。
    • 每月同 IMEI 注册上限。
  11. 写入主账号和扩展记录:
    • cy_members
    • cy_member_history
    • cy_memberstwo
    • mw_dy_channel_rel
  12. 进入注册内的 loginProcess
    • 建立 cy_member_channel_game_rel
    • 建立 nw_subaccount
    • 保存设备
    • 生成 token
    • 写 Redis

Go 迁移注意:

  • 注册函数当前把“注册”和“自动登录”合在一起,Go 中建议拆成 RegisterUserCreateSession
  • 注册频控函数名拼写为 regidterHandle,迁移时注意不要遗漏。
  • channel_id=EMPTY_CHANNEL_ID 有特殊开关。

5. 游戏充值下单流程

主入口:v1\Pay::index

实际核心服务:application\service\GamePayService::gamePay

flowchart TD
    A["客户端 Pay::index"] --> B["Pay.add 参数校验"]
    B --> C["GamePayService::gamePay"]
    C --> D["支付方式场景识别"]
    D --> E["PayHandle 随机/管控选择真实 paytype"]
    E --> F["校验用户、子账号、渠道、游戏、CP 回调地址"]
    F --> G["代金券/专属币/平台币金额计算"]
    G --> H["重复下单和 attach 去重"]
    H --> I["防沉迷充值限额"]
    I --> J["事务写 cy_pay 与 cy_paycpinfo"]
    J --> K["扣减券/专属币/平台币"]
    K --> L{"real_amount 是否为 0?"}
    L -->|是| M["订单直接支付成功"]
    L -->|否| N["PayService 生成第三方支付参数"]
    M --> O["返回订单结果"]
    N --> O

下单关键参数

来自 validate\Pay.php

  • gameid
  • userid
  • appid
  • serverid
  • servername
  • amount
  • roleid
  • attach
  • channel_id
  • productname
  • paytype
  • imeil
  • mc_id
  • coupon_member_id

支付资产类型

字段/标识 含义
amount 游戏充值原始金额
real_amount 需要第三方支付的现金金额
ptb_amt 游戏专属币使用金额
coin_amt 平台币使用金额
coupon_member_id 代金券实例 ID
coupon_amount 本单抵扣的代金券金额
mix-xxx 专属币加第三方混合支付
coin-xxx 平台币加第三方混合支付
coupon 纯代金券支付

订单写入

主要表:

  • cy_pay
  • cy_paycpinfo
  • cy_member_history
  • cy_member_channel_game_rel
  • nw_subaccount
  • member_coin/member_zscoin 相关明细表
  • cy_coupon_member

CP 回调参数

GamePayService 创建 CP 发货通知参数时,签名大致包含:

orderid={orderid}&username={sub_username}&gameid={gameid}&roleid={roleid}&serverid={serverid}&paytype={paytype}&amount={amount}&paytime={time}&attach={attach}&appkey={appkey}

签名方式:MD5。

Go 迁移注意:

  • attach 在同一 gameid 下必须唯一。
  • 支付方式有版本灰度和随机路由,不能只按客户端传入 paytype 处理。
  • 金额计算使用 bc* 高精度函数,Go 中必须用 decimal,不能用 float64。
  • 下单事务中包含订单、CP 通知、资产扣减或券状态修改,必须整体回滚。

6. 支付回调流程

主要入口:

  • v1\PayNotify.php:游戏充值现金回调。
  • v1\PayNotifyCoin.php:平台币充值现金回调。
  • service\PayNotifyService.php:平台币订单成功后加余额。
  • service\NotifyService.php:聚合渠道支付回调。

游戏充值回调

常见方法:

  • alipay
  • alipayAop
  • wxpayh5
  • ybzf_pay
  • ldzf_wx_pay
  • qzl_pay
  • yyyb_pay
  • xty_pay
  • airwallex
  • dianhun

统一核心:PayNotify::updateOrder

流程:

  1. 渠道回调入口完成验签。
  2. 归一化得到平台订单号和支付金额。
  3. 查询 cy_pay
  4. 如果已成功且金额一致,返回成功。
  5. 如果金额不一致,记录错误并失败。
  6. 如果待支付:
    • 开启事务。
    • 更新 cy_pay.status=1pay_time、必要的回调流水号。
    • 更新 cy_paycpinfo.payflag=1
    • 增加玩家累计充值。
    • 更新代金券状态。
    • 调用 PayCallback::callBackToCp(orderid)
    • 推送支付预警和渠道通知。
  7. 提交事务。

Go 迁移注意:

  • 每个支付渠道成功响应文本不同,例如 successSUCCESSok、XML、JSON。
  • 不要把渠道响应统一成 JSON,否则第三方会重复回调。
  • updateOrder 必须幂等。
  • 回调金额单位有“元”和“分”的差异,适配器必须明确标准化。

平台币充值回调

核心:PayNotifyService::updateOrderCoin

流程:

  1. 查询 member_coin_pay
  2. 已成功且金额一致,返回 true。
  3. 金额不一致,记录错误。
  4. 待支付时开启事务:
    • member_coin_pay.status=1
    • member_coin_info 余额流水。
    • 更新 cy_members.amount
  5. 提交事务。

7. 平台币充值流程

入口:v2\Coin::pay

核心服务:application\service\MemberCoinService::payCoin

流程:

  1. 判断账号类型:
    • 给自己充值。
    • 给指定账号充值。
  2. 支付方式按版本和配置随机路由。
  3. 创建 member_coin_pay 订单,订单号前缀为 COIN
  4. 调用 PayService 生成支付参数。
  5. 支付回调进入 PayNotifyCoinPayNotifyService
  6. 成功后更新平台币余额和明细。

8. 订单查询和取消流程

入口:v2\Order.php

能力:

  • orderList:游戏充值和平台币充值列表。
  • orderInfo:订单详情,含 CP 发货状态。
  • cancelOrder:取消待支付订单。
  • getMemberCoinList:平台币流水。
  • getPayInfo:支付相关信息。

状态:

status 含义
0 待支付
1 支付成功
2 已取消
3 超时取消

Go 迁移注意:

  • 游戏订单来自 cy_pay
  • 平台币订单来自 member_coin_pay
  • 游戏订单列表会按安卓/iOS 绑定游戏做跨端合并。

9. 聚合渠道登录和支付

聚合渠道登录

主要代码:v1\ComplexLogin.phpcomplex\*.php

流程:

  1. 客户端或渠道提交渠道标识、游戏 ID、渠道用户信息。
  2. 查询 nw_complex_channelcy_polychannel_game
  3. 动态实例化 app\api\complex\{Channel}
  4. 调用适配器 checkLogin 校验渠道登录签名。
  5. 归一化渠道用户数据。
  6. 创建或更新 nw_complex_members
  7. 写聚合登录日志。
  8. 返回聚合用户信息。

聚合渠道支付

主要代码:

  • v1\ComplexPay.php
  • v1\ComplexPayNotify.php
  • service\NotifyService.php
  • complex\*.php

支付回调流程:

flowchart TD
    A["渠道支付回调"] --> B["NotifyService::notify(channel,input)"]
    B --> C["查询 complex_channel"]
    C --> D["查询 polychannel_game"]
    D --> E["实例化 complex adapter"]
    E --> F["paySign 或 paySignH5"]
    F --> G["getData 归一化 orderid/amount/sub_orderid"]
    G --> H["checkOrder 校验 nw_complex_pay 金额"]
    H --> I["事务更新 nw_complex_pay 和 cy_paycpinfo"]
    I --> J["更新 nw_complex_members.total_pay_amount"]
    J --> K["PayCallback::callBackToCp"]
    K --> L["adapter getSuccess/getFail"]

Go 迁移注意:

  • 适配器返回给渠道的成功/失败响应必须保留渠道原格式。
  • getData 的归一化字段至少应统一为 order_idamountprovider_order_id
  • H5 渠道存在 paySignH5 分支,不能只实现标准 paySign

10. 实名与防沉迷

主要代码:

  • v1\Identity.php
  • v2\User::personalIdentity
  • v2\ComplexAuthentication.php
  • application\service\CommonService::authentication

实名类型:

  • 平台实名:写 cy_memberstwo
  • 中宣部实名:按游戏 game_auth_typegame_bizid 调用外部 authSdk.Authentication,结果写 nw_subaccount

防沉迷逻辑:

  • 登录限制:
    • 未成年人只允许特定日期 20:00 到 21:00。
  • 充值限制:
    • 未满 12 岁不能充值。
    • 12 到 16 岁,单笔 50,月累计 200。
    • 16 到 18 岁,单笔 100,月累计 400。

Go 迁移注意:

  • 身份证年龄计算要与 PHP helper isMeetAgeByIDCard 结果一致。
  • 中宣部认证有“查询中、成功、失败”等状态,应保留 auth_status 语义。
  • CommonService::handleBelongToBinding 会按实名信息调整渠道归属,是支付分成相关逻辑,不能遗漏。

11. 代金券流程

主要代码:v2\Coupon.php

能力:

  • getMyCoupon:用户券列表,按未使用、已使用、已失效分组。
  • getCoupon:可领取券列表。
  • receiveCoupon:领取券。
  • exchangeCoupon:兑换码换券。
  • getPayCoupon:获取当前订单金额可用券。

领取流程:

  1. 查询券模板 cy_coupon
  2. 校验展示状态、有效期、库存。
  3. 查询 cy_coupon_member 中未绑定用户的券码。
  4. 开启事务。
  5. 增加模板 receive_num
  6. 把券码绑定给当前 member_id
  7. 按固定有效期或领取后 N 天设置有效期。

支付使用:

  • 下单时 GamePayService 调用 CouponMember::getPayCoupon
  • 若订单需要现金支付,券状态从 is_use=1 改为 2
  • 若纯券支付成功,券状态从 is_use=1 改为 3

12. MLBB 活动流程

主要代码:controller\mlbb

独立登录态:

  • Token 格式:user_id|username|token_random|sdk|mlbb
  • Redis key:token|sdk|mlbb|user_id
  • 有独立的 Mlbb::validateToken

核心活动:

  • Login::index:活动登录,创建 lr_user_info
  • Game::reservation:预约。
  • Game::getRole:查询 SDK 角色。
  • Game::bandRole:绑定角色到活动账户。
  • Game::getTask / getTaskComplete:任务读取和完成。
  • Game::handleLottery:抽奖,消耗次数,发礼包码或代金券。
  • Activity::getInfo:冒险团信息。
  • Activity::claim:阶段奖励领取。
  • Other::importGift:导入礼包码。

主要表:

  • lr_user_info
  • lr_user_gathering
  • lr_user_task
  • lr_user_winning
  • lr_prize
  • lr_prize_gift
  • lr_claim

Go 迁移建议:

  • 该模块活动属性强,建议先确认是否仍在线。
  • 如果已结束,优先保留数据和后台补发能力,不作为主 SDK 迁移关键路径。