6.4 KiB
微信小程序「消息推送」配置指引
对应小程序后台:开发管理 → 开发设置 → 消息推送(就是你截图里的那个表单)。
0. 先搞清楚这个配置是干什么的
微信后台的「消息推送」= 你的服务器接收微信主动推送的入口。
| 场景 | 是否走这个配置 |
|---|---|
| 用户给小程序「客服」发消息、进入客服会话 | ✅ 走(微信 POST 到你的 URL) |
| 扫普通链接二维码打开小程序等事件 | ✅ 走 |
订阅消息(wx.requestSubscribeMessage) |
❌ 不走,用服务端 API 主动发 |
| 模板消息 | ❌ 已下线,改用订阅消息 |
1. 前置条件(不满足会一直「配置失败」)
- 域名已 ICP 备案(微信后台会校验)。
- 只用 80 / 443 端口:
https://xxx.com(443)或http://xxx.com(80),其它端口微信不接受。 - 微信服务器能直连:公网可达,不能是
127.0.0.1、内网 IP 或本机端口。 - HTTPS 证书链完整:只部署
.crt不带中间证书,微信侧会握手失败(自签证书不行)。 - 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 位密钥 |
自测命令(明文模式)
# 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):
mysql -h <host> -P <port> -u <user> -p <db> < go/docs/sql/alter_wechat_mini_app_push.sql
aes_key 以 AES-256-GCM 加密入库(复用公众号的 wechat_mp_secret_key),接口只回显掩码。