接入虎牙精英宝典最新商城前置流程
This commit is contained in:
@@ -851,6 +851,52 @@ class HuyaHttpClient:
|
|||||||
"shopMiddleUI", "getGoodsInfoV5", req, GoodsInfoRsp, uid, guid, cookie
|
"shopMiddleUI", "getGoodsInfoV5", req, GoodsInfoRsp, uid, guid, cookie
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def list_pay_channels(
|
||||||
|
self, uid: int, guid: str, cookie: str, spu_id: str, sku_id: int,
|
||||||
|
supplier_id: int = 87401, timeout: float = 15.0,
|
||||||
|
):
|
||||||
|
from .shop_structs import ListPayChannelReq, ListPayChannelRsp
|
||||||
|
|
||||||
|
req = ListPayChannelReq()
|
||||||
|
req.userId = self._build_shop_user(uid, cookie)
|
||||||
|
req.shopAppInfo = self._build_shop_app("yellowcarlist", 4)
|
||||||
|
req.supplierId = int(supplier_id or 0)
|
||||||
|
req.spuId = spu_id
|
||||||
|
req.skuId = int(sku_id or 0)
|
||||||
|
return self.call_rpc(
|
||||||
|
"shopMiddleUI", "listPayChannelV5", req, ListPayChannelRsp,
|
||||||
|
uid, guid, cookie, timeout=timeout,
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_hy_protocol(
|
||||||
|
self, uid: int, guid: str, cookie: str, timeout: float = 15.0
|
||||||
|
):
|
||||||
|
from .shop_structs import CheckHyProtocolReq, ShopCodeRsp
|
||||||
|
|
||||||
|
req = CheckHyProtocolReq()
|
||||||
|
req.userId = self._build_shop_user(uid, cookie)
|
||||||
|
req.shopAppInfo = self._build_shop_app("yellowcarlist", 4)
|
||||||
|
return self.call_rpc(
|
||||||
|
"shopMiddleUI", "checkHyProtocolV5", req, ShopCodeRsp,
|
||||||
|
uid, guid, cookie, timeout=timeout,
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_user_buy_auth(
|
||||||
|
self, uid: int, guid: str, cookie: str, spu_id: str, sku_id: int,
|
||||||
|
timeout: float = 15.0,
|
||||||
|
):
|
||||||
|
from .shop_structs import CheckUserBuyAuthReq, ShopCodeRsp
|
||||||
|
|
||||||
|
req = CheckUserBuyAuthReq()
|
||||||
|
req.userId = self._build_shop_user(uid, cookie)
|
||||||
|
req.shopAppInfo = self._build_shop_app("yellowcarlist", 4)
|
||||||
|
req.spuId = spu_id
|
||||||
|
req.skuId = int(sku_id or 0)
|
||||||
|
return self.call_rpc(
|
||||||
|
"shopMiddleUI", "checkUserBuyAuth", req, ShopCodeRsp,
|
||||||
|
uid, guid, cookie, timeout=timeout,
|
||||||
|
)
|
||||||
|
|
||||||
def query_user_order_list(
|
def query_user_order_list(
|
||||||
self,
|
self,
|
||||||
uid,
|
uid,
|
||||||
|
|||||||
@@ -639,6 +639,110 @@ class QueryUserOrderListRsp(TafStruct):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ShopAppRequest(TafStruct):
|
||||||
|
"""商城前置 RPC 的公共请求字段。"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.userId = UserId()
|
||||||
|
self.shopAppInfo = ShopAppInfo()
|
||||||
|
|
||||||
|
def read_from(self, ins: TafInputStream):
|
||||||
|
self.userId = ins.read_struct(0, UserId) or self.userId
|
||||||
|
self.shopAppInfo = ins.read_struct(1, ShopAppInfo) or self.shopAppInfo
|
||||||
|
|
||||||
|
|
||||||
|
class ListPayChannelReq(ShopAppRequest):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.supplierId = 0
|
||||||
|
self.bizType = 5
|
||||||
|
self.gameCategoryId = 507
|
||||||
|
self.spuId = ""
|
||||||
|
self.skuId = 0
|
||||||
|
|
||||||
|
def write_to(self, os: TafOutputStream):
|
||||||
|
os.write_struct(0, self.userId)
|
||||||
|
os.write_struct(1, self.shopAppInfo)
|
||||||
|
os.write_int64(2, self.supplierId)
|
||||||
|
os.write_int32(3, self.bizType)
|
||||||
|
os.write_int32(4, self.gameCategoryId)
|
||||||
|
os.write_string(5, self.spuId)
|
||||||
|
os.write_int64(6, self.skuId)
|
||||||
|
|
||||||
|
|
||||||
|
class CheckHyProtocolReq(ShopAppRequest):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.protocolVersion = 1
|
||||||
|
|
||||||
|
def write_to(self, os: TafOutputStream):
|
||||||
|
os.write_struct(0, self.userId)
|
||||||
|
os.write_struct(1, self.shopAppInfo)
|
||||||
|
os.write_int32(2, self.protocolVersion)
|
||||||
|
|
||||||
|
|
||||||
|
class CheckUserBuyAuthReq(ShopAppRequest):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.spuId = ""
|
||||||
|
self.skuId = 0
|
||||||
|
self.checkType = 3
|
||||||
|
self.payChannel = ""
|
||||||
|
self.itemCount = 0
|
||||||
|
|
||||||
|
def write_to(self, os: TafOutputStream):
|
||||||
|
os.write_struct(0, self.userId)
|
||||||
|
os.write_struct(1, self.shopAppInfo)
|
||||||
|
os.write_string(2, self.spuId)
|
||||||
|
os.write_int64(3, self.skuId)
|
||||||
|
os.write_int32(4, self.checkType)
|
||||||
|
os.write_string(5, self.payChannel)
|
||||||
|
os.write_int32(6, self.itemCount)
|
||||||
|
|
||||||
|
|
||||||
|
class ListPayChannelRsp(TafStruct):
|
||||||
|
"""shopMiddleUI.listPayChannelV5 响应。"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.code = 0
|
||||||
|
self.message = ""
|
||||||
|
self.channels: list[str] = []
|
||||||
|
|
||||||
|
def read_from(self, ins: TafInputStream):
|
||||||
|
self.channels = ins.read_list(0)
|
||||||
|
self.code = ins.read_int32(1, default=self.code)
|
||||||
|
self.message = ins.read_string(2, default=self.message)
|
||||||
|
_skip_to_struct_end(ins)
|
||||||
|
|
||||||
|
def write_to(self, os: TafOutputStream):
|
||||||
|
os.write_list(0, self.channels)
|
||||||
|
os.write_int32(1, self.code)
|
||||||
|
os.write_string(2, self.message)
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
return {"code": self.code, "message": self.message, "channels": self.channels}
|
||||||
|
|
||||||
|
|
||||||
|
class ShopCodeRsp(TafStruct):
|
||||||
|
"""checkHyProtocolV5/checkUserBuyAuth 的状态响应。"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.code = 0
|
||||||
|
self.message = ""
|
||||||
|
|
||||||
|
def read_from(self, ins: TafInputStream):
|
||||||
|
self.code = ins.read_int32(0, default=self.code)
|
||||||
|
self.message = ins.read_string(1, default=self.message)
|
||||||
|
_skip_to_struct_end(ins)
|
||||||
|
|
||||||
|
def write_to(self, os: TafOutputStream):
|
||||||
|
os.write_int32(0, self.code)
|
||||||
|
os.write_string(1, self.message)
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
return {"code": self.code, "message": self.message}
|
||||||
|
|
||||||
|
|
||||||
class OrderDetailOrder(TafStruct):
|
class OrderDetailOrder(TafStruct):
|
||||||
"""orderDetailV5 中的订单摘要;保留支付轮询所需字段。"""
|
"""orderDetailV5 中的订单摘要;保留支付轮询所需字段。"""
|
||||||
|
|
||||||
|
|||||||
@@ -656,6 +656,46 @@ class HuyaWssClient:
|
|||||||
"shopMiddleUI", "getGoodsInfoV5", req, GoodsInfoRsp, timeout=15.0
|
"shopMiddleUI", "getGoodsInfoV5", req, GoodsInfoRsp, timeout=15.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def list_pay_channels(
|
||||||
|
self, uid: int, guid: str, cookie: str, spu_id: str, sku_id: int = 0,
|
||||||
|
supplier_id: int = 87401,
|
||||||
|
):
|
||||||
|
from .shop_structs import ListPayChannelReq, ListPayChannelRsp
|
||||||
|
|
||||||
|
req = ListPayChannelReq()
|
||||||
|
req.userId = self._build_biz_user(uid, cookie)
|
||||||
|
req.shopAppInfo = self._build_shop_app("yellowcarlist", WSS_SHOP_SCENE)
|
||||||
|
req.supplierId = int(supplier_id or 0)
|
||||||
|
req.spuId = spu_id
|
||||||
|
req.skuId = int(sku_id or 0)
|
||||||
|
return await self.call_rpc(
|
||||||
|
"shopMiddleUI", "listPayChannelV5", req, ListPayChannelRsp, timeout=15.0
|
||||||
|
)
|
||||||
|
|
||||||
|
async def check_hy_protocol(self, uid: int, guid: str, cookie: str):
|
||||||
|
from .shop_structs import CheckHyProtocolReq, ShopCodeRsp
|
||||||
|
|
||||||
|
req = CheckHyProtocolReq()
|
||||||
|
req.userId = self._build_biz_user(uid, cookie)
|
||||||
|
req.shopAppInfo = self._build_shop_app("yellowcarlist", WSS_SHOP_SCENE)
|
||||||
|
return await self.call_rpc(
|
||||||
|
"shopMiddleUI", "checkHyProtocolV5", req, ShopCodeRsp, timeout=15.0
|
||||||
|
)
|
||||||
|
|
||||||
|
async def check_user_buy_auth(
|
||||||
|
self, uid: int, guid: str, cookie: str, spu_id: str, sku_id: int,
|
||||||
|
):
|
||||||
|
from .shop_structs import CheckUserBuyAuthReq, ShopCodeRsp
|
||||||
|
|
||||||
|
req = CheckUserBuyAuthReq()
|
||||||
|
req.userId = self._build_biz_user(uid, cookie)
|
||||||
|
req.shopAppInfo = self._build_shop_app("yellowcarlist", WSS_SHOP_SCENE)
|
||||||
|
req.spuId = spu_id
|
||||||
|
req.skuId = int(sku_id or 0)
|
||||||
|
return await self.call_rpc(
|
||||||
|
"shopMiddleUI", "checkUserBuyAuth", req, ShopCodeRsp, timeout=15.0
|
||||||
|
)
|
||||||
|
|
||||||
async def query_user_order_list(
|
async def query_user_order_list(
|
||||||
self,
|
self,
|
||||||
uid: int,
|
uid: int,
|
||||||
|
|||||||
@@ -117,7 +117,8 @@ UID、Cookie、guid、baseinfo、WUP requestId、支付宝 payOrderId、ctoken
|
|||||||
- 兑换提交前读取实时详情和积分,校验可兑换状态、库存、时间窗口和余额;成功后回查积分和兑换记录。
|
- 兑换提交前读取实时详情和积分,校验可兑换状态、库存、时间窗口和余额;成功后回查积分和兑换记录。
|
||||||
- 增加 `orderDetailV5` 请求/响应结构,支付监听优先按订单号查询,旧订单列表作为 fallback。
|
- 增加 `orderDetailV5` 请求/响应结构,支付监听优先按订单号查询,旧订单列表作为 fallback。
|
||||||
- 支付超时改为终态 `timeout`,不再误报成功;支付完成后回查活动积分。
|
- 支付超时改为终态 `timeout`,不再误报成功;支付完成后回查活动积分。
|
||||||
|
- 支付前增加 `checkHyProtocolV5`、`listPayChannelV5` 和 `checkUserBuyAuth`,不再直接跳过商城前置校验。
|
||||||
- 商城 HTTP fallback 的 `UserId` 改为 9.1 商城 UA/空 guid 形态;商品详情 `gameId` 改为空字符串,下单仍使用 `gameId="0"`。
|
- 商城 HTTP fallback 的 `UserId` 改为 9.1 商城 UA/空 guid 形态;商品详情 `gameId` 改为空字符串,下单仍使用 `gameId="0"`。
|
||||||
- 外部活动默认值由旧值更新为 `17096`,已有旧配置会在读取时迁移。
|
- 外部活动默认值由旧值更新为 `17096`,已有旧配置会在读取时迁移。
|
||||||
|
|
||||||
仍待下一轮实施:将批处理执行器从 HTTP fallback 迁移到统一活动/商城 WSS session;补齐 `listPayChannelV5`、`checkHyProtocolV5`、`getMyPromotion`、`calcOrderPromotion` 和 `checkUserBuyAuth` 的显式前置调用;按账号隔离商品快照。
|
仍待下一轮实施:将批处理执行器从 HTTP fallback 迁移到统一活动/商城 WSS session;补齐 `getMyPromotion`、`calcOrderPromotion` 的显式调用;按账号隔离商品快照。
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
from core.huya.activity_structs import ActPrizeDetailItem, GetActPrizeDetailResp
|
from core.huya.activity_structs import ActPrizeDetailItem, GetActPrizeDetailResp
|
||||||
from core.huya.http_client import SHOP_BIZ_UA, HuyaHttpClient
|
from core.huya.http_client import SHOP_BIZ_UA, HuyaHttpClient
|
||||||
from core.huya.shop_structs import OrderDetailData, OrderDetailOrder, OrderDetailRsp
|
from core.huya.shop_structs import (
|
||||||
|
ListPayChannelRsp,
|
||||||
|
OrderDetailData,
|
||||||
|
OrderDetailOrder,
|
||||||
|
OrderDetailRsp,
|
||||||
|
ShopCodeRsp,
|
||||||
|
)
|
||||||
from core.huya.taf_protocol import TafInputStream, TafOutputStream, TafType
|
from core.huya.taf_protocol import TafInputStream, TafOutputStream, TafType
|
||||||
|
|
||||||
|
|
||||||
@@ -66,3 +72,18 @@ def test_order_detail_round_trip_exposes_payment_status():
|
|||||||
assert decoded.order.orderId == 10938643
|
assert decoded.order.orderId == 10938643
|
||||||
assert decoded.order.orderStatus == 50
|
assert decoded.order.orderStatus == 50
|
||||||
assert decoded.order.payTime > 0
|
assert decoded.order.payTime > 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_shop_preflight_response_contracts():
|
||||||
|
channels = ListPayChannelRsp()
|
||||||
|
channels.channels = ["Zfb", "Weixin"]
|
||||||
|
channels.code = 200
|
||||||
|
decoded_channels = _round_trip(channels)
|
||||||
|
assert decoded_channels.channels == ["Zfb", "Weixin"]
|
||||||
|
assert decoded_channels.code == 200
|
||||||
|
|
||||||
|
status = ShopCodeRsp()
|
||||||
|
status.code = 200
|
||||||
|
status.message = ""
|
||||||
|
decoded_status = _round_trip(status)
|
||||||
|
assert decoded_status.code == 200
|
||||||
|
|||||||
@@ -434,6 +434,35 @@ class RechargeMixin:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
protocol = client.check_hy_protocol(uid=uid, guid="", cookie=cookie)
|
||||||
|
if protocol is None or protocol.code != 200:
|
||||||
|
self._mark_task(
|
||||||
|
worker_db, task, "failed",
|
||||||
|
getattr(protocol, "message", "商城协议校验失败") or "商城协议校验失败",
|
||||||
|
{"goods": detail, "protocol": protocol.to_dict() if protocol else None},
|
||||||
|
)
|
||||||
|
return
|
||||||
|
channels = client.list_pay_channels(
|
||||||
|
uid=uid, guid="", cookie=cookie, spu_id=spu_id, sku_id=0
|
||||||
|
)
|
||||||
|
available_channels = set(getattr(channels, "channels", []) or [])
|
||||||
|
if channels is None or channels.code != 200 or pay_channel not in available_channels:
|
||||||
|
self._mark_task(
|
||||||
|
worker_db, task, "failed", "当前支付渠道不可用",
|
||||||
|
{"goods": detail, "channels": channels.to_dict() if channels else None},
|
||||||
|
)
|
||||||
|
return
|
||||||
|
auth = client.check_user_buy_auth(
|
||||||
|
uid=uid, guid="", cookie=cookie, spu_id=spu_id, sku_id=sku_id
|
||||||
|
)
|
||||||
|
if auth is None or auth.code != 200:
|
||||||
|
self._mark_task(
|
||||||
|
worker_db, task, "failed",
|
||||||
|
getattr(auth, "message", "购买权限校验失败") or "购买权限校验失败",
|
||||||
|
{"goods": detail, "auth": auth.to_dict() if auth else None},
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
order_resp = client.create_order(
|
order_resp = client.create_order(
|
||||||
uid=uid,
|
uid=uid,
|
||||||
guid="",
|
guid="",
|
||||||
|
|||||||
Reference in New Issue
Block a user