微信生态开发:支付、登录与开放平台的坑#
微信生态不是"一个 API",是一套规则:小程序、公众号、开放平台、支付,每个都有自己的凭证、接口和坑。这篇文章是六年微信开发(小程序/服务号/支付/开放平台)的踩坑总结。
一、三种身份凭证#
| 凭证 |
用途 |
有效期 |
| AppID + AppSecret |
公众号/小程序的身份 |
永久 |
| access_token |
调用接口的令牌 |
2 小时,需缓存刷新 |
| openid / unionid |
用户身份 |
永久 |
核心概念:
二、微信登录(自动授权)#
1
2
3
4
5
6
7
8
9
10
|
// 后端(小程序登录)
func WxLogin(ctx, code string) (*LoginResult, error) {
resp, _ := http.Get(fmt.Sprintf(
"https://api.weixin.qq.com/sns/jscode2session"+
"?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
appid, secret, code))
// resp: {openid, session_key, unionid}
user := findOrCreateByOpenid(resp.Openid)
return issueToken(user), nil
}
|
坑:
三、微信支付#
核心流程#
关键:支付回调(最容易错的)#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
func PayNotify(w http.ResponseWriter, r *http.Request) {
// 1. 读回调(XML/JSON)
body := readBody(r)
// 2. 验签(用 APIv3 密钥/证书)
if !verifySignature(r, body) {
w.WriteHeader(401); return // 验签失败:不返回成功
}
// 3. 查单确认(别只信回调!回调可能重复/伪造)
order := wx.QueryOrder(body.OutTradeNo)
if order.Status != "SUCCESS" {
w.WriteHeader(400); return
}
// 4. 幂等更新(回调可能来多次!)
if !markOrderPaid(order.OutTradeNo) { // DB 幂等
w.WriteHeader(200); return // 已处理过:返回成功(不重发)
}
// 5. 必须返回成功(否则微信会重试 24h!)
w.WriteHeader(200)
}
|
回调的铁律:
四、开放平台#
五、防重与安全#
六、踩坑记录#
- access_token 并发刷新互踢:多个实例同时刷新 → 旧的失效,新的也失效 → 加分布式锁 + 本地缓存
- 金额用浮点:0.1 + 0.2 = 0.30000000000000004 → 订单金额错 → 一律用"分"(整数)
- 回调重复触发:返回非 200 → 微信重试 24h → 幂等处理 + 必须返回成功
- unionid 拿不到:小程序没绑开放平台 → 调接口没 unionid → 先绑再开发
微信生态的核心认知:
- 三种凭证 + 两种用户 ID:openid 应用内唯一,unionid 跨应用打通
- 支付回调四铁律:验签、查单、幂等、必返成功
- 金额用分:整数运算,浮点是事故源
- access_token 全局管理:缓存 + 刷新锁,防互踢
微信生态的复杂度不在"接口多",在"状态机多":登录态、支付态、退款态,每个状态转换都有规则和坑。把状态机画清楚,微信开发就成功了一半。
微信公众号「福清而不淡」
后端架构 · AI 工程 · 云原生的一线实践,扫码关注,不错过更新。
本文已同步发布到公众号,欢迎留言交流。