Files
Django/utils/weixin_token.py

132 lines
4.1 KiB
Python
Raw Permalink 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.
import logging
import requests
from django.conf import settings
from django.core.cache import cache
logger = logging.getLogger(__name__)
WX_TOKEN_CACHE_KEY = 'wx_mini_access_token'
def invalidate_weixin_mini_access_token(club_id=None):
if club_id:
cache.delete(f'{WX_TOKEN_CACHE_KEY}:{club_id}')
else:
cache.delete(WX_TOKEN_CACHE_KEY)
def get_club_mini_access_token(club_id=None, *, force_refresh=False):
"""
按俱乐部读取 wx_appid/wx_secret 获取 access_token多小程序上线必需
xq 缺配置时回退 settings其它俱乐部读 club 表。
"""
from jituan.constants import CLUB_ID_DEFAULT
from jituan.services.club_resolver import get_club_wx_credentials
club_id = club_id or CLUB_ID_DEFAULT
cache_key = f'{WX_TOKEN_CACHE_KEY}:{club_id}'
if not force_refresh:
token = cache.get(cache_key)
if token:
return token
if force_refresh:
cache.delete(cache_key)
appid, secret, _ = get_club_wx_credentials(club_id)
if not appid or not secret:
logger.error('俱乐部 %s 微信配置缺失: wx_appid/wx_secret 未设置', club_id)
return None
token, expires_in = _fetch_stable_token(appid, secret, force_refresh)
if not token:
token, expires_in = _fetch_legacy_token(appid, secret)
if token:
ttl = max(60, int(expires_in or 7200) - 200)
cache.set(cache_key, token, ttl)
return token
def get_weixin_mini_access_token(*, force_refresh=False):
"""
获取小程序 access_token。
force_refresh=True 时清除缓存并强制向微信重新获取,避免 token 非最新导致 40001。
优先调用 stable_token失败时回退普通 token 接口。
"""
if not force_refresh:
token = cache.get(WX_TOKEN_CACHE_KEY)
if token:
return token
if force_refresh:
invalidate_weixin_mini_access_token()
appid = getattr(settings, 'WEIXIN_APPID', '')
secret = getattr(settings, 'WEIXIN_SECRET', '')
if not appid or not secret:
logger.error('微信小程序配置缺失: WEIXIN_APPID 或 WEIXIN_SECRET 未设置')
return None
token, expires_in = _fetch_stable_token(appid, secret, force_refresh)
if not token:
token, expires_in = _fetch_legacy_token(appid, secret)
if token:
ttl = max(60, int(expires_in or 7200) - 200)
cache.set(WX_TOKEN_CACHE_KEY, token, ttl)
return token
def is_weixin_token_invalid(errcode=None, errmsg=''):
if errcode in (40001, 40014, 42001):
return True
msg = (errmsg or '').lower()
return 'invalid credential' in msg or 'access_token is invalid' in msg
def _fetch_stable_token(appid, secret, force_refresh):
url = 'https://api.weixin.qq.com/cgi-bin/stable_token'
payload = {
'grant_type': 'client_credential',
'appid': appid,
'secret': secret,
'force_refresh': bool(force_refresh),
}
try:
resp = requests.post(url, json=payload, timeout=5)
data = resp.json()
token = data.get('access_token')
if token:
logger.info('成功获取微信 stable access_token')
return token, data.get('expires_in', 7200)
logger.warning(
'stable_token 获取失败 errcode=%s errmsg=%s',
data.get('errcode'), data.get('errmsg'),
)
except Exception as e:
logger.warning('请求 stable_token 异常: %s', e)
return None, None
def _fetch_legacy_token(appid, secret):
url = 'https://api.weixin.qq.com/cgi-bin/token'
params = {
'grant_type': 'client_credential',
'appid': appid,
'secret': secret,
}
try:
resp = requests.get(url, params=params, timeout=5)
data = resp.json()
token = data.get('access_token')
if token:
logger.info('成功获取微信 access_token (legacy)')
return token, data.get('expires_in', 7200)
logger.error('legacy token 获取失败: %s', data)
except Exception as e:
logger.error('请求 legacy token 异常: %s', e)
return None, None