Files
zkxw/backend/app/services/wechat_mp_auth.py
T

79 lines
2.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""微信服务号:access_token、oauth code 换 openid。"""
from __future__ import annotations
import logging
from datetime import datetime, timedelta
from typing import Any, Dict, Optional
import httpx
from app.core.config import settings
logger = logging.getLogger(__name__)
_token: Optional[str] = None
_token_expire: Optional[datetime] = None
class WechatMpAuthError(Exception):
pass
def _require_mp_credentials() -> tuple:
appid = (settings.WX_MP_APPID or "").strip()
secret = (settings.WX_MP_SECRET or "").strip()
if not appid or not secret:
raise WechatMpAuthError("未配置服务号凭证(WX_MP_APPID / WX_MP_SECRET)")
return appid, secret
def get_mp_access_token() -> str:
global _token, _token_expire
if _token and _token_expire and datetime.now() < _token_expire:
return _token
appid, secret = _require_mp_credentials()
with httpx.Client(timeout=15.0) as client:
r = client.get(
"https://api.weixin.qq.com/cgi-bin/token",
params={
"grant_type": "client_credential",
"appid": appid,
"secret": secret,
},
)
r.raise_for_status()
data = r.json()
token = data.get("access_token")
if not token:
logger.warning("mp access_token failed: %s", data)
raise WechatMpAuthError("获取服务号 access_token 失败")
_token = token
_token_expire = datetime.now() + timedelta(seconds=int(data.get("expires_in", 7200)) - 300)
return token
def oauth_code_to_openid(code: str) -> Dict[str, Any]:
"""snsapi_base:code → openid / unionid。"""
c = (code or "").strip()
if not c:
raise WechatMpAuthError("code 不能为空")
appid, secret = _require_mp_credentials()
with httpx.Client(timeout=15.0) as client:
r = client.get(
"https://api.weixin.qq.com/sns/oauth2/access_token",
params={
"appid": appid,
"secret": secret,
"code": c,
"grant_type": "authorization_code",
},
)
r.raise_for_status()
data = r.json()
if data.get("errcode"):
raise WechatMpAuthError(data.get("errmsg") or "服务号授权失败")
openid = data.get("openid")
if not openid:
raise WechatMpAuthError("服务号未返回 openid")
return {"openid": openid, "unionid": data.get("unionid")}