微信开放平台与 UnionID 打通

admin 微信QQ技巧 2026-10-21 2 0

用户用小程序注册了账号,后来你上线一个 H5,让他用微信登录授权,系统里凭空多出一个同名新用户,之前攒的积分、订单、优惠券全都不见了。你查库发现两条记录 openid 完全不同,于是断定「用户换了号」。其实没有,这是同一台手机、同一个微信号,只是你在两个应用里拿到的 openid 本来就不是同一个值。想认出这是同一个人,你需要 unionid。

一、开放平台解决了什么问题

微信体系里每个应用都是独立发号者。一个微信号在某个公众号里被分配一个 openid,它只在那一个应用的语境下有意义。同一用户在你的另一个公众号、小程序、APP 里会被分配另一个 openid,彼此毫无关联。这是设计上的隐私隔离。

开放平台给这条隔离加了一把钥匙:当多个应用绑定到同一个开放平台账号下,微信会为同一用户在这些应用里额外提供一个一致的标识——unionid。于是你有了两个视角,openid 说明「他在这一个应用里是谁」,unionid 说明「他在我所有应用里是同一个人」。

绑定之后得到什么

  • 跨应用识别同一用户,做账号打通与数据合并。
  • 小程序与公众号共用一套用户体系,一次注册多端可用。
  • APP 与网页登录状态互认,用户换端不必重新注册。
  • 部分开放能力(设备、卡券类)需要开放平台资质。
  • 移动应用分享到微信时的应用信息展示,也依赖开放平台登记。

二、怎么把各端绑到同一个开放平台

前置条件是有一个完成资质认证的开放平台账号(企业主体),然后逐个绑定应用。

公众号

在公众号后台发起绑定,用开放平台管理员微信扫码确认。要求公众号已认证且未被绑到其他开放平台账号。一个公众号只能属于一个开放平台账号,绑错了要先解绑。

小程序

同样在小程序后台或开放平台侧发起,需要小程序管理员扫码。未认证的小程序也能绑定,unionid 拿得到,但很多接口权限会受限。

移动应用与网站应用

这两类要在开放平台主动创建,填写安卓包名与签名、iOS 的 Bundle ID、网站域名与备案信息,审核通过后得到独立 appid。移动应用要求包名与签名严格匹配,证书换了必须同步更新,否则微信登录直接失败。

绑定的顺序不重要,重要的是最后能在开放平台账号下看到所有应用挂在一起。少绑一个,那个端就永远拿不到 unionid。

三、openid 与 unionid 的本质区别

把它们当成两个层级的编号最容易理解:openid 是「应用内的用户编号」,unionid 是「开发者范围内的用户编号」。同一用户在不同应用里 openid 不同,unionid 相同。

对比项openidunionid
作用范围单个公众号或单个小程序同一开放平台账号下所有应用
同一用户跨应用值不同,无法关联值相同,可关联
获取前提登录授权即可须绑定开放平台,且能取到用户信息
稳定性与应用绑定,长期有效与开发者账号绑定,解绑重绑可能变化
适合做什么单应用内的会话标识、消息推送目标全局账号主键、跨端数据合并

别把 openid 当全局主键存进用户表。业务扩到第二个应用时,你会被迫做一次痛苦的数据迁移。

四、unionid 的获取时机与条件

它不是随时能拿到的,需要同时满足几个条件。

  1. 该应用已绑定到某个开放平台账号。没绑的话返回里根本不会有这个字段,你甚至察觉不到缺了什么。
  2. 这次调用拿到了用户信息。仅静默授权、只取 openid 的场景下可能不返回 unionid;走了获取昵称头像的授权才会出现。
  3. 同一微信用户在满足上述前提的各端,拿到的 unionid 一致。

不同应用类型的路径不一样,最容易混。

应用类型获取 unionid 的方式
小程序登录换取会话时返回结构可能带 unionid;或调用获取用户信息的接口拿到
公众号网页网页授权换取用户信息时返回,授权范围需为获取用户信息
移动应用走微信登录,用临时票据换用户信息时返回
网站应用扫码登录换取用户信息时返回
消息与事件推送关注等事件报文里也可能带 unionid,可用于静默对齐

接口地址统一放运维文档,别散落在代码里。

五、多端登录的账号合并策略

拿到 unionid 后,难的是存量数据怎么合。按下面顺序思考。

先看是否命中

用 unionid 查全局用户表,命中就把当前端 openid 写进这个用户的多端绑定关系,直接发登录态。

未命中再建新用户

用 unionid 建一条主体记录,同时建立「本端 openid 到该用户」的映射。这步必须原子,同一用户并发登录两次不能建出两条主体。

历史数据做一次性对齐

如果旧表以 openid 为主键,做一次离线对齐任务:遍历历史 openid 查 unionid,把重复的合并。合并规则要明确定义——积分相加、订单迁到保留记录、优惠券去重、手机号取非空的那个。别指望自动合并完美,留个人工审核入口处理冲突。

const session = await code2Session(code);
let user = session.unionid
  ? await userRepo.findByUnionid(session.unionid)
  : null;

if (!user) user = await userRepo.create({ unionid: session.unionid });

// openid 永远作为附属关系存,不承担主键职责
await bindingRepo.upsert({
  userId: user.id,
  appType: 'miniprogram',
  appId: MY_APP_ID,
  openid: session.openid
});

六、账号体系设计建议

核心一条:以 unionid 作为用户主体的唯一标识,openid 作为附属渠道身份。以后加任何一端都只是新增一条绑定记录,不动主体表。

  • 主体表只放全局属性:unionid、昵称、头像、手机号、全局积分、创建时间。
  • 绑定表放 user_id、应用类型、应用标识、openid,加唯一索引防重复绑定。
  • 不要把 openid 放进主体表的联合主键,那等于把主体绑死在一个应用上。
  • unionid 可能因解绑重绑变化,主键建议用自增或雪花 ID,unionid 做唯一索引。

七、拿不到 unionid 怎么排查

  1. 确认应用有没有绑定开放平台。后台应用列表里看不到它,就是这个原因。
  2. 确认报文里有没有该字段。很多人是代码里用了旧的字段映射,搜关键字搜不到。
  3. 确认授权范围是否足够。只静默授权拿 openid 时不带 unionid 是正常的。
  4. 用同一个微信号在两台设备、两个端各登录一次,对比日志里的值。
  5. 确认测试环境用的是哪个 appid。测试号、体验版、正式版可能对应不同登记,容易出现「线上有、体验版没有」。
  6. 检查解析代码的大小写,有些序列化框架会做驼峰转换,字段名对不上就静默变成空值。

再补一条运维经验:把 unionid 写进登录日志与报警信息。用户投诉订单不见了时,能凭一个值判断是他换了账号,还是你在某一端把它弄丢了。