为什么这么设计(含被否掉的方案)
这一页写给以后接手的人(包括六个月后的我们自己)。里面每条都是"不这么做会付出什么代价"的实测或官方口径。
核心决策:把微信能力收敂到一个独立站点
| 考虑过的方案 | 结论 | 原因 |
|---|---|---|
| A. 每个业务站各自接微信 | 否 | 网页授权域名只有 2 个名额、每月限改 5 次,且子域要逐个精确配(官方示例明确 pay.qq.com 不被
www.qq.com 覆盖);接入第 3 个站点就没名额了。密钥也会散落到多台机器。 |
| B. 用 nginx 反代把多个域名伪装成一个 | 否 | 网上有这种"授权域名做一级目录反代"的技巧,但它要求业务站能在子路径下运行、 Cookie 与 Host 会错乱,且属于钻空子而非合规路径。 |
| C. 再申请一个服务号 | 否 | 需要新的认证(300 元/次)+ 新备案域名,而且用户要关注两个号,推送分裂在两处,运营更差。 |
| D. 微信第三方平台(代公众号运营) | 暂缓(未来多主体时用) | 扫码授权换 authorizer_access_token 能绕开一堆域名限制,但需要开放平台第三方应用资质审核、
组件令牌体系与代运营协议。当前只有一个运营主体,收益不抵复杂度。 |
| E. 本方案:统一中台 + 一次性票据中转 | 采纳 | 授权域名只花 1 个名额、支付授权目录只花 1 条、密钥集中一处、新站点接入是"注册一条记录"而不是"改微信后台"。 |
关键取舍逐条
1) 为什么不接管公众号的「消息与事件推送 URL」
这个位置只能配一套,现在属于 edusoho。抢过来会让老系统的关注回复、菜单点击、关键词回复全部失效——
而 edusoho 的服务器不在这次改造范围内。于是关注状态改为主动轮询(/user/get + /user/info/batchget)。
代价:关注状态有分钟级延迟、拿不到"刚刚关注"的即时事件,因此48 小时客服消息这条兜底通道在当前形态下不可用。
如果以后 edusoho 下线或迁走,把 URL 换到中台即可解锁即时事件与客服消息(代码里已预留 WxEventController 的位置)。
2) 为什么只用稳定版 access_token 的普通模式
token 是账号级的。普通版 /cgi-bin/token 重复获取会让上一个 token 失效;
强制刷新(force_refresh=true)更是立刻顶掉。中台与 edusoho 共用同一个公众号,
所以任何一处乱刷 token 都会让另一处业务当场瘫痪。稳定版普通模式在有效期内重复调用返回同一个 token,不互踢。
3) 为什么回跳用一次性票据,而不是 URL 里带 openid
URL 会进浏览器历史、Referer、以及站点自己的访问日志。把 openid(微信身份标识)长期外泄, 等于任何看到日志的人都能替该用户发起登录流程。票据 60 秒、单次、绑定站点,且兑换需要站点密钥签名。
4) 为什么支付走托管收银台,而小程序不走
JSAPI 支付校验的是"拉起收银台的页面 host 与路径"是否命中支付授权目录(共 5 条)。
把支付页收到中台一个 host 上,就只需 1 条目录覆盖所有站点。
但小程序 web-view 内嵌 H5 支付被微信限制,所以小程序必须用 scene=miniprogram 直接调起。
5) 为什么订阅授权引导页也放中台
wx-open-subscribe 要求页面 host 在「JS 接口安全域名」(5 条)里。放中台,所有站点共用 1 条。
6) 为什么 PC 支付用"带参数二维码 → 手机打开收银台"而不是 Native 扫码
Native 需要单独申请开通,且二维码内容必须由商户自己渲染(中台不引第三方二维码服务,
把订单号交给外部网站生成二维码是信息外泄)。用公众号自带的 qrcode/create + showqrcode,
扫码后进入同一个收银台链路,不额外开产品、不额外占域名。若以后必须支持"PC 直接扫钱包码支付",
再补 scene=native 的渲染分支即可(接口已实现)。
7) 为什么密钥不下放给站点,但 session_key 例外
AppSecret、商户私钥、APIv3 属于"全局凭据",一旦下放到 N 个站点就变成 N 倍泄露面。
而小程序的 session_key 只能由持有它的服务端解密手机号,属于"单用户短期凭据",
必须交给站点服务端 —— 代价是明确要求它不落日志、不下发前端。
8) 为什么一个商户号可以给多个站点用,但不能给别的法人用
一个商户号最多可关联 50 个 AppID,同主体多站点共用完全合规。
但用 A 公司的商户号收 B 公司的钱,属于无证经营支付业务(二清),是硬性红线。
所以模型设计成 pay_channels 多条:以后复制给其他机构时,每家机构填自己的商户号。
9) 为什么短信腿留在站点侧
腾讯云国内短信必须先做企业实名资质报备,签名要与营业执照主体一致,
平台审核后还要运营商报备 7–10 个工作日,长期不发会被闲置失效。
不同机构的签名与资质不同,集中代收反而成为阻塞点,所以中台只负责微信三档(模板/订阅/小程序),
把"触达不到"如实标成 skipped 交回站点降级。
已知边界(不是缺陷,但要知道)
- unionid 依赖开放平台绑定。没绑之前跨站点认不出同一个人,接口会返回空串而不是报错。
- 昵称只能靠
snsapi_userinfo。/cgi-bin/user/info自 2021-12 起不再返回昵称头像。 - 日配额与 edusoho 共享。中台的调用计数是"账号级"的,突发大批量通知会挤占老系统的额度。
- 别名域名不参与授权链路。
wx.123xl.cn只用来开后台与文档; 若它的备案接入与服务器所在云不一致,还可能被接入商拦截 80/443。 - 单机 SQLite。这套设计的吞吐上限是"每分钟一批",适合预约/提醒这类业务量; 如果以后要做多机构大规模群发,需要把队列换成外部 MQ 并做分布式 worker。
- 未实现(按需求顺序排在后面):微信"第三方平台"代运营、开放平台网站应用扫码登录、 交易账单自动对账、消息撤回与已读回执。
如果以后要扩到多机构(复制发布)
- 每家机构:自己的服务号 + 小程序 + 商户号 → 在后台各自登记 profile 与 pay_channel。
- 域名:同主体可以继续用这一个中台 host(在它们的公众号后台填同一个授权域名即可, 前提是该域名的备案主体与它们一致);主体不同时,把中台代码整套部署到对方域名下 (本系统无状态、SQLite 单文件,可以按机构一份实例)。
- 站点注册表已经支持多站点、多账号、多通道路由,不需要改代码。
本页内容随代码一起版本化(V0.1.0)。发现文档与实际行为不一致,请当缺陷报告。