[每周一更]-(第136期):微信公众号模板消息全攻略,手把手教你高效触达用户!

在这里插入图片描述


我们需求是:在登录我们小程序的用户,在进行购买操作后,在用户绑定完样本,我们对样本进行回收-做实验-人工审核-出报告这个一系列过程,等报告出来后需要通知关注我们微信公众号的用户,来提升用户体验。

我们小程序中存在三种通知形式:短信通知、站内信、微信公众号(需关注),以下详细讲解第三种,前两种大家都不陌生,都是常规操作。

1、微信公众平台配置

前提已经是服务号认证

1.1、需要开启消息模版

在这里插入图片描述

1.2、选择合适的模版消息

在这里插入图片描述

1.3、模版消息详细参数信息:

在这里插入图片描述

1.4、服务类目,需要通过合作方协议合同、营业执照等来进行提交获取审核;

在这里插入图片描述

1.5、微信公众平台绑定公众号和小程序

在这里插入图片描述

在这里插入图片描述

2、概念

  • 先明确几个概念

2.1、微信-服务号、订阅号、公众号、公众平台、开放平台

概念定位角色功能差异
微信开放平台微信生态的“桥梁工具”打通多个应用(APP、公众号、小程序、网站)提供UnionID体系、用户身份互通、能力开放的开发平台,开发者在此注册技术接口权限。
微信公众平台后台管理系统微信公众号的管理工具(类似网站的后台)统一管理订阅号、服务号的发布、数据、设置等。
微信公众号账号类型总称订阅号 + 服务号的总称根据需求选择子类(订阅号=发内容,服务号=做服务)
微信订阅号信息传播为主适合新闻媒体、个人、教育机构等每天可发1条(折叠在订阅号列表),无支付/客服,适合长期品牌宣发。
微信服务号服务交互为主适合企业、政务机构、电商平台等每月4次群发(直接显示在微信聊天列表),支持微信支付/模板消息/多客服等。
微信生态结构:
          +-------------------+  
          |  微信开放平台       | → UnionID跨应用互通,技术接口提供
          +---------+---------+  
                    |
                    ↓
          +-------------------+  
          |  微信公众平台       | → 管理具体账号(公众号:订阅号/服务号)
          +---------+---------+  
                    |
                    ↓
          +-------------------+  
          |  微信公众号         | → 实际触达用户的内容或服务(用户看到的账号)
          +-------------------+  

2.2、小程序中的openid和公众号的openid不一样

  • appid:AppID是不同类型的产品的账号ID,是账号的唯一标识符。
  • openid:微信用户在不同类型的产品的身份ID。(小程序和微信公众号用户openid不同)
  • unionid:微信用户在同一个开放平台下的产品的身份ID。

2.3、小程序累计用户数

  • 去重统计:同一个用户多次访问小程序,只会计为 1 个用户。

  • 基于 openid:每个用户在小程序中的唯一标识是 openid,微信通过 openid 进行去重统计。

完整流程:用户通过微信快捷登录登录注册进入小程序,会保存属于小程序的openid 和 unionid,因为我们是从小程序用户着手,需要拿到unionid去数据表对比(直接无法找到)找到微信公众号的openid,才能消息通知关注的用户:你的小程序中的报告出来了,快去看吧。

2.4、UnionID获取途径

绑定了开发者账号的小程序,可以通过以下途径获取 UnionID。

  1. 开发者可以直接通过 wx.login + code2Session 获取到该用户 UnionID,无须用户授权。
  2. 小程序端调用云函数时,可在云函数中通过 Cloud.getWXContext 获取 UnionID。
  3. 用户在小程序(暂不支持小游戏)中支付完成后,开发者可以直接通过getPaidUnionId接口获取该用户的 UnionID,无需用户授权。注意:本接口仅在用户支付完成后的5分钟内有效,请开发者妥善处理。

2.5、接口文档

(1)模版消息文档:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html

