fix: proxy reyou callbacks with chatapp3 token

This commit is contained in:
zhx 2026-06-12 20:09:17 +08:00
parent dffca4dec7
commit 4024f62e53
3 changed files with 67 additions and 5 deletions

View File

@ -623,6 +623,54 @@ func TestHotgameCompatProxiesChatapp3TokenWithoutGameCallback(t *testing.T) {
}
}
func TestReyouGameCallbackProxiesChatapp3TokenWithoutGameCallback(t *testing.T) {
token := chatapp3HotgameToken("v1", 99123456, "LIKEI", 4102444800000, 1700000000000)
body := `{"gameId":"20","uid":"99123456","token":"` + token + `","sign":"s"}`
var gotHost string
var gotPath string
var gotQuery string
var gotBody string
var gotContentType string
oldTransport := http.DefaultTransport
http.DefaultTransport = roundTripFunc(func(request *http.Request) (*http.Response, error) {
gotHost = request.URL.Host
gotPath = request.URL.Path
gotQuery = request.URL.RawQuery
gotContentType = request.Header.Get("Content-Type")
raw, _ := io.ReadAll(request.Body)
gotBody = string(raw)
header := http.Header{}
header.Set("Content-Type", "application/json")
return &http.Response{
StatusCode: http.StatusOK,
Header: header,
Body: io.NopCloser(bytes.NewReader([]byte(`{"errorCode":0,"data":{"coin":1000}}`))),
Request: request,
}, nil
})
defer func() { http.DefaultTransport = oldTransport }()
gameClient := &fakeGatewayGameClient{callbackResp: &gamev1.CallbackResponse{RawBody: []byte(`{"errorCode":0}`), ContentType: "application/json"}}
handler := NewHandlerWithClients(&fakeRoomClient{}, nil, nil, &fakeUserProfileClient{})
handler.SetGameClient(gameClient)
router := handler.Routes(auth.NewVerifier("secret"))
request := httptest.NewRequest(http.MethodPost, "/api/v1/game-callbacks/reyou/getUserInfo?nonce=1", bytes.NewReader([]byte(body)))
request.Header.Set("X-App-Code", "lalu")
request.Header.Set("Content-Type", "application/json")
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, request)
if recorder.Code != http.StatusOK || recorder.Body.String() != `{"errorCode":0,"data":{"coin":1000}}` {
t.Fatalf("reyou chatapp3 proxy response mismatch: status=%d body=%s", recorder.Code, recorder.Body.String())
}
if gotHost != "jvapi.haiyihy.com" || gotPath != "/go/getUserInfo" || gotQuery != "nonce=1" || gotBody != body || gotContentType != "application/json" {
t.Fatalf("reyou chatapp3 proxy request mismatch: host=%q path=%q query=%q content_type=%q body=%s", gotHost, gotPath, gotQuery, gotContentType, gotBody)
}
if gameClient.lastCallback != nil {
t.Fatalf("reyou chatapp3 token must not enter hyapp game callback: %+v", gameClient.lastCallback)
}
}
func chatapp3HotgameToken(version string, userID int64, sysOrigin string, expireMs int64, releaseMs int64) string {
payload := url.QueryEscape(version + ":" + strconv.FormatInt(userID, 10) + ":" + sysOrigin + ":" + strconv.FormatInt(expireMs, 10) + ":" + strconv.FormatInt(releaseMs, 10))
return "sign." + base64.StdEncoding.EncodeToString([]byte(payload))

View File

@ -750,7 +750,14 @@ func (h *Handler) handleGameCallback(writer http.ResponseWriter, request *http.R
http.Error(writer, "invalid request body", http.StatusBadRequest)
return
}
h.handleGameCallbackRaw(writer, request, raw, strings.TrimSpace(request.PathValue("platform_code")), strings.TrimSpace(request.PathValue("operation")))
platformCode := strings.TrimSpace(request.PathValue("platform_code"))
operation := strings.TrimSpace(request.PathValue("operation"))
// reyou 的线上回调现在存在两种入口:根路径兼容入口和 /game-callbacks/reyou/* 通用入口。
// 通用入口同样先按 token 归属做分流chatapp3 token 原样回源 chatapp3其他 token 才交给 hyapp 自己的游戏服务。
if h.tryProxyChatapp3HotgameCallback(writer, request, raw, platformCode, operation) {
return
}
h.handleGameCallbackRaw(writer, request, raw, platformCode, operation)
}
func (h *Handler) handleGameCallbackRaw(writer http.ResponseWriter, request *http.Request, raw []byte, platformCode string, operation string) {

View File

@ -35,16 +35,23 @@ func (h *Handler) handleHotgameCompatCallback(writer http.ResponseWriter, reques
http.Error(writer, "invalid request body", http.StatusBadRequest)
return
}
// 热游固定回调地址无法按域名区分 hyapp 和 chatapp3这里用 token 结构做最小路由键。
// chatapp3 的用户 token 是 sign.base64(version:userId:sysOrigin:expire:release)hyapp 当前热游 token 是 JWT 或本服务 session。
if isChatapp3HotgameToken(hotgameCompatCallbackToken(raw)) {
h.proxyChatapp3HotgameCallback(writer, request, raw, operation)
if h.tryProxyChatapp3HotgameCallback(writer, request, raw, hotgameCompatPlatformCode, operation) {
return
}
// 非 chatapp3 token 全部继续走 hyapp 自己的 reyou 平台配置,避免 hyapp 自有热游游戏误转到 chatapp3。
h.handleGameCallbackRaw(writer, request, raw, hotgameCompatPlatformCode, operation)
}
func (h *Handler) tryProxyChatapp3HotgameCallback(writer http.ResponseWriter, request *http.Request, raw []byte, platformCode string, operation string) bool {
// 热游固定回调地址无法按域名区分 hyapp 和 chatapp3这里用 token 结构做最小路由键。
// chatapp3 的用户 token 是 sign.base64(version:userId:sysOrigin:expire:release)hyapp 当前热游 token 是 JWT 或本服务 session。
if !strings.EqualFold(strings.TrimSpace(platformCode), hotgameCompatPlatformCode) || !isChatapp3HotgameToken(hotgameCompatCallbackToken(raw)) {
return false
}
h.proxyChatapp3HotgameCallback(writer, request, raw, operation)
return true
}
func (h *Handler) proxyChatapp3HotgameCallback(writer http.ResponseWriter, request *http.Request, raw []byte, operation string) {
target, err := hotgameCompatProxyURL(operation, request.URL.RawQuery)
if err != nil {