- 芒果在线小程序webview展示公众号文章-快速滑动时候会崩溃闪退?Android
使用webview加载公众号文章 会闪退 有啥办法规避一下吗? 公众号文章地址: https://mp.weixin.qq.com/s?__biz=MzUyNzM3MTU3Ng==&mid=2247579669&idx=1&sn=7aabbb2902e0eedd8c98f7241591a7b4&chksm=fa036442cd74ed5402d9a896dfa738cb768c6b0c292f7a534a1c85e6365981bc2362351a1b0e#rd
2024-01-31 - 能不能提供小程序跳转视频号小店的功能?
希望可以支持小程序跳转视频号小店!
2024-02-25 - 小程序菜单复制链接功能复制出来的是永久链接还是临时链接?
wx.navigateToMiniProgram(Object object)小程序链接,当传递该参数后,可以不传 appId 和 path。链接可以通过【小程序菜单】->【复制链接】获取。
2023-09-19 - 解锁小程序中使用SVG新姿势
SVG 的优势 清晰度: 可以进行放大,而不失真 更小的文件体积 可扩展性,可以动态颜色 动效 可以添加动效 在小程序中使用 目前小程序 的image标签已经支持了 svg 的显示 [代码] <image src="./xx.svg"/> [代码] 如何动态的改变 svg 属性呢? 大体思路:把svg转成 base64 然后通过 image标签 src设置图片,再动态赋值svg颜色 把svg转成base64 如下一个svg 代码文件 [代码]<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="24 24 48 48"><animateTransform attributeName="transform" type="rotate" repeatCount="indefinite" from="0" to="360" dur="1400ms"></animateTransform><circle cx="48" cy="48" r="20" fill="none" stroke="#eeeeee" stroke-width="2" transform="translate\(0,0\)"><animate attributeName="stroke-dasharray" values="1px, 200px;100px, 200px;100px, 200px" dur="1400ms" repeatCount="indefinite"></animate><animate attributeName="stroke-dashoffset" values="0px;-15px;-125px" dur="1400ms" repeatCount="indefinite"></animate></circle></svg> [代码] 转成base64,其实就是 对这个svg进行 encodeURIComponent 得到 如下代码 [代码]%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%2224%2024%2048%2048%22%3E%3CanimateTransform%20attributeName%3D%22transform%22%20type%3D%22rotate%22%20repeatCount%3D%22indefinite%22%20from%3D%220%22%20to%3D%22360%22%20dur%3D%221400ms%22%3E%3C%2FanimateTransform%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%2248%22%20r%3D%2220%22%20fill%3D%22none%22%20stroke%3D%22%23eeeeee%22%20stroke-width%3D%222%22%20transform%3D%22translate%5C(0%2C0%5C)%22%3E%3Canimate%20attributeName%3D%22stroke-dasharray%22%20values%3D%221px%2C%20200px%3B100px%2C%20200px%3B100px%2C%20200px%22%20dur%3D%221400ms%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3Canimate%20attributeName%3D%22stroke-dashoffset%22%20values%3D%220px%3B-15px%3B-125px%22%20dur%3D%221400ms%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3C%2Fcircle%3E%3C%2Fsvg%3E [代码] 拼接base64 [代码] data:image/svg+xml;charset=utf-8,encodeURIComponent后的代码 [代码] 在对应svg属性上动态设置颜色,比如这里用到的是填充颜色 在js文件 data中定义 color 状态 在wxml中动态渲染 [代码] <image src="data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%2224%2024%2048%2048%22%3E%3CanimateTransform%20attributeName%3D%22transform%22%20type%3D%22rotate%22%20repeatCount%3D%22indefinite%22%20from%3D%220%22%20to%3D%22360%22%20dur%3D%221400ms%22%3E%3C%2FanimateTransform%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%2248%22%20r%3D%2220%22%20fill%3D%22none%22%20stroke%3D%22%23{{color}}%22%20stroke-width%3D%222%22%20transform%3D%22translate%5C(0%2C0%5C)%22%3E%3Canimate%20attributeName%3D%22stroke-dasharray%22%20values%3D%221px%2C%20200px%3B100px%2C%20200px%3B100px%2C%20200px%22%20dur%3D%221400ms%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3Canimate%20attributeName%3D%22stroke-dashoffset%22%20values%3D%220px%3B-15px%3B-125px%22%20dur%3D%221400ms%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3C%2Fcircle%3E%3C%2Fsvg%3E" /> [代码] [代码]注意:这里的颜色 由于是已经被编码了,所以# 已经被转义了 %23, 直接写颜色数字即可[代码] 当然你也可以 去掉%23 自己实现一个内部方法 [代码] if (color && color.startsWith('#')) { return `%23${color.slice(1)}`; } [代码] 这样其实就实现了 svg的动态渲染,可是这种写法,写在wxml中 不是特别的优雅,那么如何重构下让我们的代码看起来更优雅呢? 把 svg 单独存放 支持动态返回 动态赋值 image src 属性 svg 动态函数 loading.svg.js 文件 [代码]export const loadingSvg = (color='#ddd') =>{ const svgXml = `<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="24 24 48 48"><animateTransform attributeName="transform" type="rotate" repeatCount="indefinite" from="0" to="360" dur="1400ms"></animateTransform><circle cx="48" cy="48" r="20" fill="none" stroke="${color}" stroke-width="2" transform="translate\(0,0\)"><animate attributeName="stroke-dasharray" values="1px, 200px;100px, 200px;100px, 200px" dur="1400ms" repeatCount="indefinite"></animate><animate attributeName="stroke-dashoffset" values="0px;-15px;-125px" dur="1400ms" repeatCount="indefinite"></animate></circle></svg>` return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgXml)}` } [代码] 逻辑层引入,setData [代码] onLoad(){ const { loadingSvg } = require('./loading.svg.js') const svgImg = loadingSvg('#eee') this.setData({svgImg}) }, [代码] 渲染层使用 [代码] <image src="{{svgImg}}"/> [代码] github 使用案例 demoFormpSvg
2022-04-30 - URL Scheme 和 URL Link 优化公告
为了帮助开发者更好地为用户提供服务,降低开发者使用 URL Scheme 和 URL Link 的成本,自 2023 年 12 月 19 日起,微信团队将对 URL Scheme 和 URL Link 进行如下优化: 1、新增明文 URL Scheme,开发者无需调用接口可自行拼接并且生成明文 Scheme; 2、取消 URL Scheme 和 URL Link 打开端一人一链的限制,支持同一条链接被多名用户访问; 3、新增打开端限制:每个小程序每天 URL Scheme 和 URL Link 总打开次数上限为 300 万次。 一、新增明文 URL Scheme开发者无需调用平台接口,可自行根据如下格式拼接 appid 和 path 等参数,作为 URL Scheme 链接。 weixin://dl/business/?appid=*APPID*&path=*PATH*&query=*QUERY*&env_version=*ENV_VERSION* 其中各参数含义如下: [图片] 注意: 1、为保护开发者,通过明文 URL Scheme 拉起的小程序(页面)必须要提前在「小程序管理后台 -> 设置 -> 隐私与安全 -> 明文 scheme 拉起此小程序」中进行声明; 小程序:配置能够通过明文 scheme 进入的小程序页面[图片] 小游戏:打开开关即可通过明文 scheme 拉起小游戏[图片] 2、通过明文 URL Scheme 打开小程序的场景值为 1286; 3、明文 URL Scheme 不受每天 50 万次的生成量限制; 4、明文 URL Scheme 没有有效期的概念,可长期有效; 5、明文 URL Scheme 没有一人一链的打开限制,支持一条链接同时被多名用户访问。 二、原 URL Scheme 升级为加密 URL Scheme,并支持自行拼接参数目前已对外提供的 URL Scheme 能力平滑升级为加密 URL Scheme,取消一人一链的限制,支持开发者自行在链接后拼接参数*CUSTOM PARAMETER*。 注意:之前通过平台接口生成的 URL Scheme 可继续使用,链接自动可支持多人打开。 URL Scheme格式 weixin://dl/business/?t=*TICKET*&cq=*CUSTOM PARAMETER* 其中参数含义如下: [图片] 注意:加密 URL Scheme 打开小程序的场景值保持不变,仍为 1065。 三、原 URL Link 升级为加密 URL Link,并支持自行拼接参数目前已对外提供的 URL Link 能力平滑升级为加密 URL Link,取消一人一链的限制,支持开发者自行在链接后拼接参数*CUSTOM PARAMETER*。 注意:之前通过平台接口生成的 URL Link 可继续使用,链接自动可支持多人打开。 URL Link格式: https://wxaurl.cn/*TICKET*?cq=*CUSTOM PARAMETER* 其中参数含义如下: [图片] 注意:加密 URL Link 打开小程序的场景值保持不变,微信外打开的场景值为 1194;微信内打开会调整为开放标签打开小程序,场景值为 1167。 四、调用规则调整1、加密 URL Scheme 和 URL Link 取消一人一链,支持一条链接同时被多名用户访问,生效后之前生成的链接被多名用户访问时,不会再报错; 2、每个小程序每天能够生成加密 URL Scheme 和 URL Link 共计 50 万条的限制不变,额外增加每个小程序每天在微信外,能够通过链接打开小程序共计 300 万次的打开量限制,其中链接包括加密 URL Scheme、加密 URL Link 和明文 URL Scheme ;若链接打开小程序的次数超过 300 万次/天,则无法通过链接在微信外拉起小程序; 3、URL Scheme (加密和明文)和 URL Link (加密)仅支持非个人主体小程序使用; 4、注意事项:平台有安全策略防止开发者的链接被黑灰产大量打开,可能导致达到访问上限无法正常通过链接打开小程序的问题; 5、查询方式:开发者可复用现有的查询方式对 URL Scheme 和 URL Link 进行打开额度查询和链接状态查询。 [图片]
2023-12-19 - 常驻云函数怎么设置 有啥用啊,压根看不到设置常驻云函数功能是不是废弃了?
常驻云函数有啥用
2022-10-17 - 使用代码加固后还能取消吗?对项目有影响吗 会导致项目不可用吗?编辑器执行加固后需要重新提审并发布吗
使用代码加固后还能取消吗?对项目有影响吗 会导致项目不可用吗?编辑器执行加固后需要重新提审并发布吗
2022-11-21 - 2023-08-28
- 云开发控制台的“内容安全”,按照指示添加规则后,没有生效
[图片][图片][图片]
2022-11-23 - 在云开发控制台配置了内容安全规则,怎么过了一段时间不生效了?
平台监测小程序存在信息安全风险的提醒,然后我在这里配置了内容安全规则,测试的时候没有问题,可以重写违规内容。但过了几天平台第二次检查时又不生效了?[图片]
2023-05-15 - 目前小程序同一Url Link能否支持多人访问?
[图片]
2023-10-23 - 小程序胶囊里面的复制链接 可不可以用代码生成?
需要跳转到一个页面传不同的参数展示不同的票详情,可不可以通过动态传参数的方式生成这个链接,有这个api吗? [图片][图片]
2023-11-14 - 获取云存储永久有效url地址?
[图片] 在使用cloud.getTempFileURL获取的url地址过一段时间后失效了,查看了获取的字符串和云存储的下载地址比较后发现,少了sign之后的内容。想问下怎么才能在一开始就获得云存储的下载地址?
2022-04-23 - getTempFileURL设置maxAge为30秒不生效?
https://developers.weixin.qq.com/community/develop/doc/0008004a2a84004119a863f2956c00 参考以上链接,设置maxAge为30秒,但是一直不过期(浏览器缓存已清除,也换了浏览器测试),所以无法测试,如果我设置了30天,是否真的也不过期,还是会提前过期。 有官方更明确的答案吗? [图片] 后面认真看了下getTempFileURL的官方描述,在核对下我的存储权限“所有用户可读”,所以算“公有读”,能不能因此判断我的文件就是不需要设置maxAge,因为本身就不会过期。 [图片] [图片]
2023-05-31 - 关于微信小程序备案填写证件住所不通过
https://developers.weixin.qq.com/miniprogram/product/record/record_faq.html [图片] 小程序备案,在【验证备案类型】界面下的【证件住所】一直提示“【主体证件住所】工商数据对比不通过,如带有标点符号或字母,请检查中、英文标点符号和字母大小写”的信息,但是截图文中填写的都是中文及数字,并未包含标点或特殊符号,麻烦检查下代码是否有bug?
2023-10-19 - 云开发环境静态H5页面,跳转到不同小程序不同页面的实现
客户有需求,要短信或者其它场景中的链接静态H5页面,触发打开自家的小程序,官方也提供了 静态网站 H5 跳小程序技术文档(https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/staticstorage/jump-miniprogram.html ),不过里面只描述跳转到小程序本身绑定的云开发环境,那么是多个小程序共享的云开发环境如何实现跳转呢,该文档并没有说。 为每个小程序购买一个云开发环境当然是可以,然而,成本似乎似乎太高,不合算,为了给客户节省成本,就给一个小程序买了云开发环境,按照跨账号环境共享设置,实现共享给同主体下的不同小程序(参考:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/resource-sharing/)。 接下来我把最终结果和解决方案,贴出来共享。 场景:首先同公司名下有4个小程序:A、B、C、D,只为A小程序购买了最基础的云开发环境19块,开通静态网站,添加跨账号共享功能,实现访问同一个静态H5页面携带不同参数,打开B、C、D不同小程序的指定页面,无论是微信里还是在手机浏览器均可以跳转。 实现代码: 1、云函数public编写(参考并改进 https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/staticstorage/jump-miniprogram.html ) // 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init() // 云函数入口函数 exports.main = async (event, context) => { const wxContext = cloud.getWXContext() switch (event) { case 'getUrlScheme': { return getUrlScheme(event) } } return 'action not found' } async function getUrlScheme(event) { if(event.appid) { var clb = cloud.openapi({ appid: event.appid }); } else{ var clb = cloud.openapi; } return clb.urlscheme.generate({ jumpWxa: { path: event.path, query: event.query, }, // 如果想不过期则置为 false,并可以存到数据库 isExpire: false, // 一分钟有效期 expireTime: parseInt(Date.now() / 1000 + 60), }) } 2、静态H5页面:jump.html <html> <head> <title>H5打开小程序</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <script> window.onerror = e => { console.error(e);alert('发生错误:' + e);} </script> <!-- 调试用的移动端 console --> <!-- <script src="https://cdn.bootcss.com/eruda/1.2.4/eruda.min.js"></script> --> <!-- <script>eruda.init();</script> --> <script> function getQueryParam(key) { const reg = new RegExp('(^|&)' + key + '=([^&]*)(&|$)', 'i'); const r = window.location.search.substr(1).match(reg); if (r != null) { return decodeURI(r[2]); } return null; } //设置 资源环境ID以及绑定的appid var resAppId = '环境宿主Appid';// <!-- replace --> var resEnv = '资源环境ID'; // <!-- replace --> //资源方其它小程序组【AppID,原始id,名称,缺省打开路径】 var appIDs = [ ['wxe6ddf673521da8f0','gh_e4025e37c422','小程序A','pages/webview'], // <!-- replace --> ['wxb1abf1b1fe25f8c6','gh_a4cbe6b9f17f','小程序B',''], // <!-- replace --> ['wx24911b4d9b6971c9','gh_e544c578a3ef','小程序C',''], // <!-- replace --> ['wx6fecac42503a957b','gh_81a6106a84ce','小程序D',''] // <!-- replace --> ]; ////////////////////////////// var launchIdx = getQueryParam('id') || 0; var pagepath = launchIdx== 0 ? appIDs[launchIdx][3] : ""; pagepath = pagepath ? pagepath+(getQueryParam('url') ? "?url="+encodeURIComponent(getQueryParam('url')):"") : ""; pagepath = pagepath ? pagepath : (getQueryParam('page') ? getQueryParam('page'):""); </script> <!-- weui 样式 --> <link rel="stylesheet" href="https://res.wx.qq.com/open/libs/weui/2.4.1/weui.min.css" /> <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script> <script src="https://res.wx.qq.com/open/js/cloudbase/1.1.0/cloud.js"></script> <style>.hidden{display:none}.full{position:absolute;top:0;bottom:0;left:0;right:0}.public-web-container{display:flex;flex-direction:column;align-items:center}.public-web-container p{position:absolute;top:25%}.public-web-container a{position:absolute;bottom:40%}.wechat-web-container{display:flex;flex-direction:column;align-items:center}.wechat-web-container p{position:absolute;top:40%}.wechat-web-container wx-open-launch-weapp{position:absolute;bottom:40%;left:0;right:0;display:flex;flex-direction:column;align-items:center}.desktop-web-container{display:flex;flex-direction:column;align-items:center}.desktop-web-container p{position:absolute;top:40%} </style> </head> <body> <div class="page full"> <div id="public-web-container" class="hidden"> <p>正在唤起微信小程序...</p> <a id="public-web-jump-button" href="javascript:" class="weui-btn weui-btn_primary weui-btn_loading" onclick="openWeapp()"> <span id="public-web-jump-button-loading" class="weui-primary-loading weui-primary-loading_transparent"><i class="weui-primary-loading__dot"></i></span>点击唤起小程序</a> </div> <div id="wechat-web-container" class="hidden"> <script> document.write('<p>请点击下方按钮</p>'); document.write('<wx-open-launch-weapp id="launch-btn" username="'+appIDs[launchIdx][1]+'" path="'+pagepath+'">'); document.write(' <template><button style="width: 240px; height: 45px; text-align: center; font-size: 17px; display: block; margin: 0 auto; padding: 8px 24px; border: none; border-radius: 4px; background-color: #07c160; color:#fff;">打开微信小程序</button></template>'); document.write('</wx-open-launch-weapp>'); </script> </div> <div id="desktop-web-container" class="hidden"><p class="font-size:26px;">请在手机上打开本链接</p></div> </div> <script> function docReady(fn) { if (document.readyState === 'complete' || document.readyState === 'interactive') { fn();} else {document.addEventListener('DOMContentLoaded', fn);} } docReady(async function() { var ua = navigator.userAgent.toLowerCase() var isWXWork = ua.match(/wxwork/i) == 'wxwork'; var isWeixin = !isWXWork && ua.match(/MicroMessenger/i) == 'micromessenger'; var isMobile = isDesktop = false; if (navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|IEMobile)/i)) { isMobile = true } else { isDesktop = true } var isAndroid = ua.indexOf('android') > -1 || ua.indexOf('Adr') > -1; var isOS = ua.indexOf('iPhone') > -1 || ua.indexOf('iPad') > -1 || ua.indexOf('Mac') > -1; if (isWeixin) { var containerEl = document.getElementById('wechat-web-container'); containerEl.classList.remove('hidden'); containerEl.classList.add('full', 'wechat-web-container'); var launchBtn = document.getElementById('launch-btn'); launchBtn.addEventListener('ready', function (e) { console.log('开放标签 ready'); }); launchBtn.addEventListener('launch', function (e) { console.log('开放标签 success'); }); launchBtn.addEventListener('error', function (e) { console.log('开放标签 fail', e.detail);}); wx.config({ debug: false, appId: appIDs[launchIdx][0], // <!-- replace --> timestamp: 0, // 必填,填任意数字即可 nonceStr: 'nonceStr', // 必填,填任意非空字符串即可 signature: 'signature', // 必填,填任意非空字符串即可 jsApiList: ['chooseImage'], // 必填,随意一个接口即可 openTagList:['wx-open-launch-weapp'], // 填入打开小程序的开放标签名 }) } else if (isDesktop) { // 在 pc 上则给提示引导到手机端打开 var containerEl = document.getElementById('desktop-web-container') containerEl.classList.remove('hidden') containerEl.classList.add('full', 'desktop-web-container') } else { //腾讯云开发的免鉴权调用 var containerEl = document.getElementById('public-web-container') containerEl.classList.remove('hidden') containerEl.classList.add('full', 'public-web-container') var c = new cloud.Cloud({ identityless: true, resourceAppid: resAppId, // 资源方宿主 小程序的AppID resourceEnv: resEnv, // 资源方环境ID }) await c.init(); window.c = c; var buttonEl = document.getElementById('public-web-jump-button') var buttonLoadingEl = document.getElementById('public-web-jump-button-loading') try { await openWeapp(() => { buttonEl.classList.remove('weui-btn_loading'); buttonLoadingEl.classList.add('hidden'); }) } catch (e) { console.log('error',e) buttonEl.classList.remove('weui-btn_loading') buttonLoadingEl.classList.add('hidden'); throw e } } } ) async function openWeapp(onBeforeJump) { var c = window.c const res = await c.callFunction({ name: 'public', // 宿主环境中的云函数,注意开启权限 data: { action: 'getUrlScheme', appid: appIDs[launchIdx][0], path : pagepath }, }) if (onBeforeJump) { onBeforeJump(); } location.href = res.result.openlink; } </script> </body> </html> 调用方式:https://xxxx.xxx.xxx/jump.html?id=1&path=my/xwt-apply/xwt-apply (修改成你云环境静态网页的域名) id=1 表示jump.html页面中小程序B,path 表示小程序B中的具体页面。 你可以尝试修改成自己的,实现传参跳转到不同小程序页面。希望对你有帮助!
2023-07-10 - 小程序开发——微信外环境静态h5跳转小程序
写在前面如果你想要自主开发,但没有云开发相关经验,看官方文档仍旧无法从浏览器环境h5页面调起小程序,那么可以来学习下本教程。 可以看下本文发表时间,所贴示例代码均为实际demo代码,能成功调起小程序的。 不太方便录制视频,这里就以图文形式全流程介绍如何实现微信外环境静态h5跳转小程序。 一.先贴下官方开发文档,虽没能成功调起小程序,也是可以先看下的1.官方开发文档参考:静态网站 h5跳小程序 官方文档只提供了关键代码,但是没说怎么上传静态文件,怎么上传云函数。 2. 微信开放社区知识库:云开发短信跳小程序(自定义开发版)教程 有视频教程,有demo,有介绍怎么上传静态网站、云函数,但是视频demo跟文中提供的github源码不一致,反正我是看的有点懵。 二.自己摸索,博采众长,终于在浏览器中拉起小程序了工单提不了,提问没人回,做微信开发最头大的就是这个。后边百度搜索了好久,找到了比较简单可行的方法,这里梳理了下,即使没用过云函数的也可以参考本文操作。 经验证,本示例可以在微信内外环境中调起微信小程序。 三. 需要准备的工具、材料 微信开发者工具、非个人主体并且已认证的(微信认证)小程序。 四. 操作步骤: 1.开通云开发服务、云开发权限设置、开通静态网站功能: 1-1 开通云开发服务,微信开发者工具——云开发 [图片] 创建云开发环境,设置环境名称、付费方式(默认预付费,可以选按量付费,有一定免费额度的,无论个人开发调试还是公司项目使用,选按量付费就好了) [图片] 开通成功之后,概览界面右侧,能看到环境id,记住这个id,后边配置h5页面会用到。 [图片] 1-2 设置云开发权限,设置——权限设置,未登录用户访问云资源权限设置,勾选未登录用户访问权限: [图片] 1-3 开通镜头网站,菜单栏”更多“——静态网站——开通 [图片] [图片] 扫码确认,等初始化,可能需要点时间,无需等待,开通成功会有短信通知,可以先进行后边步骤 [图片] 2.创建云开发小程序、设置云开发环境录用户访问云资源权限设置 2-1 创建云开发小程序: 如果小程序已经是云开发服务了,那就可以直接用了。如果不是,那就创建一个云开发小程序。 这个小程序只是用来上传云函数的,appid要填要开通云开发功能的小程序,即h5要跳转的小程序。另外后端服务要选微信云开发,至于模板,随便选个及就行。只是为了实现跳转小程序这个功能,上传云函数而已,上传之后就没用了(除非要更新云函数)。 [图片] 云开发小程序创建好之后,可以看到有 cloudfunctions 和 miniprogram 两个目录。cloudfunctions 目录就是我们创建、上传云函数要用的目录了。 [图片] 2-2 设置云开发环境: cloudfunctions 目录上边单击鼠标右键,设置当前环境,选择前边创建的云环境 [图片] 3.创建云函数 请注意,这里的创建云函数不是在云开发控制台直接创建的,是需要通过小程序创建并部署的。虽然控制台也可以直接创建,但是不能直接用。别问我怎么知道,踩过这个坑而已。 3-1 下载官方云函数示例代码(点击下载),源码目录如下: [图片] 3-2 将 cloudfunctions 目录中的 public 文件夹复制到云开发小程序项目 cloudfunctions 目录下: [图片] 3-3 编辑 public/index.js 文件,将 getUrlscheme函数中的path改成要调起小程序的页面路径。 [图片] 相关代码: // 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init() // 云函数入口函数 exports.main = async (event, context) => { const wxContext = cloud.getWXContext() switch (event.action) { case 'getUrlScheme': { return getUrlScheme() } } return 'action not found' } async function getUrlScheme() { return cloud.openapi.urlscheme.generate({ jumpWxa: { path: '/pages/login/index', // query: 'i=a', }, // 如果想不过期则置为 false,并可以存到数据库 isExpire: false, // 一分钟有效期 expireTime: parseInt(Date.now() / 1000 + 60), }) } 4.安装依赖 鼠标放到public目录上,单击鼠标右键,快捷菜单选中"在内建终端中打开",然后运行 npm install 安装依赖 [图片] 5.上传部署云函数: public 目录上,单击鼠标右键,选择”上传并部署:云端安装依赖(不上传node_modules), [图片] 部署后,打开云开发控制台——云函数,就能看到前边创建部署的云函数了: [图片] 等几秒钟,云函数就能部署成功了。 6.修改云函数权限: 云开发控制台——云开发——云开发权限,自定义安全规则,点击右侧的“修改” [图片] 点允许所有用户访问,这样所有用户都可以免鉴权通过h5调起小程序了 [图片] 可选的进阶的云函数安全规则模板(只放开了让 public 云函数支持未登录访问) { // * 为通配符,表示对所有函数适用 "*": { // invoke 表示调用权限控制 // auth 包含鉴权信息,如果是未登录模式,则 auth == null "invoke": "auth != null" }, // 函数名,该规则优先级会高于通配符 "public": { // 表示允许所有来源调用,包括未登录用户 "invoke": true } } 7.编写及上传h5静态页面到云开发环境 编辑前边下载的 h5/jump-mp.html 文件,将所有标注<!-- replace -->的地方都修改成真实的内容就好了。示例代码如下: <html> <head> <title>打开小程序</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <script> window.onerror = e => { console.error(e) alert('发生错误' + e) } </script> <!-- weui 样式 --> <link rel="stylesheet" href="https://res.wx.qq.com/open/libs/weui/2.4.1/weui.min.css"> </link> <!-- 调试用的移动端 console --> <script src="https://cdn.jsdelivr.net/npm/eruda"></script> <script>eruda.init();</script> <!-- 公众号 JSSDK --> <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script> <!-- 云开发 Web SDK --> <script src="https://res.wx.qq.com/open/js/cloudbase/1.1.0/cloud.js"></script> <script> function docReady(fn) { if (document.readyState === 'complete' || document.readyState === 'interactive') { fn() } else { document.addEventListener('DOMContentLoaded', fn); } } docReady(async function() { var ua = navigator.userAgent.toLowerCase() var isWXWork = ua.match(/wxwork/i) == 'wxwork' var isWeixin = !isWXWork && ua.match(/micromessenger/i) == 'micromessenger' var isMobile = false var isDesktop = false if (navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|IEMobile)/i)) { isMobile = true } else { isDesktop = true } if (isWeixin) { var containerEl = document.getElementById('wechat-web-container') containerEl.classList.remove('hidden') containerEl.classList.add('full', 'wechat-web-container') var launchBtn = document.getElementById('launch-btn') launchBtn.addEventListener('ready', function(e) { console.log('开放标签 ready') }) launchBtn.addEventListener('launch', function(e) { console.log('开放标签 success') }) launchBtn.addEventListener('error', function(e) { console.log('开放标签 fail', e.detail) }) wx.config({ debug: false, // 调试时可开启 appId: 'wxad8exxxx', // <!-- replace --> timestamp: 0, // 必填,填任意数字即可 nonceStr: 'nonceStr', // 必填,填任意非空字符串即可 signature: 'signature', // 必填,填任意非空字符串即可 jsApiList: ['chooseImage'], // 必填,随意一个接口即可 openTagList: ['wx-open-launch-weapp'], // 填入打开小程序的开放标签名 }) } else if (isDesktop) { // 在 pc 上则给提示引导到手机端打开 var containerEl = document.getElementById('desktop-web-container') containerEl.classList.remove('hidden') containerEl.classList.add('full', 'desktop-web-container') } else { var containerEl = document.getElementById('public-web-container') containerEl.classList.remove('hidden') containerEl.classList.add('full', 'public-web-container') var c = new cloud.Cloud({ // 必填,表示是未登录模式 identityless: true, // 资源方 AppID resourceAppid: 'wxad8exxxx', // <!-- replace --> // 资源方环境 ID resourceEnv: 'cloud-mall-2gs0uxxxx', // <!-- replace --> }) await c.init() window.c = c var buttonEl = document.getElementById('public-web-jump-button') var buttonLoadingEl = document.getElementById('public-web-jump-button-loading') try { await openWeapp(() => { buttonEl.classList.remove('weui-btn_loading') buttonLoadingEl.classList.add('hidden') }) } catch (e) { buttonEl.classList.remove('weui-btn_loading') buttonLoadingEl.classList.add('hidden') throw e } } }) async function openWeapp(onBeforeJump) { var c = window.c const res = await c.callFunction({ name: 'public', data: { action: 'getUrlScheme', }, }) console.warn(res) if (onBeforeJump) { onBeforeJump() } location.href = res.result.openlink } </script> <style> .hidden { display: none; } .full { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } .public-web-container { display: flex; flex-direction: column; align-items: center; } .public-web-container p { position: absolute; top: 40%; } .public-web-container a { position: absolute; bottom: 40%; } .wechat-web-container { display: flex; flex-direction: column; align-items: center; } .wechat-web-container p { position: absolute; top: 40%; } .wechat-web-container wx-open-launch-weapp { position: absolute; bottom: 40%; left: 0; right: 0; display: flex; flex-direction: column; align-items: center; } .desktop-web-container { display: flex; flex-direction: column; align-items: center; } .desktop-web-container p { position: absolute; top: 40%; } </style> </head> <body> <div class="page full"> <div id="public-web-container" class="hidden"> <p class="">正在打开 “xxxx”...</p> <!-- replace --> <a id="public-web-jump-button" href="javascript:" class="weui-btn weui-btn_primary weui-btn_loading" onclick="openWeapp()"> <span id="public-web-jump-button-loading" class="weui-primary-loading weui-primary-loading_transparent"><i class="weui-primary-loading__dot"></i></span> 打开小程序 </a> </div> <div id="wechat-web-container" class="hidden"> <p class="">点击以下按钮打开 “xxxx”</p> <!-- replace --> <!-- 跳转小程序的开放标签。文档 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_Open_Tag.html --> <wx-open-launch-weapp id="launch-btn" username="gh_783bxxxx" path="/pages/login/index"> <!-- replace --> <template> <button style="width: 200px; height: 45px; text-align: center; font-size: 17px; display: block; margin: 0 auto; padding: 8px 24px; border: none; border-radius: 4px; background-color: #07c160; color:#fff;">打开小程序</button> </template> </wx-open-launch-weapp> </div> <div id="desktop-web-container" class="hidden"> <p class="">请在手机打开网页链接</p> </div> </div> </body> </html> 关键修改点在这三处: [图片] [图片] [图片] 8上传h5页面到云开发环境: 云开发控制台——更多——静态网站——文件管理,点击上传文件,选择上边改好的h5页面即可 [图片] 上传成功: [图片] 9.测试验证、配置域名: 静态网站窗口,选择”网站配置“,可以看到已经分配了测试域名,可以直接复制这个页面到手机浏览器中直接打开,如果前边操作没有遗漏的话,就能在浏览器中调起微信小程序了。 [图片] 四.开发中遇到的异常情况排雷: 1.-501000:environment not found 未找到环境,检查html文件中云环境id配置,直接复制云开发控制平台概览中的环境ID就好了,如下: [图片] [图片] 2.-501023: permission denied 没有权限,云开发控制平台——设置——权限设置——勾选”未登录用户访问权限“就好了 [图片]
2022-06-28 - H5页面分享不显示自定义标题和图片?
原因是H5用旧的方式申请JSAPI分享权限,后台返回无权限,因此分享的时候不能自定义,建议开发者接入新的分享方式:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html ,使用新的wx.updateAppMessageShareData和wx.updateTimelineShareData接口来设置分享数据。
2020-04-23 - 打开另一个小程序,需要用户确认跳转
请问同一个企业认证的多个小程序跳转,如何做到相互打开,不需要用户确认跳转
2023-10-19 - 社区每周 | 小程序链接生成与使用规则调整、公益编程挑战赛报名及上周问题反馈(2.28-3.04)
各位微信开发者: 以下是小程序链接生成与使用规则调整公告、公益编程挑战赛报名及上周我们在社区收到的问题反馈的处理进度,希望与大家一同打造更好的小程序生态! 小程序链接生成与使用规则调整公告 为确保小程序链接合理使用,自 2022 年 4 月 11 日起,URL Scheme 和 URL Link (以下统称为 “链接”)接口能力规则将进行以下调整: 每个 URL Scheme 或 URL Link 有效期最长 30 天,均不再支持永久有效的链接、不再区分短期有效链接与长期有效链接;链接生成后,若在微信外打开,用户可以在浏览器页面点击进入小程序。每个独立的链接被用户访问后,仅此用户可以再次访问并打开对应小程序,其他用户无法再次通过相同链接打开该小程序;单个小程序每天生成链接数(URL Scheme 和 URL Link 总数)上限为 50 万条。调整详情可点击查看原公告:《小程序链接生成与使用规则调整公告》 公益编程挑战赛报名开启 公益编程挑战赛以云开发系列产品(包含云开发、云托管、微搭低代码)为技术平台,以社会公益为主题,招募广大开发者组成 3~4 人的团队,创作具有应用潜力和社会价值的小程序/公众号网页项目,最高可获得 50 万奖励。预选报名将于 2022 年 4 月 1 日截止,快来报名,用技术创造更大的影响力! 赛事报名: [图片] 点此了解赛事详细规则 上周问题反馈和处理进度(2.28-3.04) 已修复的问题某场景下,数组中被删除的元素没有删除而是变成 null 的问题 查看详情 开发者工具开启按需注入后,组件样式污染页面样式的问题 查看详情 云开发单日出现多项系统异常错误的问题 查看详情 修复中的问题 getLatestUserKey 密钥过期后仍返回过期密钥的问题 查看详情 昵称填写在页面跳转时没有消失的问题 查看详情 微信团队 2022.3.11
2022-03-11 - 如何生成可跳转到小程序的外链?
请问各位大佬,微信或腾讯生态下有相关生成可跳转到小程序的外链工具吗?
2023-07-05 - 发货信息管理怎么关闭?
请问一下为什么我们正儿八经的开店,给我们小程序来了一出发货信息管理,怎么关闭? 如果不能关闭,那么请问一下怎么样才能和我们小程序商城的订单物流信息同步?? 现在的问题是我们商城快递签收了,客户也确认收货了,你们腾讯的小程序后台还是没任何物流状态更新。把我们的货款卡着好玩是吧?? 再不处理我直接报警,再不行直接起诉,妈的真的特么火大,什么个垃圾!!
2023-05-10 - 小程序发货信息管理服务 有预售商品 订单很多 如果通过服务器API请求 特殊发货报备?
我的小程序 会有很多订单属于预售订单,有的订单得等1-2月等东西做好了才会发货,此时需要 小程序发货信息管理服务 特殊发货报备 ,此时需想问下如何通过API请求向微信后台发送这些单子。如果由用户复制这些单子再去小程序后台页面填写这些很麻烦且容易操作失误。想问下我该如何解决这个问题。
2023-09-10 - 关于小程序待接入订单发货管理的通知?
今天客户莫名其妙出现了一个通知,提示要对接发货管理。导致客户小程序无法正常运营。 [图片] 我们是三方服务,请问如何给客户关闭这个发货管理,让客户小程序能力恢复正常。急急急!!!! 客户appid:wx13fb7ca5f171bc42
2023-09-21 - 小程序的发货管理的订单,必须在48小时内发货吗?
最近被开通了发货管理,我看运营规范是48小时内,可以超过48小时吗?我们货物时间周期长。
2023-09-04 - 实体物品电商都需要接入订单发货管理?
我们小程序是第三方的电商平台,现在微信已经把我们的支付功能封了,需要我们对接直连商户号才行。 我刚才测试了下微店等第三方电商平台的微信小程序,他们仍然使用的是微信支付的间联(服务商)方式啊,为什么只封我们?
2023-09-25 - Skyline | 快速搞定复杂的分享海报
在小程序中生成海报是一种非常有效的推广方式 用户可以使用小程序的过程中生成小程序海报并分享给他人 通过海报的形式,用户可以直观地了解产品或服务的特点和优势 [图片] 常见绘制海报方式 目前,小程序海报有两种常见的实现方式: · canvas 绘制海报 · 服务端绘制海报 这两种方式各有千秋 canvas 绘制海报使用 canvas 绘制海报主要有以下几个步骤 1、创建 [代码]canvasContext[代码] 2、获取网络图片的本地路径 3、绘制图片、文字等到 [代码]canvas[代码] 4、调用 [代码]wx.canvasToTempFilePath[代码] 导出图片 尽管 canvas 绘制功能强大,但实际使用中,这些操作看似简单,但调试起来却比较麻烦 而且面对一些复杂的排版时,使用 canvas 绘制相较于使用 CSS 绘制来说困难许多 除此之外,canvas 的宽高有最大限制,超出限制则会绘制空白 服务端绘制 小程序也可以通过调用服务端接口,将需要生成海报的数据传递给服务端, 由服务端使用 Canvas API 等第三方库来生成图片。 然而,这种绘制方式需要走网络请求,如果量大会给服务器带来一定的成本压力。 此外,对于复杂排版的实现,使用 Canvas 绘制也有一定的难度。 尽管小程序海报虽然好用,但是当遇到要求比较高的设计稿需要还原海报时,对小程序开发者来说是一个十分让人头疼的问题 考虑到海报在小程序中使用的广泛性,我们把开发者的烦恼交给官方来处理~ 小程序官方推出了 [代码]snapshot[代码] 组件,可以直接将小程序 wxml 导出图片。 snapshot 生成海报 当使用 canvas 或 服务端绘制海报遇到复杂排版时,如 圆角、百分比、自定义字体 等等,实现比较困难。 但是使用 wxml 实现却很简单 👇 下面的例子我们使用 wxml 实现海报 <view class="snapshot-box"> <view class="poster-container"> <view class="poster-header"> <image /> ... </view> <view class="description"> ... </view> <view class="footer"> ... </view> </view> </view> [图片] 接着,我们就可以导出海报啦,使用非常简单: 1、用 [代码]snapshot[代码] 组件包裹海报的 wxml 2、调用 [代码]takeSnapshot[代码] 获取图片数据 3、调用 [代码]fs.writeFileSync[代码] 将海报数据写入本地文件 4、调用 [代码]wx.saveImageToPhotosAlbum[代码] 将海报保存到本地 <snapshot id="view"> <!-- 这里是要海报的 wxml --> </snapshot> <button bindtap="tap">保存海报</button> tap() { this.createSelectorQuery().select("#view") .node().exec(res => { const node = res[0].node // 保存海报 node.takeSnapshot({ type: 'arraybuffer', format: 'png', success: (res) => { const f = `${wx.env.USER_DATA_PATH}/hello.png` const fs = wx.getFileSystemManager(); // 将海报数据写入本地文件 fs.writeFileSync(f, res.data, 'binary') this.setData({ img: f }) // 把海报图片保存到本地 wx.saveImageToPhotosAlbum({ filePath: f }) } }) }) } 最后我们来看看使用 [代码]snapshot[代码] 组件生成海报的效果吧~ [图片] 除了普通尺寸分享海报之外,对于 canvas 无法搞定的超长海报,[代码]snapshot[代码] 后续也会支持超长海报的导出~ [图片] 你的小程序也有海报生成需求吗? 赶紧 mark 下这个 代码片段 来接入使用吧~
2023-09-06 - 小程序渲染引擎Skyline小试牛刀--快书
今年年初,在官方文档上看到小程序团队要推出一款性能逼近原生的渲染引擎Skyline,就一直在关注。刚好最近打算做一款新的阅读小程序,作为一名独立开发者,对于性能和用户体验的追求是永无止境的,于是我决定用纯Skyline打造这款小程序。 当然,这个项目里面所用到的skyline特性只是冰山一角,并非全部,更多酷炫的特性请前往官方文档查阅。 接下来,我会结合快书小程序,从以下几个方面,逐条阐述关于skyline特性(快书项目中所用到的)的理解与应用: 效果演示。如何开启Skyline。新版组件swiper。新版组件scroll-view。全新组件snapshot。增强特性worklet动画。增强特性手势系统。增强特性自定义路由。增强特性共享元素动画。希望对于刚接触Skyline,或者想要了解Skyline的同学有所帮助。当然,如有错误或遗漏,欢迎在评论区批评指正,不胜感激。 一、效果演示 [图片] 二、如何开启Skyline 开启Skyline的方式非常简单,只需要在app.json文件中,加入以下配置即可(这里是全局Skyline,若只打算指定页面开启,则在指定页面的json文件中配置即可): "renderer": "skyline", "lazyCodeLoading": "requiredComponents", "rendererOptions": { "skyline": { "defaultDisplayBlock": true, } }, "componentFramework": "glass-easel", 三、新版组件-Swiper 旧版的Swiper基于webview的,在性能上有所局限,特别是当swiper-item的数量动态不断增加的情况下。当然,也可以自己想办法去优化,比如做懒加载和缓存,但相对来说比较麻烦。而Skyline版本的Swiper性能会大幅度提升,首先渲染引擎本身的性能提升了,另外官方也做了缓存的功能,只需要通过定义cache-extent的值,就能轻松定义缓存区域大小,例如值为 1 则表示提前渲染上下各一屏区域。 [图片] 用法上,和webview版本没有太大区别(这里就不放代码了),只需注意不要使用某些webview独有的特性即可。 四、新版组件-Scroll-view 同样,旧版的scroll-view也基于webview的,滚动元素过多的时候会有明显卡顿,当然也是可以通过虚拟Dom的方式自行优化。然而,Skyline版本的scroll-view官方已经实现了只会渲染在屏节点的特性,大大提升了滚动的流畅度,真正做到了开箱即用。 用法上,有以下几个点要注意的。 指定type属性,有2个可选值,分别为:list和custom,对应的是列表模式和自定义模式。如是普通列表,list即可,如果是稍微复杂的列表,比如常见的瀑布流表现形式(类似小红书那样),则可使用custom。只有直接子节点才能根据是否在屏来按需渲染。即你不能把你的列表项,都放在同一个父级view中,而是应该直接放在scroll-view组件下。 // 错误的方式: <scroll-view type="list" scroll-y> <view> <view class="item" wx:for="{{dataList}}" wx:key="id"></view> </view> </scroll-view> // 正确的方式 <scroll-view type="list" scroll-y> <view class="item" wx:for="{{dataList}}" wx:key="id"></view> </scroll-view> // 正确的方式 <scroll-view type="custom" scroll-y> <list-view> <view class="item" wx:for="{{dataList}}" wx:key="id"></view> <list-view> </scroll-view> 另外,上面提到了瀑布流的问题,实现方式也很简单,官方提供了一个叫做grid-view的组件,只需定义它的type="masonry"即可,但若是在webview下,除了性能不理想以外,还会有一些小BUG,比如我在社区提的这个问题:grid-view masonry 在webview模式下经常会出现大块区域的空白。在Skyline下,就不会出现此类问题。 [图片] <scroll-view type="custom" scroll-y> <grid-view type="masonry" main-axis-gap="15" cross-axis-gap="15"> <view wx:for="{{dataList}}" wx:key="id"></view> </grid-view> </scroll-view> 五、全新组件Snapshot 我们常常会有分享精美海报的需求,但由于海报上的内容是动态,仅仅使用一张图片分享达不到我们的目的。在以往,我们可能会使用到wxml-to-canvas,通过绘制 canvas ,导出图片。现在,在Skyline下(基础库3.0.0以上),实现此需求就非常简单。只需要将我们要分享的内容包裹在snapshot组件下就行。 [图片] // wxml: <snapshot id="target"> <view>content</view> </snapshot> // page: Page({ onReady() { this.createSelectorQuery() .select("#target") .node() .exec(res => { const node = res[0].node node.takeSnapshot({ type: 'arraybuffer', format: 'png', success: (res) => { fs.writeFileSync(savePath,res.data,'binary'); //图片保存至本地 wx.showShareImageMenu({ //唤起分享图片的界面 path:savePath }) }, fail(res) {} }) } }) 六、增强特性-worklet动画 worklet动画相比传统的方式,流畅度提升了不少,但如何使用呢?常见的普通动画无非是对于页面元素的平移,缩放,旋转等变换。那么,要让一个元素动起来,只需要做以下2件事: 将页面元素的样式与某个变量进行绑定,变量值的变化会自动触发样式的更新。实时动态地改变这个变量。结合快书的例子(下拉时,让页面缩小,松手后,页面弹回),来看一下具体的实现步骤。 [图片] 首先,如何绑定样式与参数呢?通过官方提供的一个applyAnimatedStyle函数: // Wxml: <view id="#box">content</box> // Page: this.scale = shared(1); //这里是定义一个共享变量,即可在UI线程和JS线程间同步的变量。 this.applyAnimatedStyle(`#box`, () => { 'worklet'; // 声明这是一个worklet函数 return { transform: `scale(${this.scale.value})`, }; }); // 1、这里使用共享变量是为了让后续改变这个变量时,worklet的函数能捕获到。 // 2、#box你要动起来的元素 // 3、当this.scale.value变化时,会自动触发函数体的执行,从而改变#box的样式 第二步,下拉时,根据下拉的偏移量,改变这个scale的值。 this.scale.value = (evt.deltaY / 100) * 0.15; // 这里的evt.deltaY是下拉时的位置偏移量,然后根据偏移量按比例计算缩放的值 // 如何获取这个下拉偏移量?下一小节讲手势系统时会讲到 第三步,松手时,复原scale的值。 this.scale.value = timing(1, { duration: 300, easing: Easing.ease }); // timing函数表示:在300毫秒内,scale.value会逐渐变成1 // easing: Easing.ease 表示缓动的方式,具体可参考https://easings.net // 如何知道已经松手了?下一小节讲手势系统时会讲到 更多动画参考请查阅官方文档。 七、增强特性-手势系统 还是上面那个例子,我们只说了下拉时根据下拉的偏移量改变scale的值,那如何得到下拉的偏移量呢?这里就涉及到了手势系统。下面讲讲如何让一个元素能响应拖动,缩放等手势。 说回上一小结的例子,我们只需要讲#box元素包裹在手势组件vertical-drag-gesture-handler即可。更多示例可查阅官方文档 // wxml: <vertical-drag-gesture-handler worklet:ongesture="handlePan"> <view id="box"></view> <vertical-drag-gesture-handler> // page: handlePan(evt) { 'worklet' if (evt.state === GestureState.ACTIVE) { // 拖拽时 if (evt.deltaY > 0) { // 下拉 this.scale.value = Math.max(this.scale.value - (evt.deltaY / 100) * 0.15, 0.85); } else { // 上拉 this.scale.value = Math.min(this.scale.value - (evt.deltaY / 100) * 0.15, 1); } } else if (evt.state === GestureState.END || evt.state === GestureState.CANCELLED) { // 拖拽结束或取消 this.scale.value = timing(1, { duration: 300, easing: Easing.ease }); } }, 然而,当手势组件与scroll-view等可以滚动的组件嵌套时,会出现冲突的问题。比如,同上一小节的示例,为了让文章内容过长时可以滚动,我们需要将文章的内容放在scroll-view中。当scroll-view已经滚动到顶部,再继续下拉的话,应当触发手势组件的拖拽事件,即缩放页面。相反,则继续滚动scroll-view。 [图片] // wxml: <vertical-drag-gesture-handler tag="pan" worklet:ongesture="handlePan" shouldResponseOnMove="shouldPanResponse" simultaneousHandlers="{{['scroll']}}"> <vertical-drag-gesture-handler tag="scroll" native-view="scroll-view" shouldResponseOnMove="shouldScrollResponse" simultaneousHandlers="{{['pan']}}"> <scroll-view type="list" scroll-y bindscroll="handleContentScroll">文章内容</scroll-view> </vertical-drag-gesture-handler> </vertical-drag-gesture-handler> // page: // 处理scroll-view的滚动事件,获取scrollTop的值 handleContentScroll(evt) { 'worklet' this.scrollTop.value = evt.detail.scrollTop; }, // return false 则表示scroll-view不再响应滚动事件 shouldScrollResponse(evt) { 'worklet'; const { deltaY } = evt if (this.scrollTop.value <= 0 && deltaY > 0) { //scroll-view已经滚动到顶部,继续下拉时 this.pan.value = true; return false; } if (this.scale.value < 1 && deltaY < 0) { //#box已经被缩放,继续上拉时 this.pan.value = true; return false; } this.pan.value = false; return true; }, shouldPanResponse() { 'worklet' return this.pan.value; // true表示响应手势组件的拖拽事件,false则不响应 }, 八、增强特性-自定义路由 以往在webview中,路由的的过渡动画仅支持从右到左,较为单调。在skyline之后,我们可以自定义路由的过渡动画了,比如常见的淡入淡出,从底部弹起等。比如以下这个例子,从首页点击图片,会跳转到分享的页面,这里就是用自定义路由实现的淡入效果。 [图片] 自定义路由的使用相比前几个特性稍微复杂一点,这里官方讲的更为具体和清晰,可查阅官方文档。唯一要注意的一点是,只有连续的skyline页面跳转时,才会有效果。 九、增强特性-共享元素动画 还是上面的例子,当从首页点击图片跳转到分享页面时,图片看起来像是从首页飞到了分享页,这里便是使用到了共享元素动画。我同时也做Flutter的开发,所以这里看起来非常类似Flutter的hero动画或者叫飞行动画。 使用方式也类似于Flutter。将2个页面的相似组件都用share-element组件包括起来,并且使用相同的key即可。再配合自定义路由,可使得飞行动画看起来非常的丝滑。 // A页面: <share-element key="唯一key"> <image src="imagePath" mode="aspectFill" /> </share-element> // B页面: <share-element key="唯一key"> <image src="imagePath" mode="aspectFill" /> </share-element> // 有几个要注意的地方 // 1、两个个页面的share-element组件必须使用相同的key。 // 2、key是唯一的,即同一个页面中,不能出现重复的key。 // 3、image不要写死宽高,应百分比100%,具体宽高数值写在share-element组件上。 有一个常见的问题,A页面是一个列表,B页面是详情页,列表中的数据都是通过接口从后台返回的,由于共享元素的key又不能重复,那么这个key怎么定义?一般后台返回的数据都会有一个唯一标识,假设为ID,我们可以用这个ID当作Key。 但是,另一个问题来了,如果数据是后台接口返回的,然后通过setData的方式响应到页面,那么很有可能B页面的首帧获取不到这个Key,因为这时候接口请求可能还未完成,那么动画也是不会生效的。针对这种情况,官方也提供了一种方式: 共享元素动画需保证下一个页面首帧即创建好 [代码]share-element[代码] 节点,并设置了 key,用于计算目标位置。如果是通过 [代码]setData[代码] 设置的,可能会错过首帧。针对这种情况,可以 使用 Component 构造器构造下一个页面,只要在组件 [代码]attached[代码] 生命周期前(含)通过 [代码]setData[代码] 设置上去,就会在首帧渲染 十、总结 Skyline还有一些其他有趣的特性,大家感兴趣的话可以查阅官方文档。总的来说,相比起webview,skyline对性能的提升是显而易见的,并且,一些在webview很难实现的效果,在skyline的基础上,也能轻易实现,开箱即用。目前,skyline还在不断地迭代中,还有许多的新特性还在评估和开发中,相信之后的版本会更完善更好用。 最后,大家多多使用快书呀,球球了~ [图片]
2023-09-01 - 云开发怎么实现商家转账到零钱的功能,有demo吗?
以前叫企业转账到零钱,当时网上能找到比较完整的demo,现在换成了v3接口了,找不到完整的资料,能找到的都是其他开发语言的demo,试问下有没有像下面这种方法在云开发中实现的例子 [图片]
2022-06-14 - 微信支付的文档、工具升级,我们需要你的反馈!
亲爱的微信支付商户: 大家好!微信支付官网团队一直在不懈努力,致力于提供更优质的文档和开发工具,助力开发者更高效地接入微信支付。此次,我们以【商家转账到零钱】为例,进行了一次全面的优化改版,期待借此机会能够听到您的宝贵意见和建议。 首先,我们重新设计了文档的内容架构,使其更加清晰、易于理解;同时,对所有文档进行了整理和优化,确保它们都符合新的内容架构,提升了文档的可读性和易用性;此外,我们还重点对开发工具进行了优化,帮助开发者提升接入效率。 在此向您介绍一下主要的改动点: [图片] 商家转账到零钱文档体验地址:新版 丨 旧版 问卷链接:https://wj.qq.com/s2/12944401/f109/ 期望收集的问题方向: 文档结构的优化:文档的组织方式是否清晰,是否容易找到所需信息?文档的易读性和准确性:文档的语言是否清晰易懂且准确无误,是否有过于复杂或难以理解的部分?开发者教程和指南:关键概念、开发指引、开发工具和库、最佳实践这些内容是否能够满足开发需求,是否需要更多的教程和指南,以帮助开发者更好地使用和理解我们的产品?示例Demo的质量:示例Demo是否足够清晰,是否能有效地帮助理解文档内容?文档的反馈机制:文档的反馈机制是否有效,是否容易提交反馈和建议?其他的意见和反馈 我们深知,每一次的改进和优化,都离不开您的支持和建议。我们期待您的反馈,一起让微信支付变得更好!
2023-08-25 - 更新3.0基础库,上传图片文件之类的api 不触发了?
更新3.0基础库后,wx.chooseImage、wx.chooseMedia、wx.chooseMessageFile 这些上传之类的api,在真机和模拟器上都不触发了。2.33.0基础库正常
2023-08-16 - 关于小程序隐私保护指引设置的公告
为规范开发者的用户个人信息处理行为,保障用户的合法权益,自2023年9月15日起,对于涉及处理用户个人信息的小程序开发者,微信要求,仅当开发者主动向平台同步用户已阅读并同意了小程序的隐私保护指引等信息处理规则后,方可调用微信提供的隐私接口。 开发者首先需确定小程序是否涉及处理用户个人信息,如涉及,则需配置用户隐私授权弹窗,且仅有在平台《小程序用户隐私保护指引》中声明了所处理的用户个人信息,才可以调用平台提供的对应接口或组件。(隐私相关接口) 隐私协议设置整体流程参考下方指引: 一、设置《小程序用户隐私保护指引》 开发者需在「小程序管理后台」设置《小程序用户隐私保护指引》 [图片] [图片] 二、填写《小程序用户隐私保护指引》 [图片] 只有在指引中声明所处理的用户个人信息,才可以调用平台提供的对应接口或组件。若未声明,对应接口或组件将无法调用成功。隐私接口与对应的处理的用户个人信息关系可见:小程序用户隐私保护指引内容介绍 三、配置用户隐私授权弹窗 微信提供了wx.onNeedPrivacyAuthorization(function callback) 接口,意为用户触发了一个微信侧未记录过同意的隐私接口调用,开发者可通过响应该事件选择提示用户的时机。此外,微信还提供了 wx.requirePrivacyAuthorize(Object object) 接口,可用于模拟触发 onNeedPrivacyAuthorization 事件。 小程序开发者可自行设计提示方式与触发时机,详细文档可查看隐私协议开发指南。 仅有在指引中声明所处理的用户个人信息,才可以调用平台提供的对应接口或组件。若未声明,对应接口或组件将直接禁用。 [图片] (参考样例) 四、如要进行代码提审,开发者需先自行声明是否有采集用户隐私,如有,则需在提审页面-「用户隐私保护设置」选择“采集用户隐私” [图片]
2023-09-18 - chooseMedia和chooseImage特定手机都失败?
报错信息:chooseMedia:fail privacy permission is not authroized 我的手机测试突然之间一直报这个错,之前都是好的。试了其他机型也都是正常的。 微信最新版 机型:nova10
2023-08-15 - 刚收到通知获取手机号收费开始了?
[图片] https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getRealtimePhoneNumber.html https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/paymentManage.html 未来:旧版本接口依然可以使用,只是需要收费而已,不想做改动的交钱就行了。如果想用新API方法就去改吧,多花一分钱。 可能支持的省钱办法: 授权手机号后,服务端将openId、手机号进行绑定。用户onLaunch打开小程序的时候通过wx.login获取code去解密openId,同时由于服务端已经绑定过手机号,所以可以使用该手机号进行登录,并同步返回token、jwtToken等登录态。这样可以做到用户冷启动小程序时自动登录上,减少使用授权的逻辑。业务按钮点击后 先调用wx.login,如果返回token则进行后续业务,如果没返回则弹出自定义弹窗,弹窗内点击按钮再进行手机号授权。(也可以在部分页面onLoad里wx.login),这个场景因为会延长流程,所以产品说不考虑,先直接打开页面就登录上,你们的各自看各自的业务场景吧。然后有四个疑问: 充值购买次数后会,如果小程序被封禁了,充值的金额是否可退款。购买数量是否支持按量付费?如果次数用完了,未购买新的次数,用户端的表现是什么?如果次数用完了,之前文档说的余量20%、10%、5%时会发模板消息提醒,文档相关现在已经删除了,是否还会发?[图片] ———————————————————————————————————————————————— 今天看了下文档做了改动: 退款规则:若购买有误,且未正式开始使用资源包前,可以在支付成功后的7天内申请退款。款项将在3-5个工作日内从原支付路径返回;若资源包已经开始使用(使用1次及以上),则不能申请退款;若支付成功后超过7天,未发起退款申请,亦不能再申请退款。 那么小程序被封了应该是不退的。不确定,等官方回复次数用完了,用户授权不会弹出授权弹窗,会返回一个errNo:1400001,用户判断等于这个errNo的时候跳转到自己的账密登录页面。不确定,等官方回复———————————————————————————————————————————————— 据了解老版本的快速验证组件(获取手机号),180天才会发送短信验证一次,为啥能每次授权都收费0.03元。 社区搜了一张图,180天没验证的应该会弹这个,不是说是短信运营成本么?为啥不是第180天验证那次费用让我们付,而是每次授权都付? [图片] 手机号授权改造后的效果: 打开职位详情页:优先调用接口判断openId是否绑定过。 如果未绑定:使用button的open-type=“getPhoneNumber”,点击报名弹出手机号授权,授权成功后与openId进行绑定落库。 如果已绑定,页面通过变量判断使用wx.login静默授权,同时服务端拿到绑定的手机号后进行登录操作,同步返回登录态(token/jwtToken)。 退出登录页面增加解绑操作(服务端解除openId与手机号的绑定),此时用户再次点击报名,就会弹出手机号授权,方便用户切换手机号。 [视频]
2023-07-27 - 【重要】关于11月25日商家转账到零钱新产品策略正式上线的通知
尊敬的微信支付商户: 为保障商户资金安全、优化商户体验,商家转账到零钱产品将于11月25日起对现有产品能力逐步调整,主要包括: 1、批次转账的明细数量上限调整; 2、商户基于转帐场景提交资料,平台将基于不同场景确定商家转账限额及使用方式; 3、支持下载5年内账单并优化业务账单格式,同时支持下载2年内电子回单。 详情请参考:商家转账产品文档 特别提示:其中使用转账给员工、付款给合作伙伴的场景,需要在商户平台-商家转账到零钱添加收款用户openid及姓名,仅可向列表中已添加且姓名一致性校验通过的用户转账。 为了不影响您的使用,请尽快在商户平台添加需要付款的用户openid及姓名,否则调用转账接口会返回报错信息“未配置收款用户列表,请前往商户平台-产品中心 - 商家转账到零钱 - 转账场景中添加”或“该openid不在收款用户列表中,请前往商户平台-商家转账到零钱-转账场景中添加”。 财付通支付科技有限公司 2022年11月25日
2022-11-29 - 企业付款到银行卡已下线?
微信支付商户经营工具-「企业付款到银行卡」, 这个功能是不是没有拉? 后台都没有显示了,只有付款到零钱
2022-08-05 - 境外小程序支付攻略(云开发篇),看这一篇就够了。
一、前言 境外微信支付的实现方式,与国内支付的方式大不同,即不是V2,也不是V3,而是经由海外支付服务商,通过间连的方式来实现的。(香港地区融合钱包除外)。 以下专门介绍具体的实现攻略: 二、开通 首先,你需要先了解当地有哪些第三方支付服务商,然后选择其中一家,接入他们提供的支付服务,从而实现微信支付的开通。 1、全球第三方支付服务商: https://pay.weixin.qq.com/index.php/public/wechatpay_en/partner_search#/ 找到当地服务商的联系方式,与他们联系,他们会告诉你所需提供哪些资料,并帮你开通商户号,接入微信支付; 资料信息包括: 营业执照 翻译认证 公司银行账户 法人及联系人信息 其他信息等。 2、香港融合钱包 融合钱包以下不做介绍,技术方面和v3支付完全一样。 技术方案可以参考: https://pay.weixin.qq.com/wiki/doc/api/wxpay/en/pages/MiniProgramPay_fw.shtml 三、第三方服务商提供的支付服务 每个第三方服务商所提供的支付服务各不相同,以下分别举例: Yaband Pay (Europe PayPro B.V.): 微信支付, 支付宝, PayPal, SOFORT, IDEAL, 银联支付, List 2paynow (Europe Sepay B.V.): 微信支付, 支付宝 VR Payment (Europe CardProcess GmbH): 微信支付, 支付宝 Supay (Australia): 微信支付, 信用卡支付 其他地区第三方支付服务商,提供哪些服务,需要向他们咨询。 注: 1、境外小程序对除微信支付之外的支付方式,卡得并不象国内这样严,一般情况下,PayPal, 信用卡等方式,都支持并审核通过,支付宝会稍难点; 2、小程序里实现其他支付方式的唯一方案:生成支付链接,复制到剪贴板,在手机浏览器或微信浏览器中打开,完成支付流程。 四、第三方支付账号和接入文档 第三方帮你接入微信支付后,会回复邮件给你,并提供相关支付信息: 1. 一个属于第三方的支付账号 比如Yaband pay,支付账号类似如下: ID: yourname@yoursite.com Key: f4f70db6107xxxxxxxb75efa1f1490b4 2paynow,支付账号类似如下: ID: 541102252 Key: osVx_0POxxxxF9h3JSCls 2. 该第三方支付服务商支付接入文档 比如Yaband pay 支付文档: https://www.yabandmedia.com/api/cn-api.html 比如2paynow支付文档,是由技术支持人员发送给你的PDF文档,具体内容暂不介绍。 3. 第三方支付服务商的商家管理后台 你可以登录该后台进行配置,并查看详细的订单数据。 [图片] 五、云开发实现境外支付 因为商家可以自主选择第三方支付服务商,所以我们在实现支付功能时,需要同时支付多个第三方支付服务商。 用云开发来实现,一般会有两种方式: (1)、每个第三方的支付功能,分别用一个云函数实现; (2)、一个云函数集成所有第三方支付功能;(我们采用的是该方式) 1、云函数pay.js: 按支付接口、支付服务商、支付方式分流: const cloud = require('wx-server-sdk') const rp = require('request-promise') const CryptoJS = require('crypto-js') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db = cloud.database() exports.main = async (event, context) => { switch (event.action) {//根据小程序端传入的支付相关接口分流 case 'unifiedorder': return await unifiedorder(event)//统一下单 case 'queryorder': return await queryorder(event)//订单查询 case 'refund': return await refund(event)//退款 default: return 'wrong action' } } async function unifiedorder(event) { let res = await db.collection('config').doc('payment').get().catch() if (res && res.data) { } else return 'wrong action'//注意:res?.data在云函数中不支持 let config = res.data//云数据库中保存了支付配置信息 switch (config.pay_sp) {//根据支付服务商pay_sp来分流 case '2paynow': return await unifiedorder_2paynow(event, config) case 'yaband': return await unifiedorder_yaband(event, config) case 'cloudpay': return await unifiedorder_cloudpay(event, config) case 'supay': return await unifiedorder_supay(event, config) case 'vr': return await unifiedorder_vr(event, config) case 'v2': return await unifiedorder_v2(event, config) case 'v3': return await unifiedorder_v3(event, config) case 'v2sp': return await unifiedorder_v2(event, config)//v2服务商模式 case 'v3sp': return await unifiedorder_v3(event, config)//v3服务商模式 default: return 'wrong action' } } async function queryorder(event) { let res = await db.collection('config').doc('payment').get().catch() if (res && res.data) { } else return 'wrong action' let config = res.data switch (config.pay_sp) { case '2paynow': return await queryorder_2paynow(event, config) case 'supay': return await queryorder_supay(event, config) case 'vr': return await queryorder_vr(event, config) default: return 'wrong action' } } async function refund(event) { let res = await db.collection('config').doc('payment').get() if (res && res.data) { } else return 'wrong action' let config = res.data switch (res.data.pay_sp) { case '2paynow': return await refund_2paynow(event, config) case 'yaband': return await refund_yaband(event, config) case 'cloudpay': return await refund_cloudpay(event, config) case 'supay': return await refund_supay(event, config) case 'vr': return await refund_vr(event, config) default: return 'wrong action' } } 2、统一下单(Yaband pay为例) 接口文档:https://www.yabandmedia.com/api/cn-api.html // -------------------yaband----------------------------------------------------------- async function unifiedorder_yaband(event, config) { let notify_url = config.notify_url let user = config.mchid let key = config.key let timeStamp = parseInt(Date.now() / 1000) let payMethod = event.pay_method if (payMethod == 'wechat') {//微信支付 const wxContext = cloud.getWXContext() let param = { "user": user, "method": "v3.CreatePaymentsWechatMiniPay", "time": timeStamp, } let data = { "pay_method": "online", "sub_pay_method": "WeChat Pay", "order_id": event.out_trade_no, "amount": event.total_fee || "0.1", "currency": event.fee_type || config.currency, "description": event.body || "YabandPay pay", "demo": event.demo || "order", "timeout": "0", "notify_url": notify_url, "sub_app_id": wxContext.FROM_APPID || wxContext.APPID, "sub_open_id": wxContext.FROM_OPENID || wxContext.OPENID } let sign = getSign_yaband({ ...param, ...data }, key) let body = { ...param, sign, data } let res = await rp({ url: "https://mapi.yabandpay.com/Payments", method: 'POST', body, json: true }) let payment = res.data.parameters return { payment, sp_trade_no: res.data.trade_id } } if (payMethod == 'sofort') {//省略 } if (payMethod == 'alipay') {//省略 } if (payMethod == 'paypal') {//Paypal支付 let param = { "user": user, "method": "v3.CreatePayments", "time": timeStamp, } let data = { "pay_method": "online", "sub_pay_method": "PAYPAL/RECURRING", "order_id": event.out_trade_no, "amount": event.total_fee || "0.1", "currency": event.fee_type || config.currency, "description": event.body || "YabandPay pay", "demo": event.demo || "order", "post_email": "xin.liu@elbsino.com", "timeout": "0", "redirect_url": "https://www.klarna.com/de/", "notify_url": notify_url } let sign = getSign_yaband({ ...param, ...data }, key) let body = { ...param, sign, data } let res = await rp({ url: "https://mapi.yabandpay.com/Payments", method: 'POST', body, json: true }) let url = res.data.url return { url }//除微信支付外,都必须返回一个url:支付链接 } } async function refund_yaband(event, config) { console.log('refund_yaband') let notify_url = config.notify_url let user = config.mchid let key = config.key let timeStamp = parseInt(Date.now() / 1000) let param = { "user": user, "method": "v3.CreateRefund", "time": timeStamp, } let data = { "trade_id": event.trade_id, "refund_amount": event.refund_amount || "0.1", "refund_currency": config.refund_currency, "refund_description": event.refund_description || "test", "notify_url": notify_url } let sign = getSign_yaband({ ...param, ...data }, key) let body = { ...param, sign, data } console.log(body) return await rp({ url: "https://mapi.yabandpay.com/Payments", method: 'POST', body, json: true }) } function getSign_yaband(args, key, sa = []) {//每个支付服务商的签名方式都不一样 for (let k in args) sa.push(k + '=' + args[k]) sa = sa.sort() let signStr = sa.join('&') let hash = CryptoJS.HmacSHA256(signStr, key) return CryptoJS.enc.Hex.stringify(hash) } function getSign_2paynow(args, key) {//2paynow的签名算法。 let signStr = args.function + args.mid + args.timestamp + key let hash = CryptoJS.MD5(signStr, key) return CryptoJS.enc.Hex.stringify(hash) } 踩坑实录: 1、每次统一下单,都会产生三种订单号: 商家订单号 支付服务商的支付订单号 微信支付订单号 记住:一定要保存支付服务商的支付订单号,因为有些支付服务商在订单查询和退款接口中,只认该订单号。 2、每个服务商的签名方案是不同的,有的甚至不需要签名,这个需要分别对待。 六、支付成功异步通知 多个服务商的异步通知,用一个云函数来实现。 云函数:notify.js const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db = cloud.database() const _ = db.command exports.main = async (event, context) => { let res = await db.collection('config').doc('payment').get() switch (res.data.pay_sp) { case '2paynow': return await notify_2paynow(event) case 'yaband': return await notify_yaband(event) case 'cloudpay': return await notify_cloudpay(event) case 'vr': return await notify_vr(event)//vr暂时不支持回调通知 case 'supay': return await notify_supay(event) case 'v2': return await notify_v2(event) case 'v3': return await notify_v3(event) default: return 'wrong action' } } async function notify_2paynow(event) { console.log('notify_2paynow') let qs = event.queryStringParameters if (qs.merchant_trade_no) { } else return 'success' await db.collection('payment').add({ data: qs }).then(res => console.log(res)) if (qs.merchant_trade_no && qs.trade_status == 'TRADE_SUCCESS') { await onPaymentSuccess(qs.merchant_trade_no, qs.trade_no, qs.original_trade_no) } return 'success' } async function notify_yaband(event) { console.log('notify_yaband') if (event.body) { } else return { statusCode: 200, body: 'ok' } let body = JSON.parse(event.body) let pay = body.data await db.collection('payment').add({ data: pay }).then(res => console.log(res)) if (pay.transaction_id && pay.state == 'paid') { await onPaymentSuccess(event.outTradeNo) } return { statusCode: 200, body: 'ok' } } async function notify_cloudpay(event) {//云支付pay_cb console.log('notify_cloudpay') if (event.outTradeNo) { await db.collection('payment').add({ data: event }).then(res => console.log(res)) if (event.returnCode == 'SUCCESS' && event.resultCode == 'SUCCESS') { await onPaymentSuccess(event.outTradeNo) } } return { "errcode": 0, "errmsg": 'SUCCESS' } } 踩坑实录: 1、有些国外支付服务商,并不提供异步通知功能,比如VR Payment,这就很尴尬了,因为我们业务的流程里,是非常依赖这个功能的,因此,后期造成了大量的代码修改工作,建议大家从一开始就考虑这种情况。 七、币种选择 统一下单时,支持两种币种: 1、CNY 2、当地币种,比如EUR 境外退款,一般只能使用当地币退款,不支持RMB。 八、境外小程序云开发禁用后的选择 目前境外小程序的云开发功能已经被禁用,可以通过环境共享的模式支持以上云开发境外支付方案; 1、共享在禁用令之前的同主体小程序的云环境; 2、我们是采用开放平台第三方服务商批量云开发模式; 九、境外小程序相关文章 1、https://developers.weixin.qq.com/community/develop/article/doc/000aec921e4fd8a320ec0f9795bc13 2、https://developers.weixin.qq.com/community/develop/article/doc/0000e805af0900b37f6c900c356c13 3、https://developers.weixin.qq.com/community/develop/article/doc/000a68f1a24a687242eb2427556013 4、https://developers.weixin.qq.com/community/develop/article/doc/000860b434cd180a749b9268f51c13
2022-05-31 - 云支付的回调函数会一直被调用,这是正常的吗?
云支付的回调函数会一直被调用,这是正常的吗?没有调用支付,回调函数也会一直调用 // 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db = cloud.database(); // 云函数入口函数 exports.main = async (event, context) => { const wxContext = cloud.getWXContext() if(event.returnCode == 'SUCCESS' && event.resultCode == 'SUCCESS') //两个都成功,支付完成 { var time = "444"; var order = await db.collection('orderlist').where({outTradeNo : event.outTradeNo}) .update({ data:{ info:{ paystate:'已支付', orderstate:'生产中', payTime:time } } }) } const res = {errcode:0, errmsg:'SUCCESS'} return res } [图片]
2022-02-09 - [ios] map放进swiper中,第二个map里面内嵌的view不见了?
<swiper style="height: 600rpx;"> <swiper-item wx:for="{{arr}}" wx:key="*this"> <map style="height: 400rpx;"> <view style="width:100rpx;height:100rpx;background-color:yellow;">1{{item}}</view> </map> <view>这里用来滑动</view> </swiper-item> </swiper> 如上面代码所示 一共三个地图,第一张左上角还能看到1aa,切换swiper,到第二张就什么都看不见了,安卓一切正常。期望ios的表现能和安卓一样。 [图片] [图片]
2023-02-07 - scroll-view 横向滚动内容使用absolute定位第一个不显示出?
ios上面横向滚动定位在真机上面第一个显示不全[图片]
2023-03-13 - 如何解决组件中轮播图swiper层级太高?
我想把轮播图置于底部但是发现,z-index 和cover-view都是失效
2022-01-23 - 双层 swiper 最内层 image 元素层级 ,iOS下有时候image层级会特别高,偶发性的?
swiper 全屏竖向滚动 ,元素是 swiper ,内层swiper元素是 图片横向滑动,iOS image下图片层级会特别高,偶发性的,有的image是正常的,有的image就会直接编程顶层,覆盖在了最顶层,好奇怪,如何解决? ----iOS真机可以复现 录屏如下, https://app.brainco.cn/starkids_mini/images/temp/RPReplay_Final1661244798.MP4 [图片]
2022-08-23 - 建议:swiper组件可以增加item划动位置的传入参数吗?
在页面swiper组件上方有遮罩层(如固定位置的文字等)时,可以跟踪手指在遮罩层的位置并传入swiper组件,从而实现透过遮罩层划动swiper-item。
2023-02-03 - 关于微信原生组件swiper 在IOS中可能造成滑动时swiper-item上内容消息的问题处理方案
最近在做一款类似小红书的小程序时,有使用到swiper及嵌套功能。 经过开发测试发现IOS在滑动时容易造成swipter-item上的内容丢失。 经过真机测试和调试发现,原生组件中存在IOS兼容的BUG。 具体操作看下图: [图片] 从上图中我们看到原生的swiper-item 真机调试时自带了position:abolute; IOS测试过程中发现,只要在真机调试上鼠标移动到对应的VIEW标签上文字就出现了。 于是在页面样式中加上: swiper-item{ position:fixed !import; } 问题成功解决。无论怎么上下左右滑动都不会出现swiper-item上文字消失的情况。 建议官方及时完善这个BUG。
2022-06-28 - swiper ,live-player+view,在iOS上划两下,view就被盖住,如何解决?
swiper 里有live-player,然后盖了一层view,在iOS上划两下,view就被live-player覆盖了!android和开发工具上好的😓 下面是swiper-item里的布局 <view class="player-container"> <live-player class="player-fill" id="{{member}}" object-fit="contain" src="rtmpLinkPull" autoplay="true" mode="RTC"> </live-player> <view class="userInfo-small"> <block wx:if="{{!mic}}"> <image src="/assets/eshare/mic-off-view.svg" class="mic-off"></image> </block> <view class="name">{{memberInfo[member].name}}</view> </view> </view>
2022-10-28 - 云开发实现企业付款到零钱
云开发实现企业付款到零钱 ~ [图片] 一、前言 本博客实现的功能需求很单一,就是实现微信商户号中的企业付款到零钱的功能。 简单的来说,就是提现功能,最为普遍的使用场景大概是小程序/APP中举办一些活动,然后给予用户现金的奖励,由用户从小程序/APP提现到微信钱包中。 但是本文比较特殊,传统实现此功能避免不了使用服务器,但是微信小程序已经推出了云开发这一能力,那么能不能在云开发的云函数中实现提现这一功能呢?即免服务器实现。 答案是可以的。(不然我还写啥?) 二、实现的可行性分析 目前云开发基本成为未来小程序开发模式的一大趋势,对于全栈开发者/中小型项目开发来说,无疑是提供了巨大的便捷性。 但是请求外网API,例如文章中要讲的提现这种需要调用外网API的功能来说,云函数环境是否能够胜任呢? 需要满足什么样的条件的外网接口能够使用云函数环境代替传统服务器进行开发呢? 或许看到这里, 大家会觉得这一"实现的可行性分析”是啰嗦之语,但是因为我希望能把这种思路提供给大家。 让大家能够在项目开发前进行需求分析时对采用云开发是否能满足类似的需求有一个判断。进而减少开发的错误。 其实: 对于大部分无需证书(*.pem),普通的数据获取接口,都是可以通过云函数进行数据请求的。例如:需要爬取某网站的数据/请求、接收第三方API的数据 。 当接口请求的文档里出现: 需要配置请求IP白名单、需要携带证书、需要提供服务器回调地址 这些关键字眼的时候就要特别注意了。 因为可能云函数环境没办法实现。 [图片] 1、配置请求IP地址白名单 目前云函数是可以实现固定一个IP请求接口,在云函数设置-高级里面可以设置以及查看请求的ip地址。所以这个是可以实现的。 2、需要携带证书访问(*.pem文件) 一般需要证书访问的接口,文档里面都会提到提供存放证书的绝对路径。 例如支付宝的付款到用户余额。 云函数其实也是可以拿到这个绝对的路径的。绝对路径为:/var/user/证书名称.pem。 也可以通过 __dirname() 方法获取到云函数运行时的绝对路径。具体用法百度即可,注意是两个_。 所以这个基本也可以满足。 3、需要提供服务器回调地址 有些接口这个参数是非必填的,但是如果文档里提示是必填的,那就需要考虑一下了。云函数目前没办法提供这个地址,所以要想其他办法实现。这个满足不了。 三、实际分析 根据上面的可行性分析,我们来实际分析一下微信的企业付款到零钱的接口,是否能够满足在云函数实现的条件。打开企业付款到微信的开发文档 调用要求里面说需要携带证书进行访问。 关于API证书 其实这里不熟悉的开发者(例如我)一开始看可能会有些迷惑,以为是需要携带3个证书才可以成功访问接口。 就会很疑惑,因为.p12证书云函数没办法安装,仔细看了几次才发现,其实是两种选择。 3个证书中: 要么请求环境安装了.p12证书(仅限windows系统) 要么是请求的时候携带两个.pem证书。 这样看来,云函数其实是可以实现的这一接口调用的。 可行性分析完毕,接下来就开始实现代码了。 四、实现过程 实现前提 开通产品 要实现这个功能,前提当然是企业的商户号成功开通了企业付款到零钱这一功能。 申请开通的门槛还是稍微有点高的。 如果满足了其他条件,唯独不满足连续三十日交易流水这个条件的话,可以试一下每天随机支付金额,刷一下流水,有几率可以开通成功。 如果是比较老的商户号,有一定的支付记录了,大概率是可以开通的。 如果是新的商户号的话,自测。 检查证书以及API密钥 请求过程中需要携带API证书,以及需要API密钥生成签名组成请求参数。所以二者缺一不可。 具体的可以直接参照文档去操作。这个没有坑。生成的证书以及设置的API密钥记得保存好。 代码实现 完成了上面的前提之后,就可以进行代码的编写了。 1、生成签名 签名生成算法文档 可以不看文档,直接看代码。 请求参数 具体你的业务逻辑需要什么参数,直接去文档里看就行,然后放到这个参数对象里就行。 需要注意的是,提现的金额的单位是分,也就是说,假设提现1块钱,需要传入的参数数值则是 100。 openid、随机字符串、随机订单号这些变量,自行填入即可,测试时可以写死。 上面是需要提交的请求数据对象,需要将其进行排序、加密,得出签名。 计算签名模块 const crypto = require('crypto') // 引入MD5加密模块 /** * 根据传入的参数对象以及密钥,经过处理以及加密, 生成并返回sing参数 * @param {object} obj 参数组成的对象 * @param {String} mchKey 商户平台设置设置的密钥 * @returns */ const getSign = (obj, mchKey) => { // 对传入的对象进行排序 let arr = new Array(); let num = 0; for (let i in obj) { arr[num] = i; num++; } let sortArr = arr.sort(); let sortObj = {}; for (let i in sortArr) { sortObj[sortArr[i]] = obj[sortArr[i]]; } // 对传入的对象进行拼接 let sortStr = '' sortArr.forEach(key => { sortStr += key + "=" + sortObj[key] + "&" }); // 减去最后一个参数的&连接符 // sortStr = sortStr.substring(0, sortStr.length - 1) // 拼接密钥 sortStr += "key=" + mchKey // 使用MD5进行加密, 并将加密结果的英文字母全部转换成大写 const sign = crypto.createHash('md5').update(sortStr, 'utf8').digest('hex').toUpperCase(); return sign; } module.exports = { getSign: getSign } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 调用签名模块,组成最终的提交数据对象(json格式) const key = '32位支付密钥' // 支付密钥 const signStr = getSign.getSign(paramObj, key) // 获取签名 paramObj.sign = signStr // 将签名放入请求参数对象 1 2 3 发起请求函数 这里需要引入两个模块。 const request = require('request') // 网络请求模块 const fs = require('fs') // 文件读取模块 1 2 证书放置位置 请求代码逻辑 const requestFun = (paramObj) => { // 将json请求数据转换成xml let xml = '<xml>\n' Object.keys(paramObj).forEach(function (key) { xml += '<' + key + '>' + paramObj[key] + '</' + key + '>\n' }); xml += '</xml>' // 读取证书 const cert = fs.readFileSync('./apiclient_cert.pem', 'ascii') const prikey = fs.readFileSync('./apiclient_key.pem', 'ascii') // 请求数据,放入API证书 const opt = { url: 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers', body: xml, key: prikey, cert: cert } // 发起请求 return new Promise((resolve, reject) => { request.post(opt, (err, res) => { resolve(res) }) }) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 请求结果 如果没有出现什么问题,此时应该会返回接口的请求结果。可能会出现以下几种情况: 签名错误 使用在线密钥检查工具检测以下是否密钥生成有误。 直接把代码中生成的xml打印出来,整个粘贴进去,输入密钥,然后校验就行。看看签名是否出现问题。按文档中操作即可。 API证书错误 检查一下API密钥以及API证书是否有误,如果确认无误还是不行,尝试一下更换新的证书。 余额不足 扣款有两种情况: 1、如果没有开通运营账户,则在基本账户余额扣除。 2、如果开通了运营账户,则在运营账户扣除。 余额不足,直接充值进对应得账户即可。 低于多少元下限或高于多少元上限制 查看企业付款到零钱的设置,提现最低门槛可手动设置至0.3元。 在设置的提现金额范围内发起提现即可。 解析返回结果xml为json格式 该接口返回的结果的格式是XML格式,对于交易结果的判断比较麻烦,因此为了便利,将结果转成JSON的格式。 xml解析模块 直接将返回的xml数据整个传入即可。 const xml2js = require('xml2js') //引入xml解析模块 /** * 解析付款到零钱返回的xml为JSON * @param xmlText 返回的xml */ const parseXMLToJSON = (xmlText) =>{ let xmlJsonText = '' xml2js.parseString(xmlText, (err, result)=>{ xmlJsonText = result }) xmlJsonText = JSON.stringify(xmlJsonText) const xmlJson = JSON.parse(xmlJsonText) let resultObj = xmlJson.xml let resultJson = {} Object.keys(resultObj).forEach((key)=>{ resultJson[key] = resultObj[key][0] }) return resultJson } module.exports = { parseXMLToJSON: parseXMLToJSON } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 调用 打印输出结果示例(云函数日志) 到账提示 目录结构 五、结语 主要的请求逻辑基本就在上面了。至于请求后的业务逻辑处理,例如数据入库等操作,基本就是可以自行实现了。这个没有什么可说的。 希望以上的文章可以帮助到大家,有不当的地方或者我没写明白的地方, 欢迎大家能够在评论区提出来。 六、完整的index.js代码 // 云函数入口文件 const cloud = require('wx-server-sdk') const request = require('request') // 网络请求模块 const fs = require('fs') // 文件读取模块 const getSign = require('./utils/getSing.js') // 计算签名模块 const xmlToJson = require('./utils/xmlToJson.js') // xml转json模块 cloud.init({ evn: '云环境id' }) const db = cloud.database() const _ = db.command // 云函数入口函数 exports.main = async (event) => { const partner_trade_no = getOrderId('商户号') // 随机订单号 const nonce_str = getNonceStr() // 随机字符串 let paramObj = { mchid: "商户号", mch_appid: 'mch_appid', device_info: 1000, nonce_str: nonce_str, partner_trade_no: partner_trade_no, openid: event.openid, check_name: 'FORCE_CHECK', // 是否校验实名,可选,详看文档 re_user_name: event.name, // 详看文档 amount: event.amount * 100, // 注意金额的单位,我传入的是以元为单位的,所以需要乘以100 desc: event.desc } const key = '32位支付密钥' // 支付密钥 const signStr = getSign.getSign(paramObj, key) // 获取签名 paramObj.sign = signStr // 发起提现请求 const xmlText = await requestFun(paramObj) // 将返回的xml解析成JSON格式 const xmlJson = xmlToJson.parseXMLToJSON(xmlText.body) console.log(xmlJson) // 判断提现状态 // 通讯成功 if (xmlJson.return_code == 'SUCCESS') { // 交易成功 if (xmlJson.result_code == 'SUCCESS') { // 提现成功后的业务逻辑 } // 交易失败 else if (xmlJson.result_code == 'FAIL') { // 提现失败后的业务逻辑 } } return xmlJson } /** * 请求函数 * @param paramObj 请求时提交的参数对象,JSON格式 */ const requestFun = (paramObj) => { // 将json请求数据转换成xml let xml = '\n' Object.keys(paramObj).forEach(function (key) { xml += '<' + key + '>' + paramObj[key] + '\n' }); xml += '' // 读取证书 const cert = fs.readFileSync('./apiclient_cert.pem', 'ascii') const prikey = fs.readFileSync('./apiclient_key.pem', 'ascii') // 请求体 const opt = { url: 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers', body: xml, key: prikey, cert: cert } // 发起请求 return new Promise((resolve, reject) => { request.post(opt, (err, res) => { resolve(res) }) }) } /** * 获取32位随机支付订单号 */ const getOrderId = (mchid) => { let randomStr = "" const timeStamp = new Date().getTime() for (let i = 0; i < (32 - 13 - mchid.length); i++) { let randomNum = Math.floor(Math.random() * 10) // 获取 0-9随机整数 randomStr += randomNum } // 拼接 商户号 + 随机数字 + 时间戳 返回 return mchid + randomStr + timeStamp } /** * 获取32位随机字符串 */ const getNonceStr = () => { let str = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; let randomStr = ''; for (let i = 32; i > 0; --i) { randomStr += str[Math.floor(Math.random() * str.length)]; } return randomStr } ~云开发实现企业付款到零钱 ~ [图片] 一、前言 本博客实现的功能需求很单一,就是实现微信商户号中的企业付款到零钱的功能。 简单的来说,就是提现功能,最为普遍的使用场景大概是小程序/APP中举办一些活动,然后给予用户现金的奖励,由用户从小程序/APP提现到微信钱包中。 但是本文比较特殊,传统实现此功能避免不了使用服务器,但是微信小程序已经推出了云开发这一能力,那么能不能在云开发的云函数中实现提现这一功能呢?即免服务器实现。 答案是可以的。(不然我还写啥?) 二、实现的可行性分析 目前云开发基本成为未来小程序开发模式的一大趋势,对于全栈开发者/中小型项目开发来说,无疑是提供了巨大的便捷性。 但是请求外网API,例如文章中要讲的提现这种需要调用外网API的功能来说,云函数环境是否能够胜任呢? 需要满足什么样的条件的外网接口能够使用云函数环境代替传统服务器进行开发呢? 或许看到这里, 大家会觉得这一"实现的可行性分析”是啰嗦之语,但是因为我希望能把这种思路提供给大家。 让大家能够在项目开发前进行需求分析时对采用云开发是否能满足类似的需求有一个判断。进而减少开发的错误。 其实: 对于大部分无需证书(*.pem),普通的数据获取接口,都是可以通过云函数进行数据请求的。例如:需要爬取某网站的数据/请求、接收第三方API的数据 。 当接口请求的文档里出现: 需要配置请求IP白名单、需要携带证书、需要提供服务器回调地址 这些关键字眼的时候就要特别注意了。 因为可能云函数环境没办法实现。 [图片] 1、配置请求IP地址白名单 目前云函数是可以实现固定一个IP请求接口,在云函数设置-高级里面可以设置以及查看请求的ip地址。所以这个是可以实现的。 2、需要携带证书访问(*.pem文件) 一般需要证书访问的接口,文档里面都会提到提供存放证书的绝对路径。 例如支付宝的付款到用户余额。 云函数其实也是可以拿到这个绝对的路径的。绝对路径为:/var/user/证书名称.pem。 也可以通过 __dirname() 方法获取到云函数运行时的绝对路径。具体用法百度即可,注意是两个_。 所以这个基本也可以满足。 3、需要提供服务器回调地址 有些接口这个参数是非必填的,但是如果文档里提示是必填的,那就需要考虑一下了。云函数目前没办法提供这个地址,所以要想其他办法实现。这个满足不了。 三、实际分析 根据上面的可行性分析,我们来实际分析一下微信的企业付款到零钱的接口,是否能够满足在云函数实现的条件。打开企业付款到微信的开发文档 调用要求里面说需要携带证书进行访问。 关于API证书 其实这里不熟悉的开发者(例如我)一开始看可能会有些迷惑,以为是需要携带3个证书才可以成功访问接口。 就会很疑惑,因为.p12证书云函数没办法安装,仔细看了几次才发现,其实是两种选择。 3个证书中: 要么请求环境安装了.p12证书(仅限windows系统) 要么是请求的时候携带两个.pem证书。 这样看来,云函数其实是可以实现的这一接口调用的。 可行性分析完毕,接下来就开始实现代码了。 四、实现过程 实现前提 开通产品 要实现这个功能,前提当然是企业的商户号成功开通了企业付款到零钱这一功能。 申请开通的门槛还是稍微有点高的。 如果满足了其他条件,唯独不满足连续三十日交易流水这个条件的话,可以试一下每天随机支付金额,刷一下流水,有几率可以开通成功。 如果是比较老的商户号,有一定的支付记录了,大概率是可以开通的。 如果是新的商户号的话,自测。 检查证书以及API密钥 请求过程中需要携带API证书,以及需要API密钥生成签名组成请求参数。所以二者缺一不可。 具体的可以直接参照文档去操作。这个没有坑。生成的证书以及设置的API密钥记得保存好。 代码实现 完成了上面的前提之后,就可以进行代码的编写了。 1、生成签名 签名生成算法文档 可以不看文档,直接看代码。 请求参数 具体你的业务逻辑需要什么参数,直接去文档里看就行,然后放到这个参数对象里就行。 需要注意的是,提现的金额的单位是分,也就是说,假设提现1块钱,需要传入的参数数值则是 100。 openid、随机字符串、随机订单号这些变量,自行填入即可,测试时可以写死。 上面是需要提交的请求数据对象,需要将其进行排序、加密,得出签名。 计算签名模块 const crypto = require('crypto') // 引入MD5加密模块 /** * 根据传入的参数对象以及密钥,经过处理以及加密, 生成并返回sing参数 * @param {object} obj 参数组成的对象 * @param {String} mchKey 商户平台设置设置的密钥 * @returns */ const getSign = (obj, mchKey) => { // 对传入的对象进行排序 let arr = new Array(); let num = 0; for (let i in obj) { arr[num] = i; num++; } let sortArr = arr.sort(); let sortObj = {}; for (let i in sortArr) { sortObj[sortArr[i]] = obj[sortArr[i]]; } // 对传入的对象进行拼接 let sortStr = '' sortArr.forEach(key => { sortStr += key + "=" + sortObj[key] + "&" }); // 减去最后一个参数的&连接符 // sortStr = sortStr.substring(0, sortStr.length - 1) // 拼接密钥 sortStr += "key=" + mchKey // 使用MD5进行加密, 并将加密结果的英文字母全部转换成大写 const sign = crypto.createHash('md5').update(sortStr, 'utf8').digest('hex').toUpperCase(); return sign; } module.exports = { getSign: getSign } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 调用签名模块,组成最终的提交数据对象(json格式) const key = '32位支付密钥' // 支付密钥 const signStr = getSign.getSign(paramObj, key) // 获取签名 paramObj.sign = signStr // 将签名放入请求参数对象 1 2 3 发起请求函数 这里需要引入两个模块。 const request = require('request') // 网络请求模块 const fs = require('fs') // 文件读取模块 1 2 证书放置位置 请求代码逻辑 const requestFun = (paramObj) => { // 将json请求数据转换成xml let xml = '<xml>\n' Object.keys(paramObj).forEach(function (key) { xml += '<' + key + '>' + paramObj[key] + '</' + key + '>\n' }); xml += '</xml>' // 读取证书 const cert = fs.readFileSync('./apiclient_cert.pem', 'ascii') const prikey = fs.readFileSync('./apiclient_key.pem', 'ascii') // 请求数据,放入API证书 const opt = { url: 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers', body: xml, key: prikey, cert: cert } // 发起请求 return new Promise((resolve, reject) => { request.post(opt, (err, res) => { resolve(res) }) }) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 请求结果 如果没有出现什么问题,此时应该会返回接口的请求结果。可能会出现以下几种情况: 签名错误 使用在线密钥检查工具检测以下是否密钥生成有误。 直接把代码中生成的xml打印出来,整个粘贴进去,输入密钥,然后校验就行。看看签名是否出现问题。按文档中操作即可。 API证书错误 检查一下API密钥以及API证书是否有误,如果确认无误还是不行,尝试一下更换新的证书。 余额不足 扣款有两种情况: 1、如果没有开通运营账户,则在基本账户余额扣除。 2、如果开通了运营账户,则在运营账户扣除。 余额不足,直接充值进对应得账户即可。 低于多少元下限或高于多少元上限制 查看企业付款到零钱的设置,提现最低门槛可手动设置至0.3元。 在设置的提现金额范围内发起提现即可。 解析返回结果xml为json格式 该接口返回的结果的格式是XML格式,对于交易结果的判断比较麻烦,因此为了便利,将结果转成JSON的格式。 xml解析模块 直接将返回的xml数据整个传入即可。 const xml2js = require('xml2js') //引入xml解析模块 /** * 解析付款到零钱返回的xml为JSON * @param xmlText 返回的xml */ const parseXMLToJSON = (xmlText) =>{ let xmlJsonText = '' xml2js.parseString(xmlText, (err, result)=>{ xmlJsonText = result }) xmlJsonText = JSON.stringify(xmlJsonText) const xmlJson = JSON.parse(xmlJsonText) let resultObj = xmlJson.xml let resultJson = {} Object.keys(resultObj).forEach((key)=>{ resultJson[key] = resultObj[key][0] }) return resultJson } module.exports = { parseXMLToJSON: parseXMLToJSON } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 调用 打印输出结果示例(云函数日志) 到账提示 目录结构 五、结语 主要的请求逻辑基本就在上面了。至于请求后的业务逻辑处理,例如数据入库等操作,基本就是可以自行实现了。这个没有什么可说的。 希望以上的文章可以帮助到大家,有不当的地方或者我没写明白的地方, 欢迎大家能够在评论区提出来。 六、完整的index.js代码 // 云函数入口文件 const cloud = require('wx-server-sdk') const request = require('request') // 网络请求模块 const fs = require('fs') // 文件读取模块 const getSign = require('./utils/getSing.js') // 计算签名模块 const xmlToJson = require('./utils/xmlToJson.js') // xml转json模块 cloud.init({ evn: '云环境id' }) const db = cloud.database() const _ = db.command // 云函数入口函数 exports.main = async (event) => { const partner_trade_no = getOrderId('商户号') // 随机订单号 const nonce_str = getNonceStr() // 随机字符串 let paramObj = { mchid: "商户号", mch_appid: 'mch_appid', device_info: 1000, nonce_str: nonce_str, partner_trade_no: partner_trade_no, openid: event.openid, check_name: 'FORCE_CHECK', // 是否校验实名,可选,详看文档 re_user_name: event.name, // 详看文档 amount: event.amount * 100, // 注意金额的单位,我传入的是以元为单位的,所以需要乘以100 desc: event.desc } const key = '32位支付密钥' // 支付密钥 const signStr = getSign.getSign(paramObj, key) // 获取签名 paramObj.sign = signStr // 发起提现请求 const xmlText = await requestFun(paramObj) // 将返回的xml解析成JSON格式 const xmlJson = xmlToJson.parseXMLToJSON(xmlText.body) console.log(xmlJson) // 判断提现状态 // 通讯成功 if (xmlJson.return_code == 'SUCCESS') { // 交易成功 if (xmlJson.result_code == 'SUCCESS') { // 提现成功后的业务逻辑 } // 交易失败 else if (xmlJson.result_code == 'FAIL') { // 提现失败后的业务逻辑 } } return xmlJson } /** * 请求函数 * @param paramObj 请求时提交的参数对象,JSON格式 */ const requestFun = (paramObj) => { // 将json请求数据转换成xml let xml = '<xml>\n' Object.keys(paramObj).forEach(function (key) { xml += '<' + key + '>' + paramObj[key] + '</' + key + '>\n' }); xml += '</xml>' // 读取证书 const cert = fs.readFileSync('./apiclient_cert.pem', 'ascii') const prikey = fs.readFileSync('./apiclient_key.pem', 'ascii') // 请求体 const opt = { url: 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers', body: xml, key: prikey, cert: cert } // 发起请求 return new Promise((resolve, reject) => { request.post(opt, (err, res) => { resolve(res) }) }) } /** * 获取32位随机支付订单号 */ const getOrderId = (mchid) => { let randomStr = "" const timeStamp = new Date().getTime() for (let i = 0; i < (32 - 13 - mchid.length); i++) { let randomNum = Math.floor(Math.random() * 10) // 获取 0-9随机整数 randomStr += randomNum } // 拼接 商户号 + 随机数字 + 时间戳 返回 return mchid + randomStr + timeStamp } /** * 获取32位随机字符串 */ const getNonceStr = () => { let str = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; let randomStr = ''; for (let i = 32; i > 0; --i) { randomStr += str[Math.floor(Math.random() * str.length)]; } return randomStr } ~
2022-06-18 - 共享云环境时代来了,解决fileID带来不兼容问题。
云开发收费了,不管你怎么选择,只要你还继续使用云开发,共享云环境的课题就不可避免。 我们知道,共享云环境下,fileID是无法使用的,怎么兼容,一个最简的方法如下: <wxs module="wxs"> module.exports = { getUrl: function (link) { if (link) { } else return '' if (link.substring(0, 5) == 'cloud') { } else return link var arr = link.split('/') arr[0] = 'https:' arr[2] = arr[2].split('.')[1] + '.tcb.qcloud.la' return arr.join('/') } } </wxs> <image src="{{wxs.getUrl(link)}}"></image> 可见:只要将原项目所有的fileID换成wxs.getUrl(link) 其他代码可以一分不动,也不需要用到wx.cloud.getTempFileURL 可以将wxs.getUrl放在lib.wxs里,任何wxml引用即可。
2022-10-28 - 云存储到对象存储COS的迁移
云开发计费调整后,对存储和CDN用量较大的业务成本影响较大。把文件从云存储迁移到对象存储(下文简称 COS ),是一个成本优化的选择。 我自己的小程序比较下来,每月的费用大约是不迁的三分之一(刚好也赶上双11的活动攒了一波优惠资源);如果不考虑活动,正常买资源包,大约也能降低一半。 但相比从零开始就是「云函数 + 云数据库 + COS」的架构,就急需一个高效稳定的迁移方案,迁移云存储中的文件,并同时更新数据库记录。最好还无需停服。 这里就不赘述那些尝试过的失败方案,仅记录并分享最后跑通的流程,供有类似需求的开发者朋友参考。 一、开通并设置COS、CDN 在腾讯云创建COS的存储桶(最好和云存储同地域,之后迁移既快又经济),「权限管理」设置为“私有读写”,避免外网的直接下行访问;注册或找个域名,设置为 COS 的「自定义CDN加速域名」,开启「回源鉴权」;进入「内容分发网络」的「域名管理」,在「管理」的「访问控制」,开启「防盗链配置」——类型选「白名单」,填入小程序域名“servicewechat.com”。这里需要开启「允许空 referer 访问」,如不开启,手机端将无法访问 video 组件的视频文件(这个问题至今尚未修复)。二、准备小程序的更新版,提交审核(增删改查指向COS新路径) 在腾讯云控制台「访问管理」中创建一个子用户角色,获取 secretId 和 secretKey;写一个云函数,通过 secretId 和 secretKey 生成临时密钥;客户端上传文件时,先触发这个云函数获取COS的临时密钥,用于客户端 wx.uploadFile 文件上传;上传完成后,回调函数里获取 COS 文件路径,写入云数据库;以上是“增”,“删改查”同理,不赘述(访问 COS 的云函数,需要通过 npm 给云函数装 COS 的 Node.js SDK)。详细实践可参考腾讯云文档:小程序直传实践。 新版本准备好了便提交小程序审核。 三、写迁移云函数 因云函数默认内存256M、超时时间3s,最大也只可设置为 1024M、60s,如不够用,可将迁移过程拆为任务发起函数和执行函数;发起函数获取需要迁移的文件数据(如数据量大,可根据数据结构再拆分云函数读取),异步调用执行函数,不等执行结果;每个执行函数只执行一个文件的迁移、及更新数据记录的任务。这样执行时云函数都不会超过内存和时间限制;执行函数里,先 cos.downloadFile 云存储的文件到云函数的缓存(注意路径是“/tmp/文件名”,不是“__dirname/文件名”),再从缓存 cos.uploadFile 到 COS。四、发布小程序、执行迁移 小程序审核通过、迁移云函数都准备好后: 小程序新版发布上线;通过IDE编译,触发迁移云函数里的发起函数。迁移全程同地域内网执行(不要用本地调试。公网执行慢很多,容易超时,且会产生公网流量费);因为执行函数并发量大,容易受到云函数并发数的500/分钟的限制,500以上的都会执行失败(500是入门版到团队进阶版的每分钟并发数,个人版100,更贵的版本更高)。因此执行函数里只能执行一个文件迁移就立即更新相应记录,不能汇总了再更新;如数据量很大、发起函数拆分后读取仍超时,可在数据库记录任务发起情况,多触发几次,总会迁移完。在这个尝试迁移的过程中,有幸得到腾讯云工单同学、客户经理、云开发产品经理的支持和点拨,多次在挠头时刻解惑。感谢。 才疏学浅,如有谬误,请多多指正~
2022-12-20 - [Component] property received type-uncompatible
给子组件传的参是从 computed 里获取的值,第一次会报警告(看得难受) [图片] 2.18.1 以下的基础库不会有此问题 相关问题可见:https://developers.weixin.qq.com/community/develop/doc/0002628cac8fb0a8158c3713f5bc00?page=2#comment-list
2022-03-17 - 如何解决SharedArrayBuffer需要跨源隔离?
[图片]
2022-11-14 - wx.navigateTo() 出现警告[Deprecation] ?
[Deprecation] SharedArrayBuffer will require cross-origin isolation as of M92, around July 2021. See https://developer.chrome.com/blog/enabling-shared-array-buffer/ for more details. e.onmessage @ worker.js?libName=WAAccelerateWorker.js:1 [worker] reportRealtimeAction:fail not support e.workerInvokeJsApi @ worker.js?libName=WAAccelerateWorker.js:1 (anonymous) @ WAWorker.js:2 k @ WAWorker.js:2 invoke @ WAWorker.js:2 f @ WAWorker.js:2 (anonymous) @ WAWorker.js:2 re @ WAWorker.js:2 y @ WAWorker.js:2 l @ WAWorker.js:2 (anonymous) @ WAWorker.js:2 (anonymous) @ WAWorker.js:2 setTimeout (async) globalThis.setTimeout @ WAWorker.js:2 (anonymous) @ WAWorker.js:2 Q @ WAWorker.js:2 (anonymous) @ WAWorker.js:2 (anonymous) @ WAWorker.js:2 S @ WAWorker.js:2 eval @ VM10:1 e.onmessage @ worker.js?libName=WAAccelerateWorker.js:1
2022-06-21 - 虚拟业务指南请收好。
在小程序生态中,基于苹果运营规范,小程序内暂不支持iOS端虚拟支付业务。为此小编为大家整理了一份虚拟支付业务指南,希望大家在做虚拟业务时有所帮助: [视频] 那么,到底什么是虚拟支付业务呢? 虚拟支付业务是指购买非实物商品。比如:VIP会员、充值、录制课程、录制音频视频等虚拟产品。目前iOS端暂不支持虚拟支付业务。 我们常见iOS虚拟支付的不合规示例有哪些呢? 示例一 :小程序内存在付费购买虚拟内容或道具。商品多体现为提前编辑好的、录制好的虚拟商品。如录制视频课程、游戏道具。 整改建议 :建议去除小程序内所有付费购买虚拟服务,并根据提示修改相关内容及文案,文案可参照“由于相关规范,iOS功能暂不可用”。 [图片] 示例二 :付费解锁优质服务。多体现为提供虚拟商品的小程序可通过支付购买、开通虚拟会员等形式,体验小程序付费服务。比如:支付阅读章节小说、同城生活服务平台付费发帖/付费置顶等。 整改建议 :建议可以关闭iOS端虚拟支付通道,并将【马上充值】更改为【由于相关规范,iOS功能暂不可用】,并不再提供iOS端会员服务。 [图片] 示例三 :关闭iOS端虚拟支付功能后,虚拟商品页面仍然保留货架价格标签展示、购买/付费/订阅等功能或按钮。 整改建议 :建议去除小程序中的虚拟商品的价格展示,并更改为【免费】;并将【订阅 ¥128】更改为【由于相关规范,iOS功能暂不可用】,并不再提供iOS端虚拟商品购买服务。 [图片] 示例四 :关闭iOS端虚拟支付功能后,提供引导用户前往其他支付的路径/文案,完成虚拟支付闭环。 整 改建议 :建议去除iOS端小程序内引导用户前往其他支付路径/文案,并不再提供iOS端虚拟商品购买服务。 [图片] 示例五 :小程序含需要付费的虚拟商品,并设置限时免费的服务,限时免费结束后需付费才能继续提供服务。 整改建议 :建议将iOS端小程序中所有虚拟付费内容更改为免费,并不再提供iOS端虚拟商品购买服务。 [图片] 示例六 :关闭iOS端虚拟支付功能后,小程序中虚拟产品页面不可以含有付费性质的关键字(如:购买、已购、付费、支付等),包括但不限于功能按钮、功能页面、支付提示及任何商品介绍等。 整改建议 :建议将小程序iOS端虚拟产品页面中的文案/按钮/功能tab含有限制的关键字更改为【免费】或删除。并不再提供iOS端虚拟商品购买服务。 [图片] 如小程序内存在以上不合规的虚拟支付内容,请开发者重视并及时整改。对于首次违规的小程序,平台将下发站内信整改通知,并给予三天整改时间,请开发者按照提示在限期内完成整改。平台将会对到期未完成整改的小程序进行搜索策略调整,并在小程序功能使用上进行一定的限制,直到小程序完成内容整改。
2020-04-23 - 什么小程序需要社交相关类目?
什么内容或服务的小程序需要补充社交相关类目? [视频] [图片] 1、陌生人交友:小程序内涉及提供在线陌生人交友服务,需补充社交-陌生人交友类目。 所需资质:《增值电信业务经营许可证》(核准服务项目包含“互联网信息服务业务”) 案例:如下图小程序涉及通过展示用户微信号、电话等信息,提供陌生人交友服务,需补充社交-陌生人交友类目。 [图片] 2、熟人交友:小程序内涉及提供在线熟人交友服务,需补充社交-陌生人交友类目。 所需资质:《增值电信业务经营许可证》(核准服务项目包含“互联网信息服务业务”) 案例:如下图小程序通过用户分享好友关系群,提供熟人交友服务,需补充社交-熟人交友类目。 [图片] 3、社区/论坛:小程序内提供社区/论坛服务,包括UGC内容的发布与交流,如:论坛、贴吧、社群等,需补充社交-社区/论坛类目。 所需资质(2选1): ①《非经营性互联网信息服务备案核准》 ②《组织机构代码证》或《统一社会信用代码证》(适用于政府主体) 案例:如下图小程序用户可通过自定义发布帖子功能,提供论坛服务,需补充社交-社区/论坛类目。 [图片] 4、直播:小程序内涉及提供在线直播服务,需补充社交-直播类目。 所需资质(3选1): ①《信息网络传播视听节目许可证》 ②《网络文化经营许可证》(经营范围含网络表演)(适用于含表演性质的直播) ③《统一社会信用代码》《情况说明函件》(适用于政府主体) 案例:如下图小程序某个模块涉及提供在线产品直播服务,需补充社交-直播类目。 [图片] 5、笔记:小程序内涉及提供自定义内容的记录及分享,包括文字、图片、视频、音频等服务,需补充社交-笔记类目。 所需资质(2选1): ①《非经营性互联网信息服务备案核准》 ②《组织机构代码证》或《统一社会信用代码证》(适用于政府主体) 案例:如下图小程序提供自定义音频录制,需补充社交-笔记类目。 [图片] 6、婚恋:小程序内涉及提供婚恋交友服务,需补充社交-婚恋类目。 所需资质:《增值电信业务经营许可证》(核准服务项目包含“互联网信息服务业务”) 案例:如下图小程序涉及通过发布婚恋需求者个人信息提供婚恋交友服务,需补充社交-婚恋类目。 [图片] 7、问答:小程序内涉及提供用户可以根据自身的需求,有针对性地发布、提出和解决问题,或者搜索其他用户解答的平台服务,需补充社交-问答类目。 所需资质(2选1): ①《非经营性互联网信息服务备案核准》 ②《组织机构代码证》或《统一社会信用代码证》(适用于政府主体) 案例:如下图小程序支持用户自定义发布问题或答案,提供平台服务,需补充社交-问答类目。 [图片] 8、直播答题:小程序内涉及提供直播答题服务,需补充社交-直播答题类目。 所需资质:《信息网络传播视听节目许可证》 案例:如下图小程序涉及提供在规定时间点提供在线答题直播服务,需补充社交-直播答题类目。 [图片]
2020-12-11 - 使用云开发CMS能力实现简易商场
源码 点此领取 技术栈 云开发 CloudBase:云端一体化的 Serverless 后端服务解决方案。Taro:一套遵循 React 语法规范的 多端开发 解决方案开发工具 建议提前安装好 微信开发者工具Node LTS 版本VS Code 编辑器CloudBase VS Code 插件需求分析 只考虑基本的功能: 商品列表与下单:展示商品信息,创建订单订单列表:展示订单列表 资源准备 1. 在微信开发者工具中开通云开发,请选择按量付费 如果你的环境是预付费,请到设置中,将支付方式转换为按量付费 [图片] 2. 安装 CMS 系统 (1)更新到最新的 Nightly 版本工具,在工具顶部 Tab 栏中,点击「更多」-「内容管理」。 [图片] (2)点击开通,勾选同意协议后,点击确定。 [图片] (3)开通内容管理需要填写管理员账号,填写账号后,点击「确定」完成。 [图片] (4)开通拓展需要一定时间,请耐心等待。 (5)完成后,点击「更多」-「内容管理」,即可看到内容管理的入口和相关信息。点击访问地址,即可在弹出的窗口中进行内容管理的相关配置。 [图片] 3. 登录 CMS 系统,创建资源 CloudBase CMS 已经部署在当前环境下的静态网站托管中,访问地址的格式如下:云开发静态托管默认域名/部署路径,例如 https://envid.ap-shanghai.app.tcloudbase.com/tcb-cms/(结尾有 / 符号)。默认域名可以访问控制台查看。 打开 CloudBase CMS 后,你需要先登录,账号密码为安装时设置的管理员账号和密码。 在开始管理内容数据前,我们需要先创建一个项目。CloudBase CMS 使用项目划分不同类的内容,便于区分内容数据用途,进行权限管理。 首先,我们需要点击新建项目下方的创建新项目按钮,创建一个名为小商店,Id 为 shop 的项目。 [图片] 创建完项目后,点击项目卡片,进入项目的管理页面,我们会看到项目的欢迎页面。 [图片] 创建商品类型,管理商品信息 创建一个名称为商品的内容模型,数据库名为 goods,即将商品数据存储到 goods 数据集合中。如果新建内容的时候指定的集合不存在,CloudBase CMS 会自动新建集合。 [图片] 在创建完内容模型后,我们会得到一个空的内容模型。接下来,我们需要为商品添加商品名称,商品图片,价格,库存数量等字段。 为商品添加商品名称属性,因为商品名称通常是比较短的文字,所以我们可以选择单行字符串字段,点击右侧的单行字符串卡片,填写商品名称的字段信息。除了基本的名称,数据库字段名之外,我们还可以为此字段添加其他的限制,如最大长度,限制填写商品名称时的最大长度,创建商品时,是否必需填写商品等。 [图片] 类似的,我们可以创建数字类型的价格字段以及库存数量,图片类型的商品图片字段。在创建图片字段时,考虑到商品的图片可能有多张,我们可以打开允许多个内容按钮,表明可以上传多张图片。 [图片] 创建的 goods 数据库集合的结构如下: [图片] 同上,类似的创建一个名称为订单列表,数据库集合名为 order 的内容模型,来管理订单信息。创建的 order 数据库集合的结构如下: [图片] 添加一个商品 [图片] 创建项目 1、拉取模板 # 安装 taro cli 工具 npm install -g @tarojs/cli@2.2.7 # 拉取模板 git clone https://github.com/TencentCloudBase/cloudbase-minishop.git 使用微信开发者工具导入项目,进入 client 目录,安装依赖: npm i 项目目录 cloud/functions 包含写好的微信支付的两个云函数, pay 和接收支付消息推送的 pay-callback 云函数。使用时需使用微信开发者工具上传这两个云函数。 2、项目目录 . ├── client // 小程序源码 │ ├── config │ └── src │ ├── assets │ ├── components │ └── pages │ ├── index │ └── order-list └── cloud // 云开发相关源码 │ └── functions │ ├── pay │ └── pay-callback ├── cloudbaserc.json // 云开发配置 ├── project.config.json // 小程序配置 微信支付下单流程 1、小程序调用云函数,在云函数中调用统一下单接口,参数中带上接收异步支付结果的云函数名和其所在云环境 Id。 const cloud = require("wx-server-sdk"); const res = await cloud.cloudPay.unifiedOrder({ envId: '', subMchId: '', body: "商品名", totalFee: 100, outTradeNo: '订单号', spbillCreateIp: "127.0.0.1", functionName: "pay-callback" }); // 返回 res.payment 支付结果回调的云函数必须返回如下一个对象,否则会视为回调不成功,云函数会收到重复的支付回调。 { errcode: '', errmsg: '', } 2、统一下单接口返回的成功结果对象中有 payment 字段,该字段即是小程序端发起支付的接口(wx.requestPayment)所需的所有信息。 3、小程序端拿到云函数结果,调用 wx.requestPayemnt 发起支付 wx.requestPayment({ ...payment, success (res) { }, fail (res) { }tt })https://docs.cloudbase.net/ 4、支付完成后,在统一下单接口中配置的云函数将收到支付结果通知。 多端支持 - 跨平台 小程序Web 相关文献 云开发文档 云开发微信支付 支付接口
2021-09-10 - 使用第三方腾讯视频插件播放视频,是否需要添加 文娱-视频 类目?
若小程序已接入文娱-视频插件,暂不需补充文娱-视频类目。审核将以实际提交的小程序服务内容为准。
2019-12-27 - (14)腾讯视频插件
想要在自己的电商小程序中增加商品介绍视频,但自己搭建视频服务开发成本太大? 想要在餐饮服务中添加菜品视频,但苦于带宽成本过高? 想要在门票预订服务中用视频打动用户,但视频资质申请流程太长? 腾讯视频插件来帮你~ 今天,我们跟大家分享腾讯视频插件的故事。 腾讯视频插件提供了完整视频播放能力,方便开发者可以给用户提供更好的视频体验。 插件基于腾讯视频的CDN节点和成熟视频方案,可以做到为不同地点的用户提供更流畅的播放服务,更清晰的视频,更稳定的播放质量。只需要视频的vid,开发者即可直接在小程序内播放腾讯视频上的内容! 腾讯视频插件使用场景 场景一:电商类小程序 除了对商品的语言描述,实惠的价格,精美的图片,也需要生动直观、360度动态介绍商品,比单一的图片更能引起消费者的好感。 现在,在商品介绍里直接链入相关商品视频。比如,买衣服的电商小程序,可以把电视或电影中明星穿过的同款视频链入,在满足用户感官享受的同时,提升转化率。 场景二:文娱推荐类小程序 不局限于苍白的文字描述,推荐类小程序也可在小程序中添加电影视频、预告片,为用户带来完美的购票、观影体验。开发者无需独立开发视频功能,直接使用“腾讯视频”插件,即可实现视频播放功能。 [图片] 场景三:资讯类小程序 “腾讯视频”插件还可以是内容创作者的一大利器,以游戏攻略类小程序为例。小程序就可以直接用视频的形式展现,一方面清晰明了地展示攻略流程,同时也增加了用户在小程序内的停留时长,进一步引导用户做更多动作。 或者生活居家小技巧类的小程序,比如想教大家一种打结的方式,纯文字的形式即很难说清楚,又浪费内容运营者的时间,这时候直接使用插件链入视频,就能很快地教会大家,给予用户更好的内容体验。 轻松接入腾讯视频插件 “腾讯视频”提供了这么多的功能,使用起来也同样很方便: 1 在“小程序管理后台→设置→第三方服务→插件管理”中查找插件名称“腾讯视频”,并申请使用。 [图片] 2 参考详情页中的 [开发文档],在小程序代码中使用插件。 了解完“腾讯视频”插件后,还想看看其他小程序是如何实际运用的?一起来体验一下 腾讯+ ! [图片] 页面视频服务是由腾讯视频插件提供
2018-08-17 - 关于微信小程序部分类目报备审核说明
自微信小程序平台上线以来,为了保障小程序内容合规,发布时事新闻、具有社交属性或以视频、电台为载体的小程序需在上线前,完成向省/自治区/直辖市属地网信部门申请报备的工作。特别是,为避免小程序违法违规风险,UGC小程序需要对用户发布的内容做好安全审查措施。 对于开发者们持续以来关注的报备审核相关疑问,做以下说明: 报备审核范围 经与主管部门确认,以类目为范围界定需要报备的小程序,具体报备类目如下: [图片] 社交类目的小程序帮助用户与用户之间产生连接,为用户提供即时通讯、互动、交友、UGC内容发布与分享、直播、直播答题等功能;文娱-视频、文娱-FM/电台类目指小程序内有以视频、电台为载体的内容。时政信息类目小程序内发布政治(含法律法规)、经济、军事、外交、农业、历史、社会突发事件等各种类型的文章/图片/视频/音频的新闻报道; 平台在代码审核环节会确认内容和类目的一致性。如果小程序内涉及有关内容,却没有相关类目,会造成审核不通过并要求补充相应类目。 [图片] 报备审核流程 [图片] 1. 微信侧小程序代码审核:确认是否进入报备审核 在小程序涉及报备类目的版本首次提审时,微信侧审核通过后,自动进入报备审核流程。若微信侧审核不通过,不会进入报备审核流程,下次通过微信侧审核时小程序将自动进入报备审核流程。 小程序涉及报备类目的版本更新提审时,若小程序特定类目曾完成过报备审核,新版本未涉及其他报备类目,无需进入报备审核流程。 撤回代码审核将中止流程,再次提交时若涉及敏感类目会重新进入报备审核。 2、进入属地网信部门复核 开发者在小程序MP后台通知中心收到以下通知,代表小程序进入报备审核流程。从该通知发出时间起,7个自然日内将完成报备审核(境外非企业主体小程序除外)。 [图片] 属地网信部门工作人员会根据当前小程序的主体信息及代码提审信息进行安全审核。也可能会通过开发者预留在微信小程序MP后台的手机号主动联系开发者确认或补充材料。建议开发者在进入报备审核流程后,及时关注和接听来访电话,避免遗漏审核信息,影响报备审核流程。 若开发者收到当地网信部门联系时,请注意核实对方身份,先与其确认真实性后(可通过对方来电、微信、QQ、邮箱等方式询问确认),再提供相关材料。 3、审核周期及预上线说明 报备审核需7个自然日审核周期(不包括境外非企业主体小程序),暂无加急渠道。如遇紧急上线等特殊情况,建议开发者自行联系省/自治区/直辖市属地网信部门。因属地不同,无法列举联系方式,建议自行查看属地相关政府网站查找联系方式。 7个自然日审核周期开发者若未收到审核结果,将收到预上线通知,则开发者可进行版本发布。境外非企业主体小程序无预上线流程。 [图片] 在预上线期间,属地网信部门仍然可能对小程序进行报备审核反馈。预上线的小程序若存在违规或未允许内容,未通过报备审核,将以站内信的形式通知开发者。未通过报备审核的开发者需要及时下架小程序服务,整改小程序内容。 报备审核前重要准备 提供UGC自定义发布内容功能的小程序,需要在小程序内接入官方“内容安全检测接口”校验用户输入的文本/图片,拦截政治、色情、违法等敏感词。用户自定义发布的内容包括:昵称/花名、个人资料签名/日志/聊天/评论、头像/表情/相片、直播等各种场景。其格式内容包括但不限于短文本、长内容、图片或视频等。请开发者做好小程序内容安全识别工作,接入微信公众平台内容安全API(imgSecCheck、msgSecCheck、mediaCheckAsync)能力,以及通过其他技术或人工审核手段做好内容审核,校验用户输入的文本/图片,拦截政治、色情、违法等敏感词,保证用户上传的内容安全健康。 为辅助小程序顺利通过报备审核,代码审核环节会针对小程序的安全保障能力进行敏感类关键词检测,未接入官方“内容安全检测接口”的小程序将无法通过代码审核。开发者需提前做好小程序内容安全识别工作,以免小程序无法通过审核延误版本迭代。若小程序发布后出现UGC内容安全违规问题,也将按平台规则进行处置。 为了满足网信部门的监管要求,平台希望开发者能够明晰小程序报备审核流程,合理规划小程序上线周期,主动预留时间,做好审核准备,配合平台完成向省/自治区/直辖市网信部门报备的工作,保障小程序服务的准时上线。
2022-01-14