(2)小程序登录,生成小程序的openid和unionid:

参考文档:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html

  • 在小程序或公众号端调用 wx.login() 获取临时登录凭证 code
  • https://api.weixin.qq.com/sns/jscode2session ,获取openid和unionid。
  • 业务数据库中如果没有存unionid,则通过比对openid来更新unionid。

(3)通过unionid查找微信公众号的openid,来完成模版消息发送

参考文档:https://developers.weixin.qq.com/doc/offiaccount/User_Management/Get_users_basic_information_UnionID.html#UinonId

2.6、获取公众号openid完整流程

用户登录小程序 → 获取小程序的openid → 通过UnionID关联 → 查询公众号openid
1. 准备工作
  • 绑定到同一开放平台:
    • 将小程序和公众号绑定到同一微信开放平台账号(登录微信开放平台 > 管理中心 > 绑定公众号/小程序)。
  • 认证要求:
    • 公众号必须是已认证的服务号(认证后才能获取UnionID)。

2. 小程序端获取用户信息

在小程序中,用户授权登录后,通过 wx.login 获取 code,发送到开发者服务器换取用户的 openid 和可能的 unionid

wx.login({
  success(res) {
    if (res.code) {
      // 将 code 发送至服务端,换取 openid 和 unionid
      wx.request({
        url: 'YOUR_SERVER_URL',
        data: { code: res.code }
      });
    }
  }
});

3. 服务端调用微信接口,获取 UnionID

小程序

服务端使用小程序的 code 调用 code2Session 接口,获取用户的 session_key 和可能的 unionid

请求接口:
GET https://api.weixin.qq.com/sns/jscode2session?appid=小程序APPID&secret=小程序SECRET&js_code=CODE&grant_type=authorization_code

返回示例:
{
  "openid": "小程序OPENID",
  "session_key": "SESSION_KEY",
  "unionid": "UNIONID"  // 若用户授权且绑定开放平台,才会有此字段
}

**4. 通过 公众号的 openid 获取 UnionID **

公众号

如果 code2Session 接口返回了 unionid,可以通过该 unionid 调用开放平台接口,获取用户在不同公众号的 openid

请求方式:
GET https://api.weixin.qq.com/cgi-bin/user/info?access_token=公众号的ACCESS_TOKEN&openid=openid&lang=zh_CN

注意:
- 若用户未关注公众号,或在其他场景未授权,可能返回空值。
- 接口返回的数据中,会包含该用户在公众号下的 `openid`

注意事项

1.UnionID 条件

  • 用户必须在小程序和公众号中授权过登录。
  • 需确保小程序和公众号绑定到同一开放平台。(原有业务中遗漏这一步,导致小程序的unionid都是空,需要补充逻辑让用户登录的时候更新)
  • 若用户未关注公众号,返回结果可能为空。

2.公众号的 access_token

3.用户未关注公众号的情况

  • 如果用户未关注公众号,微信接口无法返回其 openid。此时只能通过用户主动关注后,关联 UnionID。

4.小程序和公众号获取unionid的对比

