Document wemini credentials and native weapp workflow in README; add jscode2session auth endpoint. Co-authored-by: Cursor <cursoragent@cursor.com>
58 lines
1.8 KiB
Python
58 lines
1.8 KiB
Python
"""WeChat mini-program helpers (jscode2session)."""
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import os
|
|
from typing import Any
|
|
|
|
import httpx
|
|
|
|
WECHAT_APPID = os.getenv("WECHAT_APPID", "").strip()
|
|
WECHAT_SECRET = os.getenv("WECHAT_SECRET", "").strip()
|
|
WECHAT_ALLOW_DEVTOOLS = os.getenv("WECHAT_ALLOW_DEVTOOLS", "true").lower() in {
|
|
"1", "true", "yes", "on",
|
|
}
|
|
|
|
|
|
async def code2session(js_code: str) -> dict[str, Any]:
|
|
code = (js_code or "").strip()
|
|
if not code:
|
|
raise RuntimeError("缺少微信登录 code")
|
|
|
|
# Local / 开发者工具联调:无密钥或 mock code 时降级
|
|
mock = code in {"dev", "the code is a mock one"} or code.startswith("mock")
|
|
if mock or (not WECHAT_APPID or not WECHAT_SECRET):
|
|
if WECHAT_ALLOW_DEVTOOLS or mock:
|
|
digest = hashlib.sha256(code.encode("utf-8")).hexdigest()[:16]
|
|
return {
|
|
"openid": f"devtools_{digest}",
|
|
"session_key": f"devtools_session_{digest}",
|
|
"unionid": None,
|
|
}
|
|
raise RuntimeError("未配置 WECHAT_APPID / WECHAT_SECRET")
|
|
|
|
url = "https://api.weixin.qq.com/sns/jscode2session"
|
|
params = {
|
|
"appid": WECHAT_APPID,
|
|
"secret": WECHAT_SECRET,
|
|
"js_code": code,
|
|
"grant_type": "authorization_code",
|
|
}
|
|
async with httpx.AsyncClient(timeout=15) as client:
|
|
r = await client.get(url, params=params)
|
|
data = r.json()
|
|
|
|
err = data.get("errcode")
|
|
if err:
|
|
if WECHAT_ALLOW_DEVTOOLS and int(err) in {40029, 40163, 40252}:
|
|
digest = hashlib.sha256(code.encode("utf-8")).hexdigest()[:16]
|
|
return {
|
|
"openid": f"devtools_{digest}",
|
|
"session_key": f"devtools_session_{digest}",
|
|
"unionid": None,
|
|
}
|
|
raise RuntimeError(f"code2session failed: {data}")
|
|
if not data.get("openid"):
|
|
raise RuntimeError(f"code2session missing openid: {data}")
|
|
return data
|