- 个人小程序为什么文字祝福都属于信息资讯类目?
个人小程序,因为有文字祝福内容,就被判定违规:小程序涉及资讯服务,请补充资讯-信息资讯类目,目前资讯-信息资讯类目暂不对个人开放,建议选择企业类型小程序注册使用,如无法注册企业类型小程序,请下架涉及资质类目的运营内容。
2022-12-06 - 公众号运营:公众号被永久封禁,申诉找回公众号的申诉理由该如何填写(个人建议,仅供参考。)
公众号被封禁是每个公众号运营者最不想面对的一件事。遇到后也不知道该怎么办。看到有申诉按钮就去点。 一般填写的申诉理由也是没有想好就胡乱写一些就点提交了。这样我很明确的告诉你。你这个号解封不了了。 做为一名资深运营者来讲,申诉理由是给审核人员审核观看。所以你要换一种方式,尽量站到公众号去看你这号有没有充足的理由被封禁。当然,确定你自己有重大违规、违法行为,就不要申诉了,基本没用。怎样确认自己的违规行为,具体可参考: 微信公众平台运营规范: https://mp.weixin.qq.com/mp/opshowpage?action=newoplaw#t3-4 再说申诉部分,公众号赋予申诉权利。所以再次强调,认真对待唯一一次申诉机会。周围运营公众号申诉成功的案例都是非常认真总结好申诉材料才填写申诉理由。 参考理由分几点: 1、确认有违规情况下,承认错误,且希望从轻处理,千万不能避重就轻一句概过违规事实。 2、确认没有违规的情况下,把材料准备全,包括图片、引用、自己的内容记录等可以证明的一切资料。 3、确认自己运营的公众号,在公众号体系中的贡献,优质内容等均可截图,包括留言中人们对于该公众号认同,别的公众号对该公众号的推荐等,都可做为申诉理由之一截图上传。 4、确认自己未来对公众号的规划,这点也是建议填写。 当然,也有没有想到的地方,可自行填写。 写申诉理由文字有限,尽量简短精炼。 最后也是重重要的一点,认真了解公众号运营规范,避免封号发生。 期待每位公众号主都能天天编辑优质的内容,实现自己做自媒体的初衷。
2023-07-27 - 开放标签跳转小程序,ios正常跳转,安卓跳转报base_resp:{ ret: -2}?
wx-open-launch-weapp>[图片][图片]
2020-08-14 - 开放标签跳转小程序,ios正常跳转,安卓跳转报base_resp:{ ret: -2}
场景: 进入创客村公众号,点击商城入口进入商城首页,点击右下角直播浮标进入小程序直播列表 [图片][图片][图片] 以下上相关代码 [图片] [图片]
2020-09-17 - 想问下微信公众号的网页授权域名 如果我配置了一级域名 那么二级域名是否可以直接获取到授权code?
[图片]
2021-02-23 - 小程序流量组关于单屏内不允许显示两个及两个以上广告位问题?
流量主在单屏内容里展示两个及以上banner、原生广告位、视频前贴、视频广告位;流量主使广告位无规则悬浮等。不是很理解上述规则,在视频播放页,加了视频前贴,在页面底部就不能加视频广告了吗?在单屏内同时展示插屏,视频前贴,视频广告是不允许的吗?
2022-03-23 - 申请开通电商收付通,需要ICP许可证还是EDI许可证?
申请服务商,在同一小程序内需要使用多商户订单合并支付的方式。这种类型是需要ICP或EDI许可证,还是两个证都同时要呢?
2021-01-14 - 共享环境下能调unifiedOrder唤起支付吗?
将小程序A云开发环境共享给小程序B。小程序B有微信支付功能,故调用A的云函数用unifiedOrder方法来计算签名返回前端。但unifiedOrder方法没有appid入参,导致算出来的签名里的appId是小程序A的,不是小程序B的,从而在支付时提示“商户传入的appid参数不正确,请联系商户处理”。 那么在共享环境下能调用unifiedOrder来支付吗?
2022-11-14 - 小程序云开发获取并保存用户IP属地
现在各大平台发表文章、评论等内容都显示出了用户的IP属地,现在来探讨一下小程序使用云开发怎么获取并保存用户IP属地。 1、获取到用户ip,这里演示使用云函数获取。 2、使用腾讯位置服务的WebService API的IP定位接口,获取归属地。 响应示例: { "status": 0, "message": "Success", "result": { "ip": "111.206.145.41", "location": { "lat": 39.90469, "lng": 116.40717 }, "ad_info": { "nation": "中国", "province": "北京市", "city": "北京市", "district": "", "adcode": 110000 } } } 演示代码: // 云函数入口文件 const cloud = require('wx-server-sdk') const axios = require('axios') cloud.init() // 云函数入口函数 exports.main = async (event, context) => { const wxContext = cloud.getWXContext(); var ip = wxContext.CLIENTIP ? wxContext.CLIENTIP : wxContext.CLIENTIPV6; if (ip) { const res = await axios.get("https://apis.map.qq.com/ws/location/v1/ip", { params: { ip: ip, key: "xxx" // 使用腾讯WebService API:https://lbs.qq.com/service/webService/webServiceGuide/webServiceIp } }); return res; } return null; }
2022-05-11 - 云函数接收公众号消息推送
公众号的消息推送和处理,也可以云开发来做了,不需要搭建自己的服务器了。 具体步骤如下: 1、将小程序的云环境共享给公众号的appid。 操作如下:小程序开发工具--云开发--更多--环境共享--添加共享--添加公众号appid。共享成功; 2、配置公众号云开发 打开开发工具--回到初始页项目管理页--左则项目类型栏选择“公众号网页”--云开发--弹出框里填入公众号appid--进入云控制台 3、配置公众号消息推送 以公众号appid进入云控制台后--更多--环境共享--消息推送--添加消息推送--选择event--选择subscribe_and_unsubscribe--选择接收消息推送的云环境--选择接收消息推送的云函数--结束 4、配置完成,接收消息推送 在云函数里写处理消息推送的代码。 在云函数里: console.log(event)//获取消息包JSON数据 console.log(wxContext)//获取公众号用户的openid和unionid。 5、结束。
2021-10-21 - 【物流-查询组件】1000+快递公司物流信息免费接入
快递100“快递跟踪”微信小程序插件上线啦! 帮助所有小程序解决快递物流查询问题,现面向所有第三方小程序、小程序开发服务商(包括个体小程序)免费开放。 目前已经有1000家小程序申请接入了快递100【快递跟踪】插件, 点链接→https://fuwu.weixin.qq.com/service/detail/00008caeab84c07c17dcdabf55b815,立即添加插件(添加时请从电脑端打开链接) 公开数据显示,今年上半年微信小程序数量已超过430万。 随着小程序生态不断发展,越来越多商家和开发者在小程序上建立自有商城。大到京东这样的巨型平台,小到一个公众号、博主自己开的店铺,用户都可以在小程序上下单。业务逐渐壮大后,物流却成为困扰不少商家和开发者的一大难题。 大平台有大量的资金和人力来调配资源,自主开发接入物流公司系统,给顾客及时物流反馈;而对于那些中小店铺的小程序商家们来说,没有足够人力、财力支撑,无法自主开发接入。 近日,中国领先的快递物流信息服务商快递100宣布,正式上线“快递跟踪”小程序功能插件,开放快递物流信息查询模块,允许第三方小程序 免费 接入。 “快递跟踪”小程序插件整合了快递100快递查询能力,支持全球1000+快递物流公司信息查询,对全行业的小程序免费开放接入,包括电商平台、商家、医药寄送、信息查询或本地生活服务平台等 任何有物流查询需求的小程序开发者,为企业、商家、个体小程序赋能。 点链接→https://fuwu.weixin.qq.com/service/detail/00008caeab84c07c17dcdabf55b815,立即添加插件 01 无门槛免费接入 无论是电商商城,还是社群团购、回收类等任何有涉及快递物流环节的小程序,物流信息查询是必须重视的一项服务。卖家是否能提供及时的物流信息更新服务,会影响到用户的二次购买决策。 “快递跟踪”小程序插件,是免费接入。接入插件后,用户只要在小程序内点击快递单号,就可以查看最新物流信息,有效提升用户的购物体验,提高小程序的回访率和复购转化率。 02 原生体验,无第三方跳转 快递100“快递跟踪”插件依托微信小程序生态,第三方小程序接入后无需任何跳转,在自己的小程序内即可直接查看物流信息,简化用户操作流程。 [图片] 接入方式也非常简单快捷,模板化快速接入,无需再次开发,几个小时即可完成接入,大大降低开发和运营成本。 快递100“快递跟踪”插件开放接入,不仅能够帮助小程序开发者降低物流服务的开发门槛和成本,同时也为小程序商家提供了更好服务用户的方式。 03 支持国内外1000+家快递公司物流查询 通过快递100“快递跟踪”小程序插件,支持全网快递物流查询,可查看国内、国际1000+快递物流公司的信息,同时还提供官方客服热线。 “快递跟踪”插件服务稳定,让商家、开发者管理更加方便。 除了常见的电商场景,“快递跟踪”插件同样非常适合有特定物品物流信息查询需求的机构和企业接入 —— 例如医院类公众号,病历档案预约寄出后的进度查询;驾校机构,寄出驾照后的进度查询;校园机构的报到证、档案等资料的快递查询等。 点链接→https://fuwu.weixin.qq.com/service/detail/00008caeab84c07c17dcdabf55b815,立即添加插件 另外,快递100也可提供快递信息推送、实时快递、地图轨迹API等服务,点这里→https://api.kuaidi100.com/ 了解详情 快递100是中国领先的快递物流信息服务商,国家高新技术企业、新基建代表企业。 快递100目前拥有个人注册用户1.6亿,企业客户60万+,日均查询量3亿次,是国内查询量最大的快递物流信息查询平台;年寄件量超8亿单,寄件功能官方合作京东、邮政、德邦、圆通、韵达、DHL、TNT、UPS等多家国内外快递公司。 快递100致力构建中国最大的物流信息服务枢纽,始终秉承开放态度与快递行业共创共赢,为用户、商家、企业提供专业、可靠的服务,实现互联互通互动。 点链接→https://fuwu.weixin.qq.com/service/detail/00008caeab84c07c17dcdabf55b815,立即添加插件
2021-11-26 - 求官方解疑~ 小程序内嵌的h5里有用户评论功能 需要选社区类目么?
您好,请问 1、如果小程序内嵌的h5里有用户评论功能,评论经过审核后会放出来,其他用户可以给评论点赞,但是不会相互评论。这种形式,需要选社区类目么?(是否需要《非经营性互联网信息服务备案核准》?) 2、《非经营性互联网信息服务备案核准》是否就是icp? 感谢回答~
2018-11-21 - 小程序用户头像昵称获取规则调整公告
更新时间:2022年11月9日由于 PC/macOS 平台「头像昵称填写能力」存在兼容性问题,对于来自低于2.27.1版本的访问,小程序通过 wx.getUserProfile 接口将正常返回用户头像昵称,插件通过 wx.getUserInfo 接口将正常返回用户头像昵称。 更新时间:2022年9月28日考虑到近期开发者对小程序用户头像昵称获取规则调整的相关反馈,平台将接口回收的截止时间由2022年10月25日延期至2022年11月8日24时。 调整背景在小程序内,开发者可以通过 wx.login 接口直接获取用户的 openId 与 unionId 信息,实现微信身份登录,支持开发者在多个小程序或其它应用间匿名关联同一用户。 同时,为了满足部分小程序业务中需要创建用户的昵称与头像的诉求,平台提供了 wx.getUserProfile 接口,支持在用户授权的前提下,快速使用自己的微信昵称头像。 但实践中发现有部分小程序,在用户刚打开小程序时就要求收集用户的微信昵称头像,或者在支付前等不合理路径上要求授权。如果用户拒绝授权,则无法使用小程序或相关功能。在已经获取用户的 openId 与 unionId 信息情况下,用户的微信昵称与头像并不是用户使用小程序的必要条件。为减少此类不合理的强迫授权情况,作出如下调整。 调整说明自 2022 年 10 月 25 日 24 时后(以下统称 “生效期” ),用户头像昵称获取规则将进行如下调整: 自生效期起,小程序 wx.getUserProfile 接口将被收回:生效期后发布的小程序新版本,通过 wx.getUserProfile 接口获取用户头像将统一返回默认灰色头像,昵称将统一返回 “微信用户”。生效期前发布的小程序版本不受影响,但如果要进行版本更新则需要进行适配。自生效期起,插件通过 wx.getUserInfo 接口获取用户昵称头像将被收回:生效期后发布的插件新版本,通过 wx.getUserInfo 接口获取用户头像将统一返回默认灰色头像,昵称将统一返回 “微信用户”。生效期前发布的插件版本不受影响,但如果要进行版本更新则需要进行适配。通过 wx.login 与 wx.getUserInfo 接口获取 openId、unionId 能力不受影响。「头像昵称填写能力」支持获取用户头像昵称:如业务需获取用户头像昵称,可以使用「头像昵称填写能力」(基础库 2.21.2 版本开始支持,覆盖iOS与安卓微信 8.0.16 以上版本),具体实践可见下方《最佳实践》。小程序 wx.getUserProfile 与插件 wx.getUserInfo 接口兼容基础库 2.27.1 以下版本的头像昵称获取需求:对于来自低版本的基础库与微信客户端的访问,小程序通过 wx.getUserProfile 接口将正常返回用户头像昵称,插件通过 wx.getUserInfo 接口将正常返回用户头像昵称,开发者可继续使用以上能力做向下兼容。对于上述 3,wx.getUserProfile 接口、wx.getUserInfo 接口、头像昵称填写能力的基础库版本支持能力详细对比见下表: [图片] *针对低版本基础库,兼容处理可参考 兼容文档 请已使用 wx.getUserProfile 接口的小程序开发者和已使用 wx.getUserInfo 接口的插件开发者尽快适配。小游戏不受本次调整影响。 最佳实践小程序可在个人中心或设置等页面使用头像昵称填写能力让用户完善个人资料: [图片] 微信团队 2022年5月9日
2023-09-26 - 云函数中时区问题
[图片] https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/functions/notice.html
2019-09-25 - 问个问题,如果做个微信群的小程序,能过审核吗
就是做个微信群集合,把用户建立的微信群都放在小程序上展示,可以提交自己群的二维码,也可找自己想加入的群
2018-05-30 - 能在小程序里对C端用户发布群二维码吗?
小程序运营方希望能在小程序里对C端用户展示群二维码,招揽用户进群做服务和种子用户,不知是否合规
2021-11-17 - 微信小程序可以引导用户通过二维码加入群聊吗?
如题,请帮忙解答
2021-10-08 - 小程序个人能做群二维码发布吗?
小程序个人能做群二维码发布吗? 就是别人的群给我二维码,我能发布到小程序里面分享吗? 或者我在网上找到的微信群二维码,可以发布到小程序做分享吗? 会不会违规。
2021-05-05 - 微信小程序UI组件库合集
UI组件库合集,大家有遇到好的组件库,欢迎留言评论然后加入到文档里。 第一款: 官方WeUI组件库,地址 https://developers.weixin.qq.com/miniprogram/dev/extended/weui/ 预览码: [图片] 第二款: ColorUI:地址 https://github.com/weilanwl/ColorUI 预览码: [图片] 第三款: vantUI(又名:ZanUI):地址 https://youzan.github.io/vant-weapp/#/intro 预览码: [图片] 第四款: MinUI: 地址 https://meili.github.io/min/docs/minui/index.html 预览码: [图片] 第五款: iview-weapp:地址 https://weapp.iviewui.com/docs/guide/start 预览码: [图片] 第六款: WXRUI:暂无地址 预览码: [图片] 第七款: WuxUI:地址https://www.wuxui.com/#/introduce 预览码: [图片] 第八款: WussUI:地址 https://phonycode.github.io/wuss-weapp/quickstart.html 预览码: [图片] 第九款: TouchUI:地址 https://github.com/uileader/touchwx 预览码: [图片] 第十款: Hello UniApp: 地址 https://m3w.cn/uniapp 预览码: [图片] 第十一款: TaroUI:地址 https://taro-ui.jd.com/#/docs/introduction 预览码: [图片] 第十二款: Thor UI: 地址 https://thorui.cn/doc/ 预览码: [图片] 第十三款: GUI:https://github.com/Gensp/GUI 预览码: [图片] 第十四款: QyUI:暂无地址 预览码: [图片] 第十五款: WxaUI:暂无地址 预览码: [图片] 第十六款: kaiUI: github地址 https://github.com/Chaunjie/kai-ui 组件库文档:https://chaunjie.github.io/kui/dist/#/start 预览码: [图片] 第十七款: YsUI:暂无地址 预览码: [图片] 第十八款: BeeUI:git地址 http://ued.local.17173.com/gitlab/wxc/beeui.git 预览码: [图片] 第十九款: AntUI: 暂无地址 预览码: [图片] 第二十款: BleuUI:暂无地址 预览码: [图片] 第二十一款: uniydUI:暂无地址 预览码: [图片] 第二十二款: RovingUI:暂无地址 预览码: [图片] 第二十三款: DojayUI:暂无地址 预览码: [图片] 第二十四款: SkyUI:暂无地址 预览码: [图片] 第二十五款: YuUI:暂无地址 预览码: [图片] 第二十六款: wePyUI:暂无地址 预览码: [图片] 第二十七款: WXDUI:暂无地址 预览码: [图片] 第二十八款: XviewUI:暂无地址 预览码: [图片] 第二十九款: MinaUI:暂无地址 预览码: [图片] 第三十款: InyUI:暂无地址 预览码: [图片] 第三十一款: easyUI:地址 https://github.com/qq865738120/easyUI 预览码: [图片] 第三十二款 Kbone-UI: 地址 https://wechat-miniprogram.github.io/kboneui/ui/#/ 暂无预览码 第三十三款 VtuUi: 地址 https://github.com/jisida/VtuWeapp 预览码: [图片] 第三十四款 Lin-UI 地址:http://doc.mini.talelin.com/ 预览码: [图片] 第三十五款 GraceUI 地址: http://grace.hcoder.net/ 这个是收费的哦~ 预览码: [图片] 第三十六款 anna-remax-ui npm:https://www.npmjs.com/package/anna-remax-ui/v/1.0.12 anna-remax-ui 地址: https://annasearl.github.io/anna-remax-ui/components/general/button 预览码 [图片] 第三十七款 Olympus UI 地址:暂无 网易严选出品。 预览码 [图片] 第三十八款 AiYunXiaoUI 地址暂无 预览码 [图片] 第三十九款 visionUI npm:https://www.npmjs.com/package/vision-ui 预览码: [图片] 第四十款 AnimaUI(灵动UI) 地址:https://github.com/AnimaUI/wechat-miniprogram 预览码: [图片] 第四十一款 uView 地址:http://uviewui.com/components/quickstart.html 预览码: [图片] 第四十二款 firstUI 地址:https://www.firstui.cn/ 预览码: [图片]
2023-01-10 - 小程序TabBar动画技巧
小程序实现TabBar创意动画(文末附完整源代码) 小程序日益增多的情况下,UI风格显得越来越重要,在页面中如果能让[代码]TabBar[代码]个性化一点,加一些小交互,用户体验会大大提升。由于小程序对[代码]svg[代码]不太友好,所以我们尽量使用[代码]css[代码]动画进行实现。之前文章小程序开发技巧中提到过[代码]TabBar[代码]自定义方案,感兴趣的可以了解一下。下面就分享一下今天写的几个交互效果,文末也会分享源代码。记得点赞+关注+收藏! NO.1 这种效果主要使用了[代码]transform[代码]和[代码]opacity[代码]来实现。文字默认隐藏并缩小,点击后[代码]icon[代码]图标[代码]transform[代码]的[代码]y轴[代码]方向上移,同时控制文字的[代码]opacity[代码]。圆形块根据点击的[代码]index[代码]去动态计算[代码]x轴[代码]的偏移位置即可。 [图片] 核心css代码(完整代码见文末): [代码] .tabbar .item .text{ position: absolute; width: 100%; bottom: 10rpx; text-align: center; font-size: 22rpx; opacity: 0; transition: all .8s; transform: scale(0.8); width: 100%; } .tabbar .item.active .text{ opacity: 1; transform: scale(1); } .tabbar .item.active .icon{ color: #3561f5; transform: translateY(-55rpx); } .tabbar .item .icon{ font-size: 50rpx!important; text-align: center; transition: all .8s; } [代码] NO.2 这个效果用到一个css动画工具库:bouncejs,它可以在线生成css动画,然后复制到项目中使用即可。下方效果采用跳跃式切换,整体看上去非常有活力。使用了[代码]animation[代码]动画。由于css动画代码过多,想看完整代码见文末[代码]github[代码]地址。 [图片] NO.3 下方这个效果还是用bouncejs在线编辑,编辑完成后只需要点击后给相应的元素添加类名即可。 [图片] 结尾 如需源代码可以移步github。 👉欢迎关注+收藏+点赞,感谢支持~
2021-06-17 - 免鉴H5跳转小程序的坑,微信可跳,H5无法跳、自定义传参、云函数上传失败等小白解决方案
本文适合小白交流,大佬勿喷。把自己遇到的坑,小白解决方案贴出来交流。 坑1:微信跳转与H5跳转,跳转的路径不是一样的。 跟我一样粗心伙伴注意了 官方说明:网页会判断所在的环境来觉得采用哪种跳转方式,如检测到微信客户端内,则免鉴权使用开放标签跳转,如检测到在外部浏览器或 App,则使用 URL Scheme 跳转小程序。区别在于在html代码里面的属于标签跳转,URL Scheme跳转是在云函数里面的。主要问题是没看清文档及不熟悉云开发,以为前台代码的path就是所有跳转的url。html的path=xxx 仅指的是当在微信H5情况下跳转的路径(可带参数) <wx-open-launch-weapp id="launch-btn" username="小程序原始账号 ID(gh_ 开头的)" path="要跳转到的页面路径"> <!-- 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> 如果想在其他浏览器(非微信H5上做跳转,跳转的路径及参数需要在云函数写) async function getUrlScheme(options) { return cloud.openapi.urlscheme.generate({ jumpWxa: { path: '/page/component/index', // <!-- replace --> query: 'i=aaabbb', }, // 如果想不过期则置为 false,并可以存到数据库 isExpire: true, // 一分钟有效期 expireTime: parseInt(Date.now() / 1000 + 60 其中path是路径,不能带参数(不太确定),简约如果有参数则写在query里面去,例如 query:'openid=developers' 这样的话微信跳转的路径其实可以与H5跳转路径不一样都没问题,第一个坑解决了。 坑2:哪怕按照上面写了,还是微信能跳,H5不能跳,云函数一直无法上传。 我也查了社区很多贴,没有一个好的解决方案,这个坑主要问题是在基础没有打好的问题,我想部分人应该也是刚接触云开发不久,都是用到这个开发能力才去接触这个开发能力 解决方案: 官方在云函数的地方,告诉我们需要新建一个云函数名字为public,在这个目录下面创建一个js。但是因为这个要用到wx-server-sdk。 打开命令行,定位到public,安装一下所需要的环境,云函数中使用 wx-server-sdk 需在对应云函数目录下安装 wx-server-sdk 依赖(自己理解) npm install --save wx-server-sdk@latesty 这个安装上去的了,你的public目录下就会多一个node_modules目录,云端安装就不说了,这里我是直接全部上传的。 安装好后,上传代码后,基本上H5就可以跳转到浏览器了。(如果你的还行不行,检查一下权限,还有报错,可能还需要按照node_sdk) 【如果还是不行,可以回复本帖,尝试帮忙排查解决】 坑3:路径后自定义传参问题?参数是动态的问题。 因为自己对云函数认识不足,所以用了自己的解决方案。 我当时项目的需求是,动态传参,每个参数都是不一样,如果按官方的文档是固定路径的 我是获取当前h5的url参数,提取自己要的参数出来。然后拼接到 path里面去。拼接的方法有很多,当时为了速度快点就用了最白痴的方法。 需要注意,微信的跳转与h5跳转写法不一样。 //获取url的参数 function getQueryVariable(variable) { var query = window.location.search.substring(1); var vars = query.split("&"); for (var i=0;i<vars.length;i++) { var pair = vars[i].split("="); if(pair[0] == variable){return pair[1];} } return(false); } //如果你的url是 qq.com/i=88888 ,则getQueryVariable("i") 就是取i的值:88888 var i = getQueryVariable("i"); 微信H5的自定义参数跳转 我把官方的这个代码 <wx-open-launch-weapp id="launch-btn" username="小程序原始账号 ID(gh_ 开头的)" path="要跳转到的页面路径"> <!-- replace --> <template> <button>打开小程序</button> </template> </wx-open-launch-weapp> 改成了(大佬别吐槽,当时为了实现功能) <script type="text/javascript"> document.write("<wx-open-launch-weapp id='launch-btn' username='' path='pages/index/index?i="+i+"'>"); </script> <template> <button>打开小程序</button> </template> </wx-open-launch-weapp> 其他H5的自定义参数跳转 把代码拉到最下面去,他是通过这个来执行云函数的,然后拿到回传的信息的链接,最后直接js跳转到该链接。 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 所以我们可以把我们的值也传过去。我在data里面加了一个action1 async function openWeapp(onBeforeJump) { var c = window.c const res = await c.callFunction({ name: 'public', data: { action: 'getUrlScheme', action1: 'i='+i, }, }) // console.log(i,"8888") console.warn(res) if (onBeforeJump) { onBeforeJump() } location.href = res.result.openlink 云函数那边我没有用原本那个方法,而是直接改了原本的。(可以按照自己思路来) 最终我的云函数代码就是 // 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init() // 云函数入口函数 exports.main = async (event, context) => { const wxContext = cloud.getWXContext() try { const result = await cloud.openapi.urlscheme.generate({ "jumpWxa": { "path": 'pages/index/index', "query": event.action1 }, "isExpire": false, "expireTime": parseInt(Date.now() / 1000 + 60) }) return result } catch (err) { return err } } 最后可以实现根据自定义传参到url,h5、微信h5都能自动跳转到微信小程序。 以此文提示自己需努力补前端基础,细心阅开发文档。
2021-08-09 - 简单几步,用云函数上传文件至静态网站托管
作者:布道师 XiaoLin 云开发CloudBase为开发者提供静态网站托管的能力,静态资源(HTML、CSS、JavaScript、字体等)的分发由对象存储 COS 和拥有多个边缘网点的 CDN 提供支持。 本篇文章将使用云开发 Node.js 管理端 SDK演示,只需简单几步即可在云函数中上传文件到静态网站托管。 准备创建云开发按量计费环境;在按量计费环境下开通静态网站托管;查询腾讯云访问密钥。操作步骤一、获取访问密钥:secretId 和 secretKey打开腾讯云控制台,在右上角头像位置下拉菜单中找到访问管理并点击进入。 [图片] 选择左侧菜单栏中的访问密钥-API密钥管理,点击新建密钥。 点击“显示”,根据提示操作即可获得密钥。 [图片] 二、创建云函数打开云开发 CloudBase 控制台,选择按量付费环境,选择左侧菜单栏的云函数。 [图片] 点击新建云函数,然后根据提示信息填写云函数配置信息,完成创建即可。 [图片] 点击刚刚创建的云函数,进入编辑页面,点击“函数代码”。 [图片] 复制测试代码到新创建的云函数中,并修改envId(云开发环境id)、secretId和secretKey(获取方式见上一步)。 const CloudBase = require('@cloudbase/manager-node') const fs = require('fs') const { hosting } = new CloudBase({ secretId: 'Your SecretId', // 腾讯云API密钥SecretId https://console.cloud.tencent.com/cam/capi secretKey: 'Your SecretKey', // 腾讯云API密钥SecretKey https://console.cloud.tencent.com/cam/capi envId: 'Your envId' // 云开发环境ID,可在腾讯云云开发控制台获取 https://console.cloud.tencent.com/tcb/env/index }) exports.main = async(event, context) => { //写入文件到云函数临时目录 const content = '测试内容!' fs.writeFile('/tmp/test.txt', content, (err) => { if (err) { return console.log(err); } console.log("File saved successfully!"); }) let fileCount = 0 // 上传文件 await hosting.uploadFiles({ files: [{ localPath: '/tmp/test.txt', cloudPath: 'hosting/test_data/data.txt' }], ignore: ['**/ignore.*'], onFileFinish: () => { fileCount++ } }) console.log(fileCount) // 1 return fileCount } 以及 package.json 文件: { "name": "tcb_scf2hosting", "version": "1.0.0", "description": "使用云函数上传文件到静态网站托管", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { "@cloudbase/manager-node": "latest" }, "author": "XiaoLin", "license": "ISC" } 三、保存并安装依赖编辑完成后点击左下方保存并安装依赖按钮。并等待右上角出现云函数更新成功提示后即可。 [图片] 四、测试打开右上角的测试按钮,点击运行测试,出现如下结果说明测试成功。 [图片] 打开所操作云环境的静态网站托管即可看到刚刚上传的资源信息。 [图片] 结语本文通过云开发 Node.js 管理端 SDK 以及运用云函数临时读写文件的 tmp 目录,来实现使用云函数上传文件到静态网站托管。 除了云函数,云开发 manager-node sdk 还支持开发者通过接口形式对云开发提供的数据库、文件存储等资源进行创建、管理、配置等操作,欢迎体验。详见文档(点击文末阅读原文直达):https://docs.cloudbase.net/api-reference/manager/node/introduction.html 产品介绍云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为开发者提供高可用、自动弹性扩缩的后端云服务,包含计算、存储、托管等serverless化能力,可用于云端一体化开发多种端应用(小程序,公众号,Web 应用,Flutter 客户端等),帮助开发者统一构建和管理后端服务和云资源,避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。 开通云开发:https://console.cloud.tencent.com/tcb?tdl_anchor=techsite 产品文档:https://cloud.tencent.com/product/tcb?from=12763 技术文档:https://cloudbase.net?from=10004 技术交流加Q群:601134960 最新资讯关注微信公众号【腾讯云云开发】
2021-06-10 - weapp-qrcode-canvas-2d在微信小程序中生成二维码,新版canvas-2d接口
weapp-qrcode-canvas-2d weapp-qrcode-canvas-2d 是使用新版canvas-2d接口在微信小程序中生成二维码(外部二维码)的js包。canvas 2d 接口支持同层渲染且性能更佳,建议切换使用,可大幅提升生成图片的速度。 仓库地址 weapp-qrcode-canvas-2d【码云gitee】 weapp-qrcode-canvas-2d【github】 [图片] 测试环境 微信小程序基础库版本:2.10.4 开发者工具版本:Stable 1.03.2101150 Usage 先在 wxml 文件中,创建绘制的 [代码]canvas[代码],并定义好 [代码]width[代码], [代码]height[代码], [代码]id[代码] , [代码]type[代码] ,其中type的值必须为[代码]2d[代码] [代码]<canvas type="2d" style="width: 260px; height: 260px;" id="myQrcode"></canvas> [代码] 安装方法1:直接引入 js 文件 直接引入 js 文件,使用 [代码]drawQrcode()[代码] 绘制二维码 [代码]// 将 dist 目录下,weapp.qrcode.esm.js 复制到项目中。路径根据实际引用的页面路径自行改变 import drawQrcode from '../../utils/weapp.qrcode.esm.js' [代码] 安装方法2:npm安装 [代码]npm install weapp-qrcode-canvas-2d --save [代码] // 然后需要在小程序开发者工具中:构建npm [代码]import drawQrcode from 'weapp-qrcode-canvas-2d' [代码] 安装完成后调用 例子1:没有使用叠加图片 [代码]const query = wx.createSelectorQuery() query.select('#myQrcode') .fields({ node: true, size: true }) .exec((res) => { var canvas = res[0].node // 调用方法drawQrcode生成二维码 drawQrcode({ canvas: canvas, canvasId: 'myQrcode', width: 260, padding: 30, background: '#ffffff', foreground: '#000000', text: 'abc', }) // 获取临时路径(得到之后,想干嘛就干嘛了) wx.canvasToTempFilePath({ canvasId: 'myQrcode', canvas: canvas, x: 0, y: 0, width: 260, height: 260, destWidth: 260, destHeight: 260, success(res) { console.log('二维码临时路径:', res.tempFilePath) }, fail(res) { console.error(res) } }) }) [代码] 例子2:使用叠加图片(在二维码中加logo) [代码]const query = wx.createSelectorQuery() query.select('#myQrcode') .fields({ node: true, size: true }) .exec((res) => { var canvas = res[0].node var img = canvas.createImage(); img.src = "/image/logo.png" img.onload = function () { // img.onload完成后才能调用 drawQrcode方法 var options = { canvas: canvas, canvasId: 'myQrcode', width: 260, padding: 30, paddingColor: '#fff', background: '#fff', foreground: '#000000', text: '123456789', image: { imageResource: img, width: 80, // 建议不要设置过大,以免影响扫码 height: 80, // 建议不要设置过大,以免影响扫码 round: true // Logo图片是否为圆形 } } drawQrcode(options) // 获取临时路径(得到之后,想干嘛就干嘛了) wx.canvasToTempFilePath({ x: 0, y: 0, width: 260, height: 260, destWidth: 600, destHeight: 600, canvasId: 'myQrcode', canvas: canvas, success(res) { console.log('二维码临时路径为:', res.tempFilePath) }, fail(res) { console.error(res) } }) }; }) [代码] API drawQrcode([options]) options Type: Object 参数 必须 说明 示例 canvas 必须 画布标识,传入 canvas 组件实例 canvasId 非 绘制的[代码]canvasId[代码] [代码]'myQrcode'[代码] text 必须 二维码内容 ‘123456789’ width 非 二维码宽度,与[代码]canvas[代码]的[代码]width[代码]保持一致 260 padding 非 空白内边距 20 paddingColor 非 内边距颜色 默认与background一致 background 非 二维码背景颜色,默认值白色 [代码]'#ffffff'[代码] foreground 非 二维码前景色,默认值黑色 [代码]'#000000'[代码] typeNumber 非 二维码的计算模式,默认值-1 8 correctLevel 非 二维码纠错级别,默认值为高级,取值:[代码]{ L: 1, M: 0, Q: 3, H: 2 }[代码] 1 image 非 在 canvas 上绘制图片,层级高于二维码,v1.1.1+版本支持。具体使用见:例子2 [代码]{imageResource: '', width:80, height: 80, round: true}[代码]
2023-04-02 - 小程序流量主、广告位类型和广告收益分析
小程序流量主、广告位类型和广告收益分析 ## 本文介绍 最近在小程序的几个微信群,经常有朋友问到以下几个问题 1、小程序怎么盈利 2、小程序流量主是什么以及怎么开通 3、小程序广告有哪些类型,哪种广告类型相对收益最大 4、 ## 小程序如何盈利 目前对个人小程序开发者而言,只有通过开通流量主,并且按照官方规范要求添加广告位,才能获取收益,当然打赏除外。 ## 什么是流量主如何开通 流量主是微信对外提供的一个服务,通过开通流量主,就可以在小程序合适的位置引入广告位,进而实现收益 登录公众号后台( https://mp.weixin.qq.com/ )在左侧菜单中,找到 推广-流量主,点击进去会看到如下截图 [图片] 小程序流量主: 1.开通条件:小程序累计独立访客(UV)1000以上,且无违规记录,即可开通流量主功能。 温馨提示:如满足条件仍无法开通,可能是数据同步问题,建议等待1-2个工作日后再试。 2.申请方法:进入微信公众平台小程序后台,点击左侧面板“流量主”,满足开通门槛的小程序开发者点击“开通”,提交财务资料,待审核通过后成功开通流量主功能,即可创建相应的广告位。 3.广告接入指引: 广告接入可查看: 微信小程序广告接入指引 在开通流量主的过程中,会绑定个人银行卡,以方便进行后续的广告收益结算,目前结算每月两次,具体官方公告可以查阅 [流量主结算周期及开票规则调整说明][2019-12-03发布] https://mp.weixin.qq.com/promotion/readtemplate?t=notice/detail_page&time=1575340587¬ice_id=634169 ## 广告类型有哪些 Banner激励式视频插屏视频广告前贴视频 以下为各广告类型,截图示例, [banner广告] [图片] [插屏广告] [图片] 视频广告 [图片] 由于插屏广告会影响用户体验,所以不建议放太多场景使用。 具体不同类型广告体验,可以扫码 [图片] 首页模块-->>插屏广告使用说明-->>视频广告关于我们-->>banner广告 ## 哪种广告类型收益相对最大 [图片] 在10月30号,将banner广告同一替换为激励式视频广告和视频广告,收益很明显从30元上升到90元、150元 可以看到视频广告相对于banner广告,对于收益增加是有用的。 下图是某小程序12月4号一天的收益数据 [图片] 12月4号一天,不同广告类型,收益分析 总收益 194.74+23.27+147.82=365.83 具体分拆来看 广告类型点击量总收益单个点击收益(元)banner1956194.740.099插屏广告6223.270.375激励式视频广告152147.820.972 通过上图我们对比分析,不难得出以下结论:激励式视频广告单个点击的收益最大、 当然我们不能通过单一维度来了解哪种收益最好,还要综合考虑,比如哪种广告对用户影响最小,毕竟不管哪种方式,广告的接入肯定会带来交互体验上的障碍, 我们必须在交互体验和广告收益这两者之间做好权衡。 ## 系统公告 激励式广告于7月31日支持30秒视频素材,广告流量将逐步放开,MP后台-广告位管理模块可支持选择6-15秒视频或6-30秒视频素材的功能,请流量主根据产品进行调整。程序视频广告已于9月4日正式全量上线,开通后即按广告曝光获得分成收入,进一步提升流量变现收益。小程序视频前贴广告组件已于8月30日正式全量上线,开通后即按广告曝光获得分成收入,进一步提升流量变现收益。## 官方文档 小程序广告组件流量主操作指引https://wximg.qq.com/wxp/pdftool/get.html?id=BJSyDkLqz&pa=14&name=miniprogramAds_supplier_manual应用规范https://wxa.wxs.qq.com/mpweb/delivery/legacy/pdftool/get.html?id=rynYA8o3f&pa=10&name=miniprogramAds_supplier_guidance小程序流量主应用规范https://wximg.qq.com/wxp/pdftool/get.html?id=rynYA8o3f&pa=10&name=miniprogramAds_supplier_guidance处罚标准https://wxa.wxs.qq.com/mpweb/delivery/legacy/pdftool/get.html?id=BkTGkbs2G&pa=1&name=miniprogramAds_supplier_regulation小程序视频广告流量主指引https://wximg.qq.com/wxp/pdftool/get.html?post_id=1317小程序视频前贴广告流量主指引https://wximg.qq.com/wxp/pdftool/get.html?post_id=1318## 总结三点 从纯收益的角度来讲,在各种广告类型中,视频广告(包含激励式视频广告、视频广告、视频前贴广告)要比banner广告要好,而且好很多从用户体验来讲,插屏广告是首次打开带插屏广告的页面强制弹出的,但是广告过后,在页面是不占空间的,这是区分与其他广告的地方,banner广告、激励式视频广告、视频广告、视频前贴广告都是在页面中占固定的空间的,这一点要小程序运营同学权衡。Banner广告是按点击,激励式视频、视频广告、插屏广告都是按照曝光来收取广告费用的,这一点非常重要,难怪我每次手工点击我的视频广告没有见流量的增加[哭脸.jpg]。[感谢 @ 仙森 补充于2019年12月9号] 虽然对个人开发者而言,我们开发小程序的目的是为了收益(当然也有为了情怀而开发),在了解如何收益的情况下,我们还是应该尽量把精力放在小程序本身的开发上面。 感谢 在此特别感谢,小程序运营讨论群的两位小伙伴,微信号中间两位已打码 1、@迭戈 (yang_##chun) 2、@风猫 (cs##26)
2020-12-25 - 服务商的特约商户,如何开通商家收款码?
子商户只有商家收款码收款的需求,如何帮助其开通
2021-02-26 - 【官方来看看】nodejs转换音频格式,报502 bad gateway
在本地测试没问题,上传测试就报502 [代码]let stream = bufferToStream(Buffer.from(postdata, [代码][代码]'base64'[代码][代码]));[代码] [代码] [代码] [代码]let bufarr = [];[代码] [代码] [代码] [代码]let cmd = ffmpeg(stream);[代码] [代码] [代码] [代码]cmd.format([代码][代码]'wav'[代码][代码]);[代码] [代码] [代码] [代码]let ffs = cmd.pipe();[代码] [代码] [代码] [代码]ffs.on([代码][代码]'data'[代码][代码],chunk=>{[代码][代码] [代码][代码]bufarr.push(chunk)[代码][代码]})[代码] [代码].on([代码][代码]'end'[代码][代码], () => {[代码] [代码] [代码][代码]Buffer.concat(bufarr)[代码][代码];[代码][代码]})[代码] [代码].on([代码][代码]'error'[代码][代码],err=>{[代码] [代码] [代码][代码]console.log(err)[代码][代码]}); [代码]
2018-08-08 - v3图片上传接口终于调通了
折腾了一天,终于把这个图片接口(https://api.mch.weixin.qq.com/v3/merchant/media/upload)调通了。看到社区里面有不少小伙伴遇到了一样的问题,这里简单分享一下我的经验。 开发图片上传接口要先了解微信支付v3的接口规范,还好我之前有开发过v3的接口,所以这块问题不大,不清楚的小伙伴可以先看V3接口规范:https://wechatpay-api.gitbook.io/wechatpay-api-v3/ PS:我第一次开发v3的接口时,也遇到不少问题,也简单说一下注意事项: 1、v3接口使用rsa签名方式,如果现有的商户号不是使用的权威ca证书,要先升级证书,这部分可以参考:https://wechatpay-api.gitbook.io/wechatpay-api-v3/ren-zheng/zheng-shu 2、签名的传递和计算方式和之前的接口不太一样,具体可以参考:https://wechatpay-api.gitbook.io/wechatpay-api-v3/ren-zheng/qian-ming-he-zheng-shu 言归正传,来说说图片上传接口的问题。我之前没有开发过上传文件的接口,所以对接口文档里面提到的boundary内容完全一脸懵。于是网上搜了一下,发现这篇文章说的算比较清楚了:https://blog.csdn.net/Ivanljt/article/details/52663726,于是就照葫芦画瓢去试试。 除了上面说到的post提交文件的问题,当时我还遇到一个签名的问题。之前开发v3接口,是要把post的json数据参与签名,然后实际post的也是json的数据。但是这次图片上传接口有点不一样,计算签名的时候需要把这个json数据{"filename":"apitest.jpeg","sha256":"67ed2c80b789a3d0e524c62c1fcff058da02b448dcb8644ecdab9b70ee04bf4b"}参与签名,但是post的是boundary那块内容。这个也是我测试多次后才解决的。 所以计算签名的原串是下面这样的: POST /v3/merchant/media/upload 1567054192 //时间戳 8e2c381d4dd04f1c55093f22c59c3a08 //随机数 {"filename":"apitest.jpeg","sha256":"67ed2c80b789a3d0e524c62c1fcff058da02b448dcb8644ecdab9b70ee04bf4b"} 接下来看看怎么处理boundary那块内容,参考了上述链接那哥们的代码,post的内容格式如下: --123456qwert111222 Content-Disposition: form-data;name="meta"; Content-Type: application/json; //这里必须要空一行 {"filename":"apitest.jpeg","sha256":"67ed2c80b789a3d0e524c62c1fcff058da02b448dcb8644ecdab9b70ee04bf4b"} --123456qwert111222 Content-Disposition: form-data;name="file"; filename="apitest.jpeg"; Content-Type: image/jpeg //这里必须要空一行 //这里是文件的二进制数据 --123456qwert111222-- 备注: 123456qwert111222是我自定义的boundary内容,大家可以改成自己定义的字符串; 内容必须以--boundary开头,中间的内容以--boundary分割,结尾以--boundary--结束; 每一行结尾要加上换行符\r\n 最后放上我的测试代码,水平有限,如有错漏之处,请多多指教。 https://developers.weixin.qq.com/community/pay/doc/000ee4d8e10e18bc3d1999e0d54404
2019-08-29 - 云开发如何对外提供回调 URL
在云开发的使用过程中,有不少的用户会提出自己的疑问,我如何才能在自己的云开发应用中加入一个回调地址? 在实际开发过程中,不少涉及到支付、订单等异步操作的场景,会需要提供一个回调 URL,以确保在用户完成自己的支付过程后,由支付服务器对回调的 URL 发起请求,确认调用成功。因此,不少用户在使用时提出了自己的疑问,应该如何实现这样的功能? 由于目前云开发云函数尚未对外提供 HTTP 调用的能力,因此,我们可以借助腾讯云提供的云函数和 API 网关,来实现类似的功能。 架构介绍 [图片] 在进行调用时开发者所设置的回调地址是一个 API 网关的地址,微信支付等服务的服务器在完成操作以后,会执行 HTTP 请求,请求 API 网关。 API 网关对应的是腾讯云云函数,我们可以在腾讯云云函数中调用云开发的 SDK,或者是云开发的 HTTP API,完成对云开发数据库的操作。 具体实现 依赖 腾讯云账号 小程序的 AppID 和 App Secret 实现 1. 创建云函数 访问 https://console.cloud.tencent.com/ ,使用你的腾讯云账号登陆,在顶部菜单栏中找到「云产品」— 「Serverless」—「云函数」 [图片] 在云函数中,选择「函数服务」,并在函数服务页面点击「新建」 [图片] 创建一个新的云函数,其中函数名称根据你的需要填写 [图片] 运行环境选择 Node.js 8.9,创建方式选择空白函数,完成后点击下一步。 在下一页不需要做修改,直接点击完成。 2. 创建 API 网关调用 在创建完成云函数以后,我们会进入到这样的界面,在这个界面中选择「触发方式」 [图片] 在触发方式页面新增一个触发方式,使用 「API 网关触发」,请求方法选择「ANY」,发布环境选择「发布」,鉴权方法选择「免鉴权」。 [图片] 填写完成后,点击保存,然后你会获得一个地址,这个地址就是你后续的回调地址。 [图片] 你可以将这个地址在浏览器中打开,可以看到这样的数据 [图片] 则说明我们的云函数完成了配置。 3. 编写程序调用 当我们完成了云函数的配置以后,接下来我们可以修改云函数,使其完成我们自己想要的功能。 我们希望云函数可以从外部发来的请求中获取到参数,并借助云开发提供的 API,对云开发数据库中的数据进行更新。则我们可以这样操作 我们在本地新建一个目录,并在其中执行如下命令(需要你提前安装了 N ode.js 环境) [代码]cd 新建文件夹 npm init -y npm install -y got [代码] 然后创建一个文件[代码]index.js[代码],并在其中加入如下代码,并设置其中的 APPID、SECRET、ENV_ID 等字段。 [代码]'use strict'; const got = require('got'); const APPID = ''; // 小程序 APPID const SECRET = ''; // 小程序 Secret const ENV_ID = '' const TOKEN_URL = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${SECRET}` const QUERY_URL = 'https://api.weixin.qq.com/tcb/databasecollectionget?access_token=' exports.main_handler = async (event, context, callback) => { // 1. 提取参数 可以从 URL?id=1&status=2 中提取出 1 和 2 const id = event.queryString.id; const status = event.queryString.status; // 2. 获取 Token,用于后续的调用 let token_resp = await got(TOKEN_URL); let token = JSON.parse(token_resp.body).access_token const url = QUERY_URL + token; // 执行云开发命令 let result = await got(url, { method: 'POST', json: true, headers: { 'content-type': 'applicaiton/json', 'accept-encoding':'gzip' }, body: { "env": ENV_ID } }) return result.body }; [代码] 配置完成后,保存文件。 4. 上传代码 打包完成后,重新访问腾讯云控制台,找到刚刚创建的云函数,进入到「函数代码」中,选择其中的「本地上传文件夹」,选择你刚刚创建的文件夹,等待其自动压缩完成后,点击保存上传。 [图片] 上传完成后,会自动进行部署。 你可以重新访问刚刚生成的 API 网关地址,并在其后加入 id 和 status 参数,比如我的最后的地址是 [代码]https://service-51pn7koc-1251337088.gz.apigw.tencentcs.com/release/myFunction?id=1&status=2[代码] [图片] 访问以后,得到的结果是这样的 [图片] 这样就说明我成功的从云函数中访问了云数据库,并且通过一个无需鉴权的 API 地址获取到了信息。 总结 实际上,借助于 API 网关、云函数以及云开发的 API & SDK,你可以实现非常多的功能,能不能实现,就看你的想象力有没有达到啦。
2019-09-18 - 订阅信息恶心至极?没有服务器的我们也能直接发消息到微信(公众号)! [即抄即用,拎包入住]
更新时间(2020/12/2) 大家好,众所周知,今年左右新出的订阅消息对商家和用户的友好度都极低,少了一个直接发信息到微信的最重要的渠道。 [图片] 那么我们动动歪心思,直接发消息到公众号(其实这是不是wx的本意???)。 所有服务号都可以在功能->添加功能插件处看到申请模板消息功能的入口,但只有认证后的服务号才可以申请模板消息的使用权限并获得该权限。 为方便公众号用户方便、快捷地接入小程序服务,公众号用户可复用公众号资质创建小程序。当前每个账号的模板消息的日调用上限为10万次,单个模板没有特殊限制。当账号粉丝数超过10W/100W/1000W时,模板消息的日调用上限会相应提升。这还不美滋滋吗,对于小商家来说等于无限次了。 所以我们一开始最好就在公众号进行微信认证,复用公众号的资质来注册小程序(注意,可以复用5个(好像))。 (我付出了¥300的惨痛代价)。 -------------------------------------------------------------------------------- 以上搞定后,流程走起 重点中的重点:为您的函数申请公网固定ip 有了这个ip之后,没有服务器的我们(穷)就能绕过ip白名单,这不正是真正的云开发精神吗? [图片] 0.用你的小程序账号登陆腾讯云,并在里面新建一个云函数 [图片]>>[图片] 选择你要发消息到公众号的小程序 [图片] [图片] -----------------我是2020/10/23的拎包哥----------------------- 创建并选择角色 记得在 https://console.cloud.tencent.com/cam/role 创建角色 [图片] 勾选tcb,scf [图片] 在搜索框里搜scf,tcb后,有什么策略就勾选什么策略 [图片] [图片] 创建角色名 [图片] 在云函数启用刚刚新建的角色 [图片] ps. 记得做好这一步,不然各种报错 missing authorationo key // 报错:缺失授权键 you are not authorized to xxx // 你没有权限去xxx 1.打开 https://cloud.tencent.com/document/product/583/38198 ,申请白名单 [图片] 2.审核通过后,再跟着步骤走。 [图片] 最后你的云函数会得到一个公网固定ip [图片] 3.开始码云函数的代码 如下,记得把wx-server-sdk和request-promise的包都npm下来。 依赖包可以通过这里上传上来 [图片] 我的做法 把获取的accessToken储存在云开发的数据库里。这样就不用担心access token的生成次数超过限制了 'use strict'; const cloud = require('wx-server-sdk') cloud.init() const db = cloud.database() const resInfo = db.collection('resInfo') const appid = '公众号的APPID'; // APPID const secret = '公众号的密钥'; // Secret const rp = require('request-promise') exports.main_handler = async (event, context, callback) => { var that = this var options = { url: 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' + appid + '&secret=' + secret, json: true } return await rp(options) .then(async res1 => { return await resInfo.where({name:'outInfo'}).update({ data: { access_token: res1.access_token } }).then(res2 => { return res2 }) }).catch(err => { return err }) } 公众平台以access_token为接口调用凭据,来调用接口,所有接口的调用需要先获取access_token,access_token在2小时内有效,过期需要重新获取,但1天内获取次数有限,开发者需自行存储,详见获取接口调用凭据(access_token)文档。4.设置触发器(触发器偶尔会失灵,所以最好是59min触发一次)。 [图片] 权限设置 由于云函数的访问不存在openid,所以安全规则必须为任何人可读可写。 [图片] 5.有了可以稳定刷新的access_token后,根据需求挑选你的公众号模板消息,开始你的表演。 例如:做餐饮小程序的朋友都想用户下单后发送订单信息到商户。那么就需要 获取商户的openid 步骤 注:公众号的openid在小程序开发工具就可以查出来 5.1 获取公众号openid列表 https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN 注意我没有加上next_openid = NEXT_OPENID,是为了取出公众号的所有的openidopenid列表在 res.data.openid5.2 for循环openid列表,根据商户的微信信息(nickname,city等等)找出只属于商户的openid https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN --------------------------------------- var openidList = openid列表 for(var i in openidList){ wx.request({ url:'https://api.weixin.qq.com/cgi-bin/user/info?access_token='+ openList[i] + '&lang=zh_CN', success(){ console.log(....) } }) } 这里就不用unionid了,不用浪费时间在上面), 就可以使用只发给商户的模板消息了。 公众号对应文档链接 https://developers.weixin.qq.com/doc/offiaccount/User_Management/Getting_a_User_List.htmlhttps://developers.weixin.qq.com/doc/offiaccount/User_Management/Get_users_basic_information_UnionID.html#UinonId, -----------------------------------------效果图--------------------------------------- [图片] -------------------------------------------------------------------------------- 哎,差不多了,感觉是有点折腾,如果感觉还是不够直白的,可以指出来我继续补充 。 求点赞,你的评论就是对拎包哥最大的支持。 [图片][图片] ===================更新于2020/10/23======================
2020-12-02 - 云函数http访问参数接收问题?
1.官方有个示例代码sum exports.main = async (event, context) => { console.log(event) console.log(context) return { sum:event.a+event.b } } 逻辑非常简单,通过云函数内置方法调用传入{"a":1,"b":2}没任何问题 通过postman ,与 wx.request 请问怎么调用呢? 这边打印参数永远在event.body里面 , 问题是我想这个云函数支持小程序云函数内置方法调用,与http调用,就没办法兼容吗?
2020-11-24 - 12月8日,客服消息被直接转到小程序云端消息推送。为了用户满意,被迫关闭用于接收微信支付回调的“云函数接收消息推送”功能
之前开启了小程序自带的云端云函数接收消息推送功能,接收云支付后的回调信息。当时也测试了客服功能,确认没有开启“消息推送”,一直用微信客服小程序与用户交流。 我的客服信息不多,最后一条是前天收到的,因为微信客服小程序的及时提醒,与用户及时进行了交流。 今天突然发现客服消息被推送到云端,由接收支付的回调云函数接收了信息,而客服小程序没有任何信息和提醒。检查了小程序管理后台,客服设置中没有开启“消息推送”。 我猜想,应该是最新更新后的变化吧,已经注意到 Nightly 1.03.2012032版的云端管理界面最新变化。 但是客服消息的变化给缺乏24小时值守的个体运营者带来了很大的打击: 1、没有准备云模式的客服互动功能,这会增加大量的开发量; 2、即使立即加班开发小程序的云客服响应功能,因没有永久的及时提醒消息,也会影响用户满意度,特别是针对微量客服需求的运营者。 3、小程序中自己开发云模式的客服功能,将增加小程序的复杂度。 面临最新变化,个体运营者将不得不关闭小程序云端“云函数接收消息推送”功能,舍弃接收微信支付返回信息的便利,先确保能及时回复少量的用户客服量。
2020-12-08 - 使用nodejs解码支付退款req_info字段问题
我使用的语言是nodejs,可是解密怎么都解不出来,可否提供一个demo
2019-09-19 - 小程序云开发支付,没有我自己的服务器。怎样用一个云函数收到支付回调通知?
我的小程序纯粹云开发,用云数据库,没有自己的服务器。 业务需求:微信购物,支付,成功后更改商品状态(比如“待支付”变成“已支付”)。 目前用的是这个tenpay (https://blog.csdn.net/qiushi_1990/article/details/99347580),完成下单,其实tenpay背后应该是用了“统一下单API (https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1)”,可以指定一个“通知地址 notify_url 参数”但是必须是我自己的服务器网址,比如http://hello-world.com/。但是我没有自己的服务器,也就意味着我无法获得用户支付成功的通知,也不能靠这个来触发回调函数(比如更改商品状态)。 客户端支付的代码是这个API: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/payment/wx.requestPayment.html。在这个客户端我可以写一个支付成功的success回调函数,比如 success(){ /* 从客户端触发云函数,更改商品状态为“已支付” */ call_cloud_function_to_change_products_to_paid_status(); } 如果这样做就意味着第一步:用户A进行了支付。第二步:用户A自己通知后台“我已经付了钱了。请发货。”由于这2步代码都在前端,那么黑客岂不是可以直接触发第二步函数,骗商家发货。理想方法应该是第一步:用户A支付。第二步:腾讯主动触发我的云函数X,通知我继续发货等。请问该怎么解决我的问题? 另外,我也注意到,如果用CloudPay.unifiedOrder() https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-sdk-api/open/pay/CloudPay.unifiedOrder.html 则可以指定一个云函数作为支付成功后的回调,但是它需要 sub_mch_id (子商户号)而不能用 mch_id 或 merchant_id (商户号)。目前我的情况是只有一个商户号,没有子商户号。我是否应该去弄一个子商户号?
2020-12-06 - 服务商的特约商户和普通商户的区别?
1、服务商不能给自己(同主体)申请特约商户,是吗? 2、服务商只能新建特约商户,而不能绑定已存在的普通商户号,是吗? 3、服务商的特约商户可以作为普通商户单独使用吗? 4、服务商的一个特约商户最多可以绑定多少appid? 5、服务商的特约商户绑定了小程序,在小程序管理后台不支持查看绑定情况,是吗? 6、服务商的appid为申请服务商时的服务号的appid,是吗?
2020-05-18 - 云开发聚合怎么提取出数组里的值?
[图片][图片] 链接:https://pan.baidu.com/s/1CzIQe3dbTSbDfQBDKcv9nw 提取码:0o30 json格式数据库demo上传,可以拿来调试!
2020-07-15 - 云函数new Date()时间不对 相差8小时 如何设置北京时间
在云函数中,如果使用: let date = new Date(); console.log(date); 结果是比正常时间少8小时,为什么呢? 经过查询,得出了原因: [图片] 文档路径:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/functions/notice.html 找到原因就可以解决了 在微信开发者工具——>云开发——>云函数——>点击指定函数的版本管理——>配置 [图片] [图片] [图片] 大功告成!!!
2020-11-06 - 搞懂微信支付 v3 接口规则-【附Java源码】
简介 为了在保证支付安全的前提下,带给商户简单、一致且易用的开发体验,我们推出了全新的微信支付API v3。 其实还要一个主要因素是「为了符合监管的要求」。 主要是为了符合监管的要求,保证更高的安全级别。《中华人民共和国电子签名法》、《金融电子认证规范》及《非银行支付机构网络支付业务管理办法》中规定 “电子签名需要第三方认证的,由依法设立的电子认证服务提供者提供认证服务。”,所以需使用第三方 CA 来确保数字证书的唯一性、完整性及交易的不可抵赖性。 支付宝支付也是如此,从之前的「普通公钥方式」新增了 「公钥证书方式」。今天的主角是微信支付 Api v3 这里就不展开讲支付宝支付了。 微信支付 Api v3 接口规则 官方文档 v2 与 v3 的区别 V3 规则差异 V2 JSON 参数格式 XML POST、GET 或 DELETE 提交方式 POST AES-256-GCM加密 回调加密 无需加密 RSA 加密 敏感加密 无需加密 UTF-8 编码方式 UTF-8 非对称密钥SHA256-RSA 签名方式 MD5 或 HMAC-SHA256 微信支付 Api-v2 版本详细介绍请参数之前博客 微信支付,你想知道的一切都在这里 干货多,屁话少,下面直接进入主题,读完全文你将 Get 到以下知识点 如何获取证书序列号 非对称密钥 SHA256-RSA 加密与验证签名 AES-256-GCM 如何解密 API 密钥设置 请登录商户平台进入【账户中心】->【账户设置】->【API安全】->【APIv3密钥】中设置 API 密钥。 具体操作步骤请参见:什么是APIv3密钥?如何设置? 获取 API 证书 请登录商户平台进入【账户中心】->【账户设置】->【API安全】根据提示指引下载证书。 具体操作步骤请参见:什么是API证书?如何获取API证书? 按照以上步骤操作后你将获取如下内容: apiKey API 密钥 apiKey3 APIv3 密钥 mchId 商户号 apiclient_key.pem X.509 标准证书的密钥 apiclient_cert.p12 X.509 标准的证书+密钥 apiclient_cert.pem X.509 标准的证书 请求签名 如何生成签名参数?官方文档 描述得非常清楚这里就不啰嗦了。 示例代码 构造签名串 [代码] /** * 构造签名串 * * @param method {@link RequestMethod} GET,POST,PUT等 * @param url 请求接口 /v3/certificates * @param timestamp 获取发起请求时的系统当前时间戳 * @param nonceStr 随机字符串 * @param body 请求报文主体 * @return 待签名字符串 */ public static String buildSignMessage(RequestMethod method, String url, long timestamp, String nonceStr, String body) { return new StringBuilder() .append(method.toString()) .append("\n") .append(url) .append("\n") .append(timestamp) .append("\n") .append(nonceStr) .append("\n") .append(body) .append("\n") .toString(); } [代码] 构造 HTTP 头中的 Authorization [代码]/** * 构建 v3 接口所需的 Authorization * * @param method {@link RequestMethod} 请求方法 * @param urlSuffix 可通过 WxApiType 来获取,URL挂载参数需要自行拼接 * @param mchId 商户Id * @param serialNo 商户 API 证书序列号 * @param keyPath key.pem 证书路径 * @param body 接口请求参数 * @param nonceStr 随机字符库 * @param timestamp 时间戳 * @param authType 认证类型 * @return {@link String} 返回 v3 所需的 Authorization * @throws Exception 异常信息 */ public static String buildAuthorization(RequestMethod method, String urlSuffix, String mchId, String serialNo, String keyPath, String body, String nonceStr, long timestamp, String authType) throws Exception { // 构建签名参数 String buildSignMessage = PayKit.buildSignMessage(method, urlSuffix, timestamp, nonceStr, body); // 获取商户私钥 String key = PayKit.getPrivateKey(keyPath); // 生成签名 String signature = RsaKit.encryptByPrivateKey(buildSignMessage, key); // 根据平台规则生成请求头 authorization return PayKit.getAuthorization(mchId, serialNo, nonceStr, String.valueOf(timestamp), signature, authType); } /** * 获取授权认证信息 * * @param mchId 商户号 * @param serialNo 商户API证书序列号 * @param nonceStr 请求随机串 * @param timestamp 时间戳 * @param signature 签名值 * @param authType 认证类型,目前为WECHATPAY2-SHA256-RSA2048 * @return 请求头 Authorization */ public static String getAuthorization(String mchId, String serialNo, String nonceStr, String timestamp, String signature, String authType) { Map<String, String> params = new HashMap<>(5); params.put("mchid", mchId); params.put("serial_no", serialNo); params.put("nonce_str", nonceStr); params.put("timestamp", timestamp); params.put("signature", signature); return authType.concat(" ").concat(createLinkString(params, ",", false, true)); } [代码] 拼接参数 [代码] public static String createLinkString(Map<String, String> params, String connStr, boolean encode, boolean quotes) { List<String> keys = new ArrayList<String>(params.keySet()); Collections.sort(keys); StringBuilder content = new StringBuilder(); for (int i = 0; i < keys.size(); i++) { String key = keys.get(i); String value = params.get(key); // 拼接时,不包括最后一个&字符 if (i == keys.size() - 1) { if (quotes) { content.append(key).append("=").append('"').append(encode ? urlEncode(value) : value).append('"'); } else { content.append(key).append("=").append(encode ? urlEncode(value) : value); } } else { if (quotes) { content.append(key).append("=").append('"').append(encode ? urlEncode(value) : value).append('"').append(connStr); } else { content.append(key).append("=").append(encode ? urlEncode(value) : value).append(connStr); } } } return content.toString(); } [代码] 从上面示例来看我们还差两个参数 serial_no 证书序列号 signature 使用商户私钥对待签名串进行 SHA256 with RSA 签名 如何获取呢?不要着急,容我喝杯 「89年的咖啡」提提神。 获取证书序列号 通过工具获取 openssl x509 -in apiclient_cert.pem -noout -serial 使用证书解析工具 https://myssl.com/cert_decode.html 通过代码获取 [代码]// 获取证书序列号 X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream("apiclient_cert.pem 证书路径")); System.out.println("输出证书信息:\n" + certificate.toString()); System.out.println("证书序列号:" + certificate.getSerialNumber().toString(16)); System.out.println("版本号:" + certificate.getVersion()); System.out.println("签发者:" + certificate.getIssuerDN()); System.out.println("有效起始日期:" + certificate.getNotBefore()); System.out.println("有效终止日期:" + certificate.getNotAfter()); System.out.println("主体名:" + certificate.getSubjectDN()); System.out.println("签名算法:" + certificate.getSigAlgName()); System.out.println("签名:" + certificate.getSignature().toString()); /** * 获取证书 * * @param inputStream 证书文件 * @return {@link X509Certificate} 获取证书 */ public static X509Certificate getCertificate(InputStream inputStream) { try { CertificateFactory cf = CertificateFactory.getInstance("X509"); X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream); cert.checkValidity(); return cert; } catch (CertificateExpiredException e) { throw new RuntimeException("证书已过期", e); } catch (CertificateNotYetValidException e) { throw new RuntimeException("证书尚未生效", e); } catch (CertificateException e) { throw new RuntimeException("无效的证书", e); } } [代码] SHA256 with RSA 签名 获取商户私钥 [代码] /** * 获取商户私钥 * * @param keyPath 商户私钥证书路径 * @return 商户私钥 * @throws Exception 解析 key 异常 */ public static String getPrivateKey(String keyPath) throws Exception { String originalKey = FileUtil.readUtf8String(keyPath); String privateKey = originalKey .replace("-----BEGIN PRIVATE KEY-----", "") .replace("-----END PRIVATE KEY-----", "") .replaceAll("\\s+", ""); return RsaKit.getPrivateKeyStr(RsaKit.loadPrivateKey(privateKey)); } public static String getPrivateKeyStr(PrivateKey privateKey) { return Base64.encode(privateKey.getEncoded()); } /** * 从字符串中加载私钥 * * @param privateKeyStr 私钥 * @return {@link PrivateKey} * @throws Exception 异常信息 */ public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception { try { byte[] buffer = Base64.decode(privateKeyStr); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); return keyFactory.generatePrivate(keySpec); } catch (NoSuchAlgorithmException e) { throw new Exception("无此算法"); } catch (InvalidKeySpecException e) { throw new Exception("私钥非法"); } catch (NullPointerException e) { throw new Exception("私钥数据为空"); } } [代码] 私钥签名 [代码]/** * 私钥签名 * * @param data 需要加密的数据 * @param privateKey 私钥 * @return 加密后的数据 * @throws Exception 异常信息 */ public static String encryptByPrivateKey(String data, String privateKey) throws Exception { PKCS8EncodedKeySpec priPkcs8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey)); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); PrivateKey priKey = keyFactory.generatePrivate(priPkcs8); java.security.Signature signature = java.security.Signature.getInstance("SHA256WithRSA"); signature.initSign(priKey); signature.update(data.getBytes(StandardCharsets.UTF_8)); byte[] signed = signature.sign(); return StrUtil.str(Base64.encode(signed)); } [代码] 至此微信支付 Api-v3 接口请求参数已封装完成。 执行请求 [代码]/** * V3 接口统一执行入口 * * @param method {@link RequestMethod} 请求方法 * @param urlPrefix 可通过 {@link WxDomain}来获取 * @param urlSuffix 可通过 {@link WxApiType} 来获取,URL挂载参数需要自行拼接 * @param mchId 商户Id * @param serialNo 商户 API 证书序列号 * @param keyPath apiclient_key.pem 证书路径 * @param body 接口请求参数 * @param nonceStr 随机字符库 * @param timestamp 时间戳 * @param authType 认证类型 * @param file 文件 * @return {@link String} 请求返回的结果 * @throws Exception 接口执行异常 */ public static Map<String, Object> v3Execution(RequestMethod method, String urlPrefix, String urlSuffix, String mchId, String serialNo, String keyPath, String body, String nonceStr, long timestamp, String authType, File file) throws Exception { // 构建 Authorization String authorization = WxPayKit.buildAuthorization(method, urlSuffix, mchId, serialNo, keyPath, body, nonceStr, timestamp, authType); if (method == RequestMethod.GET) { return doGet(urlPrefix.concat(urlSuffix), authorization, serialNo, null); } else if (method == RequestMethod.POST) { return doPost(urlPrefix.concat(urlSuffix), authorization, serialNo, body); } else if (method == RequestMethod.DELETE) { return doDelete(urlPrefix.concat(urlSuffix), authorization, serialNo, body); } else if (method == RequestMethod.UPLOAD) { return doUpload(urlPrefix.concat(urlSuffix), authorization, serialNo, body, file); } return null; } [代码] 网络请求库默认是使用的 Hutool 封装的一套 Java 工具集合来实现 GET 请求 [代码]/** * @param url 请求url * @param authorization 授权信息 * @param serialNumber 公钥证书序列号 * @param jsonData 请求参数 * @return {@link HttpResponse} 请求返回的结果 */ private HttpResponse doGet(String url, String authorization, String serialNumber, String jsonData) { return HttpRequest.post(url) .addHeaders(getHeaders(authorization, serialNumber)) .body(jsonData) .execute(); } [代码] POST 请求 [代码] /** * @param url 请求url * @param authorization 授权信息 * @param serialNumber 公钥证书序列号 * @param jsonData 请求参数 * @return {@link HttpResponse} 请求返回的结果 */ private HttpResponse doPost(String url, String authorization, String serialNumber, String jsonData) { return HttpRequest.post(url) .addHeaders(getHeaders(authorization, serialNumber)) .body(jsonData) .execute(); } [代码] DELETE 请求 [代码]/** * delete 请求 * * @param url 请求url * @param authorization 授权信息 * @param serialNumber 公钥证书序列号 * @param jsonData 请求参数 * @return {@link HttpResponse} 请求返回的结果 */ private HttpResponse doDelete(String url, String authorization, String serialNumber, String jsonData) { return HttpRequest.delete(url) .addHeaders(getHeaders(authorization, serialNumber)) .body(jsonData) .execute(); } [代码] 上传文件 [代码] /** * @param url 请求url * @param authorization 授权信息 * @param serialNumber 公钥证书序列号 * @param jsonData 请求参数 * @param file 上传的文件 * @return {@link HttpResponse} 请求返回的结果 */ private HttpResponse doUpload(String url, String authorization, String serialNumber, String jsonData, File file) { return HttpRequest.post(url) .addHeaders(getUploadHeaders(authorization, serialNumber)) .form("file", file) .form("meta", jsonData) .execute(); } [代码] 构建 Http 请求头 [代码]private Map<String, String> getBaseHeaders(String authorization) { String userAgent = String.format( "WeChatPay-IJPay-HttpClient/%s (%s) Java/%s", getClass().getPackage().getImplementationVersion(), OS, VERSION == null ? "Unknown" : VERSION); Map<String, String> headers = new HashMap<>(3); headers.put("Accept", ContentType.JSON.toString()); headers.put("Authorization", authorization); headers.put("User-Agent", userAgent); return headers; } private Map<String, String> getHeaders(String authorization, String serialNumber) { Map<String, String> headers = getBaseHeaders(authorization); headers.put("Content-Type", ContentType.JSON.toString()); if (StrUtil.isNotEmpty(serialNumber)) { headers.put("Wechatpay-Serial", serialNumber); } return headers; } private Map<String, String> getUploadHeaders(String authorization, String serialNumber) { Map<String, String> headers = getBaseHeaders(authorization); headers.put("Content-Type", "multipart/form-data;boundary=\"boundary\""); if (StrUtil.isNotEmpty(serialNumber)) { headers.put("Wechatpay-Serial", serialNumber); } return headers; } [代码] 构建 Http 请求返回值 从响应的 HttpResponse 中获取微信响应头信息、状态码以及 body [代码]/** * 构建返回参数 * * @param httpResponse {@link HttpResponse} * @return {@link Map} */ private Map<String, Object> buildResMap(HttpResponse httpResponse) { Map<String, Object> map = new HashMap<>(); String timestamp = httpResponse.header("Wechatpay-Timestamp"); String nonceStr = httpResponse.header("Wechatpay-Nonce"); String serialNo = httpResponse.header("Wechatpay-Serial"); String signature = httpResponse.header("Wechatpay-Signature"); String body = httpResponse.body(); int status = httpResponse.getStatus(); map.put("timestamp", timestamp); map.put("nonceStr", nonceStr); map.put("serialNumber", serialNo); map.put("signature", signature); map.put("body", body); map.put("status", status); return map; } [代码] 至此已完成构建请求参数,执行请求。接下来我们就要实现响应数据的解密以及响应结果的验证签名 对应的官方文档 证书和回调报文解密 签名验证 验证签名 构建签名参数 [代码]/** * 构造签名串 * * @param timestamp 应答时间戳 * @param nonceStr 应答随机串 * @param body 应答报文主体 * @return 应答待签名字符串 */ public static String buildSignMessage(String timestamp, String nonceStr, String body) { return new StringBuilder() .append(timestamp) .append("\n") .append(nonceStr) .append("\n") .append(body) .append("\n") .toString(); } [代码] 证书和回调报文解密 官方文档文末有完整的源码这里就不贴了。贴一个示例大家参数一下 [代码]try { String associatedData = "certificate"; String nonce = "80d28946a64a"; String cipherText = "DwAqW4+4TeUaOEylfKEXhw+XqGh/YTRhUmLw/tBfQ5nM9DZ9d+9aGEghycwV1jwo52vXb/t6ueBvBRHRIW5JgDRcXmTHw9IMTrIK6HxTt2qiaGTWJU9whsF+GGeQdA7gBCHZm3AJUwrzerAGW1mclXBTvXqaCl6haE7AOHJ2g4RtQThi3nxOI63/yc3WaiAlSR22GuCpy6wJBfljBq5Bx2xXDZXlF2TNbDIeodiEnJEG2m9eBWKuvKPyUPyClRXG1fdOkKnCZZ6u+ipb4IJx28n3MmhEtuc2heqqlFUbeONaRpXv6KOZmH/IdEL6nqNDP2D7cXutNVCi0TtSfC7ojnO/+PKRu3MGO2Z9q3zyZXmkWHCSms/C3ACatPUKHIK+92MxjSQDc1E/8faghTc9bDgn8cqWpVKcL3GHK+RfuYKiMcdSkUDJyMJOwEXMYNUdseQMJ3gL4pfxuQu6QrVvJ17q3ZjzkexkPNU4PNSlIBJg+KX61cyBTBumaHy/EbHiP9V2GeM729a0h5UYYJVedSo1guIGjMZ4tA3WgwQrlpp3VAMKEBLRJMcnHd4pH5YQ/4hiUlHGEHttWtnxKFwnJ6jHr3OmFLV1FiUUOZEDAqR0U1KhtGjOffnmB9tymWF8FwRNiH2Tee/cCDBaHhNtfPI5129SrlSR7bZc+h7uzz9z+1OOkNrWHzAoWEe3XVGKAywpn5HGbcL+9nsEVZRJLvV7aOxAZBkxhg8H5Fjt1ioTJL+qXgRzse1BX1iiwfCR0fzEWT9ldDTDW0Y1b3tb419MhdmTQB5FsMXYOzqp5h+Tz1FwEGsa6TJsmdjJQSNz+7qPSg5D6C2gc9/6PkysSu/6XfsWXD7cQkuZ+TJ/Xb6Q1Uu7ZB90SauA8uPQUIchW5zQ6UfK5dwMkOuEcE/141/Aw2rlDqjtsE17u1dQ6TCax/ZQTDQ2MDUaBPEaDIMPcgL7fCeijoRgovkBY92m86leZvQ+HVbxlFx5CoPhz4a81kt9XJuEYOztSIKlm7QNfW0BvSUhLmxDNCjcxqwyydtKbLzA+EBb2gG4ORiH8IOTbV0+G4S6BqetU7RrO+/nKt21nXVqXUmdkhkBakLN8FUcHygyWnVxbA7OI2RGnJJUnxqHd3kTbzD5Wxco4JIQsTOV6KtO5c960oVYUARZIP1SdQhqwELm27AktEN7kzg/ew/blnTys/eauGyw78XCROb9F1wbZBToUZ7L+8/m/2tyyyqNid+sC9fYqJoIOGfFOe6COWzTI/XPytCHwgHeUxmgk7NYfU0ukR223RPUOym6kLzSMMBKCivnNg68tbLRJHEOpQTXFBaFFHt2qpceJpJgw5sKFqx3eQnIFuyvA1i8s2zKLhULZio9hpsDJQREOcNeHVjEZazdCGnbe3Vjg7uqOoVHdE/YbNzJNQEsB3/erYJB+eGzyFwFmdAHenG5RE6FhCutjszwRiSvW9F7wvRK36gm7NnVJZkvlbGwh0UHr0pbcrOmxT81xtNSvMzT0VZNLTUX2ur3AGLwi2ej8BIC0H41nw4ToxTnwtFR1Xy55+pUiwpB7JzraA08dCXdFdtZ72Tw/dNBy5h1P7EtQYiKzXp6rndfOEWgNOsan7e1XRpCnX7xoAkdPvy40OuQ5gNbDKry5gVDEZhmEk/WRuGGaX06CG9m7NfErUsnQYrDJVjXWKYuARd9R7W0aa5nUXqz/Pjul/LAatJgWhZgFBGXhNr9iAoade/0FPpBj0QWa8SWqKYKiOqXqhfhppUq35FIa0a1Vvxcn3E38XYpVZVTDEXcEcD0RLCu/ezdOa6vRcB7hjgXFIRZQAka0aXnQxwOZwE2Rt3yWXqc+Q1ah2oOrg8Lg3ETc644X9QP4FxOtDwz/A=="; AesUtil aesUtil = new AesUtil(wxPayV3Bean.getApiKey3().getBytes(StandardCharsets.UTF_8)); // 平台证书密文解密 // encrypt_certificate 中的 associated_data nonce ciphertext String publicKey = aesUtil.decryptToString( associatedData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), cipherText ); // 保存证书 FileWriter writer = new FileWriter(wxPayV3Bean.getPlatformCertPath()); writer.write(publicKey); // 获取平台证书序列号 X509Certificate certificate = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes())); return certificate.getSerialNumber().toString(16).toUpperCase(); } catch (Exception e) { e.printStackTrace(); } [代码] 验证签名 [代码]/** * 验证签名 * * @param signature 待验证的签名 * @param body 应答主体 * @param nonce 随机串 * @param timestamp 时间戳 * @param certInputStream 微信支付平台证书输入流 * @return 签名结果 * @throws Exception 异常信息 */ public static boolean verifySignature(String signature, String body, String nonce, String timestamp, InputStream certInputStream) throws Exception { String buildSignMessage = PayKit.buildSignMessage(timestamp, nonce, body); // 获取证书 X509Certificate certificate = PayKit.getCertificate(certInputStream); PublicKey publicKey = certificate.getPublicKey(); return RsaKit.checkByPublicKey(buildSignMessage, signature, publicKey); } /** * 公钥验证签名 * * @param data 需要加密的数据 * @param sign 签名 * @param publicKey 公钥 * @return 验证结果 * @throws Exception 异常信息 */ public static boolean checkByPublicKey(String data, String sign, PublicKey publicKey) throws Exception { java.security.Signature signature = java.security.Signature.getInstance("SHA256WithRSA"); signature.initVerify(publicKey); signature.update(data.getBytes(StandardCharsets.UTF_8)); return signature.verify(Base64.decode(sign.getBytes(StandardCharsets.UTF_8))); } [代码] 至此微信支付 Api-v3 接口已介绍完,如有疑问欢迎留言一起探讨。 完整示例 SpringBoot 参考资料 你真的了解 HTTPS 吗? WechatPay-API-v3
2021-03-02 - 微信支付APIv3的Nodejs版SDK,让开发变得简单不再繁琐
在向云端推送这个 [代码]wechatpay-axios-plugin[代码] 业务实现时,发现0.1系列还不够好用,还需要进行更多层级的包裹包装,遂再次做了重大更新,让SDK使用起来更简单、飘逸。 先看官方文档,每一个接口,文档都至少标示了[代码]请求URL[代码] [代码]请求方式[代码] [代码]请求参数[代码] [代码]返回参数[代码] 这几个要素,[代码]URL[代码] 可以拆分成 [代码]Base[代码] 及 [代码]URI[代码],按照这种思路,封装SDK其实完全就可以不用动脑,即,对[代码]URI[代码]资源的 [代码]POST[代码] 或 [代码]GET[代码] 请求(条件带上[代码]参数[代码]),取得[代码]返回参数[代码]。 更近一步,我们设想一下,如果把众多接口的[代码]URI[代码]按照斜线([代码]/[代码] [代码]slash[代码])分割,然后组织在一起,是不是就可以构建出一颗树,这颗树的每个节点(实体[代码]Entity[代码])都存在有若干个方法([代码]HTTP METHODs[代码]),这是不是就能把接口[代码]SDK实现[代码]更简单化了?! 例如: /v3/certificates /v3/bill/tradebill /v3/ecommerce/fund/withdraw /v3/ecommerce/profitsharing/orders /v3/marketing/busifavor/users/{openid}/coupons/{coupon_code}/appids/{appid} 树形化即: [代码]v3 ├── certificates ├── bill │ └── tradebill ├── ecommerce │ ├── fund │ │ └── withdraw │ └── profitsharing │ └── orders └── marketing └── busifavor └── users └── {openid} └── coupons └── {coupon_code} └── appids └── {appid} [代码] 按照这种树形构想,我们来看下需要做的[代码]封装实现[代码]工作: 把实体对象,按照实体的排列顺序,映射出请求的URI; 每个对象实体,包含有若干操作方法,其中可选带参数发起RPC请求; 随官方放出更多的接口,SDK需要能够弹性扩容; wechatpay-axios-plugin~0.2.0 版本实现了上述这3个目标,代码包如下截屏: [图片] 我们用伪代码来校验看一下这个[代码]封装实现[代码]: [代码]require('util').inspect.defaultOptions.depth = 10; const { Wechatpay } = require('wechatpay-axios-plugin'); const wxpay = new Wechatpay({mchid: '1', serial: '2', privateKey: '3', certs: {'4': '5'}}); wxpay.v3.certificates; wxpay.v3.bill.tradebill; wxpay.v3.ecommerce.fund.withdraw; wxpay.v3.marketing.busifavor.users['{openid}'].coupons.$coupon_code$.appids['wx233544546545989']; console.info(wxpay); //以下是输出内容 { entities: [], withEntities: [Function: withEntities], get: [AsyncFunction: get], post: [AsyncFunction: post], upload: [AsyncFunction: upload], v3: { entities: [ 'v3' ], withEntities: [Function: withEntities], get: [AsyncFunction: get], post: [AsyncFunction: post], upload: [AsyncFunction: upload], certificates: { entities: [ 'v3', 'certificates' ], withEntities: [Function: withEntities], get: [AsyncFunction: get], post: [AsyncFunction: post], upload: [AsyncFunction: upload] }, bill: { entities: [ 'v3', 'bill' ], withEntities: [Function: withEntities], get: [AsyncFunction: get], post: [AsyncFunction: post], upload: [AsyncFunction: upload], tradebill: { entities: [ 'v3', 'bill', 'tradebill' ], withEntities: [Function: withEntities], get: [AsyncFunction: get], post: [AsyncFunction: post], upload: [AsyncFunction: upload] } }, ecommerce: { entities: [ 'v3', 'ecommerce' ], withEntities: [Function: withEntities], get: [AsyncFunction: get], post: [AsyncFunction: post], upload: [AsyncFunction: upload], fund: { entities: [ 'v3', 'ecommerce', 'fund' ], withEntities: [Function: withEntities], get: [AsyncFunction: get], post: [AsyncFunction: post], upload: [AsyncFunction: upload], withdraw: { entities: [ 'v3', 'ecommerce', 'fund', 'withdraw' ], withEntities: [Function: withEntities], get: [AsyncFunction: get], post: [AsyncFunction: post], upload: [AsyncFunction: upload] } } }, marketing: { entities: [ 'v3', 'marketing' ], withEntities: [Function: withEntities], get: [AsyncFunction: get], post: [AsyncFunction: post], upload: [AsyncFunction: upload], busifavor: { entities: [ 'v3', 'marketing', 'busifavor' ], withEntities: [Function: withEntities], get: [AsyncFunction: get], post: [AsyncFunction: post], upload: [AsyncFunction: upload], users: { entities: [ 'v3', 'marketing', 'busifavor', 'users' ], withEntities: [Function: withEntities], get: [AsyncFunction: get], post: [AsyncFunction: post], upload: [AsyncFunction: upload], '{openid}': { entities: [ 'v3', 'marketing', 'busifavor', 'users', '{openid}' ], withEntities: [Function: withEntities], get: [AsyncFunction: get], post: [AsyncFunction: post], upload: [AsyncFunction: upload], coupons: { entities: [ 'v3', 'marketing', 'busifavor', 'users', '{openid}', 'coupons' ], withEntities: [Function: withEntities], get: [AsyncFunction: get], post: [AsyncFunction: post], upload: [AsyncFunction: upload], '$coupon_code$': { entities: [ 'v3', 'marketing', 'busifavor', 'users', '{openid}', 'coupons', '{coupon_code}' ], withEntities: [Function: withEntities], get: [AsyncFunction: get], post: [AsyncFunction: post], upload: [AsyncFunction: upload], appids: { entities: [ 'v3', 'marketing', 'busifavor', 'users', '{openid}', 'coupons', '{coupon_code}', 'appids' ], withEntities: [Function: withEntities], get: [AsyncFunction: get], post: [AsyncFunction: post], upload: [AsyncFunction: upload], wx233544546545989: { entities: [ 'v3', 'marketing', 'busifavor', 'users', '{openid}', 'coupons', '{coupon_code}', 'appids', 'wx233544546545989' ], withEntities: [Function: withEntities], get: [AsyncFunction: get], post: [AsyncFunction: post], upload: [AsyncFunction: upload] } } } } } } } } } } [代码] 注: API树实体节点,存储在每个 [代码]entities[代码] 属性上,方便后续的[代码]get[代码], [代码]post[代码] 抑或 [代码]upload[代码] 方法调用调用前,反构成最终请求的[代码]URI[代码];特别地,对于动态树实体节点来说,每个实体节点均提供了 [代码]withEntities[代码] 方法,用来在最终请求前,把动态实体节点替换成实际的值。 正常用法示例如下: [代码]const {Wechatpay} = require('wechatpay-axios-plugin'); const wxpay = new Wechatpay({/*初始化参数,README有*/}, {/*可选调整axios的参数*/}); //拿证书 wxpay.v3.certificates.get(); //带参申请交易账单 wxpay.v3.bill.tradebill.get({params: {bill_date}}); //带参发起账户余额提现 wxpay.v3.ecommerce.fund.withdraw.post({sub_mchid, out_request_no, amount, remark, bank_memo}); //查询用户单张券详情 wxpay.v3.marketing.busifavor.users['{openid}'].coupons.$coupon_code$.appids['wx233544546545989'].withEntities({openid, coupon_code}).get(); [代码] 请求APIv3是不是就“丧心病狂”般的简单了?! 详细功能说明及用法示例,npmjs及github的README均有。 如果喜欢,就给来个 赞 及 Star 吧。
2020-07-17 - 全云开发V3支付电商收付通,第一天,统一下单,居然秒过了。
接了个甲方的项目,要求用全云开发实现微信支付V3电商收付通,谈妥后,于是开干。 先看文档,再找攻略,第一步开始写段统一下单的测试代码。非常神奇的事情发生了,居然是秒过的,直接拿到prepaid_id!秒过,秒过,秒过,一个红错误都没有,难以置信; V3收付通文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/guide.shtml 遥想当年做V2小程序支付,仅一个统一下单的接口,就调得让人欲生欲死,前前后后一个星期不止。 我自己也有点懵圈,这是什么情况,V3新的接口里,签名?Authorization?Headers?这些都是闹着玩的吗?能不能严肃点? 想了一会儿静静,继续,看能不能在小程序里拉起wx.requestPayment,结果V3文档里一句小程序支付的都没有,难道不支持?立马在论坛大佬群里@了一下娇华同学,得到肯定回答,于是,wx.requestPayment拉起,美滋滋地支付了0.01元,统一下单,完美。 云函数相关代码: //JSAPI下单+JS调起支付 async function partner_transactions_jsapi(event) { let wxContext = cloud.getWXContext() let jsonStr = JSON.stringify({ sp_appid: wxContext.APPID, sp_mchid: mchid, notify_url: config.notify_url, payer: { sp_openid: wxContext.OPENID }, ...event.unified_order,//小程序端传入的统一下单参数 }) let method = 'POST' let url = '/v3/pay/partner/transactions/jsapi' let headers = getHeaders(method, url, jsonStr) let res = await rp({ method, uri: config.host + url, headers, body: jsonStr }) let prepay_id = JSON.parse(res).prepay_id return getPayment(prepay_id) //返回拉起小程序 wx.requestPayment 所需参数 } 小程序端测试代码: //JSAPI下单+JS调起支付 partner_transactions_jsapi: async function () { console.log('partner_transactions_jsapi') let res = await wx.showModal({content: '统一下单?'}) if (res.confirm) { } else return wx.showToast({ icon: 'loading' }) let unified_order = { //测试订单 sub_mchid: SUB_MCHID, description: '电商收付通测试', out_trade_no: "otn" + Math.random().toString(36).substr(2, 15) + parseInt(Date.now() / 1000), amount: { total: 10, currency: 'CNY' }, } res = await wx.cloud.callFunction({ name: 'wxPayV3', data: { action: 'partner_transactions_jsapi', unified_order } }) res = await wx.requestPayment({ ...res.result }).catch(err => console.log(err)) console.log(res) wx.showToast({}) }, 今天先弄这么多,更多踩坑实录继续。 更多相关内容: [图片]
2020-10-20 - 微信支付API-v3在云开发中也需要签名吗?
在云开发中调用发放代金券的接口,也需要签名吗?云开发不是自动鉴权吗?有没有简便的方法?
2020-09-11 - 只用微信云开发小程序,怎么处理微信支付的回调通知
微信支付成功的回调通知没办法触发云函数,所以似乎没有办法接收微信支付的回调通知,只能在云函数里主动查询支付结果。这样好像不是最佳实践,大家有没有其他办法?
2020-02-21 - 服务商分账时将profit_sharing加在unifiedOrder中支付报错怎么解决呢?
hi,将profit_sharing加在cloud.cloudPay.unifiedOrder中报“调用支付JSAPI缺少参数:total_fee”,该如何解决?是不是加的地方不对呀?我用的是云开发。 求大佬指教,感谢!!!
2020-11-05 - 云支付的订单加了分账标识后,支持分账吗?
云支付的订单加了分账标识后,支持分账吗?我是用云支付的统一下单api做的付款,因为云支付没有分账接口,我用原生分账接口做的分账,付款订单已经添加了“profit_sharing ”标识,但是分账提示“非分账订单不能分账”?我想确认一下是因为云支付的订单不能分账吗?
2020-06-12 - 分账标识 在统一下单api文档 不存在 那么它的用法是怎么样的?
[图片]
2020-11-13 - 微信开放文档|云开发|云函数|异步返回结果示例
异步返回结果经常,我们需要在云函数中处理一些异步操作,在异步操作完成后再返回结果给到调用方。此时我们可以通过在云函数中返回一个 [代码]Promise[代码] 的方法来完成。 一个最简的 [代码]setTimeout[代码] 示例: // index.js exports.main = async (event, context) => { return new Promise((resolve, reject) => { // 在 3 秒后返回结果给调用方(小程序 / 其他云函数) setTimeout(() => { resolve(event.a + event.b) }, 3000) }) } https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/functions/async.html 这里设置3000会超时。
2020-11-02 - 云开发request问题求答
exports.main = async (event, context) => { return new Promise((resolve, reject) => { let url = ' ' request( url, function (error, response, body) { return resolve(body) }) }) } 关于云开发发起网络请求 文档说: Node.js 提供的原生 [代码]http[代码] 接口在云函数中发起网络请求,我们还可以使用一个流行的 Node.js 网络请求库 request 来更便捷的发起网络请求。 但是这个不知道如何选择使用get或post方法,尤其不知道如何参数传递,百度了很久 node request的get post方法 和两个方法的参数传递 都找不到一个合适的参考。 麻烦给个代码格式参考,展示一下post方法的传参格式 谢谢!!
2018-10-15 - 在小程序中如何获取图片/照片文件的二进制数据?
可用 wx.uploadFile
2019-12-27 - 图片检查云函数imgSecCheck 踩坑指南
//小程序客户端代码 wx.chooseImage({ count:'1', sizeType: ['compressed'], sourceType: ['album', 'camera'] }).then((res)=>{ wx.showLoading(); let opt= {src:res.tempFilePaths[0], quality:5} return wx.compressImage(opt);//压缩图片发送至CDN暂存 }).then((res)=>{ let cdnUrl=wx.cloud.CDN({ type: 'filePath', filePath: res.tempFilePath,//压缩图片发送至CDN暂存 }); let opt={ name:'imgSecCheck', data:{ imgcontent: cdnUrl } }; return wx.cloud.callFunction(opt) //调用云函数检查图片是否合法 }).then((res)=>{ wx.hideLoading(); if (res.result.errCode == 0) { wx.showModal({ title: '提醒', content: '检查成功!', showCancel: false }) //todo 上传图片 }else{ wx.showModal({ title: '提醒', content: '违规图片!', showCancel: false }) } }).catch((err)=>{ console.error(err); wx.hideLoading(); if(err.errMsg!='chooseImage:fail cancel'){ wx.showModal({ title: '提醒', content: '出错了请稍后再试!', showCancel: true }) } }); //图片检查云函数定义 const cloud = require('wx-server-sdk') const axios = require('axios'); cloud.init() exports.main = async (event, context) => { let buffer=null; // 下载CDN图片进行检查 await axios({ method: 'get', url: event.imgcontent, responseType: 'arraybuffer' }).then(res => { buffer=res.data; }) try{ return await cloud.openapi.security.imgSecCheck({ media: { contentType: 'image/png', value:buffer } }) }catch(e){ return e; } }
2020-09-15 - 企业商户怎么支付到小程序某个用户?
公众号商户号已开通企业支付,独立的小程序绑定了商户,企业支付到个人只能支付到绑定的公众号openid吗?我想支付到某个小程序用户openid,有什么方法?谢谢!
2019-07-26 - 微信人脸核身接口能力
一、能力背景 近年来,国家在医疗挂号、APP注册、快递收寄、客运、运营商等多领域规定,需要用户实名才可办理业务,预计后续也会有越来越多的此类法规。因此,微信参照公安部“互联网+”可信身份认证服务平台标准,依托腾讯公司及微信的生物识别技术,建立微信“实名实人信息校验能力” ,即通过人脸识别+权威源比对,校验用户实名信息和本人操作(简称微信人脸核身)。 目前接口限定主体及行业类目开放公测,提供给资质符合要求的业务方,在合适的业务场景内使用。目前仅支持持二代身份证的大陆居民。 由于人脸核身功能涉及到用户的敏感、隐私信息,因此调用此接口的小程序,需要满足一定的条件。即:小程序的主体以及类目,需要在限定的类目范围内,且与小程序的业务场景一致。开展的业务也需要是国家相关法规、政策规定的需要“实名办理”的相关业务(其他未在范围内的业务,则暂不支持)。 以下为接口接入及开发的详细内容。如开发中遇到任何疑问,可以点击此处通过社区反馈,将有工作人员跟进回复。 文档第四部分【再次获取核验结果api】,有助于提高业务方安全性,请务必接入! 现阶段微信人脸核验能力,针对小程序,开放的主体类目范围包含: 小程序一级类目 小程序二级类目 小程序三级类目 使用人脸核验接口所需资质 物流服务 收件/派件 / 《快递业务经营许可证》 物流服务 货物运输 / 《道路运输经营许可证》(经营范围需含网络货运) 教育 学历教育(学校) / (2选1):1、公立学校:由教育行政部门出具的审批设立证明 或 《事业单位法人证书》;2、私立学校:《民办学校办学许可证》与《民办非企业单位登记证书》 医疗 公立医疗机构 / 《医疗机构执业许可证》与《事业单位法人证书》 医疗 互联网医院 / 仅支持公立医疗机构互联网医院(2选1):1、卫生健康部门的《设置医疗机构批准书》;2、 《医疗机构执业许可证》(范围均需含“互联网诊疗”或名称含“互联网医院”等相关内容 医疗服务 三级私立医疗机构 / 仅支持三级以上私立医疗机构,提供《医疗机构执业许可证》、《营业执照》及《医院等级证书》 政务民生 所有二级类目 / 仅支持政府/事业单位,提供《组织机构代码证》或《统一社会信用代码证》。 金融业 银行 / (2选1):1、《金融许可证》; 2、《金融机构许可证》。 金融业 信托 / (2选1):1、《金融许可证》; 2、《金融机构许可证》。 金融业 公募基金 / (4选1):1、《经营证券期货业务许可证》且业务范围必须包含“基金”;2、《基金托管业务许可证》; 3、《基金销售业务资格证书》;4、《基金管理资格证书》。 金融业 证券/期货 / 《经营证券期货业务许可证》 金融业 保险 / (8选1):1、《保险公司法人许可证》;2、《经营保险业务许可证》;3、《保险营销服务许可证》;4、《保险中介许可证》;5、《经营保险经纪业务许可证》;6、《经营保险公估业务许可证》或《经营保险公估业务备案》;7、《经营保险资产管理业务许可证》 ;8、《保险兼业代理业务许可证》。 金融业 消费金融 / 银监会核准开业的审批文件与《金融许可证》与《营业执照》 金融业 汽车金融/金融租赁 / 仅支持汽车金融/金融租赁主体,同时提供:1、《营业执照》(公司名称包含“汽车金融” /“金融租赁”;营业范围包含“汽车金融”/“金融租赁”业务);2、《金融许可证》或银保监会及其派出机构颁发的开业核准批复文件。 交通服务 网约车 快车/专车/其他网约车 (自营性网约车)提供《网络预约出租汽车经营许可证》。(网约车平台)提供与网约车公司的合作协议以及合作网约车公司的《网络预约出租汽车经营许可证》。 交通服务 航空 / (航司)提供《公共航空运输企业经营许可证》。(机场)提供《民用机场使用许可证》或《运输机场使用许可证》。 交通服务 公交/地铁 / 提供公交/地铁/交通卡公司《营业执照》 交通服务 水运 / (船企)提供《水路运输许可证》。(港口)提供《港口经营许可证》 交通服务 骑车 / 仅支持共享单车,提供共享单车公司《营业执照》 交通服务 火车/高铁/动车 / 仅支持铁路局/公司官方,提供铁路局/公司《营业执照》 交通服务 长途汽车 / (2选1):1、《道路运输经营许可证》(经营范围需含客运);2、官方指定联网售票平台(授权或协议或公开可查询文件)。 交通服务 租车 / 运营公司提供《备案证明》与对应公司《营业执照》,且营业执照中包含汽车租赁业务 交通服务 高速服务 / 仅支持ETC发行业务,(2选1):1、事业单位主体,需提供《事业单位法人证书》;2、官方指定的发行单位(一发单位),需提供“官方授权或协议,或公开可查询的文件”; 生活服务 生活缴费 / (供电类)提供《电力业务许可证》与《营业执照》,且《营业执照》且经营范围含供电。(燃气类)提供《燃气经营许可证》与《营业执照》,且《营业执照》且经营范围含供气。(供水类)提供《卫生许可证》与《营业执照》。(供热类)提供《供热经营许可证》与《营业执照》,且《营业执照》且经营范围含供热。 IT科技 基础电信运营商 / (2选1):1、基础电信运营商:提供《基础电信业务经营许可证》;2、运营商分/子公司:提供营业执照(含相关业务范围)。 IT科技 转售移动通信 / 仅支持虚拟运营商,提供《增值电信业务许可证》(业务种类需含通过转售方式提供移动通信业务) 旅游服务 住宿服务 / 仅支持酒店,提供《酒店业特种行业经营许可证》 商业服务 公证 / 仅支持公证处,提供《公证处执业许可证》或《事业单位法人证书》 社交 直播 / (2选1):1、《信息网络传播视听节目许可证》;2、《网络文化经营许可证》(经营范围含网络表演)。 如对以上类目或资质有疑问,可点击参考小程序“非个人主体开放的服务类目”,详细了解小程序开放的服务类目及对应资质。 二、准备接入 (请在小程序发布后,再提交人脸核身接口申请) 满足第一节中描述的类目和主体的小程序,可申请微信人脸核验接口。目前微信人脸核身接口已改为线上自助申请方式,需按照如下图例指引,进行接口申请: 第一步:请通过mp.weixin.qq.com登录小程序账号在后台“功能-人脸核身”的路径,点击开通按钮—— [图片] 第二步:仔细查阅《人脸识别身份信息验证服务条款》后,点击“同意并下一步”—— [图片] 第三步:请正确填写服务信息,并上传该小程序类目下所要求的资质—— [图片] 第四步:请按照业务实际需求填写使用人脸接口的场景和用途—— [图片] 第五步:请完善测试信息和联系人—— [图片] 第六步:提交后请耐心等待1-3个工作日的审核期,审核结果将以站内信通知—— 如申请期间遇到问题,可联系腾讯工作邮箱 wx_city@tencent.com,将会有相关工作人员进一步指引。 三、接口文档: (一)接口描述 名称: wx.startFacialRecognitionVerify(OBJECT) 功能:请求进行基于生物识别的人脸核身 验证方式:在线验证 兼容版本: 一闪:android 微信7.0.22以上版本, iOS 微信7.0.18以上版本 建议在微信官网升级至最新版本 (二)参数说明 1、OBJECT参数说明: 参数 类型 必填 说明 name String 是 姓名 idCardNumber String 是 身份证号码 success Function 否 调用成功回调 fail Function 否 调用失败回调 complete Function 是 调用完成回调(成功或失败都会回调) 2、CALLBACK返回参数 参数 类型 说明 errMsg String 错误信息 errCode Number 错误码 verifyResult String 本次认证结果凭据,第三方可以选择根据这个凭据获取相关信息 注 1:传递用户姓名和身份证有两种方式 业务方没有用户实名信息,用户需要在前端填写身份证和姓名,那么前端直接通过jsapi 调用传递 name 和 idCardNumber。 业务方已经有用户实名信息,后台通过微信提供的 api(详情见文档后面“上传姓名身份证后台 api”)上传用户身份证姓名和身份证,api 返回 user_id_key 作为凭证传给前端,前端再调用 jsapi,用户姓名、身份证信息不需要经过前端,参数只需要传递 userIdKey。Tips:使用该功能需要小程序基础库版本号>=1.9.3。 3、回调结果说明 回调结果请参考以下释义: [图片] [图片] [图片] 4、示例代码 [图片] [图片] (三)上传用户姓名身份证的后台api 1、API说明 1.1说明 业务方上传用户姓名和身份证,获取用户凭证,把凭证给到前端通过 jsapi 调用。 Tips :使用该功能需要小程序基础库版本号>=1.9.3。 1.2请求URL https://api.weixin.qq.com/cityservice/face/identify/getuseridkey?access_token={ac cess_token} 1.3请求方式 POST 2、请求数据格式 [代码]Json { "name" : “张三”, "id_card_number" : "452122xxxxxxx43215" } [代码] 请求示例 [代码]#!/bin/bash TOKEN='xxxxxxxxxxxx' URL='https://api.weixin.qq.com/cityservice/face/identify/getuseridkey' JSON='{ "name": "张三", "id_card_number": "452344xxxxxxxxxxxxx234"}' curl "${URL}?access_token=${TOKEN}" -d "${JSON}" [代码] 参数说明 json 字段 中文显示 是否必传 name 姓名 是 id_card_number 身份证号码 是 out_seq_no 业务方唯一流水号 否 3、返回数据 参数 类 型 说明 errcode int 错误码 errmsg string 错误信息 user_id_key string 用于后台交互表示用户姓名、身份证的凭证 expires_in uint32 user_id_key 有效期,过期需重新获取 [代码]{ "errcode" : 0, "errmsg" : "ok", "user_id_key" : "id_key_xxxx", "expires_in": 3600 } [代码] 4、后台消息推送 如果业务方传入out_seq_no,核身完成后会通过消息推送回调给业务方的服务器,如果回调业务方失败,会在5s尽力推送,超过5s不再推送。 参数说明 参数 类 型 说明 ToUserName string 小程序原始ID FromUserName string 事件消息openid CreateTime uint32 消息推送时间 MsgType string 消息类型 Event string 事件类型 openid string 核身用户的openid out_seq_no string 业务方唯一流水号 verify_result string 核身返回的加密key(凭据) 返回示例 [代码]{ "ToUserName": "gh_81fxxxxxxxx", "FromUserName": "oRRn15NUibBxxxxxxxxx", "CreateTime": 1703657835, "MsgType": "event", "Event": "face_identify", "openid": "oRRn15NUibBxxxxxxxxx", "out_seq_no": "test1234", "verify_result": "XXIzTtMqCxwOaawoE91-VNGAC3v1j9MP-5fZJxv0fYT4aGezzvYlUb-n6RWQa7XeJpQo0teKj8mGE4ZcRe1JI3GqzADBYORBu613rKjKAFfEXTXw_bu1bs7MnmPOpguS" } [代码] 四、再次获取核验结果api 此接口是前端完成人脸核身后,基于前端返回的凭据,通过后台api再次进行核验结果和身份信息的校验,有助于提高安全性,请务必接入! 前端获取结果不可信,存在被篡改的风险,为了保障请求结果安全性,请务必对identify_ret、id_card_number_md5、name_utf8_md5字段进行校验! (一)API说明 1、说明 人脸核身之后,开发者可以根据jsapi返回的verify_result向后台拉取当次认证的结果信息。 2、请求URL https://api.weixin.qq.com/cityservice/face/identify/getinfo?access_token={access_token} 3、请求方式 POST 4、请求格式 json (二)请求数据说明 1、请求 参数 类型 是否必填 描述 verify_result String 是 jsapi返回的加密key(凭据) 2、数据返回 HTTP 头如下 Date: Mon, 06 Feb 2017 08:12:58 GMT Content-Type: application/json; encoding=utf-8 Content-Length: 85 Connection: close json示例 [代码]{ "errcode" : 0, [代码] [代码]"errmsg" : "ok", "identify_ret" : 0, "identify_time" : 1486350357 "validate_data": "8593" [代码] [图片] (三)返回参数说明 1、返回参数 注:errcode和identify_ret同时为0,代表本次认证成功。 参数 类型 描述 errcode int 错误码, 0表示本次api调用成功 errmsg string 本次api调用的错误信息 identify_ret int 人脸核身最终认证结果 identify_time uint32 认证时间 validate_data string 用户读的数字(如是读数字) openid string 用户openid user_id_key string 用于后台交互表示用户姓名、身份证的凭证 finish_time uint32 认证结束时间 id_card_number_md5 string 身份证号的md5(最后一位X为大写) name_utf8_md5 string 姓名MD5 2、错误码对应信息 errcode 备注 84001 非法identity_id 84002 用户信息过期 84003 用户信息不存在 五、小程序辅助接口:检查设备是否支持人脸检测 1、接口名称 接 口 :wx.checkIsSupportFacialRecognition(OBJECT) 功能:检查设备是否支持人脸检测 2、接口说明和使用 小程序调用该接口,可以检测当前手机设备是否具备支持人脸检测的能力,可与以上接口分开使用,为了用户体验,建议调用后对手机设备不支持的用户做对应功能处理。 3、接口说明和使用 01 OBJECT 参数说明: 参数 类型 是否必填 描述 success Function 否 调用成功回调 fail Function 否 调用失败回调 complete Function 是 调用完成回调(成功或失败都会回调) checkAliveType Number 否 人脸核验的交互方式,默认读数字(见表 2) 表 2:checkAliveType 的值和对应的解释: 参数 解释 2 先检查是否可以屏幕闪烁,不可以则自动为读数字 02 CALLBACK 返回参数 参数 类型 说明 errMsg Boolean 错误信息 errCode Number 错误码 03 回调结果说明 回调类型 ErrCode 说明 sucess 0 支持人脸采集 fail 10001 不支持人脸采集:设备没有前置摄像头 fail 10002 不支持人脸采集:没有下载到必要模型 fail 10003 不支持人脸采集:后台控制不支持 回调结果说明仅对Android生效,iOS不返回errcode。 04 示例代码 [图片] 六、安全性说明 为保障业务可用性以及安全性,请详细研读微信人脸核身接口相关基础说明及安全说明文档:https://docs.qq.com/doc/DTFB0YWFIdGV6amly 备注:如开发中遇到任何疑问,可以点击此处通过社区反馈,将有工作人员跟进回复。 七、案例展示及补充说明 安徽医科大学第二附属医院,微信人脸核验登录: 安徽医科大学第二附属医院,是三级甲等综合医院。其小程序为用户提供挂号、门诊费用、住院费用、检查报告、体检等医疗服务,同时也提供停车、餐饮等便民服务,是医疗小程序中完整的案例。 小程序使用了微信人脸核验能力作为登录的核验。满足医院管理要求,也满足国家对于实名就医的管理规则。 案例实现的截图效果如下: [图片] [图片] 针对近期少数小程序方面反馈的两类问题,也在本课程进行补充说明。 1、本接口的开放范围,即:可支持的主体类目,是否可以扩大? 说明:基于本接口整体使用范围的评估、相关法规的参考、监管策略的理解执行等,暂时未立刻进行扩大开放范围的工作。 但我们会持续基于不同行业的法规、政策及监管要求等,逐一进行研究考量,以便确认如何扩大开放范围。 2、小程序如果涉及用户本人的生物特征采集,(如本人人脸照片、人脸视频),或涉及采集用户本人生物特征信息并开展人脸核验功能,则存在被驳回的情况? 说明:近两年“人脸识别”技术在社会上掀起了热潮。人脸识别虽然作为摆脱“中间媒介”或“承载载体”的一种直接技术手段,解决了部分政务、交通、医疗、零售等证明“操作者是本人”的问题,但也因此,引入了新的更大的安全风险。 一是,虚假安全风险。 身份认证领域的安全三因素包括“我知道什么”、“我拥有什么”、“我的特征是什么”,通用的安全做法,是要双因素认证(2FA),人脸识别技术如仅凭“我的特征是什么”这一个因素,则容易被攻破或利用。表象给用户以安全的感觉,但实际并不能达到安全效果。 二是,信息泄漏的风险。 越来越多的组织或个人,在并非必需用户敏感信息、生物特征的情况下,采集并存储此类信息。在信息加密、传输、存储过程中,容易暴漏更多的网络节点,使得此类信息有更大的风险被网络黑客拦截、窃听、窃取,或直接被脱库。 三是,消除风险的难度大。 以往基于“中间媒介”或“承载载体”的方式,如出现丢失、被冒用、恶意盗用等风险,可以通过挂失、更换、使用新载体或新媒介等方式,快速排除一定的风险。C端主动,B端主动,都能解决一部分问题。但人脸识别做为更直接的方式,一旦出现冒用、盗用,受害者将面临更大的财产及人生安全风险,且C端用户更多时候无法主动消除风险。 基于以上问题风险,加之国家出台《网络安全法》、《用户隐私保护条例》等法律法规标准,网信办、公安部、工信部及市场监管总局等四部委发起的app获取隐私整治,结合平台安全、用户敏感隐私信息保护要求及监管,针对部分暂无相关法规或要求,需要采集或生物认证方式进行身份核验的,或以“追热点”或“尝鲜”为目的,采集用户生物特征或进行身份核验的,进行严格审核,必要时不予以支持。
星期一 15:19 - 几行代码实现小程序云开发提现功能
先看效果: [图片] 纯云开发实现,下面说使用步骤: 一:开通商户的企业付款到领取功能 说明地址: https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_1 使用条件 1、商户号(或同主体其他非服务商商户号)已入驻90日 2、截止今日回推30天,商户号(或同主体其他非服务商商户号)连续不间断保持有交易 使用条件是第一难,第二难在下面这里 [图片] 在网上找了很多,感觉是云开发这里的一个不完善地方,如果不填ip,会报这种错 [代码]{"errorCode":1,"errorMessage":"user code exception caught","stackTrace":"NO_AUTH"} [代码] [代码]<xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[此IP地址不允许调用接口,如有需要请登录微信支付商户平台更改配置]]></return_msg> <mch_appid><![CDATA[wx383426ad9ffe1111]]></mch_appid> <mchid><![CDATA[1536511111]]></mchid> <result_code><![CDATA[FAIL]]></result_code> <err_code><![CDATA[NO_AUTH]]></err_code> <err_code_des><![CDATA[此IP地址不允许调用接口,如有需要请登录微信支付商户平台更改配置]]></err_code_des> </xml> [代码] 云开发没有ip这个概念,所以这里有些无从下手,不过这里我采用了个替代方案,参考了社区帖子: https://developers.weixin.qq.com/community/develop/doc/00088cff3a40d87d80f7267b65b800 之后我也亲自验证了,基本上就是这几个,当然肯定不够,但是可以自己在逻辑上进行处理,ip以下: [代码]172.81.207.12 172.81.212.74 172.81.236.99 172.81.235.12 172.81.245.51 212.64.65.131 212.64.84.22 212.64.85.35 212.64.85.139 212.64.87.134 [代码] 接着,可以动手了 二、云开发部分 1、设置云存储 证书配置地址: [图片] 下载后有三个文件,我们只需要p12结尾的那个 [图片] 然后,将这个apiclient_cert.p12文件上传到你的云存储 [图片] 这里处理完了,我们只需要一个东西,就是fileID也就是常说的云存储ID(上图红框内容) 2、配置云函数 新建云函数ref云函数 [图片] 代码如下: [代码]const config = { appid: 'wx383426ad9ffe1111', //小程序Appid envName: 'zf-shcud', // 小程序云开发环境ID mchid: '1111111111', //商户号 partnerKey: '1111111111111111111111', //此处填服务商密钥 pfx: '', //证书初始化 fileID: 'cloud://zf-shcud.11111111111111111/apiclient_cert.p12' //证书云存储id }; const cloud = require('wx-server-sdk') cloud.init({ env: config.envName }) const db = cloud.database(); const tenpay = require('tenpay'); //支付核心模块 exports.main = async(event, context) => { //首先获取证书文件 const res = await cloud.downloadFile({ fileID: config.fileID, }) config.pfx = res.fileContent let pay = new tenpay(config,true) let result = await pay.transfers({ //这部分参数含义参考https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2 partner_trade_no: 'bookreflect' + Date.now() + event.num, openid: event.userinfo._openid, check_name: 'NO_CHECK', amount: parseInt(event.num) * 100, desc: '二手书小程序提现', }); if (result.result_code == 'SUCCESS') { //如果提现成功后的操作 //以下是进行余额计算 let re=await db.collection('user').doc(event.userinfo._id).update({ data: { parse: event.userinfo.parse - parseInt(event.num) } }); return re } } [代码] 需安装的依赖:wx-server-sdk、tenpay 这里只是实现了简单原始的提现操作,关于提现后,比如防止重复提交,提现限额这些,在开源二手书商城上有完整流程,地址: https://github.com/xuhuai66/used-book-pro 这种办法,不是每次都能成功提现,小概率遇到ip未在白名单情况,还是希望,云开发团队能尽快出一个更好的解决方案吧
2019-09-21 - 小程序AR识别,三行代码实现Camera数据毫秒级转base64图片
关键词:小程序AR 图片 base64 相机 Camera onCameraFrame Canvas ArrayBuffer Uint8Array Uint8ClampedArray upng-js 核心步骤: 1 相机原始图像数据frame.data,即ArrayBuffer数组,转成Uint8Array数组 2 Uint8Array数组转成Uint8ClampedArray数组 3 wx.canvasPutImageData(Uint8ClampedArray) 详细流程如下: 最近因为项目需求,需要上传base64去做AR识别功能,和大家一起分享讨论下具体的实现方式。 首先说下实现原理,通过Camera的onCameraFrame获取实时帧数据,将实时帧数据添加到Canvas上,然后将Canvas保存为临时图片,再将临时图片转换为base64。 贴上核心实现代码: wxml: js: var nCounter = 0; openCamera: function (res) { var that = this var camera_ctx = wx.createCameraContext() listener = camera_ctx.onCameraFrame((frame) => { // nCounter等于30 是因为一开始相机会有一个对焦的过程,如果一开始获取数据,就是模糊的图片 if (nCounter == 30) { console.log(frame.data instanceof ArrayBuffer, frame.width, frame.height) var data = new Uint8Array(frame.data); var clamped = new Uint8ClampedArray(data); // 实时帧数据添加到Canvas上 wx.canvasPutImageData({ canvasId: 'myCanvas', x: 0, y: 0, width: frame.width, height: frame.height, data: clamped, success(res) { // 转换临时文件 wx.canvasToTempFilePath({ x: 0, y: 0, width: frame.width, height: frame.height, canvasId: 'myCanvas', fileType: 'jpg', destWidth: frame.width, destHeight: frame.height, // 精度修改 quality: 0.8, success(res) { // 临时文件转base64 wx.getFileSystemManager().readFile({ filePath: res.tempFilePath, //选择图片返回的相对路径 encoding: 'base64', //编码格式 success: res => { // 保存base64 that.data.mybase64 = res.data; } }) }, fail(res) { console.log(res); } }, that) } }) } nCounter++ // console.log(nCounter); if (nCounter >= 100) { nCounter = 0 } }) listener.start() } 目前网上有两种转换方式并对比下: 1:upng-js等第三方转码js库,将相机流转换成base64,一般需要1-2s左右 [图片] 2.使用canvas将相机流转变base64,都是使用js或者小程序官方的api进行转换,一般转换时间在1秒以下: [图片] 重点说明下: 如何使用wx.canvasPutImageData()将相机流添加canvas,我们查看该官方api,添加的data类型为:Uint8ClampedArray [图片] 而我们通过onCameraFrame获取的data类型为:ArrayBuffer [图片] 所有两者类型不一致,就需要转换,将ArrayBuffer=>Uint8Array=>Uint8ClampedArray var data = new Uint8Array(frame.data); var clamped = new Uint8ClampedArray(data); 成功的把onCameraFrame获取实时帧数据转换并canvasPutImageData在canvas上,并通过canvasToTempFilePath获取临时文件,如何获取临时文件getFileSystemManager转换为base64,传入云端进行AR识别,就大功告成! 技术分享来自于:北京晞翼科技有限公司 技术作者:le3d618、xiaoz0816 微信商务联系:le3d618
2020-04-30 - 【笔记】解决用户头像过期无法显示问题
小根据官方规则,用户如果修改了头像,那么一段时间之后,用户原始的头像链接会失效。而因为我们一般用户资料储存的时候只储存了链接,就会造成失效,因此需要把用户头像转换成base64直接存数据库中,这样就不怕失效了。 云开发代码 /** * 插入用户数据 */ function addUserData(openid, userInfo) { if (!userInfo) { console.log('无用户信息,更新失败') } // 将头像图片转换为base64 http.get(userInfo.avatarUrl.replace("https", "http"), function (res) { let chunks = []; //用于保存不断加载的缓冲数据 let size = 0; //保存缓冲数据的总长度 res.on('data', function (chunk) { chunks.push(chunk); //把接受到的数据逐段保存在缓冲区(Buffer size += chunk.length;//累加缓冲数据的长度 }); res.on('end', function () { var data = Buffer.concat(chunks, size);//Buffer.concat将chunks数组中的缓冲数据拼接起来 if (Buffer.isBuffer(data)) { //如果为Buffer转换为base64并赋值给avatarImg var base64Img = 'data:image/png;base64,' + data.toString('base64'); userInfo.avatarImg = base64Img } db.collection('user').doc(openid).set({ data: userInfo }).then(e => { console.log('用户数据更新成功', e) }) }); }); } 小程序端直接渲染 <!-- 直接渲染到页面 page.wxml --> <view style="background-image:url({{detail.avatarImg||detail.avatarUrl}});"></view> 小程序端将图片保存到本地 //如果需要将头像转成图片保存,如cavans绘图场景 page.js const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(src) || []; if (format) { const filePath = `${wx.env.USER_DATA_PATH}/tmp_base64src.${format}`; // console.log(filePath) // const buffer = wx.base64ToArrayBuffer(bodyData); FileSystemManager.writeFile({ filePath, data: bodyData, encoding: 'base64', success() { console.log(filePath) }, fail() { console.log (new Error('ERROR_BASE64SRC_WRITE')); }, }); } 小程序端 已授权用户进入时自动更新 //进入小程序时,自动更新授权用户的信息到云端 app.js onLaunch: function () { this.getUserAuth(); } getUserAuth: function () { wx.getSetting({ success: res => { res.authSetting['scope.userInfo'] && wx.getUserInfo({ success: res => { wx.cloud.callFunction({ name: 'user', data: { userData: res.userInfo, } }) } }) } }) },
2020-07-07 - 云开发支付的代码,有需要的进。
真机测试已通过。你照抄就行,保证可通过。 最新完美版本可供参考: 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 - 向下箭头→的动画怎么做?
[图片] 这个箭头要会向下动 https://www.sohu.com/a/165594235_607781
2020-06-13 - 【笔记】云开发通过客服消息实现自动回复进群,同时兼容客服小助手
小程序不具备小程序内扫描二维码的能力,因此如果要实现关注公众号或加用户群功能大家一般都利用微信客服功能的自动回复来实现。此时如果自己去实现微信客服自动回复,客服小助手就不能用了,很令人纠结。经过我的研究,借助云开发找到了一个方案,可以实现当用户想获取微信群走自动回复的接口,真正咨询时,直接到客服小助手进行回复。 效果如下 [图片] 原理解析 云开发在做消息推送配置的时候可以配置消息类型,这个时候如果我们只配置一种类型(小程序卡片),那么就只有卡片才会被云函数接管做自动回复,其他消息类型(图片、文字)则正常走小程序客服的通道。 实现步骤 1.小程序端设置按钮属性open-type="contact",用于用户点击时带上定义的卡片跳到客服消息界面。 申请加入 2.新建云端的函数,设置config.json定义权限,如下config.json { "permissions": { "openapi": [ "customerServiceMessage.send", "customerServiceMessage.uploadTempMedia", "customerServiceMessage.setTyping" ] } } 3.写云函数端代码,如下 if (event.Title == "我要进用户群"||event.Title =="关注公众号"||event) { //设置输入状态 cloud.openapi.customerServiceMessage.setTyping({ touser: OPENID, command: 'Typing' }) //从云储存中拉取图片数据 const qunimg = await cloud.downloadFile({ fileID: "cloud://pm-hsfip.706d-pm-hsfip-1259751853/img/qun.png", }) //上传图片素材到媒体库,并获取到图片id const qunmedia = await cloud.openapi.customerServiceMessage.uploadTempMedia({ type: 'image', media: { contentType: 'image/png', value: qunimg.fileContent } }) //向用户发送群二维码 await cloud.openapi.customerServiceMessage.send({ touser: OPENID, msgtype: 'image', image: { mediaId: qunmedia.mediaId, } }) //取消状态设置 cloud.openapi.customerServiceMessage.setTyping({ touser: OPENID, command: 'CancelTyping' }) } 4.设置消息推送,路径如下 云开发-设置-全局设置-云函数接收消息推送 中添加消息类型为miniprogrampage,绑定云函数为新建的云函数。 [图片] 5.微信公众平台绑定客服[图片] 注意事项 如果按照本教程 客服小助手无法收到消息 或 无法自动回复,可以先将以上消息推送配置删除,然后再微信后台绑定客服后,再重新进行消息推送配置。
2020-07-29 - 借助云开发10行代码实现小程序短信验证码的发送
最近在做小程序验证码登陆时,用到了短信发送验证码的需求,自己也研究了下,用云开发结合云函数来实现验证码短信发送还是很方便的。 老规矩,先看效果图 [图片] 这是我调用腾讯云的短信平台发送的登陆验证码。核心代码其实只有下面这么多 [图片] 是不是感觉实现起来特别简单,怎么说呢,我们代码调用其实就这么几行,就可以实现短信的发送,但是腾讯云短信模板的审核比较繁琐,还有我们先去申请短信模板,短信模板审核通过后才可以使用。 我们就先来说代码实现,然后再带大家简单的学习下短信模板的申请。 一,安装node类库 其实我们这里用到了云开发的云函数,我们是在云函数里调用短信发送的。为什么要在云函数里调用呢,因为我们做短信发送,需要用到腾讯云的一个短信发送的类库,而这个类库是node库,所以只能在云函数里调用了。 在安装这个类库之前,我们需要先创建一个云函数,关于云函数的创建,我其实已经讲过很多遍了,不知道的同学,去翻看下我的历史文章,或者看下我录制的云开发入门视频《5小时零基础入门小程序云开发》 我后面也会把这节内容录制出视频出来。 创建完云函数后,右键点击在终端中打开,打开终端后,在终端中输入以下命令来安装qcloudsms_js类库 [代码]npm install qcloudsms_js [代码] [图片] 这里需要注意,我们安装类库前需要先下载node并配置npm环境变量,这里我也有写文章的 《nodeJs的安装与npm全局环境变量的配置》 二,编写云函数 上面类库安装好以后,我们就可以来编写云函数了。 其实代码编写起来很简单,就下面这些,对应的注解我也都已经写出来了。 [图片] 这里要发送的手机号,和随机验证码需要动态传进来的。 三,调用云函数 调用云函数这里也很简单,我们需要传入手机号和验证码 [图片] 手机号这里,我做了一个输入框,可以动态的输入。验证码的话,我写了一个方法来随机生成数字和字母的组合验证码。 [图片] 我等下会把完整的代码贴出来给大家。 [图片] 这样我们输入完手机号以后,点击发送短信按钮,就可以成功的发送短信给到对应的手机号了。 可以看到我们生成的随机验证码如下 [图片] 我们手机接受到的短信验证码如下 [图片] 这样我们做登陆或者做校验时,用户手机短信收到的验证码,和我们随机生成的验证码一样,即代表用户验证成功。 完整的index.js代码给大家贴出来 [代码]var chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; let phone = '' Page({ //获取随机验证码,n代表几位 generateMixed(n) { var res = ""; for (var i = 0; i < n; i++) { var id = Math.ceil(Math.random() * 35); res += chars[id]; } return res; }, //调用云函数发送短信 sendSMS() { if (phone.length != 11) { wx.showToast({ icon: 'none', title: '输入11位手机号', }) return } let code = this.generateMixed(4) console.log('本地生成的验证码', code) wx.cloud.callFunction({ name: "sendSms", data: { phone: phone, code: code //生成4位的验证码 } }).then(res => { console.log('发送成功', res) }).catch(res => { console.log('发送失败', res) }) }, //获取要发送的手机号 getPhone(event) { console.log(event.detail.value) phone = event.detail.value }, }) [代码] index.wxml如下 [图片] 到这里我们的短信验证码的发送就完整的实现了,是不是很简单。 短信发送参数的设置与获取 首先是去腾讯云自己开通短信功能,然后需要自己去申请模板,填写签名。 [图片] 我这里把所需要的参数,都给大家标准出来了。大家只需要自己去官网设置对应的模板和签名,然后审核通过后,把对应的参数放到我们的云函数里即可。 短信验证的原理讲解 在网上找了一张短信验证的原理图,如下 [图片] 大家可以对照这看下,这个原理图。对应的源码我上面其实已经给大家贴出来了。 如果大家觉得不完整,我也已经把完整源码放到网盘里了,有需要的同学可以到我公号里回复“短信”获取源码。 [图片]
2020-01-03 - 小程序开发必备,这 5 款超实用开源插件!
Parser - 富文本解析 GitHub:https://github.com/jin-yufeng/Parser [图片] calendar - 预约日历 GitHub:https://github.com/jasondu/wxa-plugin-calendar [图片] cropper - 图片裁剪 GitHub:https://github.com/wx-plugin/image-cropper [图片] wxSearch - 搜索框 Github:https://github.com/icindy/wxSearch [图片] WxValidate - 表单验证 Github:https://github.com/wux-weapp/wx-extend/blob/master/docs/components/validate.md [图片] 如有收获,记得点赞、收藏 如有补充或者疑问,欢迎进行留言讨论
2020-08-14 - 云数据库聚合函数min运行报错
[图片] 代码就改了个集合名和字段名,其他照抄,然后就报了下面这个错误。。。求大佬呀 [图片]
2019-07-14