fix(weibo): 识别支付页拒绝并保护有效 CK 状态
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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
|
||||
):
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user