- 现有多个微信认证补开发票,发票收到时已过期,现在公司要发票,在我的专属客服里没人回复,怎么补开?
公司有四个小程序进行年审认证的,发票收到时已过期,现在公司要重新开一下发票 公司主体名称:宁夏希望信息产业股份有限公司 纳税人识别号:916411006249207376 联系人:刘小东 联系电话: 小程序名称和AppID如下: 易采牛:wxc984bd024d89020d,牛市通:wx9005c4193d85cdc6,肉牛溯源:wxf059d1f5e71f68d2,固原动物防疫:wxc0afdf01e5b66245
2024-01-04 - 【微信小程序备案】9月1日起,微信小程序需完成备案才可上架!已上架用户,查看这份备案手册,快人一步,抢先备案!
自2023年9月1日起,微信小程序必须完成备案后才可上架;已经上架的小程序,要于2024年3月31日前完成备案,若逾期未完成备案,微信官方平台会按照相关规定于2024年4月1日起进行清退处理,目前备案通道暂未开启,9月1号正式开启备案通道,来一间商户,可提前准备资料,熟悉流程! 备案需要准备哪些资料 主办单位证件(营业执照) 主体负责人证件(身份证) 小程序负责人证件(身份证) 需要小程序负责人扫脸验证 其他材料:根据各省通信管理局要求,上传对应补充材料。 备案流程 ➤ 登录小程序管理后台([https://mp.weixin.qq.com/])首页顶部会提示“小程序需补充备案信息”,点击“去备案”;也可以在小程序管理后台设置模块-小程序备案-点击去备案,填写备案信息,并上传材料 ➤ 1-2个工作日内完成初审并告知结果 ➤ 提交通管局后,24小时内完成短信核验 ➤ 通管局在1-20个工作日内完成审核并告知结果 ➤ 审核通过后,下发小程序备案号 备案信息填写 1、主体信息填写 分个人主体和非个人主体 [图片] (非个人主体) [图片] (个人主体) 提示:选择地区、详细地址,请填写实际通讯地址。 上传有效证件原件的彩色扫描件或拍照件,请保持图片清晰完整、不遮挡关键信息/图像、边角齐全,证件有效期大于1个月。 请对照证件信息如实选择、填写备案信息。 2、主体负责人信息填写 提示:手机号码请填写主体负责人、应急联系人本人使用的有效手机号码,需保持电话畅通,不同人不能使用同一个号码 [图片] 3、小程序信息填写 [图片] 4、小程序负责人信息填写及人脸核身 如果小程序负责人和主体负责人一致,可直接复用主体人信息,如不一致,需手动填写小程序负责人信息。 [图片] 平台初审 开发者在微信公众平台提交小程序备案信息后,平台将会在1-2个工作日内完成初审(具体请以实际审核时间为准),审核结果将以站内信息、模板消息等形式通知管理员。在平台初审的过程中,请备案相关人员保持电话畅通,以便平台对你提交的备案信息进行核验。 工信部短信核验 根据工信部的要求,小程序备案需完成短信核验。你的备案初审通过后,会收到工信部发送的核验短信(发送号码:12381),请在收到短信的24小时内登录工信部备案官网首页完成短信核验,短信核验成功后备案申请进入通管局审核流程。 若核验时提示验证码错误或无效,你可触发一次短信重发;24小时内未核验成功,备案将被工信部系统自动驳回,你需到微信公众平台重新提交备案申请 [图片] [图片] 通管局审核 各省通信管理局将在1-20个工作日内(具体以实际审核时间为准)完成审核,审核结果以站内信、模板消息、短信等形式通知。 备案成功 管局审核通过后,将下发的小程序备案号,代表你的小程序已完成备案,可以进入下一步版本发布等流程。 [图片] 此备案流程仅适用酒旅商家操作,如需查看更多,请查看微信小程序官方备案操作指引
2023-08-28 - 小程序隐私弹框组件
一、前言 文章这个编辑器真的太恶心了 代码块不仅不能设置缩进还不能使用tab增加缩进只能空格一个一个打 调个字体和代码格式太TM麻烦了 2023.08.22更新: 以下指南中涉及的 getPrivacySetting、onNeedPrivacyAuthorization、requirePrivacyAuthorize 等接口目前可以正常接入调试。调试说明: 在 2023年9月15号之前,在 app.json 中配置 [代码]__usePrivacyCheck__: true[代码] 后,会启用隐私相关功能,如果不配置或者配置为 false 则不会启用。在 2023年9月15号之后,不论 app.json 中是否有配置 [代码]__usePrivacyCheck__[代码],隐私相关功能都会启用首先了解官方提供demo: demo1: 演示使用 wx.getPrivacySetting 和 <button open-type="agreePrivacyAuthorization"> 在首页处理隐私弹窗逻辑 https://developers.weixin.qq.com/s/gi71sGm67hK0 demo2: 演示使用 wx.onNeedPrivacyAuthorization 和 <button open-type="agreePrivacyAuthorization"> 在多个页面处理隐私弹窗逻辑,同时演示了如何处理多个隐私接口同时调用。 https://developers.weixin.qq.com/s/4X7yyGmQ7EKp demo3: 演示 wx.onNeedPrivacyAuthorization、wx.requirePrivacyAuthorize、<button open-type="agreePrivacyAuthorization"> 和 <input type="nickname"> 组件如何结合使用 https://developers.weixin.qq.com/s/jX7xWGmA7UKa demo4: 演示使用 wx.onNeedPrivacyAuthorization 和 <button open-type="agreePrivacyAuthorization"> 在多个 tabBar 页面处理隐私弹窗逻辑。 https://developers.weixin.qq.com/s/g6BWZGmt7XK9 小程序后台隐私协议添加对应隐私协议(例如:剪贴板、手机号授权等)协议更新后有一定时间的缓存才能测试(大概是在半天之后可以测试)优先处理添加协议 (如果之前小程序已经添加过对应的协议,则需要重新提交审核同步信息才会返回正常用来测试)基础库设置3.23.3及以上 (微信版本:iOS 8.0.36、安卓 8.0.35会携带此基础库版本)app.json文件内这个配置__usePrivacyCheck__属性为true (uniapp开发参考下图) [图片]开发工具清除全部缓存之后重新编译、手机微信下拉删除最近小程序等进行隐私协议同步信息获取测试二、关于小程序隐私保护指引设置的公告为规范开发者的用户个人信息处理行为,保障用户的合法权益,自2023年9月15日起,对于涉及处理用户个人信息的小程序开发者, 微信要求,仅当开发者主动向平台同步用户已阅读并同意了小程序的隐私保护指引等信息处理规则后,方可调用微信提供的隐私接口。 开发者首先需确定小程序是否涉及处理用户个人信息,如涉及,则需配置用户隐私授权弹窗, 且仅有在平台《小程序用户隐私保护指引》中声明了所处理的用户个人信息,才可以调用平台提供的对应接口或组件。 三、设置《小程序用户隐私保护指引》 [图片] [图片] 四、填写《小程序用户隐私保护指引》[图片] 只有在指引中声明所处理的用户个人信息,才可以调用平台提供的对应接口或组件。若未声明,对应接口或组件将无法调用成功。 五、配置用户隐私授权弹窗 (触发方式:隐私API、组件) 1.了解隐私协议所需相关API 01.wx.requirePrivacyAuthorize() 用于模拟隐私接口调用 02.wx.openPrivacyContract() 用于打开下面封装好隐私弹框中的隐私协议 03.wx.onNeedPrivacyAuthorization() 用于监听用户是否吊起相关的隐私协议 04.wx.getPrivacySetting() 用于查询微信侧记录用户隐私协议状态 基本流程为:03 -----> 04 -----> 02 2.封装微信侧用户隐私协议状态 //获取微信侧同步的用户隐私协议开关 function getPrivacySetting(callback){ if(wx.getPrivacySetting){ wx.getPrivacySetting({ success: result => { console.log(result,"同步信息结果") // 返回结果为: result = { needAuthorization: true/false, privacyContractName: '《xxx隐私保护指引》' } if (result.needAuthorization) { // 需要弹出隐私协议 console.log("获取微信储存的用户协议同步信息-用户未同意,请弹框处理") callback && callback(result); } else { console.log("获取微信储存的用户协议同步信息-用户已同意,请忽略") } }, fail: () => {}, complete: () => {} }) } } 3.封装隐私协议弹框放置全局组件中 全局组件-wxml 用户隐私保护提示 亲爱的用户,感谢您的信任!您使用本小程序提供的产品服务前应当阅读并同意 {{urlTitle}} 当您点击同意并开始使用产品服务时,即表示你已理解并同息该条款内容,该条款将对您产生法律约束力。如您拒绝,将无法使用本小程序提供的相关产品及服务。 不同意 同意 全局组件-JS JS//引入相关模块 const { getPrivacySetting } = require("../utils/utils") //设置协议回调、关闭回调数组变量 let privacyHandler let privacyResolves = new Set() let closeOtherPagePopUpHooks = new Set() //注册并监听隐私API调用 if (wx.onNeedPrivacyAuthorization) { wx.onNeedPrivacyAuthorization(resolve => { //获取用户微信侧同步结果 getPrivacySetting(result=>{ console.log(result,"同步信息结果") // 返回结果为: result = { needAuthorization: true/false, privacyContractName: '《xxx隐私保护指引》' } if (result.needAuthorization) { // 需要弹出隐私协议 if (typeof privacyHandler === 'function') privacyHandler(resolve,result) console.log("获取微信储存的用户协议同步信息-用户未同意,请弹框处理-页面") } }) }) } //处理关闭页面其他弹框 const closeOtherPagePopUp = (closePopUp) => { closeOtherPagePopUpHooks.forEach(hook => { if (closePopUp !== hook) { hook() } }) } Component({ options: { styleIsolation: 'shared', multipleSlots: true }, data: { urlTitle: "", privacyFlag: false, height: 0, }, lifetimes: { attached: function() { const closePopUp = () => { this.disPopUp() } privacyHandler = (resolve,result) => { this.setUrlTitle(result); console.log(result,"999999") privacyResolves.add(resolve) this.popUp() // 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗 closeOtherPagePopUp(closePopUp) } closeOtherPagePopUpHooks.add(closePopUp) this.closePopUp = closePopUp }, detached: function() { closeOtherPagePopUpHooks.delete(this.closePopUp) } }, methods: { //处理隐私弹框内展示的隐私协议标题 setUrlTitle(result){ this.setData({urlTitle:result.privacyContractName}) }, //用户同意相关协议 handleAgree(e) { this.disPopUp() // 这里演示了同时调用多个wx隐私接口时要如何处理:让隐私弹窗保持单例, 点击一次同意按钮即可让所有pending中的wx隐私接口继续执行 (看page/index/index中的 wx.getClipboardData 和 wx.startCompass) privacyResolves.forEach(resolve => { resolve({ event: 'agree', buttonId: 'agree-btn' }) }) privacyResolves.clear() this.triggerEvent('handleAgree', {result:true}); }, //用户拒绝相关协议 handleDisagree(e) { this.disPopUp() privacyResolves.forEach(resolve => { resolve({ event: 'disagree', }) }) privacyResolves.clear() this.triggerEvent('handleAgree', {result:true}); }, //打开隐私弹框 popUp() { if (this.data.privacyFlag === false) { this.setData({ privacyFlag: true }) } }, //关闭隐私弹框 disPopUp() { if (this.data.privacyFlag === true) { this.setData({ privacyFlag: false }) } }, //打开微信提供的小程序侧隐私协议 openPrivacyContract() { wx.openPrivacyContract({ success: res => { console.log('openPrivacyContract success') }, fail: res => { console.error('openPrivacyContract fail', res) } }) }, checkPrivacyStatus() { let that = this; getPrivacySetting(result=>{ if (result.needAuthorization) { that.setUrlTitle(result); that.popUp(); } else { console.log("微信侧已记录用户同意"); } }) } } }) 父组件监听全局隐私弹框时间回调 父组件-wxml 父组件-JS //隐私协议弹框按钮回调 handleAgree(event){ let that = this; console.log(event); let {result} = event.detail; // result 根据用户选择可处理后续相关逻辑 true 用户同意相关隐私 例如:因昵称组件弹出的框 为true之后就可将昵称组件设置为聚焦状态 false 用户拒绝 例如:音视频组件 用户拒绝之后可返回上级页面、继续弹框进行处理等 }, 4.app.json中引入全局组件:"privacy-popup": "/components/privacyPopup" 5.全局使用(隐私弹框) 全局隐私弹框相关代码如下: 首页wxml引入组件 首页onReady时触发检测隐私弹框逻辑 that.selectComponent("#privacyPop") && that.selectComponent("#privacyPop").checkPrivacyStatus(); 6.按需使用(隐私弹框)进入单个隐私页面时优先检测(手机号授权API触发隐私弹框) 6-1.1.引入全局组件到手机号授权页面: 6-1.2.页面onReady的时候开始检测:that.selectComponent("#privacyPop") && that.selectComponent("#privacyPop").checkPrivacyStatus(); 6-2.1.在用户点击按钮即将吊起隐私弹框时使用如下代码: getPrivacySetting(result=>{ if (result.needAuthorization) { // 需要弹出隐私协议 that.selectComponent("#privacyPop") && that.selectComponent("#privacyPop").setUrlTitle(result); console.log("获取微信储存的用户协议同步信息-用户未同意,请弹框处理") } else { console.log("获取微信储存的用户协议同步信息-用户已同意,请忽略") //走隐私允许之后的逻辑 } }) 六、清空历史同步状态 1.微信下拉---最近---最近使用的小程序 2.开发工具---清除模拟器缓存---清除全部数据/授权数据 七、常见错误说明{ "errMsg": "A:fail api scope is not declared in the privacy agreement", "errno": 112 } 使用到了 A 隐私接口,但是开发者未在[mp后台-设置-服务内容声明-用户隐私保护指引]中声明收集 A 接口对应的隐私类型。还有个114的忘记统计是啥原因了(希望官方尽快能把错误码及对应的原因贴出来)
2023-10-12 - [干货]新一轮用户授权改版来了,肝起来吧!
废话不多说,直奔主题。感谢TX团队赞助,不然没机会通宵!以下思路,仅供参考。 公告开山 [图片] 改造原有授权逻辑 原来通过getUserProfile接口拿到userinfo数据,仍然不变。仅对返回结果加一次判断; [代码]wx.getUserProfile && wx.getUserProfile({ lang: 'zh_CN', desc: "desc", success: ({ userInfo }) => { if (userInfo?.nickName === '微信用户') { // 跳转至新建的资料修改页面 return } // 同步用户资料信息 api.updateUserinfo(userInfo) }, fail: (error) => { // 跳转至新建的资料修改页面 } }) [代码] 资料修改页面 页面 此处需要注意的是, 头像组件有最低版本兼容要求。而昵称填写则可以忽略版本兼容性。此处不赘述昵称填写 兼容性处理 [代码]data: { supportAvatarInput: compareVersion(version, '2.21.2') >= 0, } [代码] [代码]<!-- 代码有省略,见下 --> <button wx:if="{{supportAvatarInput}}" open-type="chooseAvatar"></button> <button wx:else></button> [代码] 页面美化 由于官方没有直接提供头像选择效果的组件可供直接使用,而是以button的形式。因此需要我们自行处理,不过也很简单position + opacity即可; 先上效果图: [图片] [代码]<view class="__input-wrapper"> <image class="avatar" src="{{avatarUrl}}"></image> <button wx:if="{{supportAvatarInput}}" class="opacity-button" open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar"></button> <button wx:else class="opacity-button" catchtap="handleChooseImage"></button> </view> [代码] [代码].__input-wrapper{ position: relative; top: 0; left: 0; } .__input-wrapper .avatar { width: 120rpx; height: 120rpx; border-radius: 50%; } .__input-wrapper .opacity-button{ position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 9; opacity: 0; padding: 0 !important; margin: 0 !important; } [代码] 到此,界面完工。接下来开始处理头像上传问题。 业务逻辑 先来处理头像上传组件本身的事件, 此处有个坑。官方的头像上传不支持裁剪,因此需要用到更新的cropImage这个API。 所以我们先来实现图片裁剪功能, 依然是注意兼容性。因为目前是从2.26.0才开始支持。 [代码] cropAvatar(url){ return new Promise((resolve, reject) => { try { if (wx.cropImage) { wx.cropImage({ src: url, cropScale: "1:1", success({ tempFilePath }) { if (!tempFilePath) return reject(Error('处理失败,未获取到路径')) resolve(tempFilePath) }, fail(error) { reject(Error(error.errmsg || error.errMsg)) } }) return } // 低版本兼容 // 此处可弹窗提醒用户手动裁剪为1:1 if (wx.editImage) return wx.editImage({ src: url, success({ tempFilePath }) { if (!tempFilePath) return reject(Error('处理失败,未获取到路径')) resolve(tempFilePath) }, fail(error) { reject(Error(error.errmsg || error.errMsg)) } }) // 其他情况返回原url resolve(url) } catch (error) { reject(error) } }) } [代码] 考虑到用户上传的图片可能会很大。也可以继续设计一个压缩图片的方法。 此处又是一个坑,官方的compressImage API模拟器上会返回错误的tempFilePath方法(真机目前没有) [代码]compressImage(url){ return new Promise(async (resolve, reject) => { // 指定压缩为480px * 480px const COMPRESS_SIZE = 480 try { const { width, height } = await this.getImageInfo(url) const size = Math.ceil(Math.sqrt(width * height)) if (size <= COMPRESS_SIZE) throw Error(`当前图片尺寸为 ${width} * ${height} (${size}) , 已符合压缩规则,忽略压缩`) const quality = Math.ceil(COMPRESS_SIZE * 100 / size) if (!wx.compressImage) throw Error("当前基础库版本太低,请升级微信客户端") wx.showLoading({ title: '压缩中', }) wx.compressImage({ src: url, quality: quality, // 以下高版本支持,低版本仍考虑 quality compressedWidth: COMPRESS_SIZE, compressHeight: COMPRESS_SIZE, success({ tempFilePath }) { if (!tempFilePath) return reject(Error('处理失败,未获取到路径')) // 检查后缀是否存在, 应对模拟器返回错误的tempFilePath问题 if (!/^.*\.(.+)$/.test(tempFilePath)) return reject(Error('SDK错误,压缩路径不正确')) resolve(tempFilePath) }, fail(error) { reject(Error(error.errmsg || error.errMsg)) }, complete() { wx.hideLoading({}) } }) } catch (error) { reject(error) } }) } [代码] 到此准备工作已就绪。 新坑来袭 又一个坑, 此处通过头像上传的url并非和之前一样。这次的是个临时链接,不能直接存储至数据库使用。因此需要服务端提供头像上传的接口。前端调用wx.uploadFile API上传。 此处不赘述 处理头像组件的回调 [代码]async onChooseAvatar({ detail }) { let url = detail.avatarUrl // 裁剪 url = await this.cropAvatar(url) // 压缩 try { url = await this.compressImage(url) } catch (error) { console.error('头像压缩失败:', error) } try { // 此处是上传接口调用。 根据自己需要改造即可 const { avatarUrl } = await app.$http.post({ url: '/upload', header: { 'content-type': 'multipart/form-data' }, data: { name: 'picture', filePath: url, }, timeout: 30 * 1000, loading: '上传中' }) this.setData({ avatarUrl: avatarUrl }) app.$Toast("上传成功") } catch (error) { wx.showModal({ content: `${error.message || "上传失败,请稍后再试"}`, showCancel: false }) } } [代码] 别忘了还有个低版本的适配, 这个简单。 不过这个还是有低版本兼容处理 [代码]handleChooseImage() { try { let self = this if (wx.chooseMedia) { return wx.chooseMedia({ count: 1, mediaType: 'image', async success(res) { const tempFilePath = res?.tempFiles[0]?.tempFilePath if (!tempFilePath) return app.$Toast("选取失败,请稍后再试") // 此处复用之前的头像上传回调 await self.onChooseAvatar({ detail: { avatarUrl: tempFilePath } }) }, fail() { app.$Toast("头像修改失败 请稍后再试") } }) } wx.chooseImage({ count: 1, async success(res) { const tempFilePath = res?.tempFilePaths[0] if (!tempFilePath) return app.$Toast("选取失败,请稍后再试") await self.onChooseAvatar({ detail: { avatarUrl: tempFilePath } }) }, fail() { app.$Toast("头像修改失败 请稍后再试") } }) } catch (error) { app.$Toast("当前版本太低,建议您先升级微信客户端") } }, [代码] 总结 希望这是最后一次 低版本兼容性要注意, 狂放的小伙伴可以设置最低版本库,一劳永逸 头像和昵称组件,自带了安全识别功能。因此会比较慢,需要吐槽的是头像图片的鉴黄识别有点拉跨,经常误报。 如果你看到提示”当前头像不可用“之类的提示,不要担心是代码问题 图片裁剪API才出, 所以在此之前官方是没有考虑到的。吐血 官方其他的bug也该修修了,就比如compressImage里模拟器返回的路径 写的仓促,如有不对,请多指正! 一点小宣传 已经第一时间接入cropImage了,很香。 欢迎体验!! 附小程序码: [图片] 体验路径 : 我的 -> 左上角”设置“ -> 个人信息修改 大家还有什么想说的! 欢迎评论区交流
2023-02-13