Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 32 additions & 10 deletions controller/channel-billing.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,13 @@ type OpenRouterCreditResponse struct {
} `json:"data"`
}

type OpenRouterKeyResponse struct {
Data struct {
LimitRemaining float64 `json:"limit_remaining"`
TotalUsage float64 `json:"usage"`
}
}

// GetAuthHeader get auth header
func GetAuthHeader(token string) http.Header {
h := http.Header{}
Expand Down Expand Up @@ -307,19 +314,34 @@ func updateChannelAIGC2DBalance(channel *model.Channel) (float64, error) {
}

func updateChannelOpenRouterBalance(channel *model.Channel) (float64, error) {
authHeader := GetAuthHeader(channel.Key)

url := "https://openrouter.ai/api/v1/credits"
body, err := GetResponseBody("GET", url, channel, GetAuthHeader(channel.Key))
if err != nil {
return 0, err
body, err := GetResponseBody("GET", url, channel, authHeader)
if err == nil {
response := OpenRouterCreditResponse{}
err = json.Unmarshal(body, &response)
if err != nil {
return 0, err
}
balance := response.Data.TotalCredits - response.Data.TotalUsage
channel.UpdateBalance(balance)
return balance, nil
}
response := OpenRouterCreditResponse{}
err = json.Unmarshal(body, &response)
if err != nil {
return 0, err

url = "https://openrouter.ai/api/v1/key"
body, err = GetResponseBody("GET", url, channel, authHeader)
if err == nil {
response := OpenRouterKeyResponse{}
err = json.Unmarshal(body, &response)
Comment on lines +323 to +336
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Use common.Unmarshal instead of json.Unmarshal in the new OpenRouter paths.

The new changed lines directly call json.Unmarshal, which violates the repo JSON-wrapper rule for Go business code.

Suggested patch
-		err = json.Unmarshal(body, &response)
+		err = common.Unmarshal(body, &response)
 		if err != nil {
 			return 0, err
 		}
@@
-		err = json.Unmarshal(body, &response)
+		err = common.Unmarshal(body, &response)
 		if err != nil {
 			return 0, err
 		}

As per coding guidelines: "**/*.go: All JSON marshal/unmarshal operations MUST use wrapper functions from common/json.go... Do NOT directly import or call encoding/json in business code."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controller/channel-billing.go` around lines 323 - 336, The new OpenRouter
code is calling json.Unmarshal directly; replace those calls with the repo
wrapper common.Unmarshal to comply with the JSON-wrapper rule. In
channel-billing.go, locate the json.Unmarshal usages around the
OpenRouterKeyResponse and other OpenRouter response parsing (the block that
follows GetResponseBody and the earlier response parsing that sets balance and
calls channel.UpdateBalance) and change them to common.Unmarshal(body,
&response); ensure the common package is imported if missing and remove any
direct encoding/json usage in these business paths.

if err != nil {
return 0, err
}
balance := response.Data.LimitRemaining
channel.UpdateBalance(balance)
return balance, nil
}
balance := response.Data.TotalCredits - response.Data.TotalUsage
channel.UpdateBalance(balance)
return balance, nil
return 0, err
}
Comment on lines +320 to 345
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Restrict fallback to 403 instead of all /credits errors.

Current logic falls back to /api/v1/key for any /credits failure. That can hide non-auth failures (network/5xx/contract), making balance updates silently succeed under the wrong path. Gate fallback to 403 and return other errors directly.

OpenRouter API docs: for GET /api/v1/credits and GET /api/v1/key, what status codes are returned for normal API keys vs admin keys, and is 403 the expected code for non-admin access to /credits?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controller/channel-billing.go` around lines 320 - 345, The current code falls
back to GET /api/v1/key for any error from GetResponseBody("GET", url, channel,
authHeader) for /credits; change the logic so the fallback to the /key path only
happens when the /credits request failed with HTTP 403 (forbidden). To do this,
modify GetResponseBody to return the HTTP status (or an error type that exposes
status) alongside body and err (e.g., GetResponseBody -> (body []byte, status
int, err error)) and update this function call in this routine to inspect the
returned status; only when status == 403 call the /api/v1/key flow (unmarshal
into OpenRouterKeyResponse and call channel.UpdateBalance), otherwise return the
original error immediately. Ensure references: GetResponseBody,
OpenRouterCreditResponse, OpenRouterKeyResponse, and channel.UpdateBalance are
updated accordingly.


func updateChannelMoonshotBalance(channel *model.Channel) (float64, error) {
Expand Down