diff --git a/app/api/v1/wallet.py b/app/api/v1/wallet.py index 454ff7c..7b0dac0 100644 --- a/app/api/v1/wallet.py +++ b/app/api/v1/wallet.py @@ -192,7 +192,7 @@ def withdraw_info( wechat_bound=bool(u and u.wechat_openid), wechat_nickname=u.wechat_nickname if u else None, wechat_avatar_url=u.wechat_avatar_url if u else None, - transfer_auth_enabled=bool(auth and auth.state == "active"), + transfer_auth_enabled=bool(auth and auth.state == "active" and auth.authorization_id), tiers=[WithdrawTierOut(**t) for t in crud_wallet.withdraw_tier_states(db, user.id, source)], ) @@ -335,7 +335,8 @@ def open_transfer_auth(user: CurrentUser, db: DbSession) -> TransferAuthResultOu def transfer_auth_status(user: CurrentUser, db: DbSession) -> TransferAuthStatusOut: auth = crud_wallet.sync_transfer_auth(db, user.id) state = auth.state if auth else "none" - return TransferAuthStatusOut(state=state, enabled=(state == "active")) + enabled = bool(auth and auth.state == "active" and auth.authorization_id) + return TransferAuthStatusOut(state=state, enabled=enabled) @router.post( diff --git a/tests/test_withdraw.py b/tests/test_withdraw.py index 55a978a..509fb0a 100644 --- a/tests/test_withdraw.py +++ b/tests/test_withdraw.py @@ -9,7 +9,7 @@ from sqlalchemy import select from app.db.session import SessionLocal from app.models.user import User -from app.models.wallet import CoinAccount, WithdrawOrder +from app.models.wallet import CoinAccount, WithdrawOrder, WechatTransferAuthorization from app.repositories import wallet as crud_wallet @@ -426,3 +426,60 @@ def test_withdraw_reject_refunds(client, monkeypatch) -> None: r = client.get("/api/v1/wallet/withdraw/status", params={"out_bill_no": bill}, headers=_auth(token)) assert r.json()["status"] == "rejected" assert r.json()["fail_reason"] == "测试拒绝" + + +# ===== §fix 免确认授权 enabled 判定收严:active+authorization_id 非空才算已授权 ===== + +def _seed_transfer_auth(phone: str, state: str, authorization_id: str | None) -> None: + """直接在库里写/改该用户的免确认授权记录(绕过微信,用于判定测试)。""" + db = SessionLocal() + try: + user = db.execute(select(User).where(User.phone == phone)).scalar_one() + auth = db.get(WechatTransferAuthorization, user.id) + if auth is None: + auth = WechatTransferAuthorization( + user_id=user.id, openid="openid_test_abc", + out_authorization_no=f"oan_{user.id}", + ) + db.add(auth) + auth.state = state + auth.authorization_id = authorization_id + db.commit() + finally: + db.close() + + +def test_withdraw_info_auth_enabled_requires_authorization_id(client, monkeypatch) -> None: + _patch_userinfo(monkeypatch) + token = _login(client, "13800002051") + # 触发建号 + 绑定微信(withdraw-info 读 openid) + client.get("/api/v1/wallet/withdraw-info", headers=_auth(token)) + client.post("/api/v1/wallet/bind-wechat", json={"code": "c1"}, headers=_auth(token)) + + # active 但无 authorization_id → 视为未授权 + _seed_transfer_auth("13800002051", "active", None) + r = client.get("/api/v1/wallet/withdraw-info", headers=_auth(token)) + assert r.status_code == 200, r.text + assert r.json()["transfer_auth_enabled"] is False + + # active 且有 authorization_id → 已授权 + _seed_transfer_auth("13800002051", "active", "wx_auth_123") + r = client.get("/api/v1/wallet/withdraw-info", headers=_auth(token)) + assert r.json()["transfer_auth_enabled"] is True + + +def test_transfer_auth_status_requires_authorization_id(client, monkeypatch) -> None: + _patch_userinfo(monkeypatch) + token = _login(client, "13800002052") + client.get("/api/v1/wallet/withdraw-info", headers=_auth(token)) + client.post("/api/v1/wallet/bind-wechat", json={"code": "c2"}, headers=_auth(token)) + + _seed_transfer_auth("13800002052", "active", None) + r = client.get("/api/v1/wallet/transfer-auth/status", headers=_auth(token)) + assert r.status_code == 200, r.text + assert r.json()["state"] == "active" + assert r.json()["enabled"] is False + + _seed_transfer_auth("13800002052", "active", "wx_auth_456") + r = client.get("/api/v1/wallet/transfer-auth/status", headers=_auth(token)) + assert r.json()["enabled"] is True