方法及接口适用场景需要授权适用用户
wx.getUserProfile()获取 unionid需要授权小程序用户
code -> session_key -> unionid(https://api.weixin.qq.com/sns/jscode2session)静默获取 openidunionid不需要授权小程序用户
access_token + openid(https://api.weixin.qq.com/cgi-bin/user/info)通过 openid 查询 unionid不需要授权公众号用户

总结完整逻辑:

  • 提前设置好微信开放平台,绑定小程序和公众号;
  • 微信公众号中增加类目,并开通模版消息,添加到自己的模版消息中;
  • 通过小程序登录将小程序用户的openid和unionid存储下来;
  • 通过接口获取微信公众号中所有用户的openid,通过数据表存储openid及unionid的信息,如数据表:ar_wechat_auth;
  • 然后通过业务逻辑中查询到由用户的报告已出,就对应找到小程序中对应的unionid,再对比ar_wechat_auth表中unionid,找到openid;
  • 通过openid调用模版消息相关代码,即可发送成功!

3、Postman验证请求

在写代码之前,先将微信提供的文章再postman中演示一遍,是否能走通流程,再继续编写代码就顺理成章了,看以下实际操作。

3.1、获取关注公众号用户列表

https://api.weixin.qq.com/cgi-bin/user/get?access_token=78_5aWNpGmNvTSCzJV9BJq7j3z8j_jJFM7Lgm4ucBg_cjq4l-900emJDQowTbHST5pYaYDovNIIeg9OkpjnQH897rZXM9suGhxjJlckY6jGEhTiOTgREPT2Vw62MoEGHKfAFASTW

  • 出错的返回值:
{
	"errcode": 40003,
    "errmsg": "invalid openid hint: [QKfb1Bmre-3SmgQa] rid: 65ef1678-66a34477-5c06c4f6"
}
  • 正确的返回值,(返回所有关注公众号的用户的openid)

在这里插入图片描述

3.2、小程序登录接口

功能描述

登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。更多使用方法详见小程序登录。

在这里插入图片描述

3.3、小程序登录 接口(微信开放平台绑定了小程序和服务号)

适用小程序

返回值可以看到有unionid了 (注:js_code 只能用一次,再次请求就得重新生成,这个来源在体验版or开发版中进入小程序说明中的开发调试,通过重新登录来获取该随机码)

在这里插入图片描述

3.4、微信公众号的openid去请求unionid

适用公众号

通过去微信公众号,找到openid,请求接口获取unionid,跟小程序中通过登录获取的unionid对比 (结果相同,需先在微信开放平台,绑定小程序和微信公众号才行)

在这里插入图片描述

3.5、公众号发送模版消息

参数:access_token需要通过公众号appid和appsecret获取
在这里插入图片描述

4、基于Go实现过程

这里边注意的是:请求access_token用的appid信息是需要找微信公众号上的,不是小程序里appid,如果用错就是存在后续错误1中遇到的问题。

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

// 获取 Access Token
func getAccessToken(appid, appsecret string) (string, error) {
	url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", appid, appsecret)
	resp, err := http.Get(url)
	if err != nil {
		return "", fmt.Errorf("请求失败: %v", err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("读取响应失败: %v", err)
	}

	var result map[string]interface{}
	if err := json.Unmarshal(body, &result); err != nil {
		return "", fmt.Errorf("解析 JSON 失败: %v", err)
	}

	// 检查是否有错误码
	if errcode, ok := result["errcode"].(float64); ok && errcode != 0 {
		errmsg := result["errmsg"].(string)
		return "", fmt.Errorf("微信返回错误: errcode=%v, errmsg=%s", errcode, errmsg)
	}

	accessToken, ok := result["access_token"].(string)
	if !ok {
		return "", fmt.Errorf("无法获取 access_token")
	}

	return accessToken, nil
}

// 发送模板消息
func sendTemplateMessage(accessToken, openid, templateID string, data map[string]interface{}) (map[string]interface{}, error) {
	url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s", accessToken)

	payload := map[string]interface{}{
		"touser":      openid,
		"template_id": templateID,
		"data":        data,
	}

	jsonData, err := json.Marshal(payload)
	if err != nil {
		return nil, fmt.Errorf("JSON 编码失败: %v", err)
	}

	resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData))
	if err != nil {
		return nil, fmt.Errorf("请求失败: %v", err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("读取响应失败: %v", err)
	}

	var result map[string]interface{}
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, fmt.Errorf("解析 JSON 失败: %v", err)
	}

	return result, nil
}

func main() {
	// 公众号
	appid := "xxx"                   // 替换为您的 AppID
	appsecret := "xxx" // 替换为您的 AppSecret
	accessToken, err := getAccessToken(appid, appsecret)
	if err != nil {
		fmt.Println("获取 Access Token 失败:", err)
		return
	}

	fmt.Println("Access Token:", accessToken)


	openid := "xxx" // 关注公众号用户的openid
	fmt.Println("OpenID:", openid)

	templateID := "xxx" // 替换为您的模板 ID

	// 模板消息内容
	/**
	模板详细内容
		报告名称{{thing1.DATA}}
		生成时间{{time2.DATA}}
		跳转文案点击查看详情管理文案
	*/
	data := map[string]interface{}{
		"thing1": map[string]string{"value": "一脉基因全基因组测序报告"},        // 报告名称
		"time2":  map[string]string{"value": "2025-03-13 16:05:00"}, // 生成时间
	}

	// 发送模板消息
	result, err := sendTemplateMessage(accessToken, openid, templateID, data)
	if err != nil {
		fmt.Println("发送模板消息失败:", err)
		return
	}

	fmt.Println("发送结果:", result)
}

生成成功的发送结果:

发送结果: map[errcode:0 errmsg:ok msgid:3.896584684167463e+18]

5、错误记录

1、错误代码:48001, 错误信息:API 功能未授权?

代码错误:map[errcode:48001 errmsg:api unauthorized rid: 67d241fc-53e693f8-5b8b4faf]
错误代码:48001, 错误信息:API 功能未授权,微信原始报文:{“errcode”:48001,“errmsg”:“api unauthorized rid: 5f714450-31a74409-24df03ef”}

  • 这里就是appid使用不当,或者是接口权限未开启,以及ip白名单配置等问题;

2、“errcode”: 40029,“errmsg”: "invalid code hint:?
是因为,你发送模板消息的access_token是“其他公众号”的,openid-a是公众号A的,而access_token是公众号B的,它不会提示你access_token错误,确实你的access_token是正确的(是B的),也找到了对应的公众号B,在B下没有找到openid-a对应的用户。这个提示貌似也没毛病。

3、发送结果: map[errcode:47003 errmsg:argument invalid! data.thing1.value is empty rid: 67d278fe-58c6e50b-5d22ceca]

  • 当传递的参数不是模版要求的格式就会报错

4、小程序wx.login +code2Ssession 获取不到unionid?

  • 需要小程序绑定开放平台 https://open.weixin.qq.com/

5、code been used, rid: 67d65260-0100bd5a-3823c34c

  • 请求接口:https://api.weixin.qq.com/sns/jscode2session,js_code只能使用一次。
要发送微信公众号模板消息,你需要先获取到模板消息模板ID和用户的openid。然后按照以下步骤进行操作: 1. 构建消息体 首先,你需要构建消息体,指明要发送的模板ID、接收者openid和消息内容。例如,以下是消息体的示例: ``` { "touser":"OPENID", "template_id":"TEMPLATE_ID", "url":"http://weixin.qq.com/download", "data":{ "first": { "value":"恭喜你购买成功!", "color":"#173177" }, "keyword1":{ "value":"巧克力", "color":"#173177" }, "keyword2": { "value":"39.8元", "color":"#173177" }, "keyword3": { "value":"2014年9月22日", "color":"#173177" }, "remark":{ "value":"欢迎再次购买!", "color":"#173177" } } } ``` 其中,touser指明接收者openid,template_id指明模板ID,url是点击模板消息跳转的链接(可选),data是消息内容。 2. 发送请求 构建好消息体后,你需要向微信服务器发送请求,调用发送模板消息的API。具体的API接口为: ``` https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN ``` 其中,ACCESS_TOKEN是你的公众号的access_token。 3. 处理返回结果 发送请求后,微信服务器会返回个JSON格式的响应。你需要根据返回结果来判断消息是否发送成功。如果发送成功,返回结果中的errcode应该是0。 以上就是发送微信公众号模板消息的主要步骤,你需要根据自己的实际需求来构建消息体和处理返回结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ifanatic

觉得对您有用,可以友情打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值