Files
yunzerwebsiteallinone/go/docs/微信小程序消息推送配置.md
T
2026-10-31 19:17:37 +08:00

136 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 微信小程序「消息推送」配置指引
对应小程序后台:**开发管理 → 开发设置 → 消息推送**(就是你截图里的那个表单)。
---
## 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=<签名>&timestamp=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 <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`),接口只回显掩码。