- [拎包哥] 批量下载云开发云存储的文件到pc端
官方教程 注:第2步和第3部的代码是反过来的。 [图片] 我的步骤: 0.按照官方教程安装tcb脚手架。 1.在桌面新建文件夹hellowWorld,新建cloudbaserc.json,json里的内容为: [图片] { "envId":"你的云开发环境的id" } 2.在cmd输入命令行。 e.g. 下载云存储里的qrCode文件夹到PC端文件夹(桌面的helloWorld文件夹)。 注:--dir在这里的意思是声明前面的 . 是一个文件夹,不需要另外修改。 [图片] tcb storage download qrCode . --dir
2021-09-28 - 【笔记】云开发聚合实现分页,涉及跨表查询、逻辑计算、判断权限、数据格式化、限制输出
背景: 之前不会用聚合,因此把数据库结构分为了用户表、帖子表、喜欢表。小程序端请求一次列表,要根据帖子列表,循环查询用户表,并且还要做一系列的逻辑运算处理,计算当前帖子的权限、是否喜欢过、喜欢人数、是否有这个帖子管理权限等信息。 这样做有很多弊端: 处理速度慢,资源耗费严重,循环查询肯定慢且耗费资源,一个列表需要21次查询。需要写大量逻辑处理代码,如计算管理权限,喜欢数量、当前用户是否喜欢,格式处理等等。于是使用聚合进行了优化: 跨表查询数据格式化逻辑计算,权限判断、是否喜欢等数据统计,喜欢总人数权限判断,是否为管理员限制输出效果: 之前:上百行代码,多次查询,需要单独判断函数,处理时间在3000ms以上之后:几行代码,一次查询,直接查询时算出结果,处理时间在300ms以内 数据库结构 [图片] 代码实现: const { OPENID } = cloud.getWXContext(context) //构建查询条件 let query = null switch (Number(event.listType)) { case 0: query = db.collection('post').aggregate() .match({ //0我的 '_openid': OPENID }) .sort({ createTime: -1 }) .skip(20 * (event.pageNum - 1)) .limit(20) break; case 1: //1 随机 query = db.collection('post').aggregate() .match({ public: true, // feeling: _.gte(50) }) .sample({ size: 20 }) break; case 2: query = db.collection('post').aggregate() .match({ //2喜欢 likes: _.all([OPENID]) }) .sort({ createTime: -1 }) .skip(20 * (event.pageNum - 1)) .limit(20) break; case 4: query = db.collection('post').aggregate() .match({ //4指定 _id: event.id }) .sort({ createTime: -1 }) .skip(20 * (event.pageNum - 1)) .limit(20) break; } //使用聚合处理后续数据 let listData = await query .lookup({ from: "user", localField: "_openid", foreignField: "_id", as: "postList" })//联表查询用户表 .replaceRoot({ newRoot: $.mergeObjects([$.arrayElemAt(['$postList', 0]), '$$ROOT']) })//将用户表输出到根节点 .addFields({ day: $.dayOfMonth('$createTime'), month: $.month('$createTime'), year: $.year('$createTime'), isLike: $.in([OPENID, '$likes']), //是否喜欢 isLiked: $.in([OPENID, '$liked']), //是否喜欢过 isAdmin: $.eq([OPENID, 'oy0T-4yk7lCRFGDefpFC4Yvx_ppU']),//是否管理员 isAuthor: $.eq(['$_openid', OPENID]),//是否为作者 like: $.size('$likes'), //喜欢该帖子数 face: $.switch({ branches: [ { case: $.gte(['$feeling', 90]), then: 9 }, { case: $.gte(['$feeling', 80]), then: 8 }, { case: $.gte(['$feeling', 70]), then: 7 }, { case: $.gte(['$feeling', 60]), then: 6 }, { case: $.gte(['$feeling', 50]), then: 5 }, { case: $.gte(['$feeling', 40]), then: 4 }, { case: $.gte(['$feeling', 30]), then: 3 }, { case: $.gte(['$feeling', 20]), then: 2 }, { case: $.gte(['$feeling', 10]), then: 1 } ], default: 0 }) //根据心情值判断对应表情 }) .project({ postList: 0, userInfo: 0, liked: 0, likes: 0, city: 0, province: 0, country: 0, language: 0, nlp: 0, saveType: 0, }) //清楚掉不需要的数据 .end() return listData
2020-05-26 - 只有三行代码的神奇云函数的功能之四:获取电话号码
这是一个神奇的网站,哦不,神奇的云函数,它只有三行代码:(真的只有三行哦) 云函数:login index.js: const cloud = require('wx-server-sdk') cloud.init() exports.main = async (event) => { return { ...event, ...cloud.getWXContext() } } 神奇功能之四:获取电话号码: 还是这三行代码,获取用户的电话号码。 wxml: <button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber" >{{mobile||"获得电话号码"}}</button> js: getPhoneNumber: function (e) { wx.cloud.callFunction({ name: 'login', data: {weRunData: wx.cloud.CloudID(e.detail.cloudID)} }).then(res => { this.setData({ mobile: res.result.weRunData.data.phoneNumber }) }) } 其他功能: 神奇功能之一:获取openid: https://developers.weixin.qq.com/community/develop/article/doc/00080c6e3746d8a940f9b43e55fc13 神奇功能之二:不用授权获取unionid: https://developers.weixin.qq.com/community/develop/article/doc/000a0c6b580338e947f9db0c65b813 神奇功能之三:100%成功获取unionid: https://developers.weixin.qq.com/community/develop/article/doc/00066a967c4e384949f93fe1151413 神奇功能之五:获取群id: 将小程序分享到某群里,可获得该群的群id, https://developers.weixin.qq.com/community/develop/article/doc/000ea802c00f70894cf9fe72556013 [图片]
2020-12-16 - 如何解决database query result size exceed 1MB?
出现错误: Error: errCode: -602001 database query result size exceed limit (1MB) | errMsg: response size exceeded 1MB. 由于我的表是用来存储马拉松运动中折gpx记录,现在其中一条全程马拉松记录数据比较大,存储进去就读取不出来了,出现上述错误,请问如何解决? [图片] 谢谢。
2020-12-09 - onCameraFrame 不执行
let listener = this.ctx.onCameraFrame((frame) => { // console.log(frame.data instanceof ArrayBuffer, frame.width, frame.height) // console.log(new Uint8Array(frame.data)); [图片]
2019-11-07 - #小程序云开发挑战赛#-小小微距-汇溪和他们的小伙伴们
应用场景 很多人对孤单,孤独,寂寞 有很多不同的理解。谈谈我的想法,陪伴不一定是异性,不一定是肉体。 我想的是一种短暂性的社交,只此一面,可能以后再无相见。所以放开的来,他/她 对你的圈子没有影响。(如果聊的来可以互留微信);想了想感觉确实与评论区的 YP 不谋而和呀(秘密,两人,孤单),首先是肯定要避免这种事件的出现,这不符合我的想法。 还有就是你的诉求不一定要求是一个人,也可以是5人,刚好凑个五黑阵容。😳 我的这个想法是以线下为核心开展的,但是有非常多的不确定性因素。最关键的是 如何知道 他/她 是不是一个好人。 现在可玩的东西这么多,孤独的状态我认为是短暂性的,不是一个长久状态。你经历了某件事,发生了一些事情,或者要去做某些事,让你感到了孤独,你才有这个需求。 (以下全是我的内心独白) 场景1:生病的状态,内心特别的脆弱的。 去医院没什么,难的是去一个人去医院。东奔西跑的心里面难受极了。 场景2: 玩游戏老是一个人,自己是渴望交流的。玩守望,最喜欢和话多的一起玩。 目标用户 那些感到孤独的人,是精神上的孤独,不是肉体上的寂寞。不要老说什么YP,我懂😨。首先本产品是不上线的,虽然我很想上线,但是实现不了😭。 基本思路 用户通过发帖,表达自己的诉求,比如:周末来一个人一起打篮球🐓。 其他用户看到了,点击沟通 双方就可以沟通了,约会时间地点 一起打篮球。 动态是一个副产品,就像发一个朋友圈 QQ空间的说说一样,表达自己日常的看法,有趣的图片,雷人的事件… 架构图(系统分析) 思维导图地址: 幕布 [图片] 效果截图 [图片] 演示视频 https://v.qq.com/x/page/g3151ub314r.html 代码链接 码云 很遗憾不能上线,但是通过开源结识了一些人,意外的惊喜了😊,欢迎各位 start。 团队简介 汇溪和他们的小伙伴们 一个人,一个人监督自己,太难了。堕落太容易了,打游戏这么舒服,我为什么要压抑住自己。学一堆让人迷惑不解的东西呢!我不想自己到而立之年是,回头望去,一点值得回味的东西都没有。在亲人面前,没有满脸得意向他们展示的东西。想想这些就可怕,我还年轻,我有的选择,我很庆幸。 我想成立一个团队,汇溪。我想让他有点影响力,这样对所有成员都有好处。团队间相互督促,相互学习。一个好的学习氛围有多重要,不用多讲都知道。学习群之类的没用,必须小而精。现在它没有丝毫影响力,可能将来也没有。
2020-09-13 - 让小程序页面和自定义组件支持 computed 和 watch 数据监听器
习惯于 VUE 或其他一些框架的同学们可能会经常使用它们的 [代码]computed[代码] 和 [代码]watch[代码] 。 小程序框架本身并没有提供这个功能,但我们基于现有的特性,做了一个 npm 模块来提供 [代码]computed[代码] 和 [代码]watch[代码] 功能。 先来个 GitHub 链接:https://github.com/wechat-miniprogram/computed 如何使用? 安装 npm 模块 [代码]npm install --save miniprogram-computed [代码] 示例代码 [代码]const computedBehavior = require('miniprogram-computed') Component({ behaviors: [computedBehavior], data: { a: 1, b: 1, }, computed: { sum(data) { return data.a + data.b }, }, }) [代码] [代码]const computedBehavior = require('miniprogram-computed') Component({ behaviors: [computedBehavior], data: { a: 1, b: 1, sum: 2, }, watch: { 'a, b': function(a, b) { this.setData({ sum: a + b }) }, }, }) [代码] 怎么在页面中使用? 其实上面的示例不仅在自定义组件中可以使用,在页面中也是可以的——因为小程序的页面也可用 [代码]Component[代码] 构造器来创建! 如果你已经有一个这样的页面: [代码]Page({ data: { a: 1, b: 1, }, onLoad: function() { /* ... */ }, myMethod1: function() { /* ... */ }, myMethod2: function() { /* ... */ }, }) [代码] 可以先把它改成: [代码]Component({ data: { a: 1, b: 1, }, methods: { onLoad: function() { /* ... */ }, myMethod1: function() { /* ... */ }, myMethod2: function() { /* ... */ }, }, }) [代码] 然后就可以用了: [代码]const computedBehavior = require('miniprogram-computed') Component({ behaviors: [computedBehavior], data: { a: 1, b: 1, }, computed: { sum(data) { return data.a + data.b }, }, methods: { onLoad: function() { /* ... */ }, myMethod1: function() { /* ... */ }, myMethod2: function() { /* ... */ }, }, }) [代码] 应该使用 [代码]computed[代码] 还是 [代码]watch[代码] ? 看起来 [代码]computed[代码] 和 [代码]watch[代码] 具有类似的功能,应该使用哪个呢? 一个简单的原则: [代码]computed[代码] 只有 [代码]data[代码] 可以访问,不能访问组件的 [代码]methods[代码] (但可以访问组件外的通用函数)。如果满足这个需要,使用 [代码]computed[代码] ,否则使用 [代码]watch[代码] 。 想知道原理? [代码]computed[代码] 和 [代码]watch[代码] 主要基于两个自定义组件特性: 数据监听器 和 自定义组件扩展 。其中,数据监听器 [代码]observers[代码] 可以用来监听数据被 [代码]setData[代码] 操作。 对于 [代码]computed[代码] ,每次执行 [代码]computed[代码] 函数时,记录下有哪些 data 中的字段被依赖。如果下一次 [代码]setData[代码] 后这些字段被改变了,就重新执行这个 [代码]computed[代码] 函数。 对于 [代码]watch[代码] ,它和 [代码]observers[代码] 的区别不大。区别在于,如果一个 data 中的字段被设置但未被改变,普通的 [代码]observers[代码] 会触发,但 [代码]watch[代码] 不会。 如果遇到问题或者有好的建议,可以在 GitHub 提 issue 。
2019-07-24 - 【新特性】双向数据绑定 & 数据监听器
双向数据绑定 & 数据监听器 开篇 微信小程序在迭代更新的速度确实快,不知不觉间,又新添了不少新属性,使用文档越来越长,这次来分享一下近期开发项目时,使用到的一些新属性,直观感受就是 —— 写代码少了。 双向数据绑定 1. 使用文档: https://developers.weixin.qq.com/miniprogram/dev/framework/view/two-way-bindings.html 2. 示例效果: [图片] 3. 编码: [代码]// list.wxml <!--双向数据绑定--> <view class="weui-cells__group weui-cells__group_form"> <view class="weui-cells__title">2. 双向数据绑定</view> <view class="weui-cells weui-cells_form"> <view class="weui-cell weui-cell_active"> <view class="weui-cell__hd"><label class="weui-label">输入框</label></view> <view class="weui-cell__bd"> <input class="weui-input" model:value="{{value2}}" placeholder="请输入" placeholder-class="weui-input__placeholder" /> </view> </view> <view class="weui-cell">您输入的值:{{value2}}</view> </view> </view> [代码] [代码]// list.js Page({ value2: '' }) [代码] 解析:例子里有一个文本输入框input标签,在原生的 [代码]value[代码] 属性前加上了 [代码]model:[代码] 的字样,底下放了一个文和输入框相同变量取值的普通标签,用来实时展示输入的文本值。 换句话说,只需要在标签属性前绑定了 [代码]model:[代码] 属性,就可以无需通过 [代码]js[代码] 代码去人为修改该属性绑定的变量值了(setData的方式),并且会同步影响到所有引用到该变量下的所有组件渲染。 4. 过去 & 现在: 在项目里我分别写了两个例子,一个是传统的数据绑定,一个是通过 [代码]model:[代码] 的方式实现,如下图所示: [图片] 数据监听器 1. 使用文档:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/observer.html 2. 示例效果: [图片] 3 编码: [代码]// vobserver.wxml <view class="weui-cells__group weui-cells__group_form"> <view class="weui-cells__title">2. model双向数据绑定 + 数据监听器</view> <view class="weui-cells weui-cells_form"> <view class="weui-cell weui-cell_active"> <view class="weui-cell__hd"><label class="weui-label">市场价</label></view> <view class="weui-cell__bd"> <input class="weui-input" model:value="{{marketPrice}}" placeholder="请输入市场价" placeholder-class="weui-input__placeholder" /> </view> </view> <view class="weui-cell weui-cell_active"> <view class="weui-cell__hd"><label class="weui-label">折扣价</label></view> <view class="weui-cell__bd"> <input class="weui-input" model:value="{{discount}}" placeholder="请输入折扣价" placeholder-class="weui-input__placeholder" /> </view> </view> <view class="weui-cell"> 销售价 <text style="font-size: 12px;color: #999; margin-left: 5px;">(销售价=市场价-折扣价)</text>:{{salePrice}} </view> </view> </view> [代码] [代码]Component({ data: { marketPrice: '', discount: '', salePrice: '', }, observers: { 'marketPrice,discount'(marketPrice = 0, discount = 0) { const salePrice = marketPrice - discount this.setData({ salePrice: salePrice < 0 ? 0 : salePrice }) } }, }) [代码] 解析:这是商城应用最常见的一个价格计算功能 —— 计算销售价。计算方式为:销售价 = 市场价 - 折扣价。上面的这个例子采用了 [代码]model:[代码] 和 [代码]observers[代码] 两个属性相结合实现的:通过 [代码]observers属性[代码] 为市场价(marketPrice)和折扣价(discount)绑定监听函数,从而计算销售价(salePrice)。并且还能实时同步到内部属性(可以在Component组件内部拿到最新的 [代码]marketPrice[代码] 和 [代码]salePrice[代码]值。) 4. 过去 & 现在: 在项目里我分别写了两个例子,一个是传统的数据监听绑定,以往我是通过 [代码]proxy[代码] 设置代理器来实现的;一个是通过 [代码]model:[代码] + [代码]observers[代码] 的方式实现,如下图所示: [图片] 结论: 采用 [代码]model:[代码] 来实现微信小程序的数据双向绑定,如果是在页面层级使用的话,我们无需通过 [代码]setData[代码] 的方式来从新设置变量值,就可以同步到页面其余使用到该绑定变量的最新值;如果是在组件层级使用的话,好处就是不需要通过子组件内部 [代码]triggerEvent[代码] 的方式回调给父级一个方法回调,父级还得通过 [代码]bind:event[代码] 的方式重新设值。 无论是怎样使用,[代码]model:[代码]的方式使用大大减少了代码的编写量,而且还减少了逻辑链路的排查,不至于在各种[代码]triggerEvent[代码] 之间绕来绕去。 原理我就不献丑了,因为很多文章都有介绍过双向数据绑定的原理,在项目里面我也用一个例子简单模仿了一个,我估摸在小程序内部对 [代码]model:[代码] 绑定的变量统一存放在了由 [代码]proxy[代码] 绑定的对象里面,并且同步到了 [代码]Page[代码] 或者 [代码]Component[代码] 的[代码]data[代码]属性里面去,用户往后对绑定的变量的操作设值,都会交[代码]proxy[代码]进行代理执行,实时同步到[代码]data[代码]对象去。 项目地址 项目地址:https://github.com/csonchen/mina-app 我想记录一些关于小程序日常开发所遇到的问题,进而引起的一些思考,能否给大家提供多一些角度去思考问题,解决问题,能帮助大家就好。希望大家多多支持,多多star哈
2020-09-22 - 小程序流量主运营技巧
前言(写给入坑的小白) 本文不涉及任何需要资质的小程序(如:视频类目)。小程序流量主是个人和小微企业主要变现途径之一,满1000人即可开通流量主(登录mp.weixin.qq.com,左侧边栏-推广-流量主-开通即可)。开通后,开发者可从流量主-广告位管理添加广告位,目前有6种广告位。 [图片] 正文(本文约很多字,分为四大主类,手里有1-10个小程序建议全部看完;手里有10个以上小程序,可跳过1、2、3,均为个人观点,不喜使劲喷) 一、小程序定位 小程序定位目前有以下四种,均不需要任何资质,个人(商城除外)/小微企业都可以做,由于本人不擅长文字表达,每个类型只选择一个做分析,谅解。 1、工具类 工具类有很多可以做:题库、技术文档、教程、去水印等。目前最火爆的应该属于疫情相关类的工具,关于疫情数据类小程序不做分析,没资质也没权利,主要说疫情周边可运营的工具。头像口罩,代表小程序:头像加口罩、戴个口罩吧、戴上口罩(每日搜索量约等于2000),可参考以下做法: [图片] 以下为近7日访问数据量 [图片] 盈利方式:流量主 延伸参考:如果仅做头像加口罩的话,那么疫情过后,这个小程序会直线下降,将无任何作用。如果开发者手里目前有类似小程序,可参考“头像加口罩”做法,逐渐去延伸头像周边功能,例: ①、头像加字:头像+数字、头像加V、头像加字、头像加圣诞帽、新年头像边框、头像加福、头像加明星等 ②、聊天背景图、壁纸:武汉加油、卡通、美女(不要漏点太多)、二次元、跑车、科技等 ③、趣味九宫格配图:类似朋友圈9张图,中间获取用户自己头像,周围8张图弄点能吸引用户的等 ④、文字秀:微信昵称下标、上标、个性昵称等 运营分析:如果参考以上4点做法,首先你的程序再疫情结束后,不至于直线下滑,最起码能留住一些用户(UI很重要) 个人建议:工具类的好处就是不需要去长时间盯着后台,建议有想法的开发者,可以入门5-10个左右工具类小程序(功能不要相同)。 推广方式:参考本文第四大板块内容 2、返利类 主流返利平台:淘宝、天猫、拼多多、京东、蘑菇街、唯品会、网易考拉,以下参考 [图片] 盈利方式:返利(主)+流量主(辅) [图片] 基础分析:每个人微信里都会有一个或多个微信群是给你们购物优惠券链接的,他们盈利方式主要是靠每个平台的返利,比如淘宝天猫的叫“阿里妈妈”、拼多多的叫“多多进宝”等 运营分析: ①、平台功能:提供所有优惠券、商品返利、代理入驻、提现(个人可做收款码、企业可对接微信支付到零钱) ②、招代理商、可以给代理商(兼职、宝妈)50%以上的返利 ③、除了商品优惠券之外,可以把返利分给一部分给到用户。首先,用户会花更少的钱买到商品;其次,用户买完东西还会赚点小钱,每个月可提现到微信零钱。这样用户会发生裂变,省钱+赚钱。 个人建议:开发者至少有一个类似的返利小程序,每个月只需运营一天,工作内容一是把用户的返利发给用户&代理商,二是自己去各大平台领取每个月的“工资” 推广方式:参考本文第四大板块内容 3、商城类(个人开发者可跳过) 商城类,本人运营的比较少,每天就10-20单左右,卖啥就不做广告了 盈利方式:差价 基础分析:如果自己手里有一些商品低价资源,可以做一个“综合服务商城类目”,然后去试着用广告主去推一下 运营分析: ①、平台功能:砍价、返利、拼团、回购、入驻、积分、抽奖、游戏营销 ②、广告主曝光&点击报价不要最低,也不要最高,理由就是最低的话,80%的钱会给你推到一些质量很差的微信用户,比如我。 ③、对接圈子,虽然圈子刚起步,不确定能不能做大,万一呢? 个人建议:企业一定要有一个自己的商城,哪怕没人买。这种东西怎么说呢,就好比一个企业站,虽然没什么用,但是得放那儿,万一客户要看呢? 推广方式:参考本文第四大板块内容 4、游戏类(非小游戏) 答题、成语、找茬等类似运营的比较多,可自行搜索,不要认为这是游戏,开发者就望而却步,在线教育类目是可以通过的,这个开发者很多都不知道。以下可参考: [图片] 盈利方式:流量主 基础分析:基本所有的模式都是闯关类型,这种类型的小程序,基本都是用户消磨时间用 运营分析:关卡尽量多,入门、初级、中级、高级,高级模式可以做类比循环,形成无限关卡模式,闯关奖励机制,签到机制等。这种类型的小程序比较方便运营,裂变起来也快。 个人建议:裂变模式一定要有,虽然微信会严格把控这方面功能,但是开发者可以做一些技巧,不要让用户强制或者主动去触发,这样微信对开发者还是很友好的。 推广方式:参考本文第四大板块内容 二、小程序开发 有实力的开发者,自己开发,云开发很快,会前端就可以了,没实力的去正规平台买源码,论坛源码也很多,有部分论坛还是嵌入了比特币勒索,自己做好防护。个人建议:开发者能开发尽量自己开发,后期迭代方便,不要像我一样,50多个小程序80%是买现成去运营的。反正各有各的好处,开发者可自行决定,运营者可选择直接购买源码直接上线运营,前提是自己看好功能是不是和自己要的一样。有些SAAS平台的开发者实力还是可以的,支持定制功能。此处不做广告,自行搜索或者询问朋友。 三、广告位位置及利润 开发者的每个页面广告位一定要分开!一定要分开!一定要分开!这样做的目的是为了分析每个广告位的利润,好去做调整,把收益最大化。 失败案例举例:小程序的主页、个人中心页用同一个banner广告位,这样做出来一点好处都没有,后台只能看到banner收益是多少,看不到是哪个页面收益。极端情况,收益全部再首页,个人中心页没有广告收益,这种情况开发者是不知道的,如果把广告位分开,这种情况可以去优化个人页面,或者主页面换成视频banner。广告位分析页面:流量主--数据统计--广告数据--广告指标明细--细分数据 [图片] [图片] 1、很多人表示,疫情期间流量主收入下滑。这个原因不是因为微信调整流量主收益,根本问题是自己的用户质量。举个例子,当你开通流量主之后,你的用户还是这1000个,假如你第一天收益为100,你很开心,1000用户就能赚100,你第二天就放弃推广了,这样的话,你的用户质量是会逐渐下滑,微信方完全可以认定为你这1000人都是自己的号,去刷广告费的。长此以往下去,你的流量主利润会无限趋向于0。举个栗子: [图片] 2、广告位位置一定要合理好看,但是不代表“流氓”,比如全明星代言的某游戏“元宝无限收一刀999”点哪儿哪充值。开发者需要注意的是小程序的质量,需要用户在每个页面停留的时长最起码30秒,这样一个完整的视频广告才能曝光完。 3、banner广告收益是按有效点击计算的。很多人好几千曝光,但是点击只有几个、十几个,这种情况需要不断去优化接入的场景/位置,提高用户点击意愿。个人技巧:banner广告位尽量不要太多,1-2个就可以。尽量多放几个视频广告位,这样曝光也有收益。格子广告没试过,用过都说不好~ 4、激励广告作为流量主最高收益是有一定道理的,用户为了获取某些奖励是必须观看完整的,所以给开发者建议:用户如果可以获得小程序内某些奖励,可以适当多放一些激励广告位。 5、所有的广告位都是根据用户年龄、爱好等参数去调取相应的广告,开发者不需要去考虑 6、广告收益个人认为:激励》视频》插屏》前贴》banner》格子(格子没试过,暂放倒数第一) 四、小程序推广 尽量做成年人主打的小程序,有些开发者觉得好玩儿,做一些儿童益智类的小程序,你是认为儿童有手机,还是认为家长愿意让孩子玩儿手机呢?这个很不解。没有鄙视的意思,也许是情怀吧~~毕竟我做小程序比较俗,就是为了赚钱。 主流推广方式:公众号引流、截流,由于涉及一些不合常规的内容,本文只说常规操作,剩下的自己领悟,或者可以联系我~ 首先小程序的名字至关重要,一个好的名字可以带来无限的流量,再加上裂变功能(邪恶的微笑)。起名字的时候可以用到的工具:搜索小程序-微信指数,查询关键字,尽量找稳定再1000万以上的搜索量,从关键字中摸索自己的小程序名字。这样用户搜索到你的小程序几率会很高~ 1、工具类核心玩儿法(适用于所有小程序推广):文章引流,截取关键字,火爆主题,比如2019年12月19日庆余年全集泄露、2020年疫情(不要发疫情数据内容,要发一些正能量的有内容文章去引流),我阅读过的文章最低的阅读量8000左右,最高的10万+,据说有好几百万的阅读量。如果你的文章写的好,结尾放一个小广告:为防止疫情蔓延,请给您的头像带上口罩~,啪,一个卡片小程序(或二维码),流量自己想~ 推广对象:18-30岁 2、返利类核心玩儿法: ①、可以参考工具类玩儿法 ②、各大微信群、QQ群,去推广,招代理等方式,或者去买一些基础流量,进行裂变,实际运营看下效果,好继续针对用户群体去推广,建立自己的群体系,群内发商品返利链接。微信好友没人?给你举个例子,我这篇文章发完,如果加个我的二维码,最起码能有100人加我,不是我文章写的有多好,是你永远不知道用户有什么样的目的和需求~ 推广对象:18-60岁 3、商城类核心玩儿法 ①、可参考返利类核心玩儿法,拥有自己的客户群体系,发一些自己的商品还是可以的,一定要带分销体系,你懂得~(最高3级,再高就是传销了) ②、广告主、目前效益个人感觉不明显,每次花1000块钱做广告,利润基本没有,和发广告的钱持平,而且用户留存也不是很高,可能是我的商品比较单一等各方面因素吧,不过赚流量还是不错的。 推广对象:18-30岁(以我的商城为例,还需看商城出售的内容) 4、游戏类核心玩儿法(非小游戏) ①、一个好的名字就够了。举例:精选商品橱窗(腾讯官方),微橱窗(我朋友的)。不得不说,这波流量很高,遗憾的是,他不是火爆的游戏类小程序~ [图片] ②、参考工具类玩儿法,文章引流截流 推广对象:18-40岁 五、小程序矩阵 矩阵一定要有,矩阵一定要有,矩阵一定要有,防截流,底配10个小程序。不是纯矩阵,是微信开发规定,每个小程序可以跳转10个小程序,开发者可以利用这个功能去添加自己的矩阵来获取更多的流量收益,保证自己的用户在自己的矩阵圈活动。 [图片] 写这篇文章主要是给大家传授经验,希望小白能学到点东西,入门后的朋友可领悟到更多运营方法,江湖之大,附月账单有缘再见 [图片]
2020-05-25 - 靠「社区团购」服务近100万家庭,周复购率达92%,这个卖生鲜的不一般
由 5 个团长起步,不到 2 年横扫闽南 1300 个小区,服务近 100 万家庭,有何高招? 2014 年,阿里铁军出身、跨境电商创业失败的老张,看到有分析文章称,「生鲜是电商最后一片蓝海」,一顿研究后,他动了心。背靠福建、台湾丰富的水果种类,老张将大本营放在厦门,一头闯入生鲜电商赛道。 回头来看,这次创业,也为他 4 年后成立的社区团购平台「优选·物美价廉」,提供了必不可少的行业经验、团队、供应链的原始积累。 2018 年 「优选·物美价廉」云店加小程序上线后 ,这位电商老兵仿佛开了挂: 由 5 个团长起步,不到 2 年横扫闽南 1300 个小区,服务近 100 万家庭; 客单价达到 120 元,是本地友商的 5-6 倍; 每周只卖 3 天,周复购率却能高达 92%,放眼同区域、主流生鲜平台,很难找到第 2 家。 让外界羡慕的还有优选的自我造血能力,即使巨头疯狂抢占市场,它依然有一定的盈利能力。 01 Together 社区团购,专注本地市场 2018 年 11 月,在电商平台卖了 4 年水果的老张,带着团队 Together 社区团购。 那时,社群电商正兴起,水果生鲜因为高频、低价,被贴上了「引流品」的标签,导致平台流量分散,流量越来越贵,毛利越来越低。而社区团购,这个刚开始就有近 30 亿规模的新赛道正以肉眼可见的速度增长,入局者众。 借助多年对水果生鲜的认知和供应链资源沉淀,老张觉得这个项目自己也可以做,「不过是换了一个地方来卖同样的东西。」 社区团购的起源地是湖南长沙,2018 年下半年,市场正热,那些拿到融资的平台正在二、三线城市疯狂扩张,福建不一定是它们扩张战略的首选。老张意识到,自己正好可以利用这一段时间差先起量、再沉淀,宜早不宜迟。 2018 年年底,出于数据安全、系统稳定性、以及节省开发成本考虑,老张选择了云店加。12 月,优选·物美价廉(以下简称优选)云店加小程序上线,老张开启了第一次社区团购的试水。 不和大平台正面「硬刚」,深耕福建区域 社区团购以生鲜食品为主,各个地区消费习惯差异明显,老张判断,自己的供应链优势更加明显。「比如湖南人喜欢吃辣的,我们福建人不太喜欢吃辣的。这些大平台不可能一盘货卖全国,他们还需要做本地特色的产品,或者符合当地市场的一些产品。它的供应链不一定会对我们造成很强大的冲击。」 还有个优势是在团队。大平台要想扩张到福建、厦门,可能往往只派一个团队,大约十几个人过来,无可避免的会出现「水土不服」以及「跨区域管理难」的问题。而这时,老张已经有一支成熟的本地运营团队,全员 Together 做社区团购,外来的小团队与之竞争,无异于鸡蛋碰石头。 不参与价格「厮杀」,专注品质和服务的提升 很多友商更青睐极致的价格,他们的用户群体也较为下沉,客单价往往低至 16-20 元。但老张没有走低价路线,而是更倾向于极致的产品,走「优选路线」。 优选面向人均 GDP 排名靠前的准一二线城市,核心客户群体为对品质有较高要求的 30-45 岁宝妈、家庭主妇,客单价达到 120 元,是大多数友商的 5-6 倍。「我们每期卖的东西,我们是要给客户挑最好的。然后通过供应链的把控做到好货不贵。」 老张对自己选择的新赛道持乐观态度。「生鲜团购虽然在电商领域,但它本质上还是做零售的生意。其实,每个城市的线下的零售消费体量是非常巨大的,拿厦门举例,它一个月线下零售的总额应该是 30~40 个亿。」 02 2 年横扫 1300 个小区,怎么做到的? 上线 1 年多,优选从 5 个小区开始起步,服务范围逐渐覆盖了 1300 个小区。 [图片] 为了找到第一批种子用户,最开始,老张和团队找来 5 个朋友,让他们做团长,在各自小区内建群推广。在他看来,「用户就是团长,有了团长才有流量,没有团长就没有人去承接。」现在,为了团长的快速扩张,优选采取了全城招募团长的方式,粉丝推荐一名新团长,新团长 2 个月交易额达到 5000 元以上,推荐人就可以拿到 500 元现金奖励。 不过,优选团长招募条件也很「优」:为了方便控制履约成本,其所在小区必须在 500 户以上才能申请成为团长,业主里有人脉的宝妈、社区店的店主优先,每个小区只招 1 名。 在老张看来,社区店有它独特的优势,「社区店有固定的点、有冰箱,商品送到小区、送到店里,它履约的完整性会更高。」在优选,社区店团长占到 20%以上。 优选通过云店加多网点及网点管理员的功能,将每个团长所在的小区设置为提货网点,1300 个小区,1300 个团长,也就有 1300 个提货点。团长不按单个产品拿佣金,而是拿提货点总业绩的 10 %。 [图片] △ 团长地推找顾客 在老张的眼里,这 1300 名团长都是优选一起创业的伙伴。「我们希望来一个客户服务好一个客户,来一个团长让他成长,做到少一点的流失率,让他们在这边能开心赚钱。」 为了实现这个目标,优选非常重视团长的深度运营。 ① 团长培训:优选每周组织新团长来总部学习,设有专门的赋能部门为团长提供日常运营帮助,提供线上操作培训、线下店面培训等。同时,为团长提供方便拓展客户、维护客户的工具,如公众号后台、社群工具等,提高他们日常的卖货效率。 ② 线下交流会:以 30-40 人为一个组织,把团长分成若干小团队,把管理下放,小团队每个月组织线下交流会,如新品试吃、优秀团长经验分享、基本内容培训等,共同成长。 ③ 明星团长树标杆:公众号上开设「明星」栏目,专访明星团长,从他们的视角分享卖货心得、成长故事。定期选举明星团长,增加团长个人荣誉感,打造标杆,给其它团长一个学习、奋斗的目标,也有利于营造一种归属感,团长与优选的粘性更强。 ④ 提供礼品、样品:节假日,为团长提供小礼物、贺卡、赠品等,方便团长拉近邻里关系。 03 卖车厘子赔了十几万, 他却说优选赚到了 为了降低库存成本,优选采取了「预售+团购」模式。固定每周日、周二、周四开团预售,次日配送到小区。据老张介绍,优选在厦门有一个占地 3000 多平方的仓库,每天接收来自基地直采的产品。 配送模式也很简单。线上开团后,根据预售产品的数量,优选从供应商那里定量采购,供应商在其规定的时间将货送到仓库,再由优选把这些商品分到每一条线路。为了提高配送效率,优选还外包了 100 多辆面包车,由这些面包车配送到各个小区。 [图片] 老张提到,减少库存还有一个比较有效的方式。「外地的供应商过来的时候,本身我们的仓库有货,我们会提前预测库存周期,然后隔几天再配送,我卖了多少,就再从外地让他发多少货过来。」 当然面对水果这类非标品,优选也踩过坑。去年 6 月,优选尝试销售乌兹别克斯坦车厘子,由于产业链不成熟,到货后不新鲜,老张立刻决定给 1000 多个客户退单,还额外赠送一盒 400 克的车厘子。虽然这回亏了 10 几万,几个月的利润没了,但老张却称自己是赚的,「一是优选赢得了好口碑和顾客信任,二是,团队对于供应链的把控会更加精益求精。」 做生鲜社区团购的第 2 年,老张对这个超级大赛道有了新的认识。「社区团购已经也跑出来了,也跑通了。今年进入这个领域可能有接近 500 个亿人民币,是 2 年前的十几倍,美团、滴滴等巨头也已经摩拳擦掌。借助社区团购的强社交属性,它仅仅可以卖商品,还有本地生活服务。」 不过,优选没有考虑要和这些「大佬」正面竞争。 在别人扩张的时候,老张选择深耕,在区域市场,把模式复制好、在每个城市先站稳脚跟。「我们每个城市每个月如果做个两三千万,如果发展到几十个城市,其实我们体量也不会小。不过,近期我们还是会在福建省好好打磨。」
2020-09-24 - 第三方平台的ip白名单必须是公网ip才行是吗?已配置好,但是。。。
公网ip已经配置到第三方平台的ip白名单,但是微信方收到的ip地址不是本公网ip,变了。变成了58.247.205.110:443这种,并返回:{"errcode":40090,"errmsg":"invalid request source (bad client ip), hints: [ req_id: vemCfz4ce-s2hKSA ]"}。是什么情况呢?
2019-06-25 - 滑块拖动排序 【恋爱小清单开发总结】
分享一下滑块拖动排序的案例,其实主要的关键点在于判断当前长按的坐标点跟滑块中心的位置关系,这次直接分享代码片段了。有兴趣的有小伙伴可以打开瞧一瞧。 https://developers.weixin.qq.com/s/qKcgIVmL7Qj7 也可以打开恋爱小清单小程序 -> 我-> 纪念日 页面效果图: [图片]
2021-09-09 - 小程序实现列表拖拽排序
小程序列表拖拽排序 [图片] wxml [代码]<view class='listbox'> <view class='list kelong' hidden='{{!showkelong}}' style='top:{{kelong.top}}px'> <view class='index'>?</view> <image src='{{kelong.xt}}' class='xt'></image> <view class='info'> <view class="name">{{kelong.name}}</view> <view class='sub-name'>{{kelong.subname}}</view> </view> <image src='/images/sl_36.png' class='more'></image> </view> <view class='list' wx:for="{{optionList}}" wx:key=""> <view class='index'>{{index+1}}</view> <image src='{{item.xt}}' class='xt'></image> <view class='info'> <view class="name">{{item.name}}</view> <view class='sub-name'>{{item.subname}}</view> </view> <image src='/images/sl_36.png' class='more'></image> <view class='moreiconpl' data-index='{{index}}' catchtouchstart='dragStart' catchtouchmove='dragMove' catchtouchend='dragEnd'></view> </view> </view> [代码] wxss [代码].map-list .list { position: relative; height: 120rpx; } .map-list .list::after { content: ''; width: 660rpx; height: 2rpx; background-color: #eee; position: absolute; right: 0; bottom: 0; } .map-list .list .xt { display: block; width: 95rpx; height: 77rpx; position: absolute; left: 93rpx; top: 20rpx; } .map-list .list .more { display: block; width: 48rpx; height: 38rpx; position: absolute; right: 30rpx; top: 40rpx; } .map-list .list .info { display: block; width: 380rpx; height: 80rpx; position: absolute; left: 220rpx; top: 20rpx; font-size: 30rpx; } .map-list .list .info .sub-name { font-size: 28rpx; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: #646567; } .map-list .list .index { color: #e4463b; font-size: 32rpx; font-weight: bold; position: absolute; left: 35rpx; top: 40rpx; } [代码] js [代码]data:{ kelong: { top: 0, xt: '', name: '', subname: '' }, replace: { xt: '', name: '', subname: '' }, }, dragStart: function(e) { var that = this var kelong = that.data.kelong var i = e.currentTarget.dataset.index kelong.xt = this.data.optionList[i].xt kelong.name = this.data.optionList[i].name kelong.subname = this.data.optionList[i].subname var query = wx.createSelectorQuery(); //选择id query.select('.listbox').boundingClientRect(function(rect) { // console.log(rect.top) kelong.top = e.changedTouches[0].clientY - rect.top - 30 that.setData({ kelong: kelong, showkelong: true }) }).exec(); }, dragMove: function(e) { var that = this var i = e.currentTarget.dataset.index var query = wx.createSelectorQuery(); var kelong = that.data.kelong var listnum = that.data.optionList.length var optionList = that.data.optionList query.select('.listbox').boundingClientRect(function(rect) { kelong.top = e.changedTouches[0].clientY - rect.top - 30 if(kelong.top < -60) { kelong.top = -60 } else if (kelong.top > rect.height) { kelong.top = rect.height - 60 } that.setData({ kelong: kelong, }) }).exec(); }, dragEnd: function(e) { var that = this var i = e.currentTarget.dataset.index var query = wx.createSelectorQuery(); var kelong = that.data.kelong var listnum = that.data.optionList.length var optionList = that.data.optionList query.select('.listbox').boundingClientRect(function (rect) { kelong.top = e.changedTouches[0].clientY - rect.top - 30 if(kelong.top<-20){ wx.showModal({ title: '删除提示', content: '确定要删除此条记录?', confirmColor:'#e4463b' }) } var target = parseInt(kelong.top / 60) var replace = that.data.replace if (target >= 0) { replace.xt = optionList[target].xt replace.name = optionList[target].name replace.subname = optionList[target].subname optionList[target].xt = optionList[i].xt optionList[target].name = optionList[i].name optionList[target].subname = optionList[i].subname optionList[i].xt = replace.xt optionList[i].name = replace.name optionList[i].subname = replace.subname } that.setData({ optionList: optionList, showkelong:false }) }).exec(); }, [代码]
2019-07-28 - 盘点商城小程序运营技巧
现在商城小程序很火,只是简单的开发一个商城小程序是远远不够的,最主要还是懂得怎么去运营商城小程序,今天就和大家分享一下商城小程序运营技巧有哪些,采用营销的手段去吸引消费者。 1.充分利用商城小程序入口 目前商城小程序已经有50多个入口,做商城小程序运营重点在于社交运营和内容运营 社交运营:不能只限于微信群、微信好友这几个推广渠道,需要多渠道的去做社交。 内容运营:内容上主要是微信公众号平台,利用好小程序关联公众号功能,一般1个小程序可以关联50个公众号,商场小程序需要充分利用好微信公众号,将粉丝进行转化,引导用户使用小程序,享受小程序服务。 2.如何获取精准用户 线上运营:①小程序入口②附近的小程序③合理布局关键词 线下运营:采用线下门店的方式做运营,在门店门口可以挂一个海报,海报里设置小程序码,引导用户通过小程序消费。 3.如何提高用户转化率 在运营过程中,转化率很关键,但你做了很久的推广,没有转化率,那就等于在做无用功。想要提高用户转化率,其实也很简单,主要从以下几个方面入手。 ①商城小程序首页很关键 首页很关键,用户进入小程序的时候,直接展示的就是首页,首页做的要足够吸引人,才能吸引用户。 ②充分利用好营销插件 除了高颜值的首页、丰富的商品介绍、合理的定价吸引用户之外,还可以通过营销插件来促使用户下单。 一般商城小程序功能有满减满送、优惠券、秒杀、积分、拼团、新人红包等,商家需要充分利用好这些功能。 以上就是商城小程序运营技巧,商家们都可以尝试哦!
2020-07-28 - 小程序第三方平台模式-应用配置(1)
我们公司做的垂直行业,所以公司开发了本行业通用小程序模板,商户只要把他的小程序授权给我们一键部署小程序。这样我们一套小程序模板可以提供给很多很多的商户用。他只需申请一个小程序即可。给我们公司开发这套小程序模板也有2年了,我觉的应该和大家多交流下,所以想先把我自己的经验分享出来供大家参考,和大家一起进步。 我们走的是微信开放平台---第三方平台开发模式,本次主题是分享如何做小程序第三方平台开发模式,本章分享的是如何配置好开放平台的应用,为接下来做开发小程序模板做铺垫。 应用配置 1.创建应用 [图片] 2.输入应用基本信息 [图片] 3.选择所需权限 [图片] 4.填写开发配置 [图片] [图片] 最后提交配置,图文上说明,是按自己理解写的,如果大家想知道更明确的官方说明可以看这个文档:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Before_Develop/third_party_platform_apply_material.html
2020-01-15 - 小程序服务商开发者,搭建一个mvp第三方平台必备的开发功能模块构成
我们在搭建小程序第三方平台过程发现官方提供的有很多接口,但不是每个接口都需要开发者用代码去实现。比如代码包管理的删除接口,就可以通过登录open.weixin.qq.com里,在列表看着更加详细的代码包版本介绍通过官方提供的界面就可以删除。即如果不是特殊需要,此接口可以不做开发。 那么我们搭建一个小程序服务商第三方平台需要哪些必要的功能模块呢? 必备功能模块概况 首先用一张图总概括下搭建一个mvp小程序服务商第三方平台必备的开发功能模块 [图片] 第三方平台权限集 先用一张图表明第三方平台账号在授权后可用的权限集的部分。 [图片] 只有平台先申请某一个权限,经过审核且全网发布后,旗下小程序才能在授权时选择某权限。有以下等 A>=B, B>=C;也就是第三方平台即使有每个微信提供的每个权限集,通过管理员授权后也不一定有全部权权限。原因: 1 单一授权被授权给其他第三方平台账号,或管理员授权时选择不把某一个权限授权给平台。比如开发和代码管理权限集 2 旗下小程序自身主体资质性质自带,不能拥有某个api权限。案例参考3 3 最终授权成功的权限集里,每个权限下都有多个api,所以在用某个api前需要看是由有权限。比如开发和代码管理权限集下,setwebviewdomain这个api不支持个人主体资质的appid调用 4 授权成功后,用token就可以用第三方平台提供的api代调用实现业务,或代调用小程序开发文档的api(标志参数需要access_token)里的接口,来实现产品开发。 授权及回调域名验证 ####### 说明 ####### 发起域名必须要在合法登记的域名名单内,且用户授权同意后的跳转url必须要在资料登记。体现在第三方平台创建时的以下两项配置中。 ① 授权发起页域名 ② 授权事件接收URL [图片] 授权回调URL源码 [代码] public void ProcessRequest(HttpContext context) { Request = context.Request; Response = context.Response; string signature = Request.QueryString["signature"]; string msg_signature = Request.QueryString["msg_signature"]; string timestamp = Request.QueryString["timestamp"]; string nonce = Request.QueryString["nonce"]; #region Response body // 微信并不会因为返回失败就再发送一次消息,相反如果不返回success, 微信会延迟推送 string ResMsg = "success"; if (Request.HttpMethod == "POST") { using (Stream stream = Request.InputStream) { Byte[] postBytes = new Byte[stream.Length]; stream.Read(postBytes, 0, (Int32)stream.Length); string postString = Encoding.UTF8.GetString(postBytes); if (postString.Contains("<AppId>")) { XElement xdoc = XElement.Parse(postString); CurAppID = xdoc.Element("AppId").Value.Trim(); LoadAppInfo(CurAppID); string postStringXmlSrc = string.Empty; Tencent.WXBizMsgCrypt wxcpt = new Tencent.WXBizMsgCrypt(CurAppToken, CurAppEncodingAESKey, CurAppID); int ret = wxcpt.DecryptMsg(msg_signature, timestamp, nonce, postString, ref postStringXmlSrc); if (ret == 0) { xdoc = XElement.Parse(postStringXmlSrc); if (xdoc != null) { string InfoType = xdoc.Element("InfoType").Value.Trim(); if (InfoType == "component_verify_ticket") { string componentVerifyTicket = xdoc.Element("ComponentVerifyTicket").Value.Trim(); WeixinDataHelper.UpdateComponentVerifyTicket(CurAppID, componentVerifyTicket); } else if (InfoType == "unauthorized") { string authorizedAppId = xdoc.Element("AuthorizerAppid").Value.Trim(); WeixinDataHelper.Unauthorized(CurAppID, authorizedAppId); } else { // 微信平台上填写的授权URL 目前就支持这两种 InfoType } } } } } } else if (Request.HttpMethod == "GET") { ResMsg = Request.QueryString["echostr"]; } ResponseEnd(ResMsg); #endregion } [代码] 各种票据有效性维护机制 1 授权后得到授权码(authorization_code ) [代码]需要用授权码去调用接口换取令牌,并保存。 [代码] 2 获取令牌和刷新令牌 [代码]用授权码获得令牌authorizer_access_token和刷新令牌authorizer_refresh_token。 需要保存令牌和刷新令牌。 [代码] 3 刷新令牌authorizer_access_token [代码]用刷新令牌定期去微信网关拉取令牌,维持令牌的有效性,保证后期代实现接口时令牌有效性。约1h左右的时间去刷新一次令牌。刷新令牌服务需要有重试机制,因为瞬时网络原因会返回失败,需要重试。 [代码] 消息与事件处理平台 1 第三方平台component_verify_ticke更新 [代码]平台审核通过后,每隔10分钟定时推送一次component_verify_ticket,开发者需要保存在数据库。再授权场景获取预授权码时需要用到这个有效的ticket。 [代码] 2 授权状态变更(成功,变更,取消) 3 代码审核通知消息 4 注意: [代码]① 这里的消息时加密的需要先解密。 ② 有开发者反馈说不知道返回信息时旗下哪个appid,这里补充下,appid是再请求头的request参数里直接返回的。 ③ 消息与通知解密部分代码 [代码] [代码] public void ProcessRequest(HttpContext context) { HttpRequest Request = context.Request; HttpResponse Response = context.Response; // 所属的已授权公众号的appid string AppID = Request.QueryString["AppID"]; string reqSignature = Request.QueryString["signature"]; string reqMsgSignature = Request.QueryString["msg_signature"]; string reqTimestamp = Request.QueryString["timestamp"]; string reqNonce = Request.QueryString["nonce"]; if (string.IsNullOrEmpty(AppID) || !WeixinHelper.ValidateWeixinInterface(reqSignature, WeixinResources.ComponentAppToken, reqTimestamp, reqNonce)) { ResponseEnd(Response, string.Empty,AppID); return; } if (AppID == WeixinResources.AutoTestAppID) { string _tmsg = new WeixinAutoTestHandler(Request, Response, WeixinResources.ComponentAppID).GetMsg(); ResponseEnd(Response, _tmsg,AppID); return; } #region Response body string ResMsg = string.Empty; if (Request.HttpMethod == "POST") { using (Stream stream = Request.InputStream) { Byte[] postBytes = new Byte[stream.Length]; stream.Read(postBytes, 0, (Int32)stream.Length); string postString = Encoding.UTF8.GetString(postBytes); string Msg = string.Empty; // 解密 Tencent.WXBizMsgCrypt wxcpt = new Tencent.WXBizMsgCrypt(WeixinResources.ComponentAppToken, WeixinResources.ComponentAppEncodingAESKey, WeixinResources.ComponentAppID); int ret = 0; ret = wxcpt.DecryptMsg(reqMsgSignature, reqTimestamp, reqNonce, postString, ref Msg); if (ret != 0) { ResponseEnd(Response, string.Empty,AppID); return; } // 生成响应消息 string resMsg = WeixinHelper.ReturnMessageAsThirdPlatform(AppID, Msg); // 加密消息 string EncryptMsg = string.Empty; ret = wxcpt.EncryptMsg(resMsg, reqTimestamp, reqNonce, ref EncryptMsg); if (ret != 0) { EncryptMsg = string.Empty; } ResMsg = EncryptMsg; } } else if (Request.HttpMethod == "GET") { ResMsg = Request.QueryString["echostr"]; } ResponseEnd(Response, ResMsg,AppID); #endregion } [代码] [代码]public static string ReturnMessageAsThirdPlatform(string AppID, string requestMsg) { string responseContent = string.Empty; XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(requestMsg); XmlNode MsgType = xmlDoc.SelectSingleNode("/xml/MsgType"); if (MsgType != null) { switch (MsgType.InnerText) { case "event": responseContent = EventHandleAsThirdPlatform(AppID, xmlDoc); //事件处理 break; case "text": case "image": case "voice": case "video": case "shortvideo": case "location": responseContent = TextHandleAsThirdPlatform(AppID, xmlDoc); //消息处理 break; default: break; } } return responseContent; } [代码] 代小程序域名设置机制 1 小程序服务器域名 为授权小程序设置服务器域名 requestdomain request合法域名 wsrequestdomain socket合法域名 uploaddomain uploadFile合法域名 downloaddomain downloadFile合法域名 2 小程序业务域名 为授权小程序提供业务域名 web-view的合法域名 第三方平台先登记后,在给旗下小程序授权 旗下小程序可以使用配置域名的子域名作为业务域名 代小程序代码包管理机制 1 提交代码包 详细关于代码包管理参考https://developers.weixin.qq.com/community/develop/article/doc/000622ad764e48a45419e25b151813 ①ext.json的配置项更改体现在代码包配置json数据里。ext_json字段对应ext.json里的字段,这里template_id关联第三方平台》小程序模板管理里的模板ID。 ② 那么要提交小程序的appid呢?还是通过ext_json字段来配置。ext_jsonjson字符串里的extAppid就是第三方平台账号旗下授权,本次要提交代码的小程序appid,通过上传代码时候的来指定extAppid就可以给一个小程序代提交代码。 [图片] [图片] 2 提交审核 3 收到审核通知审核通过后提交上线 4 查询最近一次提交审核进度 5 回退到上一个版本 第三方代码模板管理 1 创建小程序账号,授权给平台账号,并绑定到第三方平台账号的开发小程序列表 2 用开发小程序appid创建项目的开发代码包模板 3 用开发小程序appid提交代码到草稿箱 4从草稿箱添加到模板库 附加总结稿 [图片]
2020-11-05 - 利用第三方授权 更新并发布多套小程序
开发一个小程序管理平台,其他小程序管理员只需一次授权给第三方,第三方平台即可帮助他发布小程序,不同管理员的配置参数不同,其他功能都基本相同 开发步骤: 一、注册开放平台: 到微信开放平台注册账号 :https://open.weixin.qq.com/cgi-bin/readtemplate?t=regist/regist_tmpl&lang=zh_CN 二、申请第三方平台开发 申请第三方平台必须拥有一定的开发者资质,必须先通过开发者资质认证,才可以开始第三方平台开发,在开发平台账号管理中可进行资质认证 三、创建第三方平台 申请完成后,在开发平台的管理中心,点击第三方平台,在下方可看到创建第三方按钮 [图片] 点击创建第三方平台,进入下方页面,选择平台型服务商, [图片] 1.填写基本信息,与定制化服务商一致 2.选择权限,只能选择业务必须的权限集,否则无法通过审核,公众号或小程序也可能会拒绝授权给你。(权限集是公众号或小程序的权限集合,用于实现业务) 3.填写开发资料 4.开发资料 ①授权发起也域名(即用户打开我们自己的授权页域名) ②授权事件接收URL(我们接收所有授权小程序或公众号取消授权通知、授权成功通知、授权更新通知事件的url地址 , 包括接收微信平台推送的ticket) ③消息与事件接收URL (我们接收所有授权小程序或公众号的消息和事件推送,例如客服消息 微信就会推送到这个地址上) 这里要注意一点:该参数按规则填写(需包含/$APPID$,如www.abc.com/$APPID$/callback) 填写的地址需要包含/$APPID$ 我们后续可以用nginx 重写地址 把访问指向同一个地址就可以了 例如:填写的地址是 www.abc.com/msg/$APPID$/msgEventPath.php nginx重写地址: rewrite ^/msg/(.)/(.).php /msgEventPath.php last; ④其它按照提示填写就可以了,添加上白名单ip 然后提交审核就可以了,如果信息没有问题是马上就能审核成功的,然后再管理中心的第三方平台即可看到改第三方服务商,详情里面即有改第三方平台相关的配置信息 四、小程序管理员授权给第三方平台 只有小程序管理员授权给第三方,第三方才能为该小程序发布,更新部署代码。 授权开发步骤: 1.保存component_verify_ticket, 微信端会定时推送消息到配置好的授权事件接收URL(创建三方平台时填写的,可在该三方详情中查看) 上,我们需要保存这个component_verify_ticket和 不断更新,component_verify_ticket必须保持是微信端推送的最新一个 2.用component_verify_ticket去换取第三方平台的token(第三方平台指的就是我们自己在开发的平台)token是有有效期的,所以我们要保存它的过期时间,并将token做缓存,当token没过期时就不用再去换取,反之我们要利用最新的component_verify_ticket去重新获取token 3.换取预授权码pre_auth_code,pre_auth_code是用来换取微信端的授权二维码的 4.跳转到授权页面(两种方式),建议第二种,方便 用户授权的时候会先打开我们自己的一个页面 (例如 http://www.abc.com/authorization.php ),这个页面里需要做一个按钮或者用js去跳转到微信的授权页面 ①扫码授权 :跳转后得到授权码,注意这个页面只能用网页访问,小程序访问不了,因为不能将微信域名配置为业务域名用户扫码后 就可以授权给第三方平台了 https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=xxxx&pre_auth_code=xxxxx&redirect_uri=xxxx&auth_type=xxx。 ②点击移动端链接快速授权https://mp.weixin.qq.com/safe/bindcomponent?action=bindcomponent&auth_type=3&no_scan=1&component_appid=xxxx&pre_auth_code=xxxxx&redirect_uri=xxxx&auth_type=xxx&biz_appid=xxxx#wechat_redirect 请求参数(两种方式一样) component_appid 第三方平台方appid pre_auth_code 预授权码 redirect_uri 回调URI 必须和授权地址同一个域名 auth_type 要授权的帐号类型:1则商户点击链接后,手机端仅展示公众号、2表示仅展示小程序,3表示公众号和小程序都展示。如果为未指定,则默认小程序和公众号都展示。第三方平台开发者可以使用本字段来控制授权的帐号类型。 前四步总结(移动端快速授权流程): 用户自己获取授权连接: 需要后台配合,给出一个接口,请求该接口则直接返回最新的预授权码(pre_auth_code),拿到授权码之后,再通过拼接返回一个授权地址,跳转到改地址,即为授权页面下方图二 ,用户点击授权即可授权给第三方。用户点击授权后,授权页会自动跳转进入回调URI,并在URL参数中返回授权码和过期时间(redirect_url?auth_code=xxx&expires_in=600),我们可以通过 $GET[‘authcode’] 去获取授权用户的小程序或二维码 调用接口的accesstoken(有效期两小时) 并将其保存/更新,然后我们就可以获取授权用户小程序或公众号的信息 [图片] 5.使用授权码换取公众号或小程序的接口调用凭据和授权信息 接口调用请求说明 http请求方式: POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=xxxx(component_access_token在第二步可获取) POST请求参数示例: { “component_appid”:“appid_value” ,//第三方平台appid “authorization_code”: “auth_code_value”//授权code,会在授权成功时返回给第三方平台 } 请求成功后拿到 authorizer_access_token:授权方接口调用凭据(在授权的公众号或小程序具备API权限时,才有此返回值),也简称为令牌,后面调用小程序待开发的api中使用, authorizer_refresh_token:接口调用凭据刷新令牌(在授权的公众号具备API权限时,才有此返回值),刷新令牌主要用于第三方平台获取和刷新已授权用户的access_token,只会在授权时刻提供,请妥善保存。 一旦丢失,只能让用户重新授权,才能再次拿到新的刷新令牌 五、小程序模板开发 第三方平台帮助旗下已授权的小程序进行代码管理时,需先开发完成小程序模版,再将小程序模版部署到旗下小程序帐号中,具体流程如下: 第一步:绑定开发小程序 (1)第三方平台的开发人员需先到微信公众平台(mp.weixin.qq.com)申请一个普通的小程序并完善小程序的头像、昵称、简介、服务类目等信息。 (2)进入微信开放平台,在第三方平台详情中,将该小程序添加为开发小程序。 注意:绑定为开发小程序后,该小程序的在开发工具中上传,代码会直接上传到开放平台,不会上传到公众平台。 第二步:小程序模版的开发和上传 使用开发小程序的开发者微信号登录微信web开发者工具(IDE),开发者工具中按照正常的小程序开发流程进行代码开发和调试。开发完成后,在开发工具中点击上传。更新模板后需要更部署到旗下小程序之前必须上传到模板库。注意:上传时版本号要求不一样,一样的版本号会被默认为同一版本,判断为管理员没有更新 第三步:添加到小程序模版库,获得模版ID 从开发者工具中上传的代码,会先存在草稿箱中,每个开发小程序只保留最新一份上传记录。开发者可将草稿箱中的代码添加到小程序模版库中,小程序模版库中的模版不会被覆盖。最多可以有五十个代码模版,添加后可以获得模版ID(TemplateID) 拿到模板ID后,再加上之前获取到的authorizer_access_token(令牌),就能为授权过给该第三方平台的小程序部署代码了。 六、为旗下小程序进行代码管理 举个例子:为授权的小程序帐号上传小程序代码 1、为授权的小程序帐号上传小程序代码 请求方式: POST(请使用https协议) https://api.weixin.qq.com/wxa/commit?access_token=TOKEN POST数据示例 { “template_id”:0, “ext_json”:“JSON_STRING”, //ext_json需为string类型,请参考下面的格式 “user_version”:“V1.0”, “user_desc”:“test”, } 参数说明: access_token 请使用第三方平台获取到的该小程序授权的authorizer_access_token template_id 代码库中的代码模版ID ext_json 第三方自定义的配置 user_version 代码版本号,开发者可自定义(长度不要超过64个字符) user_desc 代码描述,开发者可自定义 通过此请求,第三方平台会自动将模板中的代码自动部署到授权给该第三方的小程序上 更多代码管理查看文档 https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1453779503&token=1a70ae891ca6e0339cf56bd1b3c322b0ec86eec9&lang= 持续更新中… 相关文章:https://developers.weixin.qq.com/community/develop/doc/0000ee097e0f00dcd55b8e40856800?jumpto=reply&parent_commentid=00004e9efb84388cd95ba8023514&commentid=000c0623e98068f0e85be2b97564
2020-12-09 - WeUI官方组件库:助力小程序高效设计与开发
提起 WeUI,相信大家都不陌生,WeUI 是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信内网页和微信小程序量身设计,令用户的使用感知更加统一。 不过,对于 WeUI 样式库,开发者就有疑问了。 [图片] [图片] 我们来看看 WeUI 组件库到底有什么可用的 UI 组件呢?WeUI 样式库有的各个元素,WeUI 组件库是基于 WeUI 样式库做了组件化处理,开发者可以便捷的使用,无需考虑组件层面的逻辑问题。 [图片] 有了心动的组件之后,大家肯定想知道 WeUI 组件库是怎么使用的。 [图片] 要使用 WeUI,首先要把 WeUI 引入我们的小程序项目,引入 WeUI 的方式有以下两种,使用其中一种即可~ 方法一:通过 useExtendedLib 扩展库 的方式引入,这种方式引入的组件将不会计入代码包大小。(推荐👍) 方法二:可以通过 npm方式下载构建,npm 包名为 weui-miniprogram。 与方法一不同,npm 引入的方式需要多操作一步,在 app.wxss 中引用 weui.wxss。 // app.wxss @import '/miniprogram_npm/weui-miniprogram/weui-wxss/dist/style/weui.wxss'; [图片] 引入之后,我们就要开始来使用了,WeUI 组件库是基于小程序自定义组件构建的,所以使用是以自定义组件的形式来使用。 下面通过几个例子来感受下 WeUI 组件库的使用。 由于是自定义组件的形式,所以使用组件都需要在页面配置中引入,像这样: // page.json { "usingComponents": { "mp-half-screen-dialog": "weui-miniprogram/half-screen-dialog/half-screen-dialog", "mp-searchbar": "weui-miniprogram/searchbar/searchbar" } } 引入组件之后,就可以直接在 wxml 中使用了,当然,为了让开发者接入更加简便,我们也加入了做了一些常见的实用性功能。 半屏弹窗 小程序提供了 wx.showModal、wx.showToast 供开发者进行页面交互,在开发过程中,可能需要自定义按钮相关的内容,所以 WeUI 提供了半屏弹窗让开发者可以有更多的自定义空间。 我们来看下代码,使用很简单,直接使用 mp-half-screen-dialog,配置相关属性即可。 // page.wxml <mp-half-screen-dialog bindbuttontap="buttontap" show="{{show}}" mask-closable="{{false}}" title="测试标题B" sub-title="测试标题B的副标题" desc="辅助描述内容,可根据实际需要安排" tips="辅助提示内容,可根据实际需要安排" buttons="{{buttons}}"> </mp-half-screen-dialog> // page.js data配置 buttons: [ { type: 'default', className: '', text: '辅助操作', value: 0 }, { type: 'primary', className: '', text: '主操作', value: 1 } ] 来看下半屏弹窗的效果~ u1s1,这交互体验真的爱了😍 [图片] Form 表单校验 Form 表单这里,除了基础的的功能之外,WeUI 组件库还提供了表单校验的能力,通过 rules 规则的配置(支持长度、手机号码、电子邮件、url 链接地址等),轻松解决表单校验问题。 [图片] 左滑删除 相比 Web 端,手机端的操作按钮更多的是通过⬅️左滑等来实现,考虑到左滑删除的普遍性,WeUI 组件库也是支持的。 在 mp-slideview 组件中设置 buttons属性即可。 [图片] 搜索组件 同样是基本功能的搜索,WeUI 组件库也封装了搜索组件,开发者只需配置搜索结果即可拥有搜索功能~ [图片] 除了这些组件之外,WeUI 组件库还提供了很多实用的组件,包括基础的 icon、loading,表单的 uploader、cell 等等。 [图片] 伴随客户端、小程序对 DarkMode 的支持,WeUI 组件库也同步适配 DarkMode 的模式,让 WeUI 组件库的使用同学可以快速适配 DarkMode。 在根结点(或组件的外层结点)增加属性 data-weui-theme="dark" ,即可把 WeUI 组件切换到 DarkMode 的表现,如: <view data-weui-theme="dark"> ... </view> [图片] 最后,如果想体验 WeUI 组件库的效果,欢迎点击下方小程序示例体验👏及接入使用,使用过程中如有建议或者疑问,欢迎到微信开放社区与我们交流。 [图片]
2020-05-21 - 小程序建设完成,商家下一步该干什么?
受平台多年压榨,很多小商家鼓起勇气把微信小程序这种性价比超高的自有渠道建搭建好。各种认证、资料上传、熟悉小程序业务流程。好不容易忙完了,而下一步该干什么? [图片] 其实这时你最应该做的就是把现有客流导入小程序。让客户下次从自有渠道小程序下单。慢慢改变客户在平台的下单习惯。这是重中之重! 下面通过3个小步骤应用在酒店行业,轻松把客户一网打尽: 1、客户进店扫码上网,自动关注小程序。 因为上网是住客的刚需,扫码就能一键连接WiFi。比传统要输入密码的方式方便多了! 除了连接WiFi,在连接过程中还有WiFi红包可领。方便锁定客户下次再来。 [图片] 2、入住后店内点餐、购买日用品或特产,自动关注小程序。 入住后,每个房间都贴有点餐码。除了点餐,还可以购买其它日用品、饮料、特产等。 [图片] 这也是住客的刚需。不用外出,所需物品直接送到房间。 3、离店时再次提醒领红包,自动关注小程序。 办理离店手续时,前台可以再次提醒领WiFi红包。方便下次使用。 [图片] 最后再送出一个让住客无法拒绝的大招: 这个大招是在客户有部分余款未结算的时候使用,让住客把钱充进小程序。付款马上9折!剩下的钱还保留在小程序。下次可用,又一个锁定客户的理由。
2019-07-17 - 小程序订单暴涨300%,只因一张小小的消费券!
[图片] 疫情发生以来,全国有28个省份、170多个地市统筹地方政府和社会资金,累计发放消费券达190多亿元。这些消费券取得明显效果,实现了聚焦人气、增信心、振消费的目标。 [图片] 消费券到底有多火? 为提振消费信心、促进消费潜力释放,半个月前,武汉市政府打造2020武汉市民消费节,通过微信支付开始分期发放总额达5亿元的消费券和18亿元商家券。 发放闸口一开,第一批3000万元在小程序“武汉消费券”上被火爆围观参与: 6秒:超市消费券被抢空,抢券次数超过75万次; 38秒:商场消费券也被抢光,超38万次抢券; 1分钟:开抢前25万人涌入小程序蹲守;开抢后1分钟,超43万张消费券发出; 5分钟:最多的餐饮消费券也被抢完,抢券次数超98万次; 6分钟:文体旅游消费券最快6分钟被领完,抢券超27万次; 1小时内:共136万人访问小程序,点击抢券达239万次。 乘云小程序&微信消费券三大应用场景 1、专属优惠码→电子消费券→小程序商城 [图片] 2、微信支付有礼→电子消费券→小程序商城 [图片] 3、朋友圈广告→电子消费券→小程序商城 [图片] 商家通过朋友圈广告直接派发优惠券,消费者及时可以拿到福利、让认知和交易一体,消费者所见及所得。曝光、领取、转化、到店核销数据直观可衡量。
2020-05-25 - 声明:社区用户 小肥羊 盗版个人开发小程序 智汇答题Plus,以此来获取个人利益
今天有个微信好友聊天才知道,社区用户 "小肥羊" 盗版以所谓的云开发版本的小程序获取用户的关注,私下转售个人自主开发的答题小程序 智汇答题Plus,早在半年前已经发现了"小肥羊"盗版智汇答题Plus,并且已经跟他沟通商量过,但是今天发现仍然是这样无所顾忌的盗版,而"小肥羊"所谓的云开发版本也是跟智汇答题Plus有90%的相似,对于这种盗版别人源码,并且拿着别人源码来转售的行为感到鄙视,完全侵犯了版权。 [图片] [图片] [图片] [图片] [图片] [图片] [图片]
2020-05-23 - 云开发怎么在原生微信支付回调函数传参?
我现在用云开发原生微信支付,但是需要在云函数回调里面写操作数据库逻辑,清空购物车、添加订单等。请问我这边需要怎么写这个回调函数?怎么传参?
2020-05-16 - 云支付,pay_cb/functionName的正确打开姿势
纯代码:第一步就秒过: // pay_cb 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init() const db = cloud.database() const _ = db.command exports.main = async (event, context) => { //回调信息备案 await db.collection('cloudPay').doc(event.outTradeNo).set({ data:event }) //其他业务逻辑 return { errcode:0, errmsg:'SUCCESS' } } 更多内容: [图片]
2020-10-25 - 微信支付商户平台-产品中心-怎么找不到“我的授权产品”?
从开发者工具 1.02.2005111 起,云控制台支持云开发微信支付商户绑定,在绑定完成后可在云开发中原生接入微信支付, 我已在云控制台 -> 设置 -> 全局设置中开通了申请,如图所示 [图片] 根据官方文档https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/wechatpay.html说明我复制其中一段说明出来:2)jsapi 和 api 退款权限,需要前往微信支付商户平台我的授权产品中进行确认授权 完成授权后即可调用微信支付相关接口能力。 出现的问题是: 我的商户号是1550179951,我已申请开通退款api权限,系统提示我须到商户平台进行授权,但登录商户平台之后,找不到我的授权产品这一项,以下是我的商户平台截图: [图片] 我需要使用 “云开发-微信支付-退款API权限”,但是找不到在哪里授权,请帮忙解决,感谢!!!
2020-05-16 - 免费ICP备案攻略。不花1分钱拥有一台云服务器并顺利ICP备案。
写在前面: 大家不要将ICP证和ICP备案搞混了。 ICP证指的是【电信增值业务经营许可证】,这个资质需要企业主体至少100万注金,去工信部办理,比较难办理;社交-交友需要ICP证。 而ICP备案,【非经营性互联网信息服务备案核准】仅仅是指企业主体的域名备案,可以简单的按以下步骤免费办理成功,其他社交类目如社区、论坛、笔记等,只需要ICP备案即可。 1、在腾讯云注册一个账号并认证企业主体(不吹不黑,开发小程序当然首选腾讯云,好用)。http://www.qcloud.com/ 如果你是个人主体,就不要往下看了,没必要折腾了。 2、找到腾讯云免费活动页:https://cloud.tencent.com/act/free?from=10107 3、选择一款云服务器,180天免费试用。 云服务器申请成功后,它的使命就完成了,没用了,让它自生自灭吧。 在整个备案过程中,也不需要部署网站(域名都没有备案,哪来的网站?)。 [图片] 云服务器180天到期后,可以自己决定是否续费,每个月也才99元,促销期甚至更低,完全可以接受吧。 备案成功后,该服务器就没什么作用了,让它180天后自然欠费销毁得了。 服务器销毁后会有什么影响?答:没有任何影响。 但是。。。。。 你备案的域名最后还得指向一个网站,因为腾讯云会应工信部的要求定期检查网站是否合规,所以你还是要建一个简单的网站,(备案期间,可以暂时不管网站的事,等将来需要的时候再管理)。 至于有多简单,答,多简单都行。此时你可以在七牛、腾讯云、阿里云租点免费的对象存储空间,做个简单的网站。 4、在进行ICP备案之前,你需要在腾讯云注册你的域名地址,如果你已有域名,但不在腾讯云,建议先将要域名过户到腾讯云的账号上。 5、进入控制台,开始ICP备案,这个流程就不介绍了,因为完全一看就懂。而且现在使用备案小程序后,不需要幕布或现场拍照了,极其方便,大家跟着流程走就一点问题没有,有人脸识别和在线拍一段小视频。另外,大家可以随便作,随便填,填错或者填得不合适也不用怕,会有专门的备案客服打电话告诉你哪哪要改,还会告诉你应该怎么填才更容易通过工信部的审核,客服的态度好得发指。 仅说一点其中的几个小坑: a、人脸识别的时候,白色背景、白色背景、白色背景,笔者在人脸识别的时候,满世界找白墙,结果还被打回来重拍了3次。 b、网站用途一律写:公司官网,好通过工信部审核。 6、腾讯云提交资料到工信部审核。这是一个漫长的让人无语的等待,20-30天。笔者最近两次都是20天才过审;不要幻想会有可能提前完成审核,这是政府部门在审核,提前完成说明某政府人员的工作安排有问题,会犯错误的。 7、备案成功后,会有短信通知你,但是,你需要去工信部网站查询结果,并将结果切屏拷贝下来,因为小程序类目审核需要上传这张图片。http://beian.miit.gov.cn/publish/query/indexFirst.action [图片] 把上面这张图片保存好,小程序类目审核的时候需要上传。收到通知后,如果在这里查不到结果,也别急,据说需要24小时。 8、接下来是小程序上线审核。 因为ICP备案的小程序内容肯定涉及到社交,最后小程序上线时还要提交到工信部审核,还需要7天左右的时间,加上前面ICP备案的时间,加起来怎么也得30-40天。大家估计时间,别影响小程序上线。这7天也是政府部门在审核,不要幻想会提前。 9、计算一下时间: 腾讯云注册账号和认证:1-3天; 域名备案:腾讯云环节:1-3天; 域名备案:工信部环节:20-30天; 小程序添加服务类目:社交类目审核:1-3天; 小程序上线审核:腾讯环节:1-2天; 小程序上线审核:工信部环节:7+天; 总天数:30-40天; 10、节省时间的一些建议: 在开发小程序之前,就开始备案工作,小程序可以同时开发,相互不影响; 在开发完成之前一、两星期之内,先发布一版小程序,别管功能是不是完整,能通过审核就行,这样会有7天的等待类目审核的时间,这个时间里,小程序可以照常开发,不影响进度; 只要是社交类,基本需要有文字和图片安全检查功能,别忘了加上,别到时审核通过不了。 11、结束。 [图片]
2021-01-19 - 解决textarea的placeholder层级穿透的问题
先说下遇到的问题:之前做过的一个项目改版碰到的病例上传页面发布按钮上一版本是在底部放置的,这一版改为了顶部固定。由于上传页面顶部有两个textarea输入框所以问题就产出了。之前使用的button和view标签布的局页面上滑的时候会被textarea的placeholder穿透。不知道官方什么时候可以解决textarea这个问题。 具体问题如下图: [图片] [图片] 解决方法来源,通过社区各位大佬的回复最终得出以下结论: 1.思路: 通过原生组件去覆盖textarea元素即可 textarea不是原生组件吗 view和button干不过 那我们也找原生组件不就好了吗。所以我就看了下能使用的也就剩cover-view标签了。所以第一种解决方法就是使用原生组件去替换之前的view和button组件。 [图片] 2.思路:通过滑动页面去判断textarea元素的显示和隐藏 使用onPageScroll函数来获取页面的滚动距离 当滚动距离等于textarea元素的top减去固定到顶部的盒子的距离的时候就让textarea元素隐藏或者把textarea的placeholder设置为空也是可以解决穿透问题的。 补充: 3.思路:只用view、input、text等字段去替换textarea元素,来避免textarea的pplaceholder穿透问题 如果有弹窗或者组件被穿透了,可是做一个判断,当弹窗出现的时候设置一个hide字段,并根据字段判断textarea的显示隐藏,并且当textarea隐藏后,用一个样式相同input,或者view组件代替显示原来的textarea,当弹窗消失后,再将textarea显示,将view或者input隐藏掉(注意,给textarea设置一个bindinput的方式将输入的文字显示在view或者input里面,这样基本看不出内部组件类型的变化) [图片] 感谢大佬提供的第三种方案,感觉很不错。 如果还有其他的。欢迎留言。
2020-09-07 - 小程序如何将网络请求的网址数据转成JSON?
[图片] 这是我发送请求获取到的轮播图数据信息 我无法调用JSON.parse()来转为json格式 直接报错 如何操作能将此数据转为json呢
2020-03-18 - 小程序用Promise简单封装wx.request的POST请求
const inter = ‘主域名’; var obj={}; const post=(url,obj)=>{ return new Promise( (resolve,reject)=>{ wx.showLoading({ title: ‘加载中’,mask:true}) wx.request({ url: inter+url, method:‘post’, data:obj, header: { ‘content-type’: ‘application/x-www-form-urlencoded’, }, success: function (res) { wx.hideLoading(); if (res.statusCode != 200) { reject({ error: ‘服务器忙,请稍后重试’, code: 500 }); return; } resolve(res.data); }, fail: function (res) { reject({ error: ‘网络错误’, code: 0 }); }, complete: function (res) { wx.hideLoading(); } }) }).catch(err=>{ reject(err) console.log(‘请求失败了’,err) }) } module.exports=post;
2019-11-16 - we.request请求封装
第一步在utils下面创建一个http.js, let sUrl = "https://www.baidu.com"; function getData (url, data, nb) { wxrequest({ url: sUrl + url, data: data, method: 'post', header: { // "ContentType": "json", //get请求时候"ContentType": "applicationwwwformurlencoded", //POST请求的时候这样写 'token': wx.getStorageSync('token') }, success: functionres) { returntypeof nb == "function" && nb(res.data) }, fail: functionres) { returntypeof nb == "function" && nb(res.data) } }) } module.exports = { req: getData } 第二步在app.js引入 let http = require ('utils/http.js') //封装请求res: { req: http.req //这里配置我们需要的方法 }, 然后在你需要的页面直接应用 let data = { }//data是你要传的参数 app.res.req('app-web/userproject/list', data, (res) => { }) 代码片段https://developers.weixin.qq.com/s/Pk6Pffmq7Gey
2020-01-09 - 请问云开发TCB 的WEB端需要自己的域名和服务器吗?
小程序用上了云开发,不知道公众号网页和PC网站支不支持云开发 云开发可以支持h5网页,但是不知道是完全不需要自己的服务器和域名,还是只是在自己的网站中引入云开发SDK 在官方提交工单,咨询了很多次,一直没回答清楚。 所以咨询下大家,不是小程序云开发,是WEB端云开发,谢谢大家
2019-12-09 - 开放平台开发者资质认证
最先注册的是公众平台的公众号,然后已经300块钱认证好了。 之后注册的公众平台的小程序,直接复用的公众号的认证。 之后注册支付商户,企业微信。都复用的同一个认证。 最后注册开放平台,需要绑定之前注册的公众平台账号。但是提示必须先认证才可以绑定。 我想请问一下,开放平台能不能复用公众平台的认证? 公众平台账号appid:wxcc6e0b3628b38c27 开放平台账号:wtto00@163.com 之前也有人提这个问题,好像没有答案:查看之前别人提的该问题
2019-03-19 - 已有认证微信公众号,现微信开放平台认证又需要一个公众号,是否可以将之前的直接关联?
已有认证微信公众号,现微信开放平台认证又需要一个公众号,是否可以将之前的直接关联?
2019-10-22 - 开发者福音!面向Web场景的云开发服务正式开放!
【导语】 继支持小程序开发之后,云开发也支持Web使用啦!开发者们可以使用云开发提供的云端能力,直接开发网站应用,如PC端网页、公众号中的网页等。由此开发者可以在网站应用中借助云函数实现业务逻辑,通过与云数据库、对象存储以及CDN等产品联动,即可实现产品快速上线和迭代。快来体验吧! [图片] 首先,还是得跟各位小伙伴们介绍下功能强大的云开发到底是何方神圣! 云开发(Tencent CloudBase)是腾讯云为移动开发者提供的一站式后端云服务,无需搭建服务器,就可以轻松使用云端能力。基于云开发,开发者无需再关心服务器和底层设施运维,只需专注于代码逻辑和业务本身。 目前云开发支持 小程序开发 和 Web开发。 云开发三大基础功能 目前云开发提供了三大基础能力:云数据库、云存储及云函数。这三种能力可以构成较完整的后端开发能力。 云函数 在云端运行的代码,开发者只需编写自身业务逻辑代码。 云端运行:无需采购、部署、运维传统硬件,节约人力及成本。 高效开发:每个函数单独运行、部署,上传代码后即可自动部署,提升了独立开发和迭代的速度。 弹性伸缩:根据请求量实现毫秒级实时弹性伸缩,函数未执行不产生任何费用。 云数据库 文档型数据库包含多个近似于 JSON 数组的集合,数组中的对象是记录,格式为 JSON 文档。 简单易用:数据库 API 包含增删改查,操作简单;支持触发器,满足特殊场景。 权限控制:通过 API 在客户端内和云函数内进行数据操作,安全可靠。 云存储 在网站应用前端直接上传或下载云端文件,在云开发控制台可视化管理。 快速上传:提供文件存储空间,可在客户端和云函数端通过 API 使用存储。 权限管理:基于用户身份的安全控制,带权限管理的云端下载。 CDN 加速:存储内的文件,天然 CDN 加速,提升用户体验。 云开发优势 开发更简单 使用云函数时,用户只需编写最重要的核心业务代码,不再需要关心负载均衡、自动伸缩、网关等组件,也无需构建应用的后端服务,极大地降低了后端开发搭建的复杂性。无需手动配置,云函数即可根据请求量自动横向扩缩,自动安排合理的计算资源满足业务需求。 开发更高效快捷 云函数不要求特定框架或依赖,开发者可以专注于核心代码的开发。同时开发人员可以组成多个小团队,单个模块的开发无需了解其他团队的代码细节。 独立开发和迭代的速度变得前所未有的快,帮助用户把握住产品上线的黄金时间。开发可以使用云函数编写一些目的单一、逻辑独立的业务模块,因而可以完全复用已经成熟的第三方代码实现。 运维更省事 每个云函数都是单独运行、单独部署、单独伸缩,用户上传代码后即可自动部署,免除单体式应用部署升级难的问题。 用户不再需要对 OS 入侵、登录风险、文件系统安全、网络安全和端口监听做复杂的配置和管理,一切交由平台处理,平台通过定制化的容器保证每个用户的隔离性。 成本更低 按请求数和资源端运行收费,极大节约时间和成本,同时还提供一定量端免费额度。 如何开通云开发的Web端服务? 云开发Web端服务在腾讯云官网登录使用。目前,云开发Web 端仅支持通过微信公众号登录方式进行服务端访问授权,故开发者若要开发网站应用,需要先在微信公众平台/微信开放平台进行应用注册及开通。 ↓详细开通流程可参考下图↓ [图片] 进入云开发主页 [图片] 选择“微信公众号”登录 [图片] 点击授权 [图片] 进入云开发控制台,新建环境 [图片] 填写环境信息,开通成功! Quick Start——使用云开发Web SDK 快速开发网站应用 初始化云开发能力 [图片] 登录授权 云开发目前在 Web 端支持通过微信登录方式进行服务端访问授权,故开发者若要开发网站应用,需要先在微信公众平台/微信开放平台进行应用注册及开通。 除了微信公众平台和微信开放平台登录方式以外,云开发将陆续支持邮箱、QQ、匿名登录、自定义登录等多种登录方式,敬请期待。 [图片] 应用关联 要使用云开发提供的云函数、云存储和云数据库的功能,您需要先将云开发添加到您的网站应用中,即应用关联。将云开发提供的 Web 端 SDK 关联到您的网站应用,才能使客户端通过 SDK 操作后台资源。复制下方的代码片段,粘贴到您的 HTML 代码底部(要在其他 script 标记之前),即可将云开发添加至您的网站应用。 [图片] 域名授权 为了增加安全性,云开发的身份验证服务需要先对网站应用来源进行验证,也即是域名授权。只有已授权域名下的页面才可以使用 SDK 发起对云开发服务的访问。将网站完整域名添加到安全验证到白名单中,即完成授权。 [图片] 操作数据库 快速完成对数据库基础的 CRUD 及服务端时间(serverDate)、正则查找(regExp)和地理位置(geo)等特殊数据结构的使用。 [图片] 操作文件存储 快速完成文件上传、获取下载链接或者删除文件等操作。 [图片] 操作云函数 快速实现对云函数的安全调用。 [图片] 关于新开放Web端云开发服务有任何问题欢迎在下方留言,我们会尽快回复~ 如果你有关于使用云开发CloudBase相关的技术故事/技术实战经验想要跟大家分享,欢迎留言联系我们哦~比心! [图片]
2019-09-19 - 公众号网页和云开发可以同时用么?
公众号网页和云开发可以同时用么? 前端用的是公众号 只能在开发者工具里打开 后台用的云开发 也是只能在开发者工具打开 怎么同时调试啊
2019-09-18 - 小程序如何发送永久模板消息
大家都很清楚模板消息的使命即将结束,具体可查看下文 10月12日,微信在小程序模板消息能力方面公布了一项重大调整。原有的模板消息将升级为「订阅消息」,支持一次性和长期性订阅消息。而模板消息将于2020年1月10日下线。 查看该通知详情请移步 https://developers.weixin.qq.com/community/develop/doc/00008a8a7d8310b6bf4975b635a401 [图片] 但是模板消息存在的一次表单只能发一次模板消息的限制,在订阅消息方案中并没有解决,那么有没有一种可永久推送消息的实现方案呢,便是本文接下来要讲的内容 关于模板消息转成订阅消息的各种坑,在下面帖子中很详细 https://developers.weixin.qq.com/community/develop/doc/0006088c2940586de249dffbb5b400 在聊具体方案之前,先看看群里讨论的几张截图 [图片] [图片] [图片] [图片] 该方案在微信记账本中实现,微信记账本是腾讯官方推出的一个用于同步账单的小程序 [图片] 可永久推送模板消息方案是: 通过小程序和订阅号绑定,消息通过公众号的模板消息推送发出,只要用户同意推送,我们就可以无限制的发送模板消息。 [图片] [图片] 关于公众号发送模板消息的文档如下所示 https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html#5 该方案的弊端就是,在使用小程序的过程中,必须同时关注绑定的订阅号,在小程序用户原大于同主体订阅号的情况下,该方案慎用。 但是为了每天能收到推送我们未尝不可以多做一步,关注下订阅号。
2019-11-28 - 小程序如何跳转公众号?
请问小程序怎么跳转到相应的公众号上面,不是公众号文章,是公众号,可以实现吗?
2020-03-23 - 已经注册完的小程序如何复用已经认证的公众号资质?
已经注册完的小程序如何复用已经认证的公众号资质,小程序已经认证,和公众号分开的认证,想问下怎么共用一个主体,这样省事省钱。 麻烦官方回答一下,谢谢啦。
2019-08-29 - 微信公众号和微信小程序相关联
微信公众号和微信小程序相关联, 制作了微信小程序商城, 那这个 微信支付功能 应该从微信公众号上开通 还是应该从微信小程序上开通?
2019-07-13 - 云数据库如何删除数组类型字段的指定索引内容?
集合abc数据结构如下: { user: [ {name: "Zhang", age: 18}, {name: "Wang", age: 18}, {name: "Li", age: 18}, {name: "Zhao", age: 18}, ] } 我想把{name: "Li", age: 18}删除掉,请问这个怎么操作? 官方文档只提供了shift、pop等从数组头或尾删除的方法,我没找到删除指定索引的方法,求教高手。
2019-09-11 - 小程序可以WIFI连接打印机吗?什么时候能支持这个API T_T!!!
- WIFI连接打印机打印小票 - WIFI连接打印机打印,并支持发送打印命令给打印打出小票。
2018-10-15 - 小程序WIFI接口和WebSocket创建局域网数据通讯
#小程序跟设备建立局域网通讯,进行数据传输; 1、让手机连接设备热点,需要用到微信 WI-FI 模块; 2、websocket支持局域网,需要用到 wx.websocket; 3、实践演变后,开放如下接口; 4、调试开启服务文件 ./utils/server.js (请自行使用node安装); [图片] 代码引入片段 : https://developers.weixin.qq.com/s/nZa78Zmu7k7R 代码引入片段更新: https://developers.weixin.qq.com/s/RiQ0POm47Xcx 更新接口规范,兼容,app.json 新增代码 “permission”: { “scope.userLocation”: { “desc”: “你的位置信息将用于小程序位置接口的效果展示” } } [图片] 引入代码调试,请查看 server.js 更新 npm install http
2019-11-12 - 微信连wifi设备类型详细说明
设备类型详细说明 [图片] 微信连WiFi能力详细说明 [图片]
2019-12-19 - 微信小程序怎么使用WiFi链接打印机打印订单?
现在有个打印订单的需求,请问小程序怎么使用WiFi链接打印机打印订单?
2020-02-06 - 打印机打印标签
项目地址:https://github.com/dyl169/WAPP_printer.git有需要的可以参考一下 [图片] [代码]1.在需要调用蓝牙的页面中 初始化是传入设备的主服务UUID base.Ble.initBle(['FFF0', '0A71']); 2.在onLoad中执行监听函数(记得在onUnload中 remove掉事件监听) //初始化所有监听 initListen: function() { //监听蓝牙可用状态 base.Event.listen(base.EventModel.EVENT_BLESTATE, function(data) { if (data.code) { console.log('蓝牙已打开') base.blestate = true; } else { console.log('蓝牙已关闭') base.blestate = false; } }) //监听扫描设备 base.Event.listen(base.EventModel.EVENT_SCAN, function(data) { if (data.stop) { console.log('停止扫描'); that.setData({ bleSearchIng: false, }) } else { var device = data.datainfo[0]; console.log('扫描到的设备 : ' + JSON.stringify(device)); that.data.deviceArray.push(device); that.setData({ deviceArray: that.data.deviceArray, showDeviceDialog: true, }) } }) //监听蓝牙连接状态 base.Event.listen(base.EventModel.EVENT_CONNSTATE, function(data) { console.log('连接状态变化'); if (data.code == true) { console.log('index 连接成功') for (var i = 0; i < that.data.deviceArray.length; i++) { if (that.data.deviceArray[i].deviceId === data.datainfo) { that.data.connDevice = that.data.deviceArray[i]; } } that.setData({ connSucced: true, connDevice: that.data.connDevice, }) } else { that.setData({ connSucced: false, }) } }) //监听蓝牙值变化 base.Event.listen(base.EventModel.EVENT_RECEIVE, function(result) { if (result.code == true && pageState == 'onLoad' || pageState == 'onShow') { var data = result.datainfo; console.log('main 页面接受到消息:' + data); } }) }, 3.如果打印机无法打印 请查看打印机说明书 知否支持TSPL或ESC指令指令 (本demo使用的佳博答应机) 自行将PrintUtil.js中的打印指令修改为对应的打印指令即可[代码]
2019-08-19 - 蓝牙打印机注意事项
打印机编程协议分了多种如:TSC/ESC/CPCL指令 小票打印注意事项 手机蓝牙打开 小程序必须获取地理位置的权限,android 6.0以上需授权地理位置权限 打印机必须是低功耗蓝牙协议,必须支持ESC指令(标签和面单打印可以使用TSC指令) 部分打印机可能需要设置打印模式(票据模式,标签模式,面单模式),如果使用的不是票据模式,使用ESC指令是不会打印的 打印中文需要使用打印机支持的编码解码 [图片]
2018-10-10 - 小程序蓝牙连接热敏式打印机,打印小票,能否设置字体大小,行间距?
小程序蓝牙连接热敏式打印机,打印小票,能否设置自定义格式,比如字体,字体大小,行间距
2020-02-17 - 借助云开发10行代码生成小程序码,可以微信扫码快速进入指定小程序页
最近老有同学问我,如何生成小程序二维码,让用户扫码后能快速进入指定页面。经过一番研究,发现用云开发的云调用来实现特别方便,基本上10行代码就可以快速的生成指定页面的二维码。这样我们在做一些线下业务时就能让用户方便快速的进入到指定页面了,比如我们的点餐小程序,我们可以在二维码里绑定桌号,这样用户用微信扫码二维码,就可以快速的进入点餐页面了,并且可以识别到用户当前所在的桌号。 注意事项 我们生成的小程序码指定的页面,必须是你小程序已经发布,如果小程序还没有发布,获取小程序码绑定的页面不存在,扫码后就会出现以下错误 [图片] 所以要想使用这个功能,必须要先发布你的小程序。接下来我们就来讲下具体的实现。 一,先看官方文档 我其实说过很多遍的,官方文档永远是最好的老师。因为官方文档永远是最标准的。所以我们实现这个功能,同样要先去看下官方文档。小程序码官方文旦 [图片] 看上图的官方文档,我们可以看出,有三种方式可以生成小程序码。 A,createQRCode:生成二维码,可接受 path 参数较长,生成个数受限,数量限制 B,get:生成小程序码,可接受 path 参数较长,生成个数受限,数量限制见 C,getUnlimited:生成小程序码,可接受页面参数较短,生成个数不受限 官方提示: 接口只能生成已发布的小程序的二维码 接口 A 加上接口 C,总共生成的码数量限制为 100,000,请谨慎调用。 接口 B 调用分钟频率受限(5000次/分钟),如需大量小程序码,建议预生成 所以我们一定要结合自己的业务场景来使用不同的方法来生成小程序码或者二维码。我们这里以点餐桌号为例。因为我们一个餐厅也没有多少桌,所以我们就用接口B来实现页面和桌号的绑定。 [图片] 看官方文档,我们可以看到,我们主要使用的还是path这个参数,这个参数用来设置页面和参数。具体代码如下图。 [图片] 代码很简单,在第11行,我们给path参数设置了小程序页面和参数name。这样我们用微信扫码后,就会打开对应的页面,并且可以拿到name参数, [图片] 其实到这里我们就成功的生成小程序码了,并且可以用微信直接扫码进入到指定页面,并携带指定参数了。 接下来呢,我就把代码改造成一个简单的工具,这样大家拿到源码后就可以直接使用这个工具快速的生成小程序码了。 创建云函数 关于云开发,云函数的创建和使用,我讲过很多遍了,还不知道的同学,可以去翻看下我之前的文章,或者看下我录的云开发视频:5小时零基础入门小程序云开发 下面我们就创建一个云函数,并编写云函数,如下图 [图片] 细心的同学应该可以看到,我这里传了两个参数进来。event.path就是用来指定我们二维码绑定的页面和携带的参数,event.name就是用来给我们的二维码图片命名的。 原理:我们这里生成小程序码的原理就是使用云开发的云调用,获取二维码,然后把二维码存到云存储,这样我们就可以拿到我们需要的二维码了 再来看下调用的方法。 [图片] 再来看下我们生成的小程序码 [图片] 可以看到我们生成的1号桌对应的二维码,这样顾客用微信扫描一号餐桌的二维码时,就可以直接进入对应的页面,并且把桌号带进去了。 [图片] 这样你只需要改造下我index.js里的path和name就可以生成你想要的小程序二维码了。 [图片] 然后再到云存储里获取对应的二维码, [图片] 下载后打印出来,贴到对应的桌子上,就可以供用户扫码点餐了。还有一点要记住奥,一定是你小程序发布后,才可以使用这个功能奥。 今天就到这里把,祝大家元旦快乐,2020年学有所成,想获取更多小程序相关的知识,请持续关注。
2020-01-01 - 分享扫码打开小程序操作实战
这是我们工作过程中的经验总结,现在分享给大家 说明:生成能打开小程序的二维码分为两种:1. 服务端调用接口生成;2. 前端生成二维码 一、服务端调用接口生成 https://api.weixin.qq.com/wxa/getwxacode?access_token=ACCESS_TOKEN 适用于需要的码数量较少的业务场景 生成小程序码,可接受 path 参数较长。 永久有效,可生成的码数量限制为 100,000 https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN 适用于需要的码数量极多的业务场景。 可接受页面参数较短,生成个数不受限。 永久有效,数量暂无限制 调用分钟频率受限(5000次/分钟) ,如需大量小程序码,预生成 https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=ACCESS_TOKEN 适用于需要的码数量较少的业务场景。 生成二维码,可接受 path 参数较长。 生成个数受限,可生成的码数量限制为 100,000 在小程序中接受参数方式:page onLoad函数中接受 [代码]Page({ onLoad(query) { // 需要使用 decodeURIComponent 才能获取到生成二维码时传入的 scene const scene = decodeURIComponent(query.scene) } }) [代码] 二、前端生成二维码 操作步骤: 1. 在小程序后台(mp.weixin.qq.com)配置链接 https://mp.weixin.qq.com/wxopen/devprofile?action=get_profile&token=1318106189&lang=zh_CN [图片] 2. 点击添加: [图片] 3. 添加完成后点击上线 4. 在JS中用第三方库将拼接好的URL(比如: https://xxxxx/homeindex?orderId=123)生成二维码 小程序获取二维码中携带的参数: app.js的 onshow/onLoad函数中获取参数,options.query.q是生成二维码的完成URL,后截取URL获取相应参数 [代码]if (!!options.query && !!options.query.q) { let url = decodeURIComponent(options.query.q) if (url.indexOf('orderId') > -1) { let index = url.slice(url.indexOf('orderId')) if (index.indexOf('&') > -1) { let orderObj = index.slice(0, index.indexOf('&')) this.globalData.orderId = orderObj.split('=')[1] } else { this.globalData.orderId = index.split('=')[1] } } } [代码]
2019-08-16 - 微信支付接口支付成功后返回requestPayment:fail cancel
- 当前 Bug 的表现 支付成功跳转到支付完成界面后,直接按home回到手机桌面,再进入打开微信进入小程序,支付接口返回requestPayment:fail cancel
2019-01-07 - 干货:如何借助小程序云开发实现小程序支付功能(含源码)
正文共:5081 字 13 图 预计阅读时间:13 分钟 我们在做小程序支付相关的开发时总会遇到这些难题 1.小程序调用微信支付时必须要有自己的服务器 2.有自己的备案域名 3.有自己的后台开发 这就导致我们做小程序支付时的成本很大 本节就来教大家如何使用小程序云开发实现小程序支付功能的开发,不用搭建自己的服务器,不用有自己的备案域名,只需要简简单单的使用小程序云开发。 老规矩先看效果图: [图片] 本节知识点 1.云开发的部署和使用 2.支付相关的云函数开发 3.商品列表 4.订单列表 5.微信支付与支付成功回调 [图片] 支付成功给用户发送推送消息的功能会在后面讲解 下面就来教大家如何借助云开发使用小程序支付功能 支付所需要用到的配置信息 1.小程序appid 2.云开发环境id 3.微信商户号 4.商户密匙 一、准备工作 1.已经申请小程序,获取小程序 AppID 和 Secret 在小程序管理后台中——【设置】 →【开发设置】 可以获取微信小程序 AppID 和 Secret。 [图片] 2.微信支付商户号,获取商户号和商户密钥在微信支付商户管理平台中——【账户中心】→【商户信息】 可以获取微信支付商户号。 [图片] 在【账户中心】 ‒> 【API安全】 可以设置商户密钥。 [图片] 这里特殊说明下——个人小程序是没有办法使用微信支付的,所以如果想使用微信支付功能必须是非个人账号(当然个人可以办个体户工商执照来注册非个人小程序账号) 3.微信开发者 IDE https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html 4.开通小程序云开发功能 https://edu.csdn.net/course/play/9604/204526 二、商品列表的实现 效果图如下 由于本节重点是支付的实现所以这里只简单贴出关键代码 [图片] wxml布局如下: [代码]<view class="container"> <view class="good-item" wx:for="{{goods}}" wx:key="*this" ontap="getDetail" data-goodid="{{item._id}}"> <view class="good-image"> <image src="{{pic}}"></image> </view> <view class="good-detail"> <view class="title">商品: {{item.name}}</view> <view class="content">价格: {{item.price / 100}} 元 </view> <button class="button" type="primary" bindtap="makeOrder" data-goodid="{{item._id}}" >下单</button> </view> </view></view> [代码] 我们所需要做的就是借助云开发获取云数据库里的商品信息然后展示到商品列表,关于云开发获取商品列表并展示本节不做讲解(感兴趣的同学可以翻看作者历史博客,有写过的) [图片] 三、支付云函数的创建 首先看下我们支付云函数都包含那些内容 [图片] 简单先讲解下每个的用处 config下的index.js是做支付配置用的,主要配置支付相关的账号信息 lib是用的第三方的支付库,这里不做讲解 重点讲解的是云函数入口 index.js 下面就来教大家如何去配置 1.配置config下的index.js, 这一步所需要做的就是把小程序appid、云开发环境ID、商户id、商户密匙填进去。 [图片] 2.配置入口云函数 [图片] 详细代码如下 代码里注释很清楚了这里不再做单独讲解: [代码]const cloud = require('wx-server-sdk') cloud.init()const app = require('tcb-admin-node');const pay = require('./lib/pay');const { mpAppId, KEY } = require('./config/index');const { WXPayConstants, WXPayUtil } = require('wx-js-utils'); const Res= require('./lib/res'); const ip = require('ip');/** * * @param {obj} event * @param {string} event.type 功能类型 * @param {} userInfo.openId 用户的openid */exports.main = async function(event, context) { const { type, data, userInfo } = event; onst wxContext = cloud.getWXContext() const openid = userInfo.openId; app.init(); const db = app.database (); const goodCollection = db.collection('goods'); const orderCollection = db.collection('order');// 订单文档的status 0 未支付 1 已支付 2 已关闭 switch (type) { // [在此处放置 unifiedorder 的相关代码] case 'unifiedorder': { // 查询该商品 ID 是否存在于数据库中,并将数据提取出来 const goodId = data.goodId let goods = await goodCollection.doc(goodId).get(); if (!goods.data.length) { return new Res ({ code: 1, message: '找不到商品' }); } // 在云函数中提取数据,包括名称、价格才更合理安全, // 因为从端里传过来的商品数据都是不可靠的 let good = goods.data[0]; // 拼凑微信支付统一下单的参数 const curTime = Date.now(); const tradeNo =`${goodId}-${curTime}`; const body = good.name; const spbill_create_ip = ip.address() || '127.0.0.1'; // 云函数暂不支付 http 触发器,因此这里回调 notify_url 可以先随便填。 const notify_url = 'http://www.qq.com'; // '127.0.0.1'; const total_fee = good.price; const time_stamp = '' + Math.ceil(Date.now() / 1000); const out_trade_no = `${tradeNo}`; const sign_type = WXPayConstants.SIGN_TYPE_MD5; let orderParam = { body, spill_create_ip, notify_url, out_trade_no, total_fee, openid, trade_type: 'JSAPI', timeStamp: time_stamp, }; // 调用 wx-js-utils 中的统一下单方法 const { return_code, ...restData } = await pay.unifiedOrder(orderParam); let order_id = null; if (return_code === 'SUCCESS' && restData.result_code === 'SUCCESS') { const { prepay_id, nonce_str } = restData; // 微信小程序支付要单独进地签名,并返回给小程序端 const sign = WXPayUtil.generateSignature ({ appId: mpAppId, nonceStr: nonce_str, package: `prepay_id=${prepay_id}`, signType: 'MD5', timeStamp: time_stamp }, KEY); let orderData = { out_trade_no, time_stamp, nonce_str, sign, sign_type, body, total_fee, prepay_id, sign, status: 0, // 订单文档的status 0 未支付 1 已支付 2 已关闭 _openid: openid, }; let order = await orderCollection.add(orderData); order_id = order.id; } return new Res({ code: return_code === 'SUCCESS' ? 0 : 1, data: { out_trade_no, time_stamp, order_id, ...restData } }); } // [在此处放置 payorder 的相关代码] case 'payorder': { // 从端里出来相关的订单相信 const { out_trade_no, prepay_id, body, total_fee } = data; // 到微信支付侧查询是否存在该订单,并查询订单状态,看看是否已经支付成功了。 const { return_code, ...restData } = await pay.orderQuery({ out_trade_no }); // 若订单存在并支付成功,则开始处理支付 if (restData.trade_state === 'SUCCESS') { let result = await orderCollection .where({ out_trade_no }) .update({ status: 1, trade_state: restData.trade_state, trade_state_desc: restData.trade_state_desc }); let curDate = new Date(); let time = `${curDate.getFullYear()}-${curDate.getMonth() + 1}-${curDate.getDate()} ${curDate.getHours()}:${curDate.getMinutes()}:${curDate.getSeconds()}`; } return new Res({ code: return_code === 'SUCCESS' ? 0 : 1, data: restData }); } case 'orderquery': { const { transaction_id, out_trade_no } = data; // 查询订单 const { data: dbData } = await orderCollection .where({ out_trade_no }).get(); const { return_code, ...restData } = await pay.orderQuery({ transaction_id, out_trade_no }); return new Res({ code: return_code === 'SUCCESS' ? 0 : 1, data: { ...restData, ...dbData[0] } }); } case 'closeorder': { // 关闭订单 const { out_trade_no } = data; const { return_code, ...restData } = await pay.closeOrder({ out_trade_no }); if (return_code === 'SUCCESS' && restData.result_code === 'SUCCESS') { await orderCollection .where({ out_trade_no }) .update({ status: 2, trade_state: 'CLOSED', trade_state_desc: '订单已关闭' }); } return new Res({ code: return_code === 'SUCCESS' ? 0 : 1, data: restData }); } } } [代码] 其实我们支付的关键功能都在上面这些代码里面了 [图片] 再来看下支付的相关流程截图 [图片] 上图就涉及到了我们的订单列表、支付状态、支付成功后的回调 今天就先讲到这里后面会继续给大家讲解支付的其他功能——比如支付成功后的消息推送也是可以借助云开发实现的 [图片] 如果你有关于使用云开发CloudBase相关的技术故事/技术实战经验想要跟大家分享,欢迎留言联系我们哦~比心!
2019-09-19 - 云开发支付的代码,有需要的进。
真机测试已通过。你照抄就行,保证可通过。 最新完美版本可供参考: https://developers.weixin.qq.com/community/develop/article/doc/0004c4a50a03107eaa79f03cc56c13 小程序端: wx.cloud.callFunction({ name: 'getPay' , data: { total_fee: parseFloat(0.01).toFixed(2) * 100, attach: 'anything', body: 'whatever' } }) .then( res => { wx.requestPayment({ appId: res.result.appid, timeStamp: res.result.timeStamp, nonceStr: res.result.nonce_str, package: 'prepay_id=' + res.result.prepay_id, signType: 'MD5', paySign: res.result.paySign, success: res => { console.log(res) } }) }) 云函数:getPay getPay目录下共两个文件: 1、index.js 2、package.json index.js代码如下: const key = "YOURKEY1234YOURKEY1234YOURKEY123"//这是商户的key,不是小程序的密钥,32位。 const mch_id = "1413090000" //你的商户号 //将以上的两个参数换成你的,然后以下可以不用改一个字照抄 const rp = require('request-promise') const crypto = require('crypto') function paysign({ ...args }) { let sa = [] for (let k in args) sa.push( k + '=' + args[k]) sa.push( 'key=' + key) return crypto.createHash('md5').update(sa.join('&'), 'utf8').digest('hex').toUpperCase() } exports.main = async (event, context) => { const appid = event.userInfo.appId const openid = event.userInfo.openId const attach = event.attach const body = event.body const total_fee = event.total_fee const notify_url = "https://whatever.com/notify" const spbill_create_ip = "118.89.40.200" const nonce_str = Math.random().toString(36).substr(2, 15) const timeStamp = parseInt(Date.now() / 1000) + '' const out_trade_no = "otn" + nonce_str + timeStamp let formData = "<xml>" formData += "<appid>" + appid + "</appid>" formData += "<attach>" + attach + "</attach>" formData += "<body>" + body + "</body>" formData += "<mch_id>" + mch_id + "</mch_id>" formData += "<nonce_str>" + nonce_str + "</nonce_str>" formData += "<notify_url>" + notify_url + "</notify_url>" formData += "<openid>" + openid + "</openid>" formData += "<out_trade_no>" + out_trade_no + "</out_trade_no>" formData += "<spbill_create_ip>" + spbill_create_ip + "</spbill_create_ip>" formData += "<total_fee>" + total_fee + "</total_fee>" formData += "<trade_type>JSAPI</trade_type>" formData += "<sign>" + paysign({ appid, attach, body, mch_id, nonce_str, notify_url, openid, out_trade_no, spbill_create_ip, total_fee, trade_type: 'JSAPI' }) + "</sign>" formData += "</xml>" let res = await rp({ url: "https://api.mch.weixin.qq.com/pay/unifiedorder", method: 'POST',body: formData}) let xml = res.toString("utf-8") if (xml.indexOf('prepay_id')<0) return xml let prepay_id = xml.split("<prepay_id>")[1].split("</prepay_id>")[0].split('[')[2].split(']')[0] let paySign = paysign({ appId: appid, nonceStr: nonce_str, package: ('prepay_id=' + prepay_id), signType: 'MD5', timeStamp: timeStamp }) return { appid, nonce_str, timeStamp, prepay_id, paySign } } package.json 代码如下: { "name": "getPay", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "youself", "license": "ISC", "dependencies": { "crypto": "^1.0.1", "request-promise": "^4.2.2" } } 最后选择:上传和部署:云端安装依赖。
2019-12-14 - 传统原生支付用云开发实现(非云支付)
本文的代码已过时,请勿照抄。建议改用云支付。 本文的代码被论坛自动过滤了所有XML的标签,所以照抄是会出错的。需要代码的话,看以前的老版本: https://developers.weixin.qq.com/community/develop/doc/000620ec5acb482103b7bf41d51804?jumpto=comment&commentid=000ea67d7b4da8d6c47acd1e05b8 代码前提:只需要替换两个与自己相关的参数key和mch_id 1、小程序开通微信支付成功,去公众平台(https://mp.weixin.qq.com/); 成功后可以知道自己的mch_id,即商户号。 2、去这里:商户平台(https://pay.weixin.qq.com/),获取key = API密钥,如果是退款的话,还需要下载API证书。 [图片] 以下代码仅包含统一下单,以及小程序端拉起支付的代码。 小程序端: testWxCloudPay: function () { wx.cloud.callFunction({ name: 'getPay', // data: {body:"body",attach:"attach",total_fee:1}, // 可传入相关参数。 success: res => { console.log(res.result) if (!res.result.appId) return wx.requestPayment({ ...res.result, success: res => { console.log(res) } }) } }) }, 云函数getPay: const key = "ABC...XYZ" //换成你的商户key,32位 const mch_id = "1413092000" //换成你的商户号 //以下全部照抄即可 const cloud = require('wx-server-sdk') const rp = require('request-promise') const crypto = require('crypto') cloud.init() function getSign(args) { let sa = [] for (let k in args) sa.push(k + '=' + args[k]) sa.push('key=' + key) return crypto.createHash('md5').update(sa.join('&'), 'utf8').digest('hex').toUpperCase() } function getXml(args) { let sa = [] for (let k in args) sa.push('<' + k + '>' + args[k] + '') sa.push('' + getSign(args) + '') return '' + sa.join('') + '' } exports.main = async(event, context) => { const wxContext = cloud.getWXContext() const appId = appid = wxContext.APPID const openid = wxContext.OPENID const attach = 'attach' const body = 'body' const total_fee = 1 const notify_url = "https://mysite.com/notify" const spbill_create_ip = "118.89.40.200" const nonceStr = nonce_str = Math.random().toString(36).substr(2, 15) const timeStamp = parseInt(Date.now() / 1000) + '' const out_trade_no = "otn" + nonce_str + timeStamp const trade_type = "JSAPI" const xmlArgs = { appid, openid, attach, body, mch_id, nonce_str, notify_url, out_trade_no, spbill_create_ip, total_fee, trade_type } let xml = (await rp({ url: "https://api.mch.weixin.qq.com/pay/unifiedorder", method: 'POST', body: getXml(xmlArgs) })).toString("utf-8") if (xml.indexOf('prepay_id') < 0) return xml let prepay_id = xml.split("")[0] let payArgs = { appId, nonceStr, package: ('prepay_id=' + prepay_id), signType: 'MD5', timeStamp } return { ...payArgs, paySign: getSign(payArgs) } } packge.json: { "name": "getPay", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "zfe", "license": "ISC", "dependencies": { "wx-server-sdk": "latest", "crypto": "^1.0.1", "request-promise": "^4.2.2" } } 附:完整代码片段:如果你觉得在这上面花的时间超过一天了,就去下载代码片段吧。 [图片]
2020-10-20 - 订阅消息在promise内无法调用
安卓可以,开发者工具和ios客户端显示errMsgrequestSubscribeMessage:fail can only be invoked by user TAP gesture.
2020-01-02 - 如何实现一个自定义导航栏
自定义导航栏在刚出的时候已经有很多实现方案了,但是还有大哥在问,那这里再贴下代码及原理: 首先在App.js的 onLaunch中获取当前手机机型头部状态栏的高度,单位为px,存在内存中,操作如下: [代码]onLaunch() { wx.getSystemInfo({ success: (res) => { this.globalData.statusBarHeight = res.statusBarHeight this.globalData.titleBarHeight = wx.getMenuButtonBoundingClientRect().bottom + wx.getMenuButtonBoundingClientRect().top - (res.statusBarHeight * 2) }, failure() { this.globalData.statusBarHeight = 0 this.globalData.titleBarHeight = 0 } }) } [代码] 然后需要在目录下新建个components文件夹,里面存放此次需要演示的文件 navigateTitle WXML 文件如下: [代码]<view class="navigate-container"> <view style="height:{{statusBarHeight}}px"></view> <view class="navigate-bar" style="height:{{titleBarHeight}}px"> <view class="navigate-icon"> <navigator class="navigator-back" open-type="navigateBack" wx:if="{{!isShowHome}}" /> <navigator class="navigator-home" open-type="switchTab" url="/pages/index/index" wx:else /> </view> <view class="navigate-title">{{title}}</view> <view class="navigate-icon"></view> </view> </view> <view class="navigate-line" style="height: {{statusBarHeight + titleBarHeight}}px; width: 100%;"></view> [代码] WXSS文件如下: [代码].navigate-container { position: fixed; top: 0; width: 100%; z-index: 9999; background: #FFF; } .navigate-bar { width: 100%; display: flex; justify-content: space-around; } .navigate-icon { width: 100rpx; height: 100rpx; display: flex; justify-content: space-around; } .navigate-title { width: 550rpx; text-align: center; line-height: 100rpx; font-size: 34rpx; color: #3c3c3c; font-weight: bold; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } /*箭头部分*/ .navigator-back { width: 36rpx; height: 36rpx; align-self: center; } .navigator-back:after { content: ''; display: block; width: 22rpx; height: 22rpx; border-right: 4rpx solid #000; border-top: 4rpx solid #000; transform: rotate(225deg); } .navigator-home { width: 56rpx; height: 56rpx; background: url(https://qiniu-image.qtshe.com/20190301home.png) no-repeat center center; background-size: 100% 100%; align-self: center; } [代码] JS如下: [代码]var app = getApp() Component({ data: { statusBarHeight: '', titleBarHeight: '', isShowHome: false }, properties: { //属性值可以在组件使用时指定 title: { type: String, value: '青团公益' } }, pageLifetimes: { // 组件所在页面的生命周期函数 show() { let pageContext = getCurrentPages() if (pageContext.length > 1) { this.setData({ isShowHome: false }) } else { this.setData({ isShowHome: true }) } } }, attached() { this.setData({ statusBarHeight: app.globalData.statusBarHeight, titleBarHeight: app.globalData.titleBarHeight }) }, methods: {} }) [代码] JSON如下: [代码]{ "component": true } [代码] 如何引用? 需要引用的页面JSON里配置: [代码]"navigationStyle": "custom", "usingComponents": { "navigate-title": "/pages/components/navigateTitle/index" } [代码] WXML [代码]<navigate-title title="青团社" /> [代码] 按上面步骤操作即可实现一个自定义的导航栏。 如何实现通栏的效果默认透明以及滚动更换title为白色背景,如下图所示: [图片] [图片] [图片] [图片] 最后代码片段如下: https://developers.weixin.qq.com/s/wi6Pglmv7s8P。 以下为收集到的社区老哥们的分享: @Yunior: 小程序顶部自定义导航组件实现原理及坑分享 @志军: 微信小程序自定义导航栏组件(完美适配所有手机),可自定义实现任何你想要的功能 @✨o0o有脾气的酸奶💤 [有点炫]自定义navigate+分包+自定义tabbar @安晓苏 分享一个自适应的自定义导航栏组件
2020-03-10 - 一眼告诉你什么是订阅消息了,看完就懂订阅消息。
消息通知有两种: 一、A的动作后,发消息给A自己,这种容易解决,不多说明; 二、A动作后,发消息给B(比如管理员、店家、楼主),如何保证B收到消息?这种是本方案要解决的问题。 一张图片一眼告诉你什么是订阅消息,产品经理的设计UI居然让人一眼就知道订阅消息是什么玩意。 [图片] 用户 B (管理员、商家、组长、楼主)在知道订阅数不足后,打开小程序来续订阅数,否则没法收到订阅消息。 [图片] 补充一: 关于勾选按钮,请注意话述是:“总是保持以上选择,不再询问”,而不是:“总是同意接收订阅消息”,不要幻想就成了永久性订阅消息; 相当于你打电话订外卖,对店家说“老样子”,店家只会马上送一次外卖,而不是会以后每天自动给你送外卖了。 勾选和不勾选的区别是什么呢? 区别仅仅是:不勾选时,必须点击订阅10次,弹窗10次;勾选后,仍然必须点击订阅10次,但是不弹窗。无论如何“订阅”这个点击n次的动作少不了。 补充二: 一旦勾选后,就不可逆了,没有任何办法恢复或取消勾选了,除非你小程序MP后台换一次消息模板号(删除模板,重新添加一次)。 补充三: 关于如何保存订阅数。 保存在数据库中,笔者用的是云开发,数据库表user结构如下: { _id:'openid1', nickName:'老张', msg:{ "tempId1":5, "tempId2":7, } } 补充四: 关于如何获取订阅数。两种方式: 一、wx.requestSubscribeMessage的回调success里获取; 二、消息推送机制获取;https://developers.weixin.qq.com/miniprogram/dev/framework/server-ability/message-push.html
2022-09-21 - 【订阅消息】请问大家,买家下单完成如何给商家发送订阅消息?
首先我只有一个小程序,没有公众号,假如A买了东西,下单完成想发送订阅消息给商家B订单信息,请问怎么做啊?
2020-03-13 - 订阅消息如果选择选择‘总是保持以上选择,"不再询问"后的设置问题
目前是选择‘总是保持以上选择,"不再询问"后,可以在设置中开启或拒绝接收,但不会再次拉起授权弹窗
2019-10-18 - 关于小程序 订阅消息-长期 订阅
https://developers.weixin.qq.com/miniprogram/product/material/ 请问 我的服务类目是 出行与交通-加油/充电桩,不可以申请长期订阅的模板么? 请问怎样才可以添加长期订阅的模板
2020-02-15 - 小程序订阅消息开发指南
2019年10月12日微信开放了小程序订阅消息的功能。按官方的说法,目前的模板消息在实现小程序服务闭环上存在缺陷: 1. 部分开发者在用户无预期或未进行服务的情况下发送与用户无关的消息,对用户产生了骚扰;2. 模板消息需在用户访问小程序后的 7 天内下发,不能满足部分业务的时间要求模板消息确实存在上述的硬伤,不利于小程序的用户留存和用户体验。为了解决这些问题,微信官方推出了用户订阅消息功能。我在微慕专业版上加了订阅消息的功能,并验证了这个功能。这个功能是否能都达到官方的预期,这个我感觉不那么乐观。这里我先说我的感受:目前的订阅消息还不完善,后续还有很大的优化空间。 目前,官方只开放了“一次性订阅消息”,尚未开放“长期性订阅消息”,因此我只尝试了“一次性订阅消息”。 一次性订阅消息:用于解决用户使用小程序后,后续服务环节的通知问题。用户自主订阅后,开发者可不限时间地下发一条对应的服务消息;每条消息可单独订阅或退订。 订阅消息推送位置:服务通知 订阅消息下发条件:用户自主订阅 订阅消息卡片跳转能力:点击查看详情可跳转至该小程序的页面 以下我简单说明订阅消息的开发过程和使用体验。 一.订阅消息的开发1.获取订阅消息的模板ID 在微信小程序的管理后台,在左侧“功能”菜单,选择“订阅消息”,然后点击“添加” [图片] 然后选择你需要的消息模板,并配置关键词。 [图片] 配置完成后,如下图所示。 [图片] 值得关注的是,在配置好的模板详情页面里的“详细内容”很重要,这个就是开发订阅消息时需要遵循的消息格式,这个格式和模板消息有细微的差别 根据微慕小程序的需要,我选用了“新的评论提醒”和“内容更新提醒”这两个消息模版。前者用于提醒发表话题或文章的作者,有新的话题或文章评论,增强作者与读者之间的交流互动;后者是提醒订阅用户,小程序有新的文章发布,引导用户回归小程序。 订阅消息申请模板的时候,需要选择所属类目,只能选择当前小程序相关的类目模板,对于模板消息不需要选择对应类目。如果删除小程序类目,就会把订阅消息模板一起删除。因此删除类目要小心谨慎。 [图片] 2.触发用户订阅,获取下发的权限 触发用户订阅,微信小程序提供的api是: [代码]wx.requestSubscribeMessage[代码],用户发生点击行为或者发起支付回调后,才可以调起订阅消息界面。 注意:微信小程序开发工具尚不支持此功能,在开发工具触发订阅的api,会提示: requestSubscribeMessage:fail 开发者工具暂时不支持此 API 调试,请使用真机进行开发 调用api的代码示例如下: [代码]wx.requestSubscribeMessage({[代码] [代码]tmplIds: ["模板A","模板B"],[代码] [代码]success: function (res) {[代码] [代码]//成功[代码] [代码]},[代码] [代码]fail(err) {[代码] [代码]//失败[代码] [代码]console.error(err);[代码] [代码]}[代码] [代码]})[代码] wx.requestSubscribeMessage(Object object) 的回调函数[代码]object.success [代码]参数有两个:errMsg和TEMPLATE_ID; 接口调用成功时errMsg值为’requestSubscribeMessage:ok’。TEMPLATE_ID是动态的键,即模板id,值包括’accept’、’reject’、’ban’。’accept’表示用户同意订阅该条id对应的模板消息,’reject’表示用户拒绝订阅该条id对应的模板消息,’ban’表示已被后台封禁。例如 { errMsg: “requestSubscribeMessage:ok”, zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE: “accept”} 表示用户同意订阅zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE这条消息。 个人觉得这个动态键不是特别合理,代码处理起来有些麻烦,如果改成静态键的json格式比较方便处理,例如: [代码]{[代码] [代码] errMsg:"requestSubscribeMessage:ok",[代码] [代码] result: [[代码] [代码] { templateId:"zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE",[代码] [代码]status:"accept"[代码] [代码]}[代码] [代码] ][代码] [代码]}[代码] 在手机上调用此api方法会调出订阅消息的界面,如下图所示: [图片] 关于这个订阅消息的授权有几点要注意: 1) 在确认提示框里,如果用户选择“取消”表示拒绝(取消)订阅消息,选择“允许”表示用户订阅一次消息。 2) 如果用户不勾选“总是保持以上选择,不再询问”,那么每次用户触发都会弹出提示框。 3) 如果用户勾选“总是保持以上选择,不再询问”,那么将再也不会唤起这个对话框。同时,如果选择“取消”,那么以后每次调用这个api的时候,都会自动拒绝;如果选择“允许”,那么以后每次调用此api,都会自动允许授权。 目前小程序没有提供获取用户是否授权订阅消息的方法。通过wx.openSetting 方法无法获取用户是否授权消息订阅的信息,scope 列表没有订阅消息的内容。 如果想从自动拒绝转换到自动自动运行,需要打开小程序的设置去配置。设置方法:点击小程序右上角的三个点,打开如下对话框 [图片] 然后选择“设置”,在设置项里选择“订阅消息” [图片] [图片] 4)对于同一种消息,用户可以订阅多次,订阅多少次,就会收到多少次订阅消息,这个订阅次数是否有上限,官方没有说明,初步判断是不限的。但是,微信不会提供订阅的次数,因此需要在小程序的后端服务里存储用户订阅的次数。因此,我在微慕小程序专业版里,提供了一个给用户多次订阅的设置,并记录用户订阅的次数。 [图片] 如果用户需要某个消息服务,可以订阅多次,当然也可以在点击“订阅”的对话框里选择“取消”,“取消”一次也就减少一次订阅。 5)对于支付的场景,也需要用户确认是否订阅,这个我觉得不合理,支付后给用户一个订单推送消息应该是刚性需求,不需要再询问一遍用户是否订阅。 2.调用接口下发订阅消息 订阅消息下发的接口是小程序后台服务端调用:subscribeMessage.send,此方法类似下发模板消息的方法,详细调用说明见参考官方的链接: https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html 订阅消息的下发接口方法和模板消息稍有不同, 模板消息的json格式如下 [代码]"data": {[代码] [代码]"keyword1": {[代码] [代码]"value": "内容1",[代码] [代码]"color": "#000"[代码] [代码]},[代码] [代码]"keyword2": {[代码] [代码]"value": "内容2",[代码] [代码]"color": "#000"[代码] [代码]}[代码] [代码]}[代码] 而订阅消息的json格式如下: [代码]"data": {[代码] [代码]"thing1": {[代码] [代码]"value": "内容"[代码] [代码]},[代码] [代码]"number2": {[代码] [代码]"value": 20[代码] [代码]}[代码] 订阅消息的字段key是和数据类型有关,value的参数需要严格按照设置的类型提交,如果不按类型提交,会导致发送失败。同时如果是文本型的内容,字数也有限制,超过限制也会发送失败,但具体字数是多少,官方没有给出,同时中英文混合计算的长度也有差异,据我目前测试25个中文字符是可以的。希望官方能给出具体的字符长度限制的明确数字。 如果调用下发的次数大于用户的订阅次数,调用接口下发订阅消息会返回失败。报如下错误 [图片] 二.订阅消息使用心得1.订阅消息虽然把订阅的授权的交给了用户,但是也增加了用户使用难度,同时,一次性订阅只能收到一次,操作起来比较繁琐,如果不是刚需用户可能会首次就拒绝了这个服务,要想重新获取授权,需要用户自己打开小程序设置里去配置,颇为麻烦,小程序没有提供更简便的方法去唤起。 2.小程序的服务商为了获得更多给用户发送订阅消息的次数,肯定会想方设法去埋点引诱用户去点击订阅,这种诱导估计也是违规。 3.用户使用门槛和学习比较高,比如某个预约的服务,原来的场景是用户只要有提交表单,小程序就可以推送消息给用户,但是现在需要用户主动去订阅,无形中多了一步,如果用户不熟悉订阅消息或者直接点了“取消”,小程序就没法通知到用户了,用户可能因此错失服务,对商家和用户都是损失。 4.微信小程序将采用订阅消息,并逐步取消模板消息,虽然微信官方试图在方便用户和不打扰用户这两种选择里去寻求平衡,但订阅消息目前的模式恐怕无法达到这个期望,至少在我看来,无论对小程序的服务商,还是小程序的用户,都感到不方便。 update:2020年5月18日,日前订阅消息已经支持微信小程序开发工具。
2020-05-18 - 小程序模板消息能力调整通知
小程序模板消息能力在帮助小程序实现服务闭环的同时,也存在一些问题,如: 1. 部分开发者在用户无预期或未进行服务的情况下发送与用户无关的消息,对用户产生了骚扰; 2. 模板消息需在用户访问小程序后的 7 天内下发,不能满足部分业务的时间要求。 为提升小程序模板消息能力的使用体验,我们对模板消息的下发条件进行了调整,由用户自主订阅所需消息。 一次性订阅消息 一次性订阅消息用于解决用户使用小程序后,后续服务环节的通知问题。用户自主订阅后,开发者可不限时间地下发一条对应的服务消息;每条消息可单独订阅或退订。 [图片] (一次性订阅示例) 长期性订阅消息 一次性订阅消息可满足小程序的大部分服务场景需求,但线下公共服务领域存在一次性订阅无法满足的场景,如航班延误,需根据航班实时动态来多次发送消息提醒。为便于服务,我们提供了长期性订阅消息,用户订阅一次后,开发者可长期下发多条消息。 目前长期性订阅消息仅向政务民生、医疗、交通、金融、教育等线下公共服务开放,后期将逐步支持到其他线下公共服务业务。 调整计划 小程序订阅消息接口上线后,原先的模板消息接口将停止使用,详情如下: 1. 开发者可登录小程序管理后台开启订阅消息功能,接口开发可参考文档:《小程序订阅消息》 2. 开发者使用订阅消息能力时,需遵循运营规范,不可用奖励或其它形式强制用户订阅,不可下发与用户预期不符或违反国家法律法规的内容。具体可参考文档:《小程序订阅消息接口运营规范》 3. 原有的小程序模板消息接口将于 2020 年 1 月 10 日下线,届时将无法使用此接口发送模板消息,请各位开发者注意及时调整接口。 微信团队 2019.10.12
2019-10-13 - 如何申请长期订阅消息?
[图片] 公司开发的小程序是政府类的项目,需要用到模版消息推送的功能,现在接口要下线了,如何才能申请使用长期订阅消息模版呢?我们的小程序是政务民生->治安的类目。
2019-12-04 - 「笔记」订阅消息体验踩坑
前言 10月12日夜晚社区发了公告小程序模板消息能力调整通知,正式发布了 一次性订阅消息 这一能力,所以第一时间进行了体验。 本文主要是补充一下官方未提供的使用方法,和使用中与模板消息用法的不同。 文档地址 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/subscribe-message.html 使用方法 [代码]wx.requestSubscribeMessage({ tmplIds: ["模板id1","模板id2"], success: function (res) { //成功 }, fail(err) { //失败 console.error(err); } }) [代码] 第一个坑 如果不勾选红色方框内的内容,用户每次触发订阅消息功能都会弹出授权窗口,如果用户勾选了则不会出现弹窗。 [图片] 第二个坑 目前开发者工具(v1.02.191012)不支持调试,只能通过真机调试。 [图片] 第三个坑 微信不会为开发者保存订阅次数,需要自己在后台记录用户触发的次数。 超过次数调用接口下发订阅消息会返回失败。 [图片] 第四个坑 发送模板格式和原来的模板消息格式不一致,特别是data内的内容,订阅消息的字段key是和数据类型有关,value的参数需要严格按照设置的类型提交,具体使用参考后台的模板详情。 模板消息的格式: [代码]"data": { "keyword1": { "value": "内容", "color": "#000" }, "keyword2": { "value": "内容", "color": "#000" } } [代码] 订阅消息的格式: [代码]"data": { "thing1": { "value": "内容" }, "number2": { "value": 20 } [代码] 第五个坑 订阅消息申请模板的时候,需要选择所属类目,而且只能是自己小程序相关类目,模板消息是不需要选择对应类目的。 如果删除小程序类目,则会把订阅消息模板一起删除,需谨慎操作。 [图片] 第六个坑 长期订阅消息只针对特定行业开放,所以普通开发者并无法使用。 结束 暂时就先总结这些,有其它坑再补充。
2019-10-13 - async/await 还是同步怎么办?
[代码]get[代码][代码]: async function(e) {[代码][代码] [代码][代码]let[代码] [代码]$that = [代码][代码]this[代码][代码];[代码][代码] [代码][代码]let[代码] [代码]$distance = await getDistance($that.data.info._point);[代码][代码] [代码][代码]if[代码] [代码]($distance <= 8000) {[代码][代码] [代码][代码]//step1[代码][代码] [代码][代码]} [代码][代码]else[代码] [代码]{[代码][代码] [代码][代码]//step2[代码][代码] [代码][代码]}[代码][代码] [代码][代码]},[代码] [代码]let[代码] [代码]getDistance = async (point) => {[代码][代码] [代码][代码]let[代码] [代码]$geo = [代码][代码]null[代码][代码];[代码][代码] [代码][代码]await wx.getLocation({[代码][代码] [代码][代码]type: [代码][代码]"gcj02"[代码][代码],[代码][代码] [代码][代码]altitude: [代码][代码]true[代码][代码],[代码][代码] [代码][代码]success: res => {[代码][代码] [代码][代码]$geo = [res.longitude, res.latitude];[代码][代码] [代码][代码]},[代码][代码] [代码][代码]fail: err => {[代码][代码] [代码][代码]$geo = [180, -80];[代码][代码] [代码][代码]},[代码][代码] [代码][代码]complete: () => {[代码][代码] [代码][代码]let[代码] [代码]$pointJson = JSON.stringify(point);[代码][代码] [代码][代码]let[代码] [代码]$pointGeo = JSON.parse($pointJson);[代码][代码] [代码][代码]let[代码] [代码]$point = $pointGeo.coordinates;[代码][代码] [代码][代码]let[代码] [代码]$rad = 6378137;[代码][代码] [代码][代码]let[代码] [代码]$rad1 = parseFloat($geo[1]) * Math.PI / 180.0;[代码][代码] [代码][代码]let[代码] [代码]$rad2 = parseFloat($point[1]) * Math.PI / 180.0;[代码][代码] [代码][代码]let[代码] [代码]$sub1 = $rad1 - $rad2;[代码][代码] [代码][代码]let[代码] [代码]$sub2 = parseFloat($geo[0]) * Math.PI / 180.0 - parseFloat($point[0]) * Math.PI / 180.0;[代码][代码] [代码][代码]let[代码] [代码]$sub = (2 * $rad * Math.asin(Math.sqrt(Math.pow(Math.sin($sub1 / 2), 2) + Math.cos($rad1) * Math.cos($rad2) * Math.pow(Math.sin($sub2 / 2), 2)))).toFixed(0);[代码][代码] [代码][代码]return[代码] [代码]parseInt($sub);[代码][代码] [代码][代码]}[代码][代码] [代码][代码]})[代码][代码]}[代码]我在get函数中会直接跑到step2,然后才到return parseInt($sub)
2019-11-04 - 地图选择地址 及 输入地址在地图定位
准备工作: 需要去腾讯地图开放平台申请一个账号,获取到一个key参数 下载SDK;地址:https://lbs.qq.com/qqmap_wx_jssdk/index.html 注意事项: 1:调试的时候使用真机去调试! 2:在发布这篇文章之后就经历了一小段的煎熬,怎么测试版本好好的,正式版本就不行了,兼容性问题?还是其他的?后来想起开发工具去可以不检测合法域名的,去掉勾勾,果然,开发工具也不行了,加上合法域名就可以开开心心玩耍了! 我们在使用调试版本的时候,是可以 不检测合法域名 的,但是发布的正式版本却不能,所以我们一定要去微信小程序的管理平台去将request的合法域名给加上; https://apis.map.qq.com https://restapi.amap.com https://tcb-api.tencentcloudapi.com [图片] 效果截图: [图片] 代码片段:https://developers.weixin.qq.com/s/13WYowmG7fdC 注:不需要自己去实现服务端代码,相关API均来自于腾讯地图
2019-12-11 - 记录--根据经纬度计算直线距离
/** 经纬度计算两点之间的距离,不是很准确 */ [代码]function distance(lat1, lng1, lat2, lng2) { lat1 = lat1 || 0; lng1 = lng1 || 0; lat2 = lat2 || 0; lng2 = lng2 || 0; var rad1 = lat1 * Math.PI / 180.0; var rad2 = lat2 * Math.PI / 180.0; var a = rad1 - rad2; var b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0; var r = 6378.137; var distance = r * 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(rad1) * Math.cos(rad2) * Math.pow(Math.sin(b / 2), 2))); return distance; } [代码]
2019-07-24 - 怎么使小程序一直保持在前台运行?
请问, 怎么能让小程序运行时不关闭显示屏?
2019-12-10 - 小程序能否提供拖动排序组件?
小程序能否提供下拖动排序组件demo。 [图片]
2019-08-29 - 各位用云开发的老铁,你们的图片加载?
老铁们,云存储的十几k的图片,你们加载显示的慢吗? 慢的话,如何解决了的?
2019-11-03 - 关于云开发:图片上传后迟迟不更新问题!
- 当前 Bug 的表现(可附上截图) 关于云开发:图片上传后迟迟不更新问题! 新建默认的快速云开发模板,上传图片,(我每次用相同的文件名,想覆盖之前的图片)成功上传了,在小程序端上传界面也显示新上传的图片, 但是,但是,但是,在云端,看到的还是旧的图片呢?(包括下载下来也是旧图片);如果每次用的是不同的文件名,就没有这回事。 这倒底是为何?上传同名的图片会有延迟更新吗?多久(好像要几个小时的!!!) - 预期表现 ???? - 复现路径 - 提供一个最简复现 Demo
2018-11-13 - 教你怎么监听小程序的返回键
更新:2020年7月28日08:51:11 基础库2.12.0起,可以调用wx.enableAlertBeforeUnload监听原生右上角返回、物理返回以及wx.navigateBack时弹框提示 AIP详情请看: https://developers.weixin.qq.com/miniprogram/dev/api/ui/interaction/wx.enableAlertBeforeUnload.html //======================================== 怎么监听小程序的返回键? 应该有很多人想要监听用户的这个动作吧,但是很遗憾,小程序不会给你这个API的,那是不是就没辙了? 幸好我们还可以自定义导航栏,这样一来我们就可以监听用户的这一动作了。 什么?这你已经知道啦? 那好咱们就不说自定义导航栏的返回监听了,说一下物理返回和左滑?右滑?(不管了,反正是滑)返回上一页怎么监听。 监听物理返回 首先说一下这个监听方法的缺点,虽说是监听,但是还是无法真正意义上的监听并拦截来阻止页面跳转,页面还是会返回上一页,而后重新载入刚刚的页面,如果这不是你想要的,那可以不用往下看了 其次说一下用到什么东西: wx.onAppRoute、wx.showModal 最后是一些主要代码: 重写wx.showModal,主要是加个confirmStay参数和使wx.showModal Promise化 [代码]const { showModal } = wx; Object.defineProperty(wx, 'showModal', { configurable: false, // 是否可以配置 enumerable: false, // 是否可迭代 writable: false, // 是否可重写 value(...param) { return new Promise(function (rs, rj) { let { success, fail, complete, confirmStay } = param[0] param[0].success = (res) => { res.navBack = (res.confirm && !confirmStay) || (res.cancel && confirmStay) wx.setStorageSync('showBackModal', !res.navBack) success && success(res) rs(res) } param[0].fail = (res) => { fail && fail(res) rj(res) } param[0].complete = (res) => { complete && complete(res) (res.confirm || res.cancel) ? rs(res) : rj(res) } return showModal.apply(this, param); // 原样移交函数参数和this }.bind(this)) } }); [代码] 使用wx.onAppRoute实现返回原来的页面 [代码]wx.onAppRoute(function (res) { var a = getApp(), ps = getCurrentPages(), t = ps[ps.length - 1], b = a && a.globalData && a.globalData.pageBeforeBacks || {}, c = a && a.globalData && a.globalData.lastPage || {} if (res.openType == 'navigateBack') { var showBackModal = wx.getStorageSync('showBackModal') if (c.route && showBackModal && typeof b[c.route] == 'function') { wx.navigateTo({ url: '/' + c.route + '?useCache=1', }) b[c.route]().then(res => { if (res.navBack){ a.globalData.pageBeforeBacks = {} wx.navigateBack({ delta: 1 }) } }) } } else if (res.openType == 'navigateTo' || res.openType == 'redirectTo') { if (!a.hasOwnProperty('globalData')) a.globalData = {} if (!a.globalData.hasOwnProperty('lastPage')) a.globalData.lastPage = {} if (!a.globalData.hasOwnProperty('pageBeforeBacks')) a.globalData.pageBeforeBacks = {} if (ps.length >= 2 && t.onBeforeBack && typeof t.onBeforeBack == 'function') { let { onUnload } = t wx.setStorageSync('showBackModal', !0) t.onUnload = function () { a.globalData.lastPage = { route: t.route, data: t.data } onUnload() } } t.onBeforeBack && typeof t.onBeforeBack == 'function' && (a.globalData.pageBeforeBacks[t.route] = t.onBeforeBack) } }) [代码] 改造Page [代码]const myPage = Page Page = function(e){ let { onLoad, onShow, onUnload } = e e.onLoad = (() => { return function (res) { this.app = getApp() this.app.globalData = this.app.globalData || {} let reinit = () => { if (this.app.globalData.lastPage && this.app.globalData.lastPage.route == this.route) { this.app.globalData.lastPage.data && this.setData(this.app.globalData.lastPage.data) Object.assign(this, this.app.globalData.lastPage.syncProps || {}) } } this.useCache = res.useCache res.useCache ? reinit() : (onLoad && onLoad.call(this, res)) } })() e.onShow = (() => { return function (res) { !this.useCache && onShow && onShow.call(this, res) } })() e.onUnload = (() => { return function (res) { this.app.globalData = Object.assign(this.app.globalData || {}, { lastPage: this }) onUnload && onUnload.call(this, res) } })() return myPage.call(this, e) } [代码] 在需要监听的页面加个onBeforeBack方法,方法返回Promise化的wx.showModal [代码]onBeforeBack: function () { return wx.showModal({ title: '提示', content: '信息尚未保存,确定要返回吗?', confirmStay: !1 //结合content意思,点击确定按钮,是否留在原来页面,confirmStay默认false }) } [代码] 运行测试,Oj8K 是不是很简单,马上去试试水吧,效果图就不放了,静态图也看不出效果,动态图懒得弄,想看效果的自己运行代码片段吧 代码片段 https://developers.weixin.qq.com/s/hc2tyrmw79hg
2020-07-28 - 请问如何用云开发pull删除数组下标中特定的值?谢谢
[图片] test.doc('我的_id').update({ data: { menu: _.pull({ image: 'testImg' }) } } 我只想删除掉menu[0].image,但如代码所示,微信文档所提供的pull方法会删掉整个menu[0],请问应该怎么写pull方法??谢谢!!!我太难了。。。
2019-12-12 - 云开发删除字段或字段数组下某个值问题?
[图片] 各位大佬,有没有能删除一个数组字段或删除数组字段下一个值的方法,研究了一晚上,脑袋都晕了,要么可以删,但查询条件不能是变量,麻烦赐教。
2019-11-30