fix(weibo): 识别支付域登录失效

This commit is contained in:
yml2213
2026-11-08 14:06:26 +08:00
parent ed15701392
commit 7f7a8f6004
4 changed files with 86 additions and 3 deletions
+12 -2
View File
@@ -15,6 +15,7 @@ import json
import secrets
import time
from dataclasses import dataclass
from html import unescape
from html.parser import HTMLParser
from typing import Any
from urllib.parse import parse_qs, urljoin, urlsplit
@@ -321,6 +322,7 @@ class WeiboClient:
data: dict[str, Any] | None = None,
json: dict[str, Any] | None = None,
) -> dict[str, Any]:
response: requests.Response | None = None
try:
response = self.session.request(
method,
@@ -333,10 +335,18 @@ class WeiboClient:
)
response.raise_for_status()
payload = response.json()
except (requests.exceptions.JSONDecodeError, ValueError) as exc:
text = unescape(response.text if response is not None else "")
if "retcode=6102" in text or "/sso/login.php" in text:
raise WeiboAuthError(
"微博支付/活动域登录态已失效(retcode=6102),"
"请重新登录微博后重新导入 CK"
) from exc
path = urlsplit(response.url).path if response is not None else ""
suffix = f": {path}" if path else ""
raise WeiboTransportError(f"微博接口返回非 JSON{suffix}") from exc
except requests.RequestException as exc:
raise WeiboTransportError(f"请求微博失败: {exc}") from exc
except ValueError as exc:
raise WeiboTransportError("微博返回内容不是合法 JSON") from exc
if not isinstance(payload, dict):
raise WeiboTransportError("微博返回 JSON 结构异常")
return payload
@@ -8,6 +8,7 @@ generated:
status: stable
verified:
- { by: ai/codex, at: 2026-11-07T19:03:41+08:00 }
- { by: ai/codex, at: 2026-11-08T13:45:00+08:00 }
---
# 微博微豆 CK 接口与兑换链路确认
@@ -723,6 +724,37 @@ data.totalCount
| `400000` | `user not login`,CK 无效 |
| 其他 | 保留服务端 `msg` 和 `trace_id`,不要盲目重试状态变更接口 |
### 10.1 登录页 HTML 与 `retcode=6102`
部分 V+ / 支付域接口在登录态失效时仍返回 HTTP 200,但 `Content-Type` 为 `text/html`,正文是“新浪通行证”跳转页,并包含:
```text
retcode=6102
```
2026-11-08 实测到同一账号可以正常请求:
```text
GET https://vipclub.e.weibo.com/aj/mall/home
-> code=100000
```
但后续请求:
```text
GET https://vipclub.weibo.com/aj/vmember/gfsupport
GET https://vipclub.weibo.com/aj/paycomponent/userinfo
-> text/html + retcode=6102
```
这说明 CK 已经不能访问 V+ 支付/活动域,不能归类为普通“非 JSON 查询失败”。处理约定:
- `retcode=6102` 或跳转 `/sso/login.php` 归类为 `WeiboAuthError`。
- 账号 `ck_check_status` 写为 `invalid`,UI 显示「失效」。
- 提示用户重新登录微博,并在 V+ 页面可用后重新导出、导入完整 CK。
- 不要通过反复刷新、重试支付接口来“恢复”状态。
- 其他未知 HTML 或网关错误保留为 `error`,UI 显示「查询失败」,不要误标成 CK 失效。
## 11. 尚未确认
- 是否只需要 `SUB`,还是必须完整 Cookie Jar。
+16
View File
@@ -205,6 +205,22 @@ def test_refresh_marks_payment_access_failure(db, current_user):
assert "触发微博账号风控" in account.ck_check_message
def test_refresh_marks_payment_login_expiry_as_invalid(db, current_user):
account_out = import_cookies(
WeiboCookieImport(text="SCF=a; SUB=b", tag=""),
db=db,
current=current_user,
).accounts[0]
account = db.query(WeiboAccount).filter(WeiboAccount.id == account_out.id).one()
StubWeiboClient.payment_error = WeiboAuthError("支付域 retcode=6102")
with pytest.raises(WeiboAuthError, match="6102"):
weibo_service.refresh_weibo_account(db, account)
assert account.ck_check_status == "invalid"
assert "6102" in account.ck_check_message
def test_list_accounts_returns_parallel_page_shape(db, current_user):
assert (
import_cookies(
+26 -1
View File
@@ -17,6 +17,7 @@ from core.weibo import (
WeiboExchangeUncertainError,
WeiboHomeSnapshot,
WeiboRechargeError,
WeiboTransportError,
extract_bind_raw_url,
normalize_weibo_cookie,
)
@@ -50,7 +51,7 @@ class FakeResponse:
def json(self):
if not isinstance(self.payload, dict):
raise TypeError("not json")
raise requests.exceptions.JSONDecodeError("not json", self.text, 0)
return self.payload
@@ -155,6 +156,30 @@ def test_fetch_home_keeps_trace_id_for_other_api_error():
client.fetch_home()
def test_fetch_home_treats_sina_login_redirect_as_auth_error():
session = FakeSession(
[
(
"<html><head><meta http-equiv='refresh' "
"content=\"0; url='https://vipclub.weibo.com/aj/vmember/"
"gfsupport?retcode=6102'\"></head></html>"
)
]
)
client = WeiboClient("SUB=expired", session=session)
with pytest.raises(WeiboAuthError, match="重新登录微博"):
client.fetch_home()
def test_fetch_home_reports_non_json_endpoint_path():
session = FakeSession(["<html><body>gateway error</body></html>"])
client = WeiboClient("SUB=test", session=session)
with pytest.raises(WeiboTransportError, match="/aj/mall/home"):
client.fetch_home()
def test_generate_bind_scheme_posts_account_type_without_weibo_cookie():
session = FakeSession(
[