Update mengyastore

This commit is contained in:
2026-05-13 12:11:46 +08:00
parent ad54b1eb7e
commit 37983f3b60
156 changed files with 10064 additions and 5681 deletions

111
test/server.py Normal file
View File

@@ -0,0 +1,111 @@
"""
简易 Webhook 接收服务:监听 POST /webhook打印请求内容与部分头信息。
生产请将手机/脚本转发指向萌芽小店后端:`POST {YOUR_API_BASE}/api/webhooks/mengya-pay`
并在请求头附带与后端一致的 `X-Webhook-Secret`(对应环境变量 `WEBHOOK_MENGYA_SECRET`)。
本地测试环境变量:
HOST 监听地址,默认 0.0.0.0
PORT 监听端口,默认 8000
WEBHOOK_SECRET 若设置,则要求请求头 X-Webhook-Secret 与其一致,否则返回 403
"""
from __future__ import annotations
import json
import logging
import os
import secrets
from datetime import datetime, timezone
from typing import Annotated
from fastapi import FastAPI, Header, HTTPException, Request, Response
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
)
log = logging.getLogger("webhook")
app = FastAPI(title="Webhook 测试接收器", docs_url="/docs")
def _now_iso() -> str:
return datetime.now(timezone.utc).isoformat()
@app.get("/health")
def health() -> dict:
return {"status": "ok", "time": _now_iso()}
@app.post("/webhook")
async def webhook(
request: Request,
response: Response,
x_webhook_secret: Annotated[str | None, Header(alias="X-Webhook-Secret")] = None,
) -> dict:
expected = os.environ.get("WEBHOOK_SECRET")
if expected is not None:
if x_webhook_secret is None:
raise HTTPException(status_code=403, detail="missing X-Webhook-Secret")
if not secrets.compare_digest(x_webhook_secret, expected):
raise HTTPException(status_code=403, detail="invalid X-Webhook-Secret")
raw = await request.body()
ct = request.headers.get("content-type", "")
try:
if "application/json" in ct and raw.strip():
body_preview = json.loads(raw.decode())
elif raw.strip():
body_preview = raw.decode(errors="replace")
if len(body_preview) > 2000:
body_preview = body_preview[:2000] + "…(truncated)"
else:
body_preview = None
except json.JSONDecodeError:
body_preview = raw.decode(errors="replace")
interesting_headers = {
k: v
for k, v in request.headers.items()
if k.lower()
in (
"content-type",
"user-agent",
"x-github-event",
"x-gitlab-token",
"x-request-id",
)
}
event_id = datetime.now(timezone.utc).strftime("%Y%m%d%H%M%S%f")
info = {
"event_id": event_id,
"time": _now_iso(),
"client": getattr(request.client, "host", None),
"method": request.method,
"path": str(request.url.path),
"query": dict(request.query_params),
"content_type": ct or None,
"interesting_headers": interesting_headers,
"body": body_preview,
"body_bytes": len(raw),
}
log.info("webhook %s | %s", event_id, json.dumps(info, ensure_ascii=False))
response.headers["X-Webhook-Received"] = event_id
return {"ok": True, "event_id": event_id, "received_at": info["time"]}
def main() -> None:
import uvicorn
host = os.environ.get("HOST", "0.0.0.0")
port = int(os.environ.get("PORT", "8000"))
uvicorn.run("server:app", host=host, port=port, reload=False)
if __name__ == "__main__":
main()