PROJECT OVERVIEW · 2026-05-11

wx-gateway

微信公众号统一回调网关 · 8 业务方 · 双实例同代码 · 16 张表 · Next.js 15 + Prisma + PostgreSQL

2
实例(造悟者 / 莆阳)
8
已注册业务方
16
数据表
~25
HTTP 路由
01

整体架构

微信平台事件单向进入网关,网关同步/事件回调业务方;双实例共享同一份代码,各自连独立分库。

微信公众号 · 造悟者 openid · access_token · 事件推送 snsapi_userinfo OAuth 微信公众号 · 莆阳 独立 appid · 独立粉丝池 同代码 不同 env wx-gateway (Next.js 15) :3794 造悟者实例 · :3800 莆阳实例 · 同源代码 扫码登录 qr/poll/finalize OAuth 完善信息 新增 2026-05-11 支付 Payment personal_qr + JSAPI 消息转发 Fanout Route → Endpoints Admin 后台 /_a/<32hex> 业务方自助注册 /apps/register Prisma · HMAC 签名 · SSE 推流 · pnpm · PM2 业务方应用 (共 8 个已接入) ph · pack · echo copilot-proxy design-studio-prod menshen-ui admin · selftest PG · wx_gateway 造悟者实例分库 16 表 全量 PG · wx_gateway_pucs 莆阳实例分库 同 schema 独立数据 HTTP · 事件推送 GET · token/oauth SSE · webhook POST · qr/pay/finalize 实线 · 同步调用 虚线 · 事件 / 异步
02

6 大功能模块

每个模块独立可关闭(env feature flag),业务方按需对接。

① 扫码登录 stable

业务方拉带参临时二维码 → 用户扫 → SSE 推 redirect → 业务方 HMAC finalize 落 cookie。

业务方 网关 微信 qr ticket SCAN SSE redirect finalize

② OAuth 完善信息 new

requireProfileCompletion=true该开关启用后:扫码先标 scanned,公众号回 OAuth 链接,用户点击走 snsapi_userinfo 拿头像昵称 → promote confirmed。

扫码 scanned 公众号 push OAuth link 用户点击 snsapi_userinfo confirmed 拒绝→rejected

③ 支付 manual

personal_qr 模式人工审核 + JSAPI 占位。webhook HMAC event|payOrderId|status|ts,退避重试 6 次 [0,30s,2m,10m,1h,6h,24h] 不达即 dead。

/pay/create 人工审核 paid webhook(退避重试) → dead 0 · 30s · 2m · 10m · 1h · 6h · 24h

④ Fanout 消息转发 stable

公众号文本/事件按 MessageRoute 规则匹配多 FanoutEndpoint,首个 ownsReply=true 下游决定回复 XML;FanoutLog 全量留痕,DownstreamHealth 心跳监控。

消息 Route ep1 ★owns ep2 ep3

⑤ Admin 后台 stable

安全 path /_a/<32hex>/*(env ADMIN_PUBLIC_PATH),扫码登录;5+ tab:Apps / Invites / Admins / Scans / Payments / Routes / Endpoints / FanoutLogs。直访 /admin 返 404 伪装。

/_a/<hex>/dashboard Apps Invites Admins Scans Pay Routes Endpoints FanoutLog Downstreams /admin → 404 camouflage

⑥ 业务方自助注册 stable

admin 颁邀请码(hash 入库) → 业务方 POST /apps/registerX-Invite-Code → 网关现生 64hex secret(DB 只存 hash) → 业务方写 env。

admin 颁码 业务方 POST secret 64hex /apps/register · X-Invite-Code DB 存 SHA-256(secret) 明文只下发一次
03

扫码登录 · 完整时序

上图:默认路径(requireProfileCompletion=false);下图:启用 OAuth 完善信息后的扩展路径(2026-05-11 新增)。DB 写入用独立 lifeline 列。

默认路径 · requireProfileCompletion=false 用户 业务方前端 wx-gateway DB 微信平台 业务方后端 POST /wx/qr/{app} create_qrcode(带参 token) ticket qrUrl + token show QR GET /wx/poll/{token} · SSE 用户扫码 POST /wx/callback (SCAN) write → confirmed SSE event: redirect POST /finalize(token, HMAC) verify token + HMAC openid + 一次性 nonce Set-Cookie · 登录完成
OAuth 完整路径 · requireProfileCompletion=true 用户 业务方前端 wx-gateway DB 微信平台 业务方后端 POST /wx/qr/{app} qrUrl + token · 业务方挂 SSE poll 用户扫码 POST /wx/callback (SCAN) write → scanned(非 confirmed) 回复 OAuth 链接 XML 推送给用户(公众号消息) 用户点击链接 · snsapi_userinfo 展示授权页 GET /wx/oauth/callback?code= code → access_token + userinfo nickname / avatar / unionid promote → confirmed SSE event: redirect POST /finalize(HMAC + profile) Set-Cookie · 含完整 profile 用户拒绝授权 → token = rejected · SSE event: rejected
04

数据模型 · 16 张表

按业务域分 5 组,prisma/schema.prisma 单源。

Login (3)

WxLoginToken
status: pending|scanned|confirmed|rejected|expired · openid · profile
WxAccessTokenCache
appKey · accessToken · expiresAt
UserAppBinding
unionid ↔ app ↔ openid · nickname/avatar

App (2)

App
id · secretHash · finalizeUrl · requireProfileCompletion
AppInvite
codeHash · createdBy · usedAt

Admin (2)

AdminUser
unionid · role · lastLogin
ScanLog
tokenId · event · openid · createdAt

Payment (5)

Payment
orderId · amount · channel · status
PaymentWebhookDelivery
attempts · nextRetryAt · status
PaymentRefund
refundId · amount · operator
AppPaymentChannel
app · channel cfg · enabled
WxpayPlatformCert
serialNo · publicKeyPem

Fanout (4)

MessageRoute
matchType · pattern · priority
FanoutEndpoint
url · ownsReply · secret · timeoutMs
FanoutLog
routeId · endpointId · status · latency
DownstreamHealth
endpointId · lastSeen · consecutiveFails

表统计

合计 16 表
Login 3 · App 2 · Admin 2 · Payment 5 · Fanout 4
迁移工具
prisma migrate · 双实例独立执行
索引
token/openid/orderId/endpointId 复合索引
05

部署拓扑

两实例共享 GitHub 源,各自走 mvp-deployer 异步构建,PM2 起进程,Caddy 反代,凭据走门神 vault。

GitHub Restry/wx-gateway mvp-deployer deploy.mvp.restry.cn git push / hook 实例 A · 造悟者 域名: wx.mvp.restry.cn 端口: :3794 进程: PM2 wx-gateway 凭据: vault wx-gateway/* PG wx_gateway 实例 B · 莆阳 域名: wxmsg.mvp.restry.cn 端口: :3800 进程: PM2 wx-gateway-pucs 凭据: vault wx-gateway-pucs/* PG wx_gateway_pucs build · zip build · zip Caddy auto-routing TLS 同源代码 · 不同 env / 不同 vault namespace / 不同分库 · 任一实例宕机不影响另一实例
06

已注册业务方 · 8 个

真实业务 6 个 + 自吃狗粮 1 个 + 回归通道 1 个。

ph
莆阳主站
pack
打包工具产品
echo
回声 demo / 示例
copilot-proxy
Copilot 代理服务
design-studio-prod
设计工作室生产环境
menshen-ui
门神管理界面
admin
自吃狗粮 · admin 自身扫码用
selftest
自动化回归通道