需求背景
根据微信官方要求涉及处理用户个人信息的小程序开发者,需通过弹窗等明显方式提示用户阅读隐私政策等收集使用规则。
为规范开发者的用户个人信息处理行为,保障用户合法权益,微信要求开发者主动同步微信当前用户已阅读并同意小程序的隐私政策等收集使用规则,方可调用微信提供的隐私接口。
事前准备
- 在文档中查看是否使用了相关隐私接口
开发者需在此板块声明所处理的用户信息,微信会根据小程序版本隐私接口调用情况展示必填项,开发者可自主勾选其他项目。隐私接口与对应的处理的信息关系如下:
处理的信息 | 接口或组件 |
---|---|
收集你的昵称、头像 | open-type="chooseAvatar"、wx.getUserInfo (已回收)、wx.getUserProfile (已回收)、(已回收) |
收集你的位置信息 | wx.authorize({scope:'scope.userLocation'})、wx.authorize({scope: 'scope.userLocationBackground'})、wx.authorize({scope: 'scope.userFuzzyLocation'})、wx.getLocation、wx.startLocationUpdate、wx.startLocationUpdateBackground、wx.getFuzzyLocation |
收集你选择的位置信息 | wx.choosePoi、wx.chooseLocation |
收集你的地址 | wx.chooseAddress |
收集你的发票信息 | wx.chooseInvoiceTitle、wx.chooseInvoice |
收集你的微信运动步数 | wx.authorize({scope: 'scope.werun'})、wx.getWeRunData |
收集你的手机号 | open-type="getPhoneNumber"、open-type="getRealtimePhoneNumber" |
收集你的车牌号 | wx.chooseLicensePlate |
收集你选中的照片或视频信息 | wx.chooseImage、wx.chooseMedia、wx.chooseVideo |
收集你选中的文件 | wx.chooseMessageFile |
访问你的麦克风 | wx.authorize({scope: 'scope.record'})、wx.startRecord、RecorderManager.start、、wx.joinVoIPChat |
访问你的摄像头 | wx.authorize({scope: 'scope.camera'})、wx.createVKSession、、、 |
访问你的蓝牙 | wx.authorize({scope: 'scope.bluetooth'})、wx.openBluetoothAdapter、wx.createBLEPeripheralServer |
使用你的相册(仅写入)权限 | wx.authorize({scope: 'scope.writePhotosAlbum'})、wx.saveImageToPhotosAlbum、wx.saveVideoToPhotosAlbum |
使用你的通讯录(仅写入)权限 | wx.authorize({scope: 'scope.addPhoneContact'})、wx.addPhoneContact |
使用你的日历(仅写入)权限 | wx.authorize({scope: 'scope.addPhoneCalendar'})、wx.addPhoneRepeatCalendar、wx.addPhoneCalendar |
调用你的加速传感器 | wx.stopAccelerometer、wx.startAccelerometer、wx.onAccelerometerChange、wx.offAccelerometerChange |
调用你的磁场传感器 | wx.stopCompass、wx.startCompass、wx.onCompassChange、wx.offCompassChange |
调用你的方向传感器 | wx.stopDeviceMotionListening、wx.startDeviceMotionListening、wx.onDeviceMotionChange、wx.offDeviceMotionChange |
调用你的陀螺仪传感器 | wx.stopGyroscope、wx.startGyroscope、wx.onGyroscopeChange、wx.offGyroscopeChange |
读取你的剪切板 | wx.setClipboardData、wx.getClipboardData |
-
在小程序后台 - 设置 - 基本设置 - 服务内容声明 - 用户隐私保护指引添加勾选1中所列的对应隐私描述, 若不添加后期提交审核依旧会强制提示去更新协议,1中所列到的方法,项目中若使用到也需在manifest.json文件中mp-weixin模块permission中填写对应描述,微信隐私协议与permission两处必须填写否则会导致功能无法正常使用,并不是所有权限都需要添加permission,这块自己根据微信文档抉择!
permission填写的作用在于提交审核时,微信会检测隐私保护指引中是否包含所列项,若无会强制要求其去更新隐私保护指引。2.1 我司项目中较多的* wx.setClipboardData复制功能 若不填写对应隐私协议升级后则无法使用,建议已添加隐私协议的项目各自检查下是否存在该功能,能否正常使用!
开发步骤
2023.09.14更新:隐私相关功能启用时间延期至 2023年10月17日。在 2023年10月17日之前,在 app.json 中配置
__usePrivacyCheck__
: true 后,会启用隐私相关功能,如果不配置或者配置为 false 则不会启用。在 2023年10月17日之后,不论 app.json 中是否有配置__usePrivacyCheck__
,隐私相关功能都会启用。
-
开发前检查配置
1.1 先在小程序 app.json 配置中添加
"__usePrivacyCheck__": true
,微信所说的 app.json 中配置__usePrivacyCheck__: true
1.2 根据项目中使用到的相关隐私接口 增加permission相关权限描述
即:项目中manifest.json文件中mp-weixin模块中
__usePrivacyCheck__
和permission, 如下图示例:
"__usePrivacyCheck__": true,
"permission" : {
"scope.userLocation" : {
"desc" : "方便基于您的位置,提供更贴心更高效的服务"
},
"scope.camera": {
"desc": "为了上传图片或者视频提供人脸识别认证服务"
},
"scope.writePhotosAlbum": {
"desc": "保存图片到相册的权限"
},
"scope.clipboard": {
"desc": "复制到剪贴板的权限"
}
}
2. 小程序用户隐私保护授权弹窗组件
-
效果预览
-
开发业务小程序隐私弹框组件
在components下新建app-privacy-modal文件夹,文件夹下新增app-privacy-modal.vue文件,弹框文案各自可根据各自项目需求自定义。
<template> <van-popup :show="showPrivacy" :close-on-click-overlay="false" :closeable="false" :round="true" :z-index="2222222222"> <view class="privacy"> <view class="privacy-title"> <text>{{ privacyContractName }}</text> </view> <view class="privacy-content"> 亲爱的用户,感谢您信任并使用{{ privacyContractName.replace('隐私保护指引', '') }}!我们非常重视用户的隐私和个人信息保护, 您在使用我们的产品或服务时,我们可能收集或使用您个人信息的情形,详见<text @click="openPrivacyContract">{{ privacyContractName }}</text>全文, 请您认真阅读并充分理解,如您同意我们的政策内容,请点击同意并继续使用本软件。 </view> <view class="privacy-btns"> <view class="btn refuse-btn" @click="refuseEvt">拒绝</view> <button id="agree-btn" class="btn" open-type="agreePrivacyAuthorization" @agreeprivacyauthorization="agreeEvt">同意并继续</button> </view> </view> </van-popup> </template> <script> export default { data() { return { showPrivacy: false, privacyContractName: '', } }, created() { this.onNeedPrivacyAuth() }, methods: { onNeedPrivacyAuth() { // 获取是否有需要授权的隐私协议 const version = wx.getAppBaseInfo().SDKVersion; if (this.compareVersion(version, '2.32.3') >= 0) { // 检验基础库是否大于2.32.3 wx.getPrivacySetting({ success: (res) => { if (res.errMsg == "getPrivacySetting:ok") { this.privacyContractName = res.privacyContractName, this.showPrivacy = res.needAuthorization } } }) } }, openPrivacyContract() { // 打开隐私协议页面 wx.openPrivacyContract({ success: () => { console.log("*********隐私协议打开成功") }, fail: (e) => { console.log("*********隐私协议打开失败", e) uni.showToast({ title: "隐私协议打开失败", icon: "error" }) }, }) }, agreeEvt() { this.showPrivacy = false this.$emit("agree") }, refuseEvt() { // 方案一: 一直给提示 停留在当前页 强制用户必须同意 uni.showToast({ title: '必须同意后才可以继续使用当前小程序', icon: 'none' }) // this.showPrivacy = false // this.$emit("refuse") // 方法二: 直接退出小程序 // wx.exitMiniProgram() }, compareVersion(v1, v2) { // 比较当前版本号 v1 = v1.split('.') v2 = v2.split('.') const len = Math.max(v1.length, v2.length) while (v1.length < len) { v1.push('0') } while (v2.length < len) { v2.push('0') } for (let i = 0; i < len; i++) { const num1 = parseInt(v1[i]) const num2 = parseInt(v2[i]) if (num1 > num2) { return 1 } else if (num1 < num2) { return -1 } } return 0 } } } </script> <style lang="less" scoped> .flex-display(@display: flex, @flexDirection: column,@alignItems: center, @justifyContent: flex-start, @flexWrap: nowrap) { display: @display; flex-direction: @flexDirection; align-items: @alignItems; justify-content: @justifyContent; flex-wrap: @flexWrap; } .privacy { width: 650rpx; padding: 40rpx 30rpx; box-sizing: border-box; background-color: white; .privacy-title { .flex-display(@flexDirection: row, @justifyContent: center); font-size: 32rpx; font-weight: bold; } .privacy-content { padding: 30rpx 0; font-size: 28rpx; line-height: 48rpx; word-break: break-all; text { color: #2666FF; } } .privacy-btns { .flex-display(@flexDirection: row, @justifyContent: space-around); padding: 0 30rpx; height: 80rpx; .btn { margin: 0; width: 300rpx; height: 70rpx; text-align: center; line-height: 70rpx; border-radius: 12rpx; background-color: @app-theme; color: #fff; &.refuse-btn { width: 200rpx; color: #666; border: 1rpx solid #ECECF3; background-color: #fff; margin-right: 30rpx; } } } } </style>
-
使用方法
可以在所有使用了隐私接口的页面都加上该组件,授权一次之后使用所有隐私接口不再需要授权
// 业务小程序 <app-privacy-modal />
-
-
小程序隐私指引填写范本
为了分辨用户,开发者将在获取你的明示同意后,收集你的微信昵称、头像。
为了显示距离或推荐附近相关信息,开发者将在获取你的明示同意后,收集你的位置信息。
开发者收集你的地址,用于获取位置信息。
开发者收集你的发票信息,用于维护消费功能。
为了用户互动,开发者将在获取你的明示同意后,收集你的微信运动步数。
为了通过语音与其他用户交流互动,开发者将在获取你的明示同意后,访问你的麦克风。
开发者收集你选中的照片或视频信息,用于提前上传减少上传时间。
为了上传图片或者视频,开发者将在获取你的明示同意后,访问你的摄像头。
为了登录或者注册,开发者将在获取你的明示同意后,收集你的手机号。
开发者使用你的通讯录(仅写入)权限,用于方便用户联系信息。
开发者收集你的设备信息,用于保障你正常使用网络服务。
开发者收集你的身份证号码,用于实名认证后才能继续使用的相关网络服务。
开发者收集你的订单信息,用于方便获取订单信息。
开发者收集你的发布内容,用于用户互动。
开发者收集你的所关注账号,用于用户互动。
开发者收集你的操作日志,用于运营维护。
为了保存图片或者上传图片,开发者将在获取你的明示同意后,使用你的相册(仅写入)权限。
为了校验身份信息真实性,开发者将在获取你的明示同意后,收集你的车牌号。
开发者访问你的蓝牙,用于设备连接。
开发者使用你的日历(仅写入)权限,用于用户日历日程提醒。
开发者收集你的邮箱,用于在必要时和用户联系。
开发者收集你选中的文件,用于提前上传减少上传时间。
方案优势
- 对当前项目业务代码无任何代码入侵,没有过多的冗余,所有逻辑均在组件内处理。
- 可以在所有使用了隐私接口的页面都加上该组件,授权一次之后使用所有隐私接口不再需要授权。
相关文档及注意事项
-
隐私授权弹框的内容均可根据项目自定义,没必要一定要和示例项目一致
-
隐私协议弹框若被拒绝项目代码中提供了两种方案,建议使用toast提示引导用户同意授权,尽量避免直接退出小程序操作。