Files
live-hub-py/docs/HUYA_精英宝典-后端与9.1抓包对照.md
T

126 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 虎牙精英宝典后端与 9.1 全流程抓包对照
## 结论
当前后端不是“完全不能用”,而是保留了旧版 HTTP/WUP 兜底链路;最新浏览器链路已经切换为两个长连接 WSS:活动/绑定通道和商城/支付通道。查询和下单的多数字段与抓包兼容,真正需要优先更新的是兑换闭环、支付后订单状态和权益回查。
## 对照矩阵
| 优先级 | 领域 | 9.1 实际链路 | 当前后端 | 判断 | 更新动作 |
|---|---|---|---|---|---|
| P0 | 支付监听 | `orderDetailV5(orderId)` 重复查询,状态最终为 50 | `queryUserOrderList`,同时尝试 `shopMiddleUI/revenueWebUI` | 不一致 | 增加 `orderDetailV5` 结构和方法,按订单号轮询;以订单状态 50/已支付时间作为完成依据 |
| P0 | 支付超时 | 未支付仍是待支付/取消状态 | `huya_runner_recharge.py:535-537` 将 timeout 标为 `success` | 业务错误 | 超时改为 `pending``timeout`,不能写成功;前端显示“等待支付超时” |
| P0 | WSS 会话 | 活动通道 `d35bf373-ws.va.huya.com`,商城通道 `wsapi.huya.com`;每个会话有 launch、register、confirm、递增 requestId | `HuyaBatchRunner` 全部调用 `HuyaHttpClient``cdnws.api.huya.com` 单次 HTTP | 架构不一致 | 优先实现可复用的 WSS sessionHTTP 保留为显式 fallback,并记录 fallback 原因和重试次数 |
| P1 | 商城身份字段 | 最新商城业务 `UserId.sHuYaUA=web&1.0.0&huya``sGuid` 为空 | HTTP `_build_user` 使用 `webh5&0.0.1&websocket...`,并从 Cookie 派生 `sGuid` | 字段不一致 | 商城 WSS 优先;HTTP fallback 也应拆分商城 UserId,按最新 UA/空 guid 构造 |
| P1 | 兑换前置 | `getActPrizeDetail(sid=2203,pid)`,随后才 `scoreExchangePrize` | `huya_runner_goods.py:327-329` 直接提交兑换 | 缺步骤 | 增加 `GetActPrizeDetailReq/Resp`;提交前校验 `isCanExchange`、活动时间、库存、今日/用户限制和积分余额 |
| P1 | 兑换回查 | 成功后依次回查 `getUserScore``getUserPrizeRecords``getEntityPrizeFieldMap``getModuleAddress` | 成功后立即结束任务 | 缺步骤 | 将扣分、记录和地址状态写入任务结果,并更新 `HuyaAccount.points` |
| P1 | 活动初始化 | `getActInfo``getActUserTaskDetail``getUserScore``getUserPrizeRecords` 等并行查询 | `query_act_tasks` 只调用 `getActTaskDetail` | 信息不完整 | 增加 snapshot 任务或让页面初始化批量创建这些查询任务 |
| P1 | 权益到账 | 支付完成后仍需回查宝典积分/用户任务 | 只回查商城订单列表 | 信息不完整 | 订单完成后调用积分和用户任务接口,明确“已支付”和“宝典权益到账”两个状态 |
| P2 | 商品详情 | `getGoodsInfoV5` 直接返回 `spu=hy-5879340``sku=5370360`、场景 4 | 当前会先从任务详情发现 SPU,再以 sku=0 查询详情 | 兼容但脆弱 | 固定业务默认值仅作候选;优先使用实时商品详情返回的 SKU,不依赖任务类型 67 |
| P2 | 商品详情 gameId | 最新 `getGoodsInfoV5` 请求的 `gameId` 为空字符串 | `huya_runner_recharge.py:385-395` 传入 `game_id="0"` | 细节不一致 | 查询商品详情改为空字符串;`createOrderV5``gameId="0"` 仍保持不变 |
| P2 | 下单字段 | `orderType=6``src=4``orderScene=4``bizType=5``gameCategoryId=507``gameId="0"` | `create_order` 已按相同字段写入 | 基本一致 | 保留,增加请求快照脱敏日志以便字段回归 |
| P2 | 支付提交 | `payOrderSubmitV5(order,Zfb,QrCode,callback,env)` 返回支付宝网关 URL | 当前 HTTP 手工构造 WUP,字段基本一致 | 兼容但非同链路 | 迁移到商城 WSS;未迁移前保留 HTTP fallback |
| P2 | 支付渠道 | 先 `listPayChannelV5`,返回 `Zfb/Weixin` | 直接使用配置渠道 | 缺运行时校验 | 下单前读取可用渠道,不支持配置值时回退或报错 |
| P2 | 配置 | 本次活动 `sid=2203`、绑定活动 `9271`、外部活动响应为 `17096` | `outer_act_id` 默认 `9504` 且执行器未实际使用 | 配置过期/死字段 | 默认值改为当前活动值或删除该字段;将外部活动 ID 从 `getLiveLinkParam` 响应传递,不手填签名 |
| P2 | 数据隔离 | 业务响应随账号、绑定状态和库存变化 | `HuyaGoodsSnapshot``HuyaRechargeGoodsSnapshot` 为全局快照 | 并发风险 | 至少按 `sid/spu` 隔离;兑换可用性必须以执行账号的实时查询为准 |
## 已对上的部分
- `ActivityUserId``lUid``sHuYaUA`、Cookie 前缀和空 Token 形态与 9.1 活动帧兼容。
- `scoreExchangePrize` 的主体字段 `userId/sid/pid/ip/clientEnv/source/isRole` 已有结构,样本中后三项为空、`isRole=1` 与默认值一致。
- 商城 `CreateOrderReqV5` 已覆盖抓包中的完整 tag 0-32;`orderType=6` 和场景 4 正确。
- `payOrderSubmitV5` 的回调 URL、`Zfb``QrCode` 和环境 map 已覆盖抓包字段。
- `CreateOrderReqV5` 的下单 `gameId="0"` 与抓包一致;只有商品详情查询的 `gameId` 应为空字符串。
- Cookie、baseinfo、WUP requestId、支付宝订单和签名没有被当作固定常量,方向正确。
## 推荐更新顺序(按依赖关系)
之前的顺序有两个问题:把支付状态修复放在协议补齐之前,且把 WSS 迁移放在写操作之后。这样会先扩大旧链路的行为面,无法判断是业务字段还是传输方式导致失败。建议按下面的依赖顺序推进,每一步都能独立验证和回滚。
### 0. 固化 9.1 契约和回归样本
整理脱敏的请求/响应 fixture,明确活动通道、商城通道、WUP tag、状态码和动态字段边界。先不改线上任务行为,只增加解码测试。验收条件:9.1 中的关键 RPC 能稳定解码,动态 Cookie、签名、订单号不会进入 fixture。
### 1. 先修配置和身份模型
`sid=2203``bindActId=9271``outerActId=17096``moduleId=20051``scene=4``sourceId=yellowcarlist` 统一为活动配置;商城 UserId 与活动 UserId 分开,商城使用最新 `sHuYaUA`/空 `sGuid` 规则。此步只影响参数构造,不启用新写操作。
### 2. 补齐只读协议方法和响应结构
优先实现并测试:
```text
getActInfo
getActUserTaskDetail
getActPrizeDetail
getEntityPrizeFieldMap
getModuleAddress
listPayChannelV5
orderDetailV5
```
只读方法齐全后,才能用服务端实时数据做兑换和支付前置判断;不要先改 `scoreExchangePrize` 或支付提交。
### 3. 建立统一传输层,再做只读灰度
抽象活动 WSS session 和商城 WSS session,复用 launch/register/confirm、requestId FIFO、断线和超时处理。先让查询接口走 WSS,失败时明确记录原因并 fallback 到 HTTP;连续验证成功后再切换写接口。不要在每个 runner 中分别临时建 WebSocket。
### 4. 先做快照和数据隔离
页面初始化改为一次读取活动信息、用户任务、积分、奖品和记录;商品/充值商品快照至少按 `sid + spu/pid` 隔离,执行账号始终以实时详情覆盖快照。此步完成后 UI 可以展示真实状态,但仍不触发兑换或支付。
### 5. 重写兑换状态机
严格按以下顺序:
```text
实时 getActPrizeDetail
-> 校验 isCanExchange/活动时间/库存/今日限制/用户限制/可用积分
-> scoreExchangePrize
-> getUserScore + getUserPrizeRecords
-> getEntityPrizeFieldMap + getModuleAddress
```
成功、已兑换、余额不足、库存不足、需要地址和未知错误分别落库;收到成功响应后使用幂等键阻止重复提交。只有四项回查完成,任务才标记为兑换完成。
### 6. 重写开通和支付状态机
严格按以下顺序:
```text
getGoodsInfoV5
-> listPayChannelV5
-> checkHyProtocolV5 / checkUserBuyAuth
-> createOrderV5(orderType=6)
-> payOrderSubmitV5(Zfb|Weixin, QrCode)
-> 立即 orderDetailV5 轮询
-> 支付完成后回查宝典积分和用户任务
```
`orderDetailV5` 必须先于 runner 改造完成;`queryUserOrderList` 只能作为兼容 fallback。支付状态拆成 `created/pending/paid/timeout/cancelled/error`,超时绝不能标记 `success`。二维码生成后任务保持 `running/pending`,不能因为 runner 线程尚未结束而隐藏二维码。
### 7. 最后接入 UI 和批量执行
UI 只调用统一任务 API:查询使用快照任务,兑换和开通使用状态机任务;通过任务 WebSocket 推送二维码、订单状态和最终权益状态。先单账号灰度,再开启并发批量,避免旧的全局商品快照污染多个账号。
### 8. 清理旧链路
连续灰度验证通过后,删除 `create_order` 的 orderType 猜测和支付列表轮询主路径;保留 HTTP fallback、失败原因、超时和回滚开关。最后再迁移旧版虎牙任务入口,避免影响非精英宝典功能。
## 不应照搬抓包的字段
UID、Cookie、guid、baseinfo、WUP requestId、支付宝 payOrderId、ctoken、二维码 URL、`t/code/sig`、用户点击时间和 RSA2 签名均为运行时动态值。它们只能来自当前登录态、前序响应或运行时生成,不能写入默认配置或测试 fixture。
## 本轮已实施
- 增加 `getActInfo``getActUserTaskDetail``getActPrizeDetail` 和详情响应结构。
- 兑换提交前读取实时详情和积分,校验可兑换状态、库存、时间窗口和余额;成功后回查积分和兑换记录。
- 增加 `orderDetailV5` 请求/响应结构,支付监听优先按订单号查询,旧订单列表作为 fallback。
- 支付超时改为终态 `timeout`,不再误报成功;支付完成后回查活动积分。
- 支付前增加 `checkHyProtocolV5``listPayChannelV5``checkUserBuyAuth`,不再直接跳过商城前置校验。
- 商城开通任务优先使用复用的 WSS session,HTTP 仅作为连接/初始化失败时的 fallback;活动查询和兑换任务同样优先使用活动 WSS。
- 商城 HTTP fallback 的 `UserId` 改为 9.1 商城 UA/空 guid 形态;商品详情 `gameId` 改为空字符串,下单仍使用 `gameId="0"`
- 外部活动默认值由旧值更新为 `17096`,已有旧配置会在读取时迁移。
仍待下一轮实施:补齐 `getMyPromotion``calcOrderPromotion` 的显式调用;按账号隔离商品快照;绑定流程的活动 RPC 迁移到同一活动 WSS session(外部 livelink HTTP 保持独立)。