fix(weibo): 识别支付页拒绝并保护有效 CK 状态

This commit is contained in:
yml2213
2026-11-08 10:04:05 +08:00
parent b7f34df04d
commit ffbe0f41f5
4 changed files with 94 additions and 6 deletions
+8
View File
@@ -277,6 +277,7 @@ class WeiboClient:
self.cookie = normalize_weibo_cookie(cookie) if cookie else ""
self.timeout = timeout
self.session = session or requests.Session()
self._last_text_url = ""
self.session.headers.update(
{
"User-Agent": WEIBO_MOBILE_UA,
@@ -336,6 +337,7 @@ class WeiboClient:
timeout=self.timeout,
)
response.raise_for_status()
self._last_text_url = str(getattr(response, "url", "") or url)
return response.text
except requests.RequestException as exc:
raise WeiboTransportError(f"请求微博收银台失败: {exc}") from exc
@@ -821,6 +823,12 @@ class WeiboClient:
"Referer": "https://vipclub.weibo.com/",
},
)
if urlsplit(self._last_text_url).path.rstrip("/") == "/sorry":
raise WeiboRechargeError(
"微博支付页拒绝了当前账号(跳转 /sorry)。"
"CK 首页仍有效,请重新登录微博更新 CK,"
"或确认该账号具备支付/助威资格"
)
parser = _CashierParser()
parser.feed(html)
if not parser.pay_id:
+30
View File
@@ -14,6 +14,7 @@ from core.weibo import (
WeiboExchangeResult,
WeiboHomeSnapshot,
WeiboPaymentStatus,
WeiboRechargeError,
WeiboRechargeOrder,
)
from web.backend.models import User, WeiboAccount
@@ -360,6 +361,35 @@ def test_manual_recharge_order_and_status_routes_serialize(
assert status["status"] == "PAY_STATUS_PENDING"
def test_recharge_failure_keeps_valid_ck_status(monkeypatch, 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()
def reject_recharge(*_args, **_kwargs):
raise WeiboRechargeError("微博支付页拒绝了当前账号")
monkeypatch.setattr(
StubWeiboClient,
"create_recharge_order",
reject_recharge,
raising=False,
)
with pytest.raises(WeiboRechargeError, match="支付页拒绝"):
weibo_service.create_weibo_recharge_order(
db,
account,
amount_yuan=1,
)
assert account.ck_check_status == "valid"
assert account.ck_check_message == ""
def test_manual_exchange_requires_confirm_and_serializes_result(
monkeypatch, db, current_user
):
+49 -2
View File
@@ -27,9 +27,10 @@ BIND_URL = (
class FakeResponse:
def __init__(self, payload):
def __init__(self, payload, url: str = ""):
self.payload = payload
self.text = payload if isinstance(payload, str) else ""
self.url = url
def raise_for_status(self):
return None
@@ -52,7 +53,10 @@ class FakeSession(requests.Session):
result = self.responses.pop(0)
if isinstance(result, Exception):
raise result
return FakeResponse(result)
response = result if isinstance(result, FakeResponse) else FakeResponse(result)
if not response.url:
response.url = url
return response
def test_normalize_cookie_accepts_prefix_and_multiline():
@@ -334,6 +338,49 @@ def test_create_recharge_order_full_manual_chain():
assert create_call[2]["params"] == create_call[2]["data"]
def test_create_recharge_order_reports_payment_rejection():
client = WeiboClient("SCF=a; SUB=b", session=FakeSession([]))
client._load_recharge_context = lambda _activity: (
{"vuid": "vuid", "source_itemid": "20601", "F": "bowen_tjg"},
{"tab_type": "8", "cashier_id": "205"},
{
"tab_info": [
{
"is_current": 1,
"tab_type": "8",
"cashier_id": "205",
"menu": [
{
"subitemid": "2060101",
"itemid": 20601,
"appkey": 2000,
"commodity_id": "2060101",
"max_money": 1000,
}
],
}
]
},
)
client._recharge_bid = lambda *_args: "1000294191"
client._request_json = lambda *_args, **_kwargs: {
"code": 100000,
"data": {
"url": "https://pay.sc.weibo.com/api/merchant/pay/cashier?sign=test",
"orderid": "order-1",
},
}
def rejected_page(*_args, **_kwargs):
client._last_text_url = "https://pay.sc.weibo.com/sorry"
return "<title>404错误</title>"
client._request_text = rejected_page
with pytest.raises(WeiboRechargeError, match=r"支付页拒绝.*/sorry"):
client.create_recharge_order(amount_yuan=1, activity="bowen_tjg")
def test_create_recharge_order_rejects_missing_dynamic_menu():
home = {
"code": 100000,
+7 -4
View File
@@ -166,10 +166,13 @@ def _mark_manual_failure(
account: WeiboAccount,
exc: Exception,
) -> None:
account.ck_check_status = _error_status(exc)
account.ck_check_message = str(exc)
account.ck_checked_at = _now()
db.commit()
"""只有明确登录态失效才改动 CK 状态,业务失败不能把有效 CK 标坏。"""
if isinstance(exc, WeiboAuthError):
account.ck_check_status = "invalid"
account.ck_check_message = str(exc)
account.ck_checked_at = _now()
db.commit()
def query_weibo_mall(