# 微信小程序「消息推送」配置指引 对应小程序后台:**开发管理 → 开发设置 → 消息推送**(就是你截图里的那个表单)。 --- ## 0. 先搞清楚这个配置是干什么的 微信后台的「消息推送」= **你的服务器接收微信主动推送的入口**。 | 场景 | 是否走这个配置 | | --- | --- | | 用户给小程序「客服」发消息、进入客服会话 | ✅ 走(微信 POST 到你的 URL) | | 扫普通链接二维码打开小程序等事件 | ✅ 走 | | 订阅消息(`wx.requestSubscribeMessage`) | ❌ 不走,用服务端 API 主动发 | | 模板消息 | ❌ 已下线,改用订阅消息 | --- ## 1. 前置条件(不满足会一直「配置失败」) 1. **域名已 ICP 备案**(微信后台会校验)。 2. **只用 80 / 443 端口**:`https://xxx.com`(443)或 `http://xxx.com`(80),其它端口微信不接受。 3. **微信服务器能直连**:公网可达,不能是 `127.0.0.1`、内网 IP 或本机端口。 4. **HTTPS 证书链完整**:只部署 `.crt` 不带中间证书,微信侧会握手失败(自签证书不行)。 5. **5 秒内返回**:接口要在 5 秒内响应,否则微信断开并重试。 > 本项目的回调地址在 `go/conf/app.conf` 里配置: > `wechat_mini_callback_base = https://api.yunzer.cn`(留空则退化为相对路径 `/api/wechat/mini/callback`,需由网关补域名)。 --- ## 2. 第一步:在本平台生成参数 平台端:**系统设置 → 小程序配置 → 编辑某个小程序 → 消息推送** | 字段 | 操作 | 说明 | | --- | --- | --- | | 回调地址 | 自动生成,点「复制」 | 即 `https://你的域名/api/wechat/mini/callback` | | Token | 点「随机生成」 | 3-32 位字母数字 | | EncodingAESKey | 点「随机生成」 | 43 位字母数字,后台也有「随机生成」按钮,两边值必须一致 | | 加解密方式 | 明文 / 兼容 / 安全 | 与微信后台选择保持一致 | | 数据格式 | XML / JSON | 与微信后台选择保持一致 | | 自动回复文案 | 选填 | 留空 = 只接收不回复 | 填完点 **「保存消息推送配置」**(注意:这一步只存平台端,不会自动同步到微信后台)。 --- ## 3. 第二步:在微信后台把值抄过去 小程序后台 → 开发管理 → 开发设置 → 消息推送,逐项对应: | 后台字段 | 填什么 | | --- | --- | | URL(服务器地址) | 平台端「回调地址」整条复制,如 `https://api.yunzer.cn/api/wechat/mini/callback` | | Token(令牌) | 平台端「Token」 | | EncodingAESKey(消息加密密钥) | 平台端「EncodingAESKey」(43 位) | | 消息加密方式 | 与平台端一致(明文 / 兼容 / 安全) | | 数据格式 | 与平台端一致(XML / JSON) | 填完点「提交」,微信会立刻发一个 **GET** 请求到你的 URL 做校验,通过就提示配置成功。 ⚠️ 配置**提交后立即生效**,切换加密方式或数据格式前,务必先把平台端改成同样的值,否则消息会被验签/解密失败直接丢弃。 --- ## 4. 提交时微信做了什么(校验逻辑) 微信 GET 你的 URL,参数:`signature` `timestamp` `nonce` `echostr`。 - **明文 / 兼容模式**:服务端算 `sha1(sort(token, timestamp, nonce) 拼接)` 与 `signature` 比对,通过后**原样返回 `echostr`**。 - **安全模式**:`echostr` 是密文,额外带 `msg_signature = sha1(sort(token, timestamp, nonce, echostr))`;服务端验签后解密,再返回解密结果。 对应实现: - `go/services/wechatmini/push.go` —— 签名校验、AES 加解密、XML/JSON 解析、被动回复构造 - `go/controllers/api_wechat_mini.go` —— `GET /api/wechat/mini/callback` 校验 + `POST` 收消息 **多小程序**:每个小程序在自己的后台单独配置,本平台按 AppID 分别维护 Token / AESKey; 回调接口不携带 appid,服务端会逐个应用匹配签名,命中哪个就按哪个的密钥解密。 --- ## 5. 收到消息后的行为 - 微信 **5 秒**内没收到响应会断开并重试,**总共重试 3 次**(间隔 15s / 5s / 5s)。 - 返回**空串**或 `success` = 已处理,微信不再重试(我们的实现返回空串)。 - 平台端配了「自动回复文案」时,5 秒内以被动回复形式把文案回给用户。 - 复杂业务(例如按关键字回复、转发人工)可接管钩子: `services/wechatmini/push.go` 中的 `PushHandler`。 --- ## 6. 常见报错排查 | 现象 | 常见原因 | 处理 | | --- | --- | --- | | 后台一直「配置失败」 | 域名未备案 / 端口不是 80、443 / 证书链不完整 / 返回内容不是 echostr | 逐条对照第 1 节 | | 后台提示超时 | 接口处理超过 5 秒 | 先只回 echostr,业务逻辑异步做 | | 后台提示 Token 校验失败 | 平台端 Token 与后台填的不一致 | 两边都复制一遍核对(区分大小写) | | 配置成功但收不到消息 | 加解密方式或数据格式两边不一致 | 平台端与后台改成一致 | | 小程序会话提示「客服暂时无法提供服务」 | 5 秒内没回复 | 配「自动回复文案」,或让接口先返回空串 | | 明文模式正常,切安全模式收不到 | EncodingAESKey 两边不一致 | 重新复制 43 位密钥 | ### 自测命令(明文模式) ```bash # signature = sha1(sort(token, timestamp, nonce) 拼接),用与平台端相同的 token curl "https://api.yunzer.cn/api/wechat/mini/callback?signature=<签名>×tamp=1700000000&nonce=abc123&echostr=hello" # 期望输出:hello(等于传入的 echostr) ``` 服务端日志会打印 `[wechat-mini] 收到推送: ...`,看不到这行说明微信请求没到服务端(先查域名 / 端口 / 证书)。 --- ## 7. 相关接口 | 方法 | 路径 | 说明 | | --- | --- | --- | | GET/POST | `/api/wechat/mini/callback` | 微信回调(无需鉴权,内部验签) | | GET | `/platform/wechatMini/app/push?app_id=xxx` | 读取推送配置(含回调地址) | | POST | `/platform/wechatMini/app/push/save` | 保存推送配置 | | POST | `/platform/wechatMini/app/push/generate` | 生成随机 Token / EncodingAESKey | ## 8. 数据库 新增字段(`token` / `aes_key` / `encrypt_mode` / `data_format` / `push_reply`): ```bash mysql -h -P -u -p < go/docs/sql/alter_wechat_mini_app_push.sql ``` `aes_key` 以 AES-256-GCM 加密入库(复用公众号的 `wechat_mp_secret_key`),接口只回显掩码。