介紹:
消息能力是小程序能力中的重要組成部分,微信官方提供了訂閱消息能力,以便實(shí)現(xiàn)開發(fā)者實(shí)現(xiàn)服務(wù)的閉環(huán)和更優(yōu)的體驗(yàn)瓮下。可以支持在用戶自主訂閱后钝域,推送消息到用戶端(服務(wù)通知)讽坏,用戶點(diǎn)擊查看詳情可跳轉(zhuǎn)至小程序的頁面,實(shí)現(xiàn)服務(wù)的閉環(huán)例证,提高活躍度和用戶粘性路呜。
業(yè)務(wù)場景:
用戶在微信小程序使用某個(gè)服務(wù),比如購買织咧、預(yù)約等服務(wù)胀葱,在用戶預(yù)約成功后向該用戶推送一條預(yù)約成功的消息提醒。
發(fā)送訂閱消息步驟:
- 開通消息訂閱:開發(fā)者可以申請(qǐng)開通訂閱消息能力笙蒙,開啟后開發(fā)者可以調(diào)用接口向用戶推送消息抵屿;
- 配置消息模板:申請(qǐng)生成消息模板ID,確定模板詳情:標(biāo)題、內(nèi)容等捅位;
- 前端訂閱消息:通過按鈕或者支付回調(diào)向用戶發(fā)起訂閱消息的詢問轧葛;
- 后端進(jìn)行推送:用戶同意訂閱后,后端請(qǐng)求微信消息推送接口攜帶模板ID绿渣、模板內(nèi)容(可理解成數(shù)據(jù)字段)用戶open_id朝群、跳轉(zhuǎn)地址等參數(shù)進(jìn)行推送;
- 用戶收到消息:用戶在服務(wù)通知中收到該模板消息的卡片中符,點(diǎn)擊可轉(zhuǎn)向小程序中的跳轉(zhuǎn)地址,可傳參姜胖。
一、開通消息訂閱
- 打開微信公眾平臺(tái)地址淀散,掃碼登錄后臺(tái), 并找到訂閱消息
- 若您未開通消息訂閱模板, 會(huì)先提示您去開通此功能.
二右莱、配置消息模板
- 登錄微信公眾平臺(tái)之后,消息模板的配置入口在小程序管理后臺(tái)中的
基礎(chǔ)功能
-訂閱消息
里档插,你會(huì)看到下面這張圖展示的內(nèi)容: - 點(diǎn)擊
選用
按鈕慢蜓,進(jìn)入公共模板庫中
- 由于長期性訂閱消息,目前僅向政務(wù)民生郭膛、醫(yī)療晨抡、交通、金融、教育等線下公共服務(wù)開放耘柱,后期將逐步支持到其他線下公共服務(wù)業(yè)務(wù)如捅。僅就線下公共服務(wù)這一點(diǎn),長期性訂閱消息就和大部分開發(fā)者無緣了调煎。 所以我們這里只能以使用一次性訂閱消息為例镜遣。
- 手動(dòng)配置模板ID(若在公共模板庫中沒有找到合適的,可以申請(qǐng)?zhí)砑有履0濉徍送ㄟ^后即可使用).
- 根據(jù)自身業(yè)務(wù)需求士袄,選擇創(chuàng)建適合自己的模板
- 點(diǎn)擊
選用
模板悲关,進(jìn)入模板詳情頁 - 填寫模板參數(shù),并提交審核
- 配置完成后提交, 你就會(huì)在我的模板看到該條訂閱消息相關(guān)信息.
- 需等待微信官方審核通過后娄柳,方可使用寓辱。
注意:創(chuàng)建模板提交審核,盡量提前創(chuàng)建模板西土,避免審核失敗讶舰。
- 點(diǎn)擊
詳情
按鈕進(jìn)入模版詳情頁,可以看到我們配置的信息: - 下圖的
模板ID
就是我們需要的。
三需了、前端訂閱消息 前端 uni-app 代碼實(shí)現(xiàn)
1跳昼、替換你的請(qǐng)求基礎(chǔ)地址:
// 請(qǐng)求基礎(chǔ)地址
const requestBaseUrl = 'YOUR_URL_ADDRESS'
2、替換你的模版Id:
// 模版id
const tmplId = 'your_template_id'
3肋乍、代碼示例:
<template>
<view style="display: flex;align-items: center;justify-content: center;height: 100vh;">
<button @click="subscribe">點(diǎn)我喚起訂閱面板</button>
</view>
</template>
<script setup>
import { ref } from 'vue'
// 用戶 openId
const openId = ref(null)
// 請(qǐng)求基礎(chǔ)地址
const requestBaseUrl = 'http://192.168.43.245:3000/api/weChatMp'
// 發(fā)送訂閱消息
async function subscribe() {
// 如果沒有 openId 就代表未登錄
if (!openId.value) {
await wechatLogin()
}
// 模版id
const tmplId = 'GAcYabm1vG_YJwl75R_vXntgqe5q2Li3N-0XTCOEehE'
// 接口文檔地址:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/subscribe-message/wx.requestSubscribeMessage.html
uni.requestSubscribeMessage({
tmplIds: [tmplId],
success(res) {
console.log('接口調(diào)用成功的回調(diào)函數(shù)', res)
if (res[tmplId] == 'accept') { // 用戶同意后
// 發(fā)送請(qǐng)求給后端進(jìn)行通知
uni.request({
url: `${requestBaseUrl}/sendSubscribeMsg`,
method: 'POST',
data: {
touser: openId.value, // 接收者(用戶)的 openid
page: `/pages/index/index`, // 點(diǎn)擊模板卡片后的跳轉(zhuǎn)頁面鹅颊,僅限本小程序內(nèi)的頁面。支持帶參數(shù),(示例index?foo=bar)墓造。該字段不填則模板無跳轉(zhuǎn)
template_id: tmplId, // 所需下發(fā)的訂閱模板id
data: { // 模板內(nèi)容堪伍,格式形如 { "key1": { "value": any }, "key2": { "value": any } }的object
thing1: {
value: '李氏家族大合照' // 相冊(cè)名稱
},
name2: {
value: '大伯' // 回復(fù)人
},
thing3: {
value: '你和小時(shí)候一毛一樣' // 回復(fù)內(nèi)容
},
date4: {
value: '2028年11月11日' // 回復(fù)時(shí)間
},
thing5: {
value: '你的Maya' // 回復(fù)用戶
}
},
miniprogramState: 'developer', // 跳轉(zhuǎn)小程序類型:developer為開發(fā)版;trial為體驗(yàn)版觅闽;formal為正式版帝雇;默認(rèn)為正式版
lang: 'zh_CN' // 進(jìn)入小程序查看”的語言類型,支持zh_CN(簡體中文)蛉拙、en_US(英文)尸闸、zh_HK(繁體中文)、zh_TW(繁體中文)孕锄,默認(rèn)為zh_CN
},
success: (res) => {
console.log('服務(wù)器發(fā)送訂閱消息成功', res);
uni.showModal({
title: '消息訂閱提示',
content: "服務(wù)器發(fā)送訂閱消息成功吮廉!",
showCancel:false
});
},
fail: (err) => {
console.log('服務(wù)器發(fā)送訂閱消息失敗畸肆!', err);
}
})
} else if (res[tmplId] == 'reject') { // 用戶拒絕授權(quán)
wx.showModal({
title: '溫馨提示',
content: "您已關(guān)閉消息推送宦芦,如需要消息推送服務(wù),請(qǐng)點(diǎn)擊確定跳轉(zhuǎn)設(shè)置頁面打開授權(quán)后再次嘗試轴脐。",
success: (modal) => {
if (modal.confirm) { // 點(diǎn)擊確定
wx.openSetting({
withSubscriptions: true
})
}
}
})
}
},
fail(err) {
console.log('接口調(diào)用失敗的回調(diào)函數(shù)', err)
if (err.errCode == '20004') {
wx.showModal({
title: '溫馨提示',
content: "您的消息訂閱主開關(guān)已關(guān)閉调卑,如需要消息推送服務(wù)抡砂,請(qǐng)點(diǎn)擊確定跳轉(zhuǎn)設(shè)置頁面打開授權(quán)后再次嘗試。",
success: (modal) => {
if (modal.confirm) { // 點(diǎn)擊確定
wx.openSetting({
withSubscriptions: true
})
}
}
})
}
}
})
}
/**
* 微信登錄
*/
function wechatLogin() {
return new Promise((resolve, reject) => {
// 1令野、獲取臨時(shí)登錄憑證 code
uni.login({
provider: "weixin",
success: ({ code }) => {
// 2舀患、將獲取到的 code 發(fā)送到后端,用于后端向微信獲取 openId 气破。
uni.request({
url: `${requestBaseUrl}/login`,
method: 'POST',
data: { code },
success: (loginRes) => {
// 3、將后端返回的 openId 進(jìn)行保存
openId.value = loginRes.data.data.openId
resolve(loginRes)
},
fail: () => {
reject('登錄失敳颓馈现使!')
uni.showToast({
title: '登錄失敗旷痕!',
icon: 'error',
duration: 1500
});
}
})
},
fail: () => {
reject('登錄出錯(cuò)')
uni.showToast({
title: '登錄出錯(cuò)碳锈!',
icon: 'error',
duration: 1500
});
}
})
})
}
</script>
<style lang="scss" scoped>
</style>
四、后端進(jìn)行推送 后端 Node.js 代碼實(shí)現(xiàn)
1欺抗、安裝依賴
- 安裝
express
或其他 Node.js web 框架來做后端服務(wù)售碳。 - 安裝
axios
或其他 HTTP 庫來發(fā)送請(qǐng)求。 - 安裝
cors
用于處理跨域請(qǐng)求绞呈。 - 安裝
body-parser
用于解析req.body贸人。
2、替換小程序配置
const wxConfig = {// 微信小程序配置信息
appid: 'your_appid',// 替換為你的 AppID
secret: 'your_secret',// 替換為你的 AppSecret
}
3佃声、代碼示例:
const express = require('express'); // 導(dǎo)入 Express 模塊
const cors = require('cors'); // 導(dǎo)入 CORS 模塊艺智,用于處理跨域請(qǐng)求
const axios = require('axios'); // 導(dǎo)入 Axios 模塊,用于發(fā)起 HTTP 請(qǐng)求
const bodyParser = require('body-parser'); // 中間件圾亏,用于解析req.body
const app = express(); // 創(chuàng)建 Express 應(yīng)用實(shí)例
app.use(cors()); // 使用 CORS 中間件解決跨越請(qǐng)求
app.use(bodyParser.json()); // 使用中間件解析JSON數(shù)據(jù)
const port = 3000; // 設(shè)置應(yīng)用監(jiān)聽的端口號(hào)
const wxConfig = {// 微信小程序配置信息
appid: 'your_appid',// 替換為你的 AppID
secret: 'your_secret',// 替換為你的 AppSecret
}
// 設(shè)置路由處理函數(shù)十拣,用于發(fā)送訂閱消息
app.post('/api/weChatMp/sendSubscribeMsg', async (req, res) => {
if (!req.body?.touser || !req.body?.template_id || !req.body?.data) {
throw new Error('必填參數(shù)不能為空!')
}
// 獲取 Access Token
const access_token = await getAccessToken(wxConfig.appid, wxConfig.secret);
const requestData = {
touser: req.body.touser, // 接收者(用戶)的 openid
page: req.body?.page || `/pages/index/index`, // 點(diǎn)擊模板卡片后的跳轉(zhuǎn)頁面志鹃,僅限本小程序內(nèi)的頁面夭问。支持帶參數(shù),(示例index?foo=bar)。該字段不填則模板無跳轉(zhuǎn)
template_id: req.body.template_id, // 所需下發(fā)的訂閱模板id
data: req.body.data,
miniprogramState: req.body?.miniprogramState || 'developer', // 跳轉(zhuǎn)小程序類型:developer為開發(fā)版曹铃;trial為體驗(yàn)版缰趋;formal為正式版;默認(rèn)為正式版
lang: req.body?.lang || 'zh_CN' // 進(jìn)入小程序查看”的語言類型铛只,支持zh_CN(簡體中文)埠胖、en_US(英文)、zh_HK(繁體中文)淳玩、zh_TW(繁體中文)直撤,默認(rèn)為zh_CN
}
// 發(fā)送訂閱消息 接口英文名(sendMessage)
// 官方說明地址: https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/sendMessage.html
const url = `https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=${access_token}`;
const response = await axios.post(url, requestData);
if (response?.errcode) {
throw new Error(JSON.stringify(response.errcode))
}
res.send(response.data).json();
})
// 設(shè)置路由處理函數(shù),用于微信小程序用戶登陸
app.post('/api/weChatMp/login', async (req, res) => {
const code = req.body?.code
if (!code) {
throw new Error('code參數(shù)不能為空')
}
const url = `https://api.weixin.qq.com/sns/jscode2session`;
const response = await axios({
method: "GET",
url,
params: {
appid: wxConfig.appid,
secret: wxConfig.secret,
js_code: code,
grant_type: 'authorization_code',
},
});
if (response?.errcode) {
throw new Error(JSON.stringify(response))
}
res.send({
msg: '獲取openid成功',
data: {
openId: response.data.openid,
},
})
})
/**
* 獲取 Access Token
* @param {string} appid 小程序appid
* @param {string} secret 小程序secret
* @returns access_token
*/
async function getAccessToken(appid, secret) {
// 獲取接口調(diào)用憑據(jù) 接口英文名叫g(shù)etAccessToken
// 官方說明地址:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.htm
const url = `https://api.weixin.qq.com/cgi-bin/token`;
const response = await axios({
method: "get",
url,
params: {
grant_type: 'client_credential',
appid,
secret,
},
});
if (response?.errcode) {
throw new Error(JSON.stringify(getCodeRes))
}
return response.data.access_token;
}
// 監(jiān)聽端口
app.listen(port, () => {
console.log(`Example app listening on port ${port}`, `is open url http://127.0.0.1:${port}`)
})
五蜕着、用戶收到消息 最終效果:
訂閱消息添加提醒
用戶在訂閱小程序長期訂閱消息時(shí)谋竖,可以根據(jù)自己的使用情況添加提醒红柱。添加后,用戶在收到消息時(shí)蓖乘,在微信內(nèi)將由橫幅通知提醒锤悄。
當(dāng)用戶添加了提醒,開發(fā)者通過wx.getSetting獲取的該模板的訂閱狀態(tài)為acceptWithAlert
嘉抒。
訂閱彈窗樣式如下:
小程序訂閱消息有哪幾種零聚?
新版一次性訂閱消息Beta
新版一次性訂閱消息是一種無需用戶在彈窗中主動(dòng)訂閱即可向用戶下發(fā)消息的能力,用戶的訂閱方式為:
- 當(dāng)用戶在小程序中進(jìn)行微信支付后些侍,開發(fā)者可將微信支付訂單號(hào)作為
code
向用戶下發(fā)服務(wù)通知 - 開發(fā)者可在小程序中將觸發(fā)服務(wù)的
button
組件的open-type
的值設(shè)置為liveActivity
隶症,當(dāng)用戶點(diǎn)擊button
后可獲得code
,后續(xù)可使用此code
向用戶下發(fā)服務(wù)通知
詳見訂閱消息接入 Beta開發(fā)指南文檔岗宣。
一次性訂閱消息(用戶通過彈窗訂閱)
一次性訂閱消息用于解決用戶使用小程序后蚂会,后續(xù)服務(wù)環(huán)節(jié)的通知問題。
開發(fā)者在小程序中調(diào)用 requestSubscribeMessage
接口后耗式,將向用戶展示彈窗胁住,用戶可打開自己想要接受的消息開關(guān)。用戶訂閱后刊咳,開發(fā)者可不限時(shí)間地下發(fā)一條對(duì)應(yīng)的服務(wù)消息彪见;每條消息可單獨(dú)訂閱或退訂。
詳見小程序訂閱消息開發(fā)指南文檔芦缰。
長期訂閱消息(用戶通過彈窗訂閱)
一次性訂閱消息可滿足小程序的大部分服務(wù)場景需求企巢,但線下公共服務(wù)領(lǐng)域存在一次性訂閱無法滿足的場景,如航班延誤让蕾,需根據(jù)航班實(shí)時(shí)動(dòng)態(tài)來多次發(fā)送消息提醒浪规。為便于服務(wù),我們提供了長期性訂閱消息探孝,用戶訂閱一次后笋婿,開發(fā)者可長期下發(fā)多條消息。
目前長期性訂閱消息僅向政務(wù)民生顿颅、醫(yī)療缸濒、交通、金融粱腻、教育等線下公共服務(wù)開放庇配,后期將逐步支持到其他線下公共服務(wù)業(yè)務(wù)。
詳見小程序訂閱消息開發(fā)指南文檔绍些。
設(shè)備訂閱消息
設(shè)備訂閱消息是一種特殊類型的訂閱消息,它屬于長期訂閱消息類型柬批,且需要完成「設(shè)備接入」才能使用啸澡。
設(shè)備訂閱消息用于在設(shè)備觸發(fā)某些需要人工介入的事件時(shí)(例如設(shè)備發(fā)生故障袖订、設(shè)備耗材不足等),向用戶發(fā)送消息通知嗅虏。
詳見設(shè)備訂閱消息文檔洛姑。
如何做好訂閱消息?
關(guān)于訂閱消息的使用皮服,我建議:
- 關(guān)于訂閱時(shí)機(jī):讓用戶在需要用到消息的時(shí)候楞艾,觸發(fā)訂閱機(jī)制,而不要讓用戶一打開小程序就進(jìn)行訂閱冰更;
- 關(guān)于訂閱內(nèi)容:引導(dǎo)用戶訂閱跟用戶當(dāng)前的服務(wù)相關(guān)的模板消息产徊,其他暫時(shí)用不到的模板建議等用戶用到之后再訂閱,以免用戶產(chǎn)生誤解而取消訂閱蜀细;
- 關(guān)于訂閱流程:建議開發(fā)者將訂閱消息融入到自然的產(chǎn)品體驗(yàn)流程中;
- 關(guān)于強(qiáng)制訂閱:不建議進(jìn)行強(qiáng)制訂閱戈盈,避免出現(xiàn)用戶不訂閱就無法進(jìn)行下一步操作的情況奠衔,引起用戶反感。
多場景配置觸發(fā)訂閱環(huán)節(jié)及消息模板
針對(duì)不同場景下的訂閱消息發(fā)送需求塘娶,用戶觸發(fā)的場景也可以多樣化
以餐飲行業(yè)為例:(以下場景僅示例归斤,開發(fā)者可靈活配置)
業(yè)務(wù)場景 | 用戶觸發(fā)環(huán)節(jié) | 對(duì)應(yīng)的訂閱消息模板 |
---|---|---|
自助點(diǎn)餐場景,用戶支付成功后刁岸,申請(qǐng)發(fā)送取餐提醒 | 點(diǎn)擊下單按鈕 | 取餐提醒 |
排隊(duì)場景脏里,用戶取號(hào)完成后,申請(qǐng)發(fā)送排隊(duì)提醒 | 點(diǎn)擊取號(hào)按鈕 | 排隊(duì)提醒 |
會(huì)員場景虹曙,用戶領(lǐng)取會(huì)員卡后迫横,申請(qǐng)發(fā)送會(huì)員活動(dòng)提醒 | 點(diǎn)擊領(lǐng)取卡片按鈕 | 會(huì)員活動(dòng)提醒 |
領(lǐng)券場景,用戶領(lǐng)券后酝碳,申請(qǐng)發(fā)送優(yōu)惠券過期提醒及活動(dòng)提醒 | 點(diǎn)擊領(lǐng)券按鈕 | 優(yōu)惠券過期提醒 |
引導(dǎo)取消訂閱消息的用戶重新訂閱
假設(shè)用戶在訂閱申請(qǐng)彈窗中矾踱,勾選了“總是保持以上選擇,不再詢問”疏哗,并點(diǎn)擊了“取消”呛讲,則用戶取消了訂閱,可引導(dǎo)用戶重新訂閱返奉。
可引導(dǎo)用戶點(diǎn)擊小程序右上角···
贝搁,進(jìn)入設(shè)置頁,點(diǎn)擊通知管理
-> 開啟接收通知
芽偏。