- 关于小程序隐私保护指引设置的公告
为规范开发者的用户个人信息处理行为,保障用户的合法权益,自2023年9月15日起,对于涉及处理用户个人信息的小程序开发者,微信要求,仅当开发者主动向平台同步用户已阅读并同意了小程序的隐私保护指引等信息处理规则后,方可调用微信提供的隐私接口。 开发者首先需确定小程序是否涉及处理用户个人信息,如涉及,则需配置用户隐私授权弹窗,且仅有在平台《小程序用户隐私保护指引》中声明了所处理的用户个人信息,才可以调用平台提供的对应接口或组件。(隐私相关接口) 隐私协议设置整体流程参考下方指引: 一、设置《小程序用户隐私保护指引》 开发者需在「小程序管理后台」设置《小程序用户隐私保护指引》 [图片] [图片] 二、填写《小程序用户隐私保护指引》 [图片] 只有在指引中声明所处理的用户个人信息,才可以调用平台提供的对应接口或组件。若未声明,对应接口或组件将无法调用成功。隐私接口与对应的处理的用户个人信息关系可见:小程序用户隐私保护指引内容介绍 三、配置用户隐私授权弹窗 微信提供了wx.onNeedPrivacyAuthorization(function callback) 接口,意为用户触发了一个微信侧未记录过同意的隐私接口调用,开发者可通过响应该事件选择提示用户的时机。此外,微信还提供了 wx.requirePrivacyAuthorize(Object object) 接口,可用于模拟触发 onNeedPrivacyAuthorization 事件。 小程序开发者可自行设计提示方式与触发时机,详细文档可查看隐私协议开发指南。 仅有在指引中声明所处理的用户个人信息,才可以调用平台提供的对应接口或组件。若未声明,对应接口或组件将直接禁用。 [图片] (参考样例) 四、如要进行代码提审,开发者需先自行声明是否有采集用户隐私,如有,则需在提审页面-「用户隐私保护设置」选择“采集用户隐私” [图片]
2023-09-18 - 针对新手很容易出现理解误区的微信小程序订阅消息模块
1. 写在前面 微信小程序下架了模板消息功能,取而代之的是订阅消息功能。这个订阅消息目前又分为「一次性订阅」和「永久订阅」。使用订阅消息也有一段时间了,感觉对新手订阅消息很容易让新开发者进入一个理解的误区,这里觉得有必要说出来 2. 理解误区 很多新手认为,只要用户勾选了小程序端订阅消息弹出时底部的「总是保持以上选择…」后,就可以「为所欲为」的不限次数的推送订阅消息给用户了。如下图: [图片] 3. 正确理解 如果你使用的「一次性订阅」模板(目前发现绝大多数开发者都是只能用一次性的,因为永久性的订阅消息申请门槛太高),那么勾选底部的「总是…」这个并不代表以后可以直接推送了。官方原话wx.requestSubscribeMessage的介绍里是这样写的: 3.1 官方说明 wx.requestSubscribeMessage(Object object) 基础库 2.8.2 开始支持,低版本需做兼容处理。 调起客户端小程序订阅消息界面,返回用户订阅消息的操作结果。当用户勾选了订阅面板中的“总是保持以上选择,不再询问”时,模板消息会被添加到用户的小程序设置页,通过 wx.getSetting 接口可获取用户对相关模板消息的订阅状态。 注意事项 一次性模板 id 和永久模板 id 不可同时使用。 低版本基础库2.4.4~2.8.3 已支持订阅消息接口调用,仅支持传入一个一次性 tmplId / 永久 tmplId。 2.8.2 版本开始,用户发生点击行为或者发起支付回调后,才可以调起订阅消息界面。 2.10.0 版本开始,开发版和体验版小程序将禁止使用模板消息 fomrId。 3.2 重点关注 这里重点关注第7条:「用户发生点击行为或者发起支付回调后,才可以调起订阅消息界面。」这就意味着你需要在用户主动点击某个组件是触发调用wx.requestSubscribeMessage方法再次订阅,订阅后,你才可以「为所欲为」推送一次模板消息,注意只能一次。下次再想推送时,需要用户再次点击触发wx.requestSubscribeMessage。 4. 破局方案 目前订阅消息功能,就是这么个情况,所以针对这个情况的替代方案有以下 4.1 永久性订阅消息 如果能达到申请「永久性订阅」消息的模板的门槛,那自然是极好的,直接用永久性模板「为所欲为」。 4.2 使用服务号的模板消息替代 比较常用的是使用公众号服务号的模板消息代替小程序的订阅消息功能,公众号的模板消息功能限制就比订阅号好多了,基本上可以「为所欲为」的推送。但是这个方案有个致命的运营成本:必须要用户关注公众号,还有小程序要跟公众号同一主体并绑定在开放平台下。同时开发成本有所增加,要采用unionId机制来打通小程序跟公众号的openId。这个具体的实现方案,大家有兴趣的话可以讨论下。笔者目前就是用这种方案的。 5. 几个注意点 5.1 官方提示 订阅消息如果选择选择‘总是保持以上选择,"不再询问"后的设置问题: 目前是选择‘总是保持以上选择,"不再询问"后,可以在设置中开启或拒绝接收,但不会再次拉起授权弹窗 6. 长期性订阅消息 请参考官方最新文档: 小程序模板消息能力调整通知 | 微信开放社区 https://developers.weixin.qq.com/community/develop/doc/00008a8a7d8310b6bf4975b635a401 长期性订阅消息 一次性订阅消息可满足小程序的大部分服务场景需求,但线下公共服务领域存在一次性订阅无法满足的场景,如航班延误,需根据航班实时动态来多次发送消息提醒。为便于服务,我们提供了长期性订阅消息,用户订阅一次后,开发者可长期下发多条消息。 目前长期性订阅消息仅向政务民生、医疗、交通、金融、教育等线下公共服务开放,后期将逐步支持到其他线下公共服务业务。 7.题外话 鉴于被戴上各种「刷赞,冲级,让社区点赞“通货膨胀”」等等一些恶毒字眼(最近多了个职业回复的「雅称」),各种帽子戴得,做一个开发爱好者积极分享和解决各种问题太难了,姑且不论咱写一篇文章需要截图多少,单单排版就得废掉俺多少时间哈,很受伤,所以本人决定在微信开放者社区封笔。你看到是俺最后一篇发表在微信开放社区的文章。如果你想继续查看俺的一些文章可以私聊我。我会在其他平台保持继续创作。bye-bye~ 8. 最最重要的来了 看完后觉得有用记得点赞~~ ↓点赞处↓
2020-09-04 - authorize:fail auth deny
检查授权结果发现scope.userLocation未授权,设置授权为什么总是返回authorize:fail auth deny : wx.authorize({ scope: 'scope.userLocation', success: function (res){ console.log(res); }, fail: function (res) { // fail console.log(res); }, complete: function (res) { // complete console.log(res); } })
2017-07-04 - 小程序获取某项权限的通用写法
温馨提示: [代码]errMsg: "openSetting:fail can only be invoked by user TAP gesture." [代码] wx.openSetting 不能直接在程序中调用,但是可以通过 wx.showModal 拉起。 【相关链接】 1、 wx.getSetting(Object object) 2、 wx.authorize(Object object) 3、 wx.showModal(Object object) 4、 wx.openSetting(Object object) update at 2022.08.02 再次查看这篇文章发现代码并不好懂,决定修改下 ; 逻辑还是一样的(代码中有注释),只是改变了写法 (1) uni.getSetting -> (2) uni.authorize -> (3) uni.showModal -> (4) uni.openSetting 以获取录音权限为例: 正常情况下,第一次,会提示用户授权,截图如下: [图片] 如果用户选择“允许”,那么一切皆顺 如果用户选择“拒绝”,就比较麻烦了,需要在 [代码]openSetting[代码] 打开,截图如下: [图片] 因为[代码]openSetting[代码]不能直接由程序调用,所以需要由[代码]showModal[代码]拉起,截图如下: [图片] 下面是代码截图: [图片] [图片] 下面是获取某项授权的完整代码 [代码]// 获取微信录音权限 // scopeStr = "scope.record" // modalMsg = "需要您授权获取录音权限" async getWxRecordAuth(scopeStr, modalMsg) { try { let [flag1, flag2] = [true, false] // (1) wx.getSetting 检查用户之前是否已经授权 flag1 = await new Promise((resolve)=>{ wx.getSetting({ success(res) { if (res.authSetting[scopeStr]) { resolve(true) } else { resolve(false) } } }) }) if (!flag1) { // (2) wx.authorize 弹窗请用户授权 flag1 = await new Promise((resolve)=>{ wx.authorize({ scope: scopeStr, success(res) { resolve(true) }, fail() { resolve(false) } }) }) } if (!flag1) { // (3) wx.showModal ,新规范下, // 必须通过按钮或者`wx.showModal`调起`wx.openSetting` flag2 = await new Promise((resolve)=>{ wx.showModal({ title: '授权', showCancel: false, content: modalMsg, success(res) { if (res.confirm) { resolve(true) } else if (res.cancel) { resolve(false) } else { resolve(false) } } }) }) } if (flag2) { // (4) wx.openSetting 请用户打开授权 flag1 = await new Promise((resolve)=>{ wx.openSetting({ success(res) { if (res.authSetting[scopeStr]) { resolve(true) } else { resolve(false) } } }) }) } return flag1 } catch (err) { console.log(err) return false } }, [代码] update at 2022.08.02 下面是之前的内容 小程序获取某项权限 【相关链接】微信小程序:开放能力/用户信息/授权 【相关链接】表单组件/button , [代码]open-type[代码] : [代码]openSetting[代码] uni -> wx , 有些地方写的是 [代码]uni[代码] 直接替换为 [代码]wx[代码] 可以把 [代码]scope.userLocation[代码] 替换为其他值,或者封装一下,变成通用的获取权限方式 封装时,别忘记,把 [代码]"需要您授权获取地理位置权限"[代码] 也封装一下,这个是弹窗的提示信息 提示: 这里的几个方法 promise 返回的是数组,[代码]arr[0][代码] 有值,就是出错了;[代码]arr[1][代码]才是success时返回的结果值,想要获取的结果值都存放在 [代码]arr[1][代码] 中。 [代码]// (1) uni.getSetting -> (2) uni.authorize -> (3) uni.showModal -> (4) uni.openSetting //授权获取地理位置权限 async getAuth_userLocation(){ try { //(1)getSetting const authSetting = await uni.getSetting(); if (authSetting['scope.userLocation']) { return true; } else { //(2)authorize const resAuthorize = await uni.authorize({ scope: 'scope.userLocation' }) //resAuthorize[0]:拒绝,resAuthorize[1]:接受; if (resAuthorize[1]) { return true; } else { //(3)showModal const resShowModal = await uni.showModal({ title: '授权', content: '需要您授权获取地理位置权限' }); //resShowModal[0]: null, resShowModal[1]: `confirm`,`cancel`都定义在"resShowModal[1]"中 if (resShowModal[1]) { if (resShowModal[1].confirm) { //(4)openSetting const resOpenSetting = await uni.openSetting(); //resOpenSetting[0]: null, resOpenSetting[1]: `authSetting`定义在"resOpenSetting[1]"中 if (resOpenSetting[1]) { const authSetting = resOpenSetting[1].authSetting; if (authSetting && authSetting['scope.userLocation']) { return true; } else { return false; } } else { return false; } } if (resShowModal[1].cancel) { return false; } } else { return false; } } } } catch (err) { console.log(err); return false; } }, [代码] 原来的写法 [代码] getCameraAuth(){ const that = this; // 获取摄像头权限 // (1) uni.getSetting -> (2) uni.authorize -> (3) uni.showModal -> (4) uni.openSetting uni.getSetting({ success(res) { const authSetting = res.authSetting; if (authSetting['scope.camera']) { // 已经授权 that.is_camera_auth = true; } else { // 未授权 uni.authorize({ scope: 'scope.camera', success(resSuccess) { // 同意授权 that.is_camera_auth = true; },fail(resFail) { console.log("resFail: ", resFail); // 引导用户授权 uni.showModal({ title: '授权', content: '需要您授权获取摄像头权限', success: function (res) { if (res.confirm) { uni.openSetting({ success(resOpenSetting) { // resOpenSetting: {errMsg: "openSetting:ok", authSetting: {scope.camera: false}} //console.log("resOpenSetting: ", resOpenSetting) const authSetting = resOpenSetting.authSetting if (authSetting && authSetting['scope.camera']) { that.is_camera_auth = true; } else { uni.showToast({icon:'none', title: '您拒绝授权小程序获取摄像头权限', duration: 1500}); } } }); } else if (res.cancel) { uni.showToast({icon:'none', title: '您拒绝授权小程序获取摄像头权限', duration: 1500}); } } }); } }); } } }); }, [代码] 这是原来的嵌套写法,没有上面的 Promise 版本方便;放在这里作为参照。
2022-08-02 - 如何实现快速生成朋友圈海报分享图
由于我们无法将小程序直接分享到朋友圈,但分享到朋友圈的需求又很多,业界目前的做法是利用小程序的 Canvas 功能生成一张带有小程序码的图片,然后引导用户下载图片到本地后再分享到朋友圈。相信大家在绘制分享图中应该踩到 Canvas 的各种(坑)彩dan了吧~ 这里首先推荐一个开源的组件:painter(通过该组件目前我们已经成功在支付宝小程序上也应用上了分享图功能) 咱们不多说,直接上手就是干。 [图片] 首先我们新增一个自定义组件,在该组件的json中引入painter [代码]{ "component": true, "usingComponents": { "painter": "/painter/painter" } } [代码] 然后组件的WXML (代码片段在最后) [代码]// 将该组件定位在屏幕之外,用户查看不到。 <painter style="position: absolute; top: -9999rpx;" palette="{{imgDraw}}" bind:imgOK="onImgOK" /> [代码] 重点来了 JS (代码片段在最后) [代码]Component({ properties: { // 是否开始绘图 isCanDraw: { type: Boolean, value: false, observer(newVal) { newVal && this.handleStartDrawImg() } }, // 用户头像昵称信息 userInfo: { type: Object, value: { avatarUrl: '', nickName: '' } } }, data: { imgDraw: {}, // 绘制图片的大对象 sharePath: '' // 生成的分享图 }, methods: { handleStartDrawImg() { wx.showLoading({ title: '生成中' }) this.setData({ imgDraw: { width: '750rpx', height: '1334rpx', background: 'https://qiniu-image.qtshe.com/20190506share-bg.png', views: [ { type: 'image', url: 'https://qiniu-image.qtshe.com/1560248372315_467.jpg', css: { top: '32rpx', left: '30rpx', right: '32rpx', width: '688rpx', height: '420rpx', borderRadius: '16rpx' }, }, { type: 'image', url: this.data.userInfo.avatarUrl || 'https://qiniu-image.qtshe.com/default-avatar20170707.png', css: { top: '404rpx', left: '328rpx', width: '96rpx', height: '96rpx', borderWidth: '6rpx', borderColor: '#FFF', borderRadius: '96rpx' } }, { type: 'text', text: this.data.userInfo.nickName || '青团子', css: { top: '532rpx', fontSize: '28rpx', left: '375rpx', align: 'center', color: '#3c3c3c' } }, { type: 'text', text: `邀请您参与助力活动`, css: { top: '576rpx', left: '375rpx', align: 'center', fontSize: '28rpx', color: '#3c3c3c' } }, { type: 'text', text: `宇宙最萌蓝牙耳机测评员`, css: { top: '644rpx', left: '375rpx', maxLines: 1, align: 'center', fontWeight: 'bold', fontSize: '44rpx', color: '#3c3c3c' } }, { type: 'image', url: 'https://qiniu-image.qtshe.com/20190605index.jpg', css: { top: '834rpx', left: '470rpx', width: '200rpx', height: '200rpx' } } ] } }) }, onImgErr(e) { wx.hideLoading() wx.showToast({ title: '生成分享图失败,请刷新页面重试' }) //通知外部绘制完成,重置isCanDraw为false this.triggerEvent('initData') }, onImgOK(e) { wx.hideLoading() // 展示分享图 wx.showShareImageMenu({ path: e.detail.path, fail: err => { console.log(err) } }) //通知外部绘制完成,重置isCanDraw为false this.triggerEvent('initData') } } }) [代码] 那么我们该如何引用呢? 首先json里引用我们封装好的组件share-box [代码]{ "usingComponents": { "share-box": "/components/shareBox/index" } } [代码] 以下示例为获取用户头像昵称后再生成图。 [代码]<button class="intro" bindtap="getUserInfo">点我生成分享图</button> <share-box isCanDraw="{{isCanDraw}}" userInfo="{{userInfo}}" bind:initData="handleClose" /> [代码] 调用的地方: [代码]const app = getApp() Page({ data: { isCanDraw: false }, // 组件内部关掉或者绘制完成需重置状态 handleClose() { this.setData({ isCanDraw: !this.data.isCanDraw }) }, getUserInfo(e) { wx.getUserProfile({ desc: "获取您的头像昵称信息", success: res => { const { userInfo = {} } = res this.setData({ userInfo, isCanDraw: true // 开始绘制海报图 }) }, fail: err => { console.log(err) } }) } }) [代码] 最后绘制分享图的自定义组件就完成啦~效果图如下: [图片] tips: 文字居中实现可以看下代码片段 文字换行实现(maxLines)只需要设置宽度,maxLines如果设置为1,那么超出一行将会展示为省略号 代码片段:https://developers.weixin.qq.com/s/J38pKsmK7Qw5 附上painter可视化编辑代码工具:点我直达,因为涉及网络图片,代码片段设置不了downloadFile合法域名,建议真机开启调试模式,开发者工具 详情里开启不校验合法域名进行代码片段的运行查看。 最后看下面大家评论问的较多的问题:downLoadFile合法域名在小程序后台 开发>开发设置里配置,域名为你图片的域名前缀 比如我文章里的图https://qiniu-image.qtshe.com/20190605index.jpg。配置域名时填写https://qiniu-image.qtshe.com即可。如果你图片cdn地址为https://aaa.com/xxx.png, 那你就配置https://aaa.com即可。
2022-01-20 - 原生小程序根据当前版本自动切换 `开发版本、体验版本、正式版本`
原生小程序根据当前版本自动切换 [代码]开发版本、体验版本、正式版本[代码] 接口地址 使用微信官方提供的Api 完美解决上传发布手动修改接口地址 wx.getAccountInfoSync 代码实现 [图片] 源代码(方便复制粘贴)0_0 [代码]// 获取小程序当前版本信息 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/account-info/wx.getAccountInfoSync.html // 自动根据版本切换接口请求地址 const { miniProgram: { envVersion } } = wx.getAccountInfoSync(); let url = ''; switch (envVersion) { case 'develop': url = `${defaultConfig.devUrl}${params.url}`; break; case 'trial': url = `${defaultConfig.devUrl}${params.url}`; break; case 'release': url = `${defaultConfig.prodUrl}${params.url}`; break; default: url = `${defaultConfig.baseUrl}${params.url}`; break; } console.log(url, 'url'); console.log(envVersion, 'envVersion'); [代码]
2020-09-24