# api 核心业务流程 ## 1. SDK 请求协议流程 适用范围:多数继承 `app\api\controller\Api` 的接口。 ```mermaid 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` 白名单逻辑: - 控制器白名单:`register`、`sendSms`、`CheckUpdate`、`complexLogin`、`startup`、`forget`、`privacy`、`config`、`Third`。 - 方法白名单由每个控制器的 `$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 生成格式: ```text 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 中建议拆成 `RegisterUser` 和 `CreateSession`。 - 注册频控函数名拼写为 `regidterHandle`,迁移时注意不要遗漏。 - `channel_id=EMPTY_CHANNEL_ID` 有特殊开关。 ## 5. 游戏充值下单流程 主入口:`v1\Pay::index` 实际核心服务:`application\service\GamePayService::gamePay` ```mermaid 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 发货通知参数时,签名大致包含: ```text 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=1`、`pay_time`、必要的回调流水号。 - 更新 `cy_paycpinfo.payflag=1`。 - 增加玩家累计充值。 - 更新代金券状态。 - 调用 `PayCallback::callBackToCp(orderid)`。 - 推送支付预警和渠道通知。 7. 提交事务。 Go 迁移注意: - 每个支付渠道成功响应文本不同,例如 `success`、`SUCCESS`、`ok`、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. 支付回调进入 `PayNotifyCoin` 或 `PayNotifyService`。 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.php`、`complex\*.php` 流程: 1. 客户端或渠道提交渠道标识、游戏 ID、渠道用户信息。 2. 查询 `nw_complex_channel` 和 `cy_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` 支付回调流程: ```mermaid 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_id`、`amount`、`provider_order_id`。 - H5 渠道存在 `paySignH5` 分支,不能只实现标准 `paySign`。 ## 10. 实名与防沉迷 主要代码: - `v1\Identity.php` - `v2\User::personalIdentity` - `v2\ComplexAuthentication.php` - `application\service\CommonService::authentication` 实名类型: - 平台实名:写 `cy_memberstwo`。 - 中宣部实名:按游戏 `game_auth_type` 和 `game_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 迁移关键路径。