nomadweb/backend/app/services/wechat.py
eric 79dda36dd3 Ship Taro WeChat mini-app with H5-aligned UI and WeChat login.
Document wemini credentials and native weapp workflow in README; add jscode2session auth endpoint.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-27 22:05:56 -05:00

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