小程序要发订阅消息,接口一直返回 40001 invalid credential,翻文档才发现 access_token 被另一个服务刷掉了。

微信的 access_token 全局唯一,有效期 7200 秒。多个服务各自去取,后取的会把先取的顶掉,两边都开始随机报错,而且只在整点前后出现,很难复现。

几个容易混的标识

  • access_token:调用接口的凭据,全局一份
  • openid:用户在你这个应用里的 ID,不同应用不通用
  • unionid:同一主体下所有应用统一,需要绑定开放平台
  • jsapi_ticket:网页 JS-SDK 签名用,单独缓存

网页授权用 code 换网页 access_token 和 openid,小程序用 wx.login 拿 code 换 openid,两套接口别混着调,参数名都不一样。

签名和回调

JS-SDK 签名是把 jsapi_ticket、noncestr、timestamp、url 按字典序拼起来做 sha1,url 要带 query,且不含井号后面的部分。缓存两小时,但要比 7200 秒提前几分钟刷新。

回调地址必须是备案域名、80 或 443 端口。验证时把 echostr 原样返回,别包引号、别加空格,多一个字符都通不过。

看 errcode 定位

40001 凭据失效、40013 AppID 不对、45009 接口调用超限、48001 接口未授权,通常是没开通或者主体没认证。这些码比错误描述准,直接搜码更快。

编码问题也常见。消息里有表情或繁体字,不声明 UTF-8 就变成问号;POST 的 JSON 要 ensure_ascii 设成 False,否则中文全被转义成 u 开头的一串,日志里根本看不出问题。

access_token 必须集中管理:一个服务定时刷新写进 Redis,其他服务只读。微信的限频按接口算,群发要排队加间隔,一次性打满会被封到当天结束。