uniapp介绍
#uniapp介绍
#什么是快应用?
快应用是指用户无需下载安装,即点即用,享受原生性能体验的应用,例如:微信小程序,支付宝小程序,百度小程序等。
快应用的优势:
- 无需下载安装App,节约手机空间;
- 性能好,体验接近原生;
- 背靠流量;
快应用的缺点:
- 平台多,语法多,开发成本高;
- 管控难;
快应用的发展趋势:平台底层支持,扫码即用,无需安装微信、支付宝等平台,可以参看,链接(opens new window)
为什么PWA不香了?
- 浏览器环境需要考虑兼容性;
- 支付会被限制;
- 缺乏原生能力;
- 追求原生体验;
- 更高的安全性的要求(内容、代码);
#背景介绍
#技术人想偷懒
困境:
- 小程序的平台太多,需要学习多个平台的语法;
- 前端人也想用vue,react语法来写小程序;
- 想在小程序中使用npm包(庞大的第三方包);
- 小程序代码 -> 原生App,一套代码多端运行;
#各个平台之间的对比

数据源:链接 (opens new window), 2020年横评:链接(opens new window)
结论:
- 跨端支持度测评结论:
uni-app>taro>chameleon>mpvue>wepy、原生微信小程序 - 微信原生框架可达到更好的性能,但
uni-app、taro相比微信原生,性能差距并不大; - 微信原生开发手工优化,
uni-app>微信原生开发未手工优化,taro>chameleon>wepy>mpvue mpvue支持绝大部分的Vue语法;uni-app编译到微信端曾经使用过mpvue,但后来重新编写,支持了更多vue语法如filter、复杂JavaScript表达式等;wepy、chameleon都是类Vue的实现,仅支持Vue的部分语法,开发时需要单独学习它们的规则;taro对于JSX的语法支持是相对完善的,React技术栈友好;- 学习资料完善度:
uni-app>mpvue,taro>chameleon>wepy - 社区活跃度:
uni-app>taro>chameleon>wepy>mpvue
TIP
没有最好的,只有最适合的。
#应用场景
- 熟悉Vue技术栈,推荐:uniapp > wepy > mpvue;熟悉React技术栈,taro;
- 项目初期,想法论证可以使用uniapp -> 多端开发;后续,推荐Flutter跨端开发,再到后期推荐原生开发;
- 框架的更新本身,不能作为使用框架的绝对指标;选择合适的框架,解决当下的问题;
#什么是uniapp?
uni-app 是一个使用 Vue.js (opens new window)开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、Web(响应式)、以及各种小程序(微信/支付宝/百度/头条/QQ/钉钉/淘宝)、快应用等多个平台。
TIP
uni-app与mpvue的渊源:uni-app在初期借鉴了mpvue,实现了微信小程序端的快速兼容,参考链接 (opens new window)。
#开发规范
为了实现多端兼容,综合考虑编译速度、运行性能等因素,uni-app 约定了如下开发规范:
组件标签靠近小程序规范,详见uni-app 组件规范(opens new window)
有几点特别要注意的:
- 注意:所有组件与属性名都是小写,单词之间以连字符
-连接; - 每个vue文件的根节点必须为
<template>,且这个<template>下只能且必须有一个根<view>组件; - 不推荐使用HTML标签,为了管理方便、策略统一,新写代码时仍然建议使用view等组件;
- 组件上的事件绑定,需要以 vue 的事件绑定语法来绑定,如 bindchange=”eventName” 事件,需要写成
@change="eventName"; - uni-app支持的组件分为vue组件和小程序自定义组件;如果扩展组件符合uni-app的
easycom组件规范,则可以免注册,直接使用;如果组件不符合easycom规范,则需要在代码里手动import和注册组件,然后才能使用
- 注意:所有组件与属性名都是小写,单词之间以连字符
接口能力(JS API)靠近微信小程序规范,但需将前缀
wx替换为uni,详见uni-app接口规范(opens new window)数据绑定及事件处理同
Vue.js规范,同时补充了App及页面的生命周期为兼容多端运行,建议使用flex布局进行开发
#目录结构
一个uni-app工程,默认包含如下目录及文件:
|
TIP
- 编译到任意平台时,
static目录下的文件均会被完整打包进去,且不会编译。非static目录下的文件(vue、js、css 等)只有被引用到才会被打包编译进去。 static目录下的js文件不会被编译,如果里面有es6的代码,不经过转换直接运行,在手机设备上会报错。css、less/scss等资源不要放在static目录下,建议这些公用的资源放在自建的common目录下。- HbuilderX 1.9.0+ 支持在根目录创建
ext.json、sitemap.json等小程序需要的文件。
#导入静态资源
#模板内引入静态资源
template内引入静态资源,如image、video等标签的src属性时,可以使用相对路径或者绝对路径,形式如下
|
特别说明:
TIP
@开头的绝对路径以及相对路径会经过base64转换规则校验- 引入的静态资源在非h5平台,均不转为base64。
- H5平台,小于4kb的资源会被转换成base64,其余不转。
- 自
HBuilderX 2.6.6起template内支持@开头路径引入静态资源,旧版本不支持此方式 - App平台自
HBuilderX 2.6.9起template节点中引用静态资源文件时(如:图片),调整查找策略为【基于当前文件的路径搜索】,与其他平台保持一致 - 支付宝小程序组件内 image 标签不可使用相对路径
#js文件引入
js文件或script标签内(包括renderjs等)引入js文件时,可以使用相对路径和绝对路径,形式如下
|
WARNING
js文件不支持使用/开头的方式引入
#css引入静态资源
css文件或style标签内引入css文件时(scss、less文件同理),可以使用相对路径或绝对路径(HBuilderX 2.6.6)
|
WARNING
自HBuilderX 2.6.6起支持绝对路径引入静态资源,旧版本不支持此方式
css文件或style标签内引用的图片路径可以使用相对路径也可以使用绝对路径,需要注意的是,有些小程序端css文件不允许引用本地文件(请看注意事项)。
|
注意事项:
TIP
- 引入字体图标请参考,字体图标(opens new window)
@开头的绝对路径以及相对路径会经过base64转换规则校验- 不支持本地图片的平台,小于40kb,一定会转base64。(共四个平台mp-weixin, mp-qq, mp-toutiao, app v2)
- h5平台,小于4kb会转base64,超出4kb时不转。
- 其余平台不会转base64
#搭建开发环境
#集成scss/sass编译
为了方便编写样式(例如<style lang="scss">),建议大家安装sass/scss编译插件,插件的下载地址:scss/sass编译(opens new window)

登录账号 -> 无账号,即注册(邮箱验证) -> 再次点击安装插件 -> 打开HBuilderX
#自定义主题、快捷键等
- 快捷键切换
在工具 -> 预设快捷键方案切换 中可以切换自己喜欢的快捷键方案,对HBuilderX进行自定义:

- 设置主题

- 字号设置
macOS的快捷键是 Command + ,,windows的快捷键是Ctrl + ,

常见配置:
|
#创建项目
#使用HBuilderX
步骤:
下载HBuilderX:官方IDE下载地址 (opens new window)——建议使用标准版本
HBuilderX标准版可直接用于web开发、markdown、字处理场景。做App仍需要安装插件。
App开发版预置了App/uni-app开发所需的插件,开箱即用。
标准版也可以在插件安装界面安装App开发所需插件,App开发版只是一个预集成作用。
App开发插件体积大的原因主要有2方面:
- 真机运行基座,Android版、iOS版、iOS模拟器版,加起来体积就1百多M。真机运行基座需要把所有模块都内置进去,方便大家开发调试。开发者自己做app打包是不会这么大的,因为可以在manifest里选模块来控制体积。
- uni-app的编译器,依赖webpack和各种node模块,node_modules就是这么一个生态现状,文件超级多,几万个文件,解压起来很慢。
在点击工具栏里的文件 -> 新建 -> 项目:

选择
uni-app类型,输入工程名,选择模板,点击创建,即可成功创建。
在微信开发者工具里运行:进入hello-uniapp项目,点击工具栏的运行 -> 运行到小程序模拟器 -> 微信开发者工具,即可在微信开发者工具里面体验uni-app。

第一次运行的提示:

成功运行:

注意:
如果是第一次使用,需要先配置小程序ide的相关路径,才能运行成功。如下图,需在输入框输入微信开发者工具的安装路径,uni-app默认把项目编译到根目录的unpackage目录。

若HBuilderX不能正常启动微信开发者工具,需要开发者手动启动,然后将uni-app生成小程序工程的路径拷贝到微信开发者工具里面,在HBuilderX里面开发,在微信开发者工具里面就可看到实时的效果。
如果提示
[error] 工具的服务端口已关闭。要使用命令行调用工具,请在下方输入 y 以确认开启,或手动打开工具 -> 设置 -> 安全设置,将服务端口开启,如图:
微信开发者工具设置菜单,安全中打开服务端口:

#使用vue-cli命令行(VSCode)
- 初始化项目
|

- 安装组件语法提示
组件语法提示是uni-app的亮点,其他框架很少能提供。
|
如果是HBuilderX的项目,可以使用
|
另外,uni-app 项目下的 manifest.json、pages.json 等文件可以包含注释。vscode 里需要改用 jsonc 编辑器打开。
#配置AppID

#ESLint与代码格式化
TIP
ESLint与代码保存即自动格式化,仅在VSCode上有效
#使用第三方npm包
uni-app支持使用npm安装第三方包。
此文档要求开发者们对npm有一定的了解,因此不会再去介绍npm的基本功能。如若之前未接触过npm,请翻阅NPM官方文档 (opens new window)进行学习。
1. 初始化npm工程
若项目之前未使用npm管理依赖(项目根目录下无package.json文件),先在项目根目录执行命令初始化npm工程:
|
cli项目默认已经有package.json了。HBuilderX创建的项目默认没有,需要通过初始化命令来创建。
2. 安装依赖
在项目根目录执行命令安装npm包:
|
3. 使用
安装完即可使用npm包,js中引入npm包:
TIP
- 为多端兼容考虑,建议优先从 uni-app插件市场 (opens new window)获取插件。直接从 npm 下载库很容易只兼容H5端。
- 非 H5 端不支持使用含有 dom、window 等操作的 vue 组件和 js 模块,安装的模块及其依赖的模块使用的 API 必须是 uni-app 已有的 API (opens new window)(兼容小程序 API),比如:支持高德地图微信小程序 SDK (opens new window)。类似jQuery (opens new window)等库只能用于H5端。
- node_modules 目录必须在项目根目录下。不管是cli项目还是HBuilderX创建的项目。
- 支持安装 mpvue 组件,但npm方式不支持小程序自定义组件(如 wxml格式的vant-weapp),使用小程序自定义组件请参考:小程序组件支持 (opens new window)。
- 关于ui库的获取,详见多端UI库(opens new window)
#初始化ESLint
|
package.json文件配置如下:
|
新建 两个文件,.eslintrc.js:
|
创建.eslintignore文件:
|
#配置vscode自动修复功能
安装vetur、eslint插件
打开vscode的首选项配置,settings.json文件
|
#下载官方代码提示
点击 下载地址 (opens new window),放到项目目录下的 .vscode 目录即可拥有和 HBuilderX 一样的代码块。

#首页模块
#首页tabBar
#代码依赖分析
可以在基本信息中选择代码依赖分析

查看本地代码与分包大小:

#导入静态资源
- 删除原
static目录中的文件; - 下载课程的静态资源文件夹,并放置于
static的images目录中。
#布局与样式
完成效果:

创建tabBar的步骤:
- 创建tabBar对应的页面;
- 修改pages.json中的配置项:
pages和tabBar
创建页面HBuilderx方式:

创建页面的VSCode插件方式:

快速创建4个页面

并调整pages.json:
|
新建tabBar属性:
|
配置package.json中的eslint修复命令:
|
底部的阴影:
|
#导入uView UI
准备工作
|
在main.js中引入:
|
在项目根目录的uni.scss中引入样式文件:
|
调整App.vue的样式
|
配置pages.json:
|
#首页Tabs
#布局与样式
home.vue添加tabs
|
添加tabs数据
|
完成效果

#添加事件
tabs添加切换事件tabsChange
|
#接口封装
问题:小程序原生提供了request请求,但是是callback的使用方式,并不支持Promise,见链接 (opens new window)。

需求:我们前端里面写网络请求,习惯了Axios + async/await的书写风格,那在小程序中怎么去实现接口请求的封装呢?
拆分需求:
- Promise化 -> async/await支持
- 接口请求封装
- 拦截器
- 统一错误处理
- 取消重复请求
- ..
#小程序API Promise化
扩展微信小程序api支持promise
首先,本地小程序npm初始化
|
在小程序入口(app.js)调用一次promisifyAll,只需要调用一次。
|
建议的写法:
|
async/await支持的方法比较简单:开户将 JS 代码编译成 ES5即可:链接(opens new window)
在工具 1.05.2106091 版本之后,原有的
ES6 转 ES5和增强编译选项统一合并为将 JS 代码编译成 ES5,此功能和原有的增强编译逻辑一致。如需了解旧版本的文档,请点此查看 (opens new window)。
支持async/await语法,按需注入regeneratorRuntime,目录位置与辅助函数一致

平时开发的时候,一定要注意该选项有没有打开哦!!
#uniapp小程序请求封装
接口请求封装
- 拦截器
- 统一错误处理
- 取消重复请求
TIP
uniapp中已经支持Promise + async/await,但是,对于request请求的错误的处理需要自己来处理。
请求工具js:
|
#uview中请求封装
工作原理:
uView提供了 http封装:
平台差异说明
| App | H5 | 微信小程序 | 支付宝小程序 | 百度小程序 | 头条小程序 | QQ小程序 |
|---|---|---|---|---|---|---|
| √ | √ | √ | √ | √ | √ | √ |
由于某些小程序平台的限制:
- delete请求,不支持支付宝和头条小程序(HX2.6.15)
- put请求,不支持支付宝小程序(HX2.6.15)
|
url<String>请求的URL,可以完整的URL(http开头),或者是路径的一部分,这时会自动拼接上baseUrl(一般为api的域名部分)params<Object>请求的参数,对象形式,如"{name: 'lisa', age: 23}",该参数是可选的header <Object>请求的header,对象形式,如果token等字段,建议通过配置写入,该参数是可选的get和post都挂载在$u对象下,其中get和post使用方法完全一致,只是一个为this.$u.get
但是如果直接按照上面的写会报错,由于没有设置baseURL: 
官方文档也有说明:

设置baseURL的方法:配置参数的时候,需要调用$u.http.setConfig(),打开main.js:
|
修改接口:
|
然后就可以正常的请求了:

uview对于拦截器(请求/响应)的支持:
|
请求封装:
config.js配置文件:
|
common/request.js文件
|
问题是如何进行统一的错误处理?
- uview-ui的http工具类没有错误的统一处理
- 统一的错误处理的应用场景:401跳转到鉴权、给用户友好提示、refreshToken的场景
具体做法,修改uview的源码,并把uview的代码移动到项目的代码中,利用hbuilder&uniapp的按需加载,按需打包的机制,使用uview中的组件。
步骤:
复制
node_modules中的uview-ui到项目的根目录;删除
package.json中的dependencies修改pages.json中的
easycom:"easycom": {
"^u-(.*)": "@/uview-ui/components/u-$1/u-$1.vue"
}修改main.js中的引入 :
import uView from './uview-ui'uni.scss中的引入 :/* uni.scss */
@import "./uview-ui/theme.scss";还有App.vue中的引入 :
/* 注意要写在第一行,同时给style标签加入lang="scss"属性 */
@import "./uview-ui/index.scss";
#合并uview请求
目的:
- 动态引入 API 接口
- 统一所有的请求的使用方式
this.$u.api.接口
思路:
- 使用
require.context动态引入api接口 - 使用插件的方式,在Vue.prototype.$u来添加api属性
目录结构:

其中index.js代码:
|
修改main.js:
|
#内容列表
测试图片地址:链接(opens new window)
#布局与样式
添加list-item组件
|
home.vue添加内容展示区
|
监听页面加载,处理内容列表容器的padding
|
#获取数据
在通过后台接口获取数据前,我们需要对request和api进行简单的封装。
创建common文件夹,新建 request.js
|
创建api文件夹,新建 index.js,按模块进行接口划分

index.js
|
public.js
|
在main.js中引入
|
下面我们来获取首页列表数据,创建getList方法
|
最后在onshow周期请求数据
|
完成效果:

#分页与切换
上拉分页加载列表数据
|
同时在tab切换时加载不同分类的数据,在tabs切换方法tabsChange中添加 getList 方法
|
#日期过滤器
注意:用法与vue中的过滤器一致
- 安装依赖:
|
- 添加filter.js文件:
|
- main.js中使用:
|
#搜索
#首页搜索按钮
关于设计:
参考:链接1 (opens new window), 链接2(opens new window)
官方的文档目前没有对右侧按钮的说明:

在旧的文档中有说明:

从图中分析,我们可以得到如下信息:
- Android跟iOS有差异,表现在顶部到胶囊按钮之间的距离差了6pt
- 胶囊按钮高度为32pt, iOS和Android一致
关于px,em与pt单位的说明:
px是像素单位,em是相对单位,pt是绝对单位。
它们各自的好处是:
- px可以在计算机屏幕上,能达到预期的效果,在打印机和其它的高分辨率设备上,它又能取得所希望的效果。
- em的优点很多,比如在一个页面上,你给定了一个父元素的字体大小,这样就可以通过调整一个元素来成比例的改变所有元素大小。它可以自由缩放,比如用来制作可伸缩的样式表。
- pt是一种固定长度的度量单位,是能够使用测量设备测得的长度。绝对单位作用有限,因为它们不能够缩放,通常只用在已经知道是用在哪种输出媒体的情况下才使用。但大多数情况下最好使用相对单位。一般都是用px和em这两种种配搭比较好。
使用wx.getSystemInfoSync()可以在console中快速查看设备的信息。
两种方式创建uni的easycom组件:
- 创建
components/组件名同名文件夹/组件名.vue - 使用hbuilderx来创建组件,勾选
创建目录

创建search组件:
|
注意:
- 在uniapp中自定义的组件是无法直接绑定事件的;
- 可以在子组件中的最外侧的view上绑定
$emit('click')
#添加分包
在uniapp中添加分包
- 新增页面,比如
/sub-pkg/search/search - 配置
pages.json
注意:
分包的作用是为了提升小程序的加载性能,首页tabs相关的页面是不能放置在分包中的。
#布局与样式
|
pages.json中配置分包:
|
使用easycom,添加search.vue组件:
|
home.vue添加导航:
|
完成效果:
#添加事件
home.vue添加gotoSearch方法,完成页面跳转。
|
完成效果:

#搜索历史功能
- 本地数据缓存
- 新的搜索数据显示在最前面
- 点击垃圾桶删除所有数据,并只显示热门推荐
- 点击标签进行快速搜索
本地数据缓存:
|
添加对应的事件:
|
#搜索建议(列表)
- 完成推荐列表样式
- 设置请求参数
- 使用统一发送文章请求
- 本地展示列表,并隐藏推荐历史与热门推荐
- 清空搜索条件时,把推荐列表清空,并展示搜索历史&热门推荐
结构部分:
|
样式部分:
|
methods部分:
|
#搜索按钮事件绑定
|
添加对应的search方法:
|
#整体代码
|
#发帖入口
#布局与样式
在home.vue添加发帖入口
|
修改add-post样式
|
完成效果

#添加事件
添加点击事件,点击跳转发帖页面
|
#小程序鉴权登录
#需求分析
流程分析:

说明:
- 时序图的阅读方式:
- 搞清楚有几方,哪几个重要的交互部分,比如:上面有小程序、开发服务器、小程序官方后台;
- 从上至下,从左至右,跟随箭头的方向走;
- 时序图中,任何一个流程中断,后续的相关流程不会继续进行;
通过分析,我们获取用户的信息用于创建用户,并通过自己的服务器返回用户的登录态,即token信息与用户信息。
用户信息需要跨页面进行共享,同时,也需要持久化,所以惯性的思考到了 vuex + 本地缓存的方案。
#Vuex集成uniapp
#初始化store
重要:uniapp中内置vuex,所以直接按照vue中使用vuex的步骤进行集成。
创建文件store/index.js:
|
在 main.js 中导入文件:
|
state使用上的区别:
- 直接在template中不能使用
$store取值; - 在data中无法使用
this.$store.state取对应的初始值;
正确的打开姿势:
- 辅助函数(推荐)
- computed方法
|
#如何进行调试
|
当我们在页面触发mutation的时候:
|
即可以收到console中的打印日志:

#微信官方服务相关
#获取AppID与AppSecret
登录小程序的后台 https://mp.weixin.qq.com/(opens new window)

#unionid与openid
什么是unionid,openid?
openid是用户在当前小程序的唯一标识;
unionid是用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台帐号下会返回;
两者的区别在哪里?unionid的作用是什么?
都是唯一标识,针对的平台不一样,openid针对小程序,unionid针对开放平台;
开放平台是指:https://open.weixin.qq.com/(opens new window)
如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过 UnionID 来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的 UnionID 是唯一的。
换句话说,同一用户,对同一个微信开放平台下的不同应用,UnionID是相同的。
如果没有微信侧多应用场景(公众号、小程序、网页互通的需求),可以不用弄这个开放平台的账号。
unionID如何获取?
绑定了开放平台开发者帐号的小程序,可以通过以下途径获取 UnionID。
- 开发者可以直接通过 wx.login (opens new window)+
code2Session获取到该用户 UnionID,无须用户授权。 - 小程序端调用云函数时,可在云函数中通过 Cloud.getWXContext (opens new window)获取 UnionID。
- 开发者可以直接通过 wx.login (opens new window)+
开放平台如何注册与使用?
开放平台需要在开放平台地址 (opens new window)注册,同时需要认证后才能使用。

认证费用300元(xxxx马x腾)
#获取用户OpenData
登录凭证校验,通过 wx.login (opens new window)接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。更多使用方法详见 小程序登录 (opens new window)。
|
#鉴权页面样式
|
#解密微信数据
#用户信息的解密
小程序调用
getUserProfile,获取用户加密数据 + 证书,请求用户后台;后台进行加密数据的校验,确定用户传递的数据是微信侧的数据;
signature = sha1( rawData + session_key )算是校验成功;
加密数据解密,参考官方的代码 (opens new window);

代码示例:
微信解密工具js:
|
微信工具js的封装:
|
小程序侧:
|
需要注意的点:
- uni.login不需要用户感知的触发与uni.getUserProfile需要tap(用户点击)事件触发;
- uni.login与uni.getUserProfile如果需要同时使用,可以都使用callback回调的形式来使用;
#用户登录凭证维护
防止code失效,而请求失败。
解决办法:前端设置定时任务,每隔<5分钟的时间,请求一次uni.login刷新登录凭证。
|
#用户登录接口
接口部分:
产生token:
|
随机用户名:
|
微信解密用户信息的wxUtils.js:
|
查询并创建用户User.js
|
LoginController.js微信登录接口:
|
获取access_token,并按两小时维护:
|
维护access_token数据:
|
#手机登录
#业务流程图
- 获取手机号 -> 发送短信验证码页面

- 输入验证码 -> 手机号登录页面

#如何选择短信服务商
推荐腾讯云、阿里云,下面以腾讯云为示例:

基本的使用步骤:
- 创建云平台账号 -> 实名 -> 推荐公司实名
- 够买短信包 -> 添加短信模板
- 集成SDK发送短信
腾讯云的两种集成方式:
- qcloudsms_js,参考github仓库(opens new window)
- Tencentcloud-sdk-nodejs,参考官方文档(opens new window)
#登录页面样式
auth/mobile-login.vue页面
|
mobile-code.vue页面:
|
#小程序获取手机号
小程序侧逻辑:
设置open-type为getPhoneNumber:
|
getPhoneNumber方法:
|
API接口解密数据:
设置路由:
|
获取手机号LoginController.js:
|
#前后端逻辑&联调
常见问题:
参数传递异常:

解决办法:确保前后端传递的参数的正确性,比如,统一参数名称,统一请求methods。
短信服务商的问题

解决办法:1. 余额不足即充值;2. 传参非法,修改参数;3. 签名或者模板问题,修改对应的模板参数后再进行发送。
需要删除已经使用过的短信验证码数据;
如果给用户已经发送一次短信验证码,则不再相同的时间间隔内,再给用户发送,限制用户发送短信的频次;
小程序侧接口:
|
完成的手机发送验证码页面
|
校验验证码的页面:
|
后端接口LoginController.js:
|
查询并创建手机用户User.js
|
发送验证码逻辑PublicController.js:
|
需要注意的点:
- 短信发送失败时:
- 限制用户频繁发送-> 不影响重发;
- 用户提示 -> 短信失败原因 -> 这里大部分是第三方服务商的错误提示,所以需要专门进行处理;
- 短信限制发送机制设计:不让成功发送短信之后,还让用户重复发送 -> redis记录短信数据;
- 手机登录接口:
- 发送成功短信则响应,并设置redis,否则直接throw错误;
- 创建用户需要使用findOne来进行查重;
- 接口参数需要与前端进行一一对应,最好使用文档进行约定;
#统一的用户授权登录
用户授权登录是经常需要使用的功能,封装到common/checkAuth.js中:
- 需要判断小程序用户的登录状态是否失效 - 使用小程序API
checkSession - 用户登录失效,给用户一个Confirm提示框,让用户选择是否进行跳转登录
|
#消息&热门&个人中心
消息、热门、个人中心这三块的内容重点:
- 灵活使用UI框架:uview内置样式的综合使用;
- 统一鉴权跳转提示:用户登录与未登录状态下,页面跳转逻辑;
- 熟悉前后端开发流程:从前到后的开发逻辑,排查问题从源头开始找问题;
- 完善登录失效,接口鉴权失败401的页面跳转逻辑;
#消息模块
最终 完成效果:

#页面布局和样式
- 添加 tabs 切换,并设置吸顶
|
#自定义吸顶组件
吸顶效果最关键的属性:
|
可以自行创建components/t-sticky/t-sticky.vue组件:
|
使用方法:
|
#消息模块的样式
|
前端接口api/modules/user.js:
|
#接口对接与联调
调整src/model/CommentsHands.js
|
获取历史消息src/api/UserController.js:
|
完成效果(点赞):

完成效果(评论):

#热门模块
这个部分利旧接口,所以只用开发前端页面即可:
#页面布局和样式
|
自定义三个组件,热门帖子HotPostList.vue:
|
热门评论HotCommentsList.vue:
|
签到排行HotSignList.vue:
|
#接口对接与联调
PublicController.js文件:
|
完成效果(热门帖子):

完成效果(热门评论):

完成效果(签到排行):

#个人中心模块
#页面布局和样式
整体的页面分为:
- 背景
- 个人信息 + 统计信息
- 功能区
- 快速跳转区
|
#最近浏览功能
创建PostHistory模型:
|
创建src/api/StaticsController.js:
- 统计最近浏览、我的帖子、收藏夹、我的评论、我的点赞、我的获赞、个人积分、用户的签到日期;
- 个人积分需要单独写方法,其他的可以使用mongoose中的
countDocuments方法;
|
更新用户获取文章详情的方法getPostDetail,在文件src/api/ContentController.js中:
|
#接口对接与联调
前端添加接口:
|
前端页面添加请求:
|
完整的页面代码:
|
最终效果:

#RefreshToken机制
#需求分析
用户token不能设置太长的有效时间,这样可以提高接口部分的安全性。

说明:
- 登录请求/login成功之后会响应token&refreshToken;
- token的过期时间比较短,比如1小时到4小时,refreshToken的过期时间比token要长,比如1天或者7天;
- 用户在登录之后,所有的鉴权的接口带上token;
- 如果token过期,则使用refreshToken进行请求/login/refresh接口,请求新的token;
- 如果refreshToken未过期,则返回新token;
- 如果refreshToken过期,则返回401;
#创建refreshToken接口
添加src/routes/modules/loginRouter.js路由:
|
调整登录接口src/api/LoginController.js:
|
#前端页面逻辑
- 错误拦截中,针对 401的情况进行单独处理;
- 返回401之后,使用新的request实例请求refreshToken的接口;
- 请求成功之后,更新本地的token及缓存;
- 使用新的Token重新发发起旧的请求;
#登录失败后处理缓存
在src/store/index.js中新增关于清除token及用户信息的actions:
|
最终测试与效果:

#封装Simple.js
simple.js封装新的request补全,用于在默认实例请求401的情况下,发送refreshToken的请求:
|
#调整errorHandle 401逻辑
|
#请求拦截器
要注意请求拦截器中token的设置方式,可以使用config.header.Authorization = 'Bearer ' + token进行赋值:
|
#调整工具类request.js
原先Request.js封装中,errorhandle的部分只会返回错误的内容。
而我们在errorHandle中需要发送旧的请求,即需要:
- 原请求的相关配置
config,如url、method等; - 原请求的实例,上面有请求拦截器,使用token数据发送请求;
思路使用callback回调的方式,把参数传递出来:
|
完整的代码:
|
#文章详情
完成效果:

#页面布局与样式
基本的步骤
新增页面
/subcom-pkg/detail/detail配置
pages.json- 调整页面的样式与接口
配置pages.json
|
页面样式与基础逻辑
|
App.vue页面中添加公共样式:
|
调整api接口:
|
#长屏适配方案
安全区域指的是一个可视窗口范围,处于安全区域的内容不受圆角(corners)、齐刘海(sensor housing)、小黑条(Home Indicator)影响,如下图蓝色区域:

也就是说,我们要做好适配,必须保证页面可视、可操作区域是在安全区域内。
解决方案env() 和 constant():
iOS11 新增特性,Webkit 的一个 CSS 函数,用于设定安全区域与边界的距离,有四个预定义的变量:
- safe-area-inset-left:安全区域距离左边边界距离
- safe-area-inset-right:安全区域距离右边边界距离
- safe-area-inset-top:安全区域距离顶部边界距离
- safe-area-inset-bottom:安全区域距离底部边界距离
这里我们只需要关注 safe-area-inset-bottom 这个变量,因为它对应的就是小黑条的高度(横竖屏时值不一样)。
注意:当 viewport-fit=contain 时 env() 是不起作用的,必须要配合 viewport-fit=cover 使用。对于不支持env() 的浏览器,浏览器将会忽略它。
在这之前,笔者使用的是 constant(),后来,官方文档加了这么一段注释(坑):
The env() function shipped in iOS 11 with the name constant(). Beginning with Safari Technology Preview 41 and the iOS 11.2 beta, constant() has been removed and replaced with env(). You can use the CSS fallback mechanism to support both versions, if necessary, but should prefer env() going forward.
这就意味着,之前使用的 constant() 在 iOS11.2 之后就不能使用的,但我们还是需要做向后兼容,像这样:
|
注意:env() 跟 constant() 需要同时存在,而且顺序不能换。
更详细说明,参考文档: Designing Websites for iPhone X(opens new window)
#页面分享设置
与页面分享相关的uniapp的API有两个:
- uni.share(OBJECT) (opens new window)—— 针对于App分享到微信、QQ、微博
- onShareAppMessage(OBJECT) (opens new window)—— 针对于小程序的页面的分享
小程序中用户点击分享后,在 js 中定义 onShareAppMessage 处理函数(和 onLoad 等生命周期函数同级),设置该页面的分享信息。
- 用户点击分享按钮的时候会调用。这个分享按钮可能是小程序右上角原生菜单自带的分享按钮,也可能是开发者在页面中放置的分享按钮(
<button open-type="share">); - 此事件需要 return 一个Object,用于自定义分享内容。
微信小程序平台的分享管理比较严格,请参考 小程序分享指引 (opens new window)。
在详情页面中加入onShareAppMessage方法:
|
目前,微信官方没有提供正式的朋友圈分享的功能,只是在android设备上进行beta测试,参考说明:https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html#onShareTimeline
#富文本显示
#高亮方案一:自定义的highlight组件
步骤:
- 定义highlight.vue组件;
- 安装prismjs库,并使用tokenize方法进行拆分html字符串;
- 使用normalize库进行转换为对象数组;
安装依赖:
|
自定义的highlight.vue组件
|
#高亮方案二:wxParse
效果展示:

- 1.将
prism.css重命名为prism.wxss - 2.复制
prism.js和prism.wxss至utils文件夹下 - 3.替换
prism.wxss中的code[class*="language-"]为.wxParse-code[class*="language-"] - 4.
wxParse.wxss中引用prism.wxss
|
- 5.新增
highlight.js高亮工具类(代码放置在wxParse文件夹下)
|
- 6.在
wxParse文件夹下的html2json.js中引用highlight.js工具类的highligh高亮函数
|
参考链接 :
https://blog.sunriseydy.top/technology/server-blog/wordpress/wordpress-miniapp-code-highlight
https://blog.csdn.net/qq_41107410/article/details/89042212
#安全域名相关
微信小程序的安全域名必须是HTTPS,同时必须是在国内ICP备案的域名,本章来介绍如何使用acme.sh+nginx配置https与申请SSL证书。
#SSL证书申请
#前置准备
申请域名:阿里云、腾讯云等各家云服务商;
有一台云主机,有公网的IP,最好进行过备案;如果没有国内的服务器,也可以注册一台国外的服务器,地址1:搬瓦工 (opens new window),地址2:vultr (opens new window);
云主机的操作系统:Centos 7+(以下所有演示内容,均为Centos 7)或者Ubuntu 16LTS+;
DNS解析:DNSpod,cloudflare;
配置域名进行解析:

使用A记录,配置自己的域名 -> 自己的云主机IP
使用ping命令检查 是否已经网络通了:

#整体架构
配置架构图:

#基本步骤
借助acme.sh (opens new window)进行申请SSL证书,以便微信接口部分的使用,下面演示的是DNSpod进行证书申请的过程。
主要步骤:
前置:使用ssh命令连接到服务器 ->
安装 acme.sh
生成证书 ——推荐DNS的方式(opens new window)
支持的DNS列表:https://github.com/acmesh-official/acme.sh/tree/master/dnsapi(opens new window)
copy 证书到 nginx/apache 或者其他服务
更新证书
更新 acme.sh
#acme.sh安装
安装很简单, 一个命令:
|

再次打开一个新的ssh终端,使用crontab -e(Centos)查看acme.sh自动创建的定时任务:

输入i进入编辑模式,调整如上图所示,使用:wq退出。
PS:上面的配置是每天的0点23分去执行一次脚本。
#配置DNS密钥
下面介绍了DNSpod、阿里云、Cloudflare的配置过程,大家可以根据自己的云服务商选择,过程类似。
#DNSpod
选择密钥管理:

创建密钥

记录下来,后续使用:

配置密钥:
|
#阿里云
点击:https://ak-console.aliyun.com/#/accesskey (opens new window),申请密钥:

选择编程访问:

生成accessKey与密钥:

配置过程:
|
#Cloudflare

配置过程:
|
#产生证书
Dnspod:
acme.sh --issue --dns dns_dpi -d example.com -d www.example.com阿里云:
acme.sh --issue --dns dns_ali -d example.com -d www.example.comCloudflare:
acme.sh --issue --dns dns_cf -d example.com -d www.example.com
推荐域名通配符的写法:example.com 与 *.example.com,那么二级子域名,全部都可以使用https。
过程1:

上面的图片是展示出在对域名进行校验。
过程2:

证书申请成功,证书所在位置。
|
#Nginx配置
#前置准备

步骤:
- 安装docker,docker-compose -> nginx
- docker-compose来启动nginx
- 创建一个https docker网络 -> 共享给github, jenkins 等需要https的服务
- 配置
nginx.conf配置文件,创建conf.d文件目录,可以创建虚拟域名vhost.conf文件 - 使用证书安装命令安装ssl证书 -> 到指定的目录
docker-compose up -d运行nginx容器 -> 测试cron定时任务脚本
#证书安装命令
Apache配置:
|
Nginx配置:
|
--reloadcmd可以指定docker容器进行重启,或者是指定运行shell脚本。比如:
--reloadcmd "docker restart some-nginx"
#dhparam.pem证书
创建一个目录:/home/keys
|
把dhparam.pem复制到个人SSL证书安装相同的目录/home/keys。
#docker配置
手动创建网络
docker network create https
查看网络
docker network ls检查网络的信息
docker network inspect https创建
docker-compose.yml文件
|
#nginx.conf配置
|
#安装证书启动nginx

测试Nignx的配置文件:
|

#课程福利nginx配置
nginx的配置文件地址:github(opens new window)
#场景一:配置博客应用
添加宿主机的docker目录映射:
|
nginx的配置文件 conf.d/vhost.conf:
|
检测ssl网站的评级 myssl.com:

#场景二:配置小程序API接口
步骤:
- api -> 线上 -> nginx进行代理 ;
- api部署到线上的服务器;
- api服务可以被nginx访问到;
- 域名 + https形式去访问 -> 提供api服务
- 配置nginx的vhost配置 + 配置域名解析dns
- wx.yourdomain.com -> 指向服务器的IP
- 重启nginx容器让vhost的配置生效 -> 测试服务
小程序的docker-compose文件:
|
可选方案:提供Mongo、redis环境的docker-compose.yml文件:
|
MongoDB的初始化文件init.d/mongo-init.sh文件:
|
手动部署API服务:
使用docker命令进行docker镜像的构建
推送镜像或者使用手动方式导入镜像
保存(Save)
# 保留原镜像的名称和标签
docker save <IMAGE NAME>:<IMAGE TAG> > save.tar
# 不保留原镜像的基本信息,加载load后需执行tag命令重命名none镜像
docker save <IMAGE ID> > save.tar示例:
docker save elasticsearch:7.1.1 > elasticsearch-7.1.1.tar
# 或
docker save b0cb1543380d > elasticsearch-7.1.1.tar加载(Load)
docker load < save.tar示列:
docker load < elasticsearch-7.1.1.tar配置nginx文件
/home/nginx/conf.d/ws.conf:upstream target-server {
server api_online:3001 fail_timeout=0;
}
# listen on HTTP2/SSL
server {
listen 443 ssl http2;
server_name ws.wayearn.com;
# ssl certs from letsencrypt
ssl_certificate /home/acme/fullchain.pem;
ssl_certificate_key /home/acme/key.pem;
# dhparam.pem
ssl_dhparam /home/acme/dhparam.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4';
ssl_prefer_server_ciphers on;
location / {
proxy_set_header X-Forwarded-Ssl on;
proxy_redirect off;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://target-server;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'Upgrade';
}
}
# redirect HTTP and handle let's encrypt requests
server {
listen 80;
server_name ws.wayearn.com;
# send everything else to HTTPS
rewrite ^(.*)$ https://$host$1 permanent;
}添加dns解析

导入镜像,启动api服务:

使用
docker-compose up -d启动服务:
重启nginx服务:
docker restart some-nginx网络互访:
docker network inspect https
#排查问题
- 常见的防火墙问题:
|
- 云服务商的放行策略:
华为云示例:

腾讯云:

#小程序接口联调
nginx启动之后,就可以访问
https://域名了导出测试数据库
docker exec -it mongodb mongodump -u test -d testdb -o /tmp/使用docker cp命令来复制目录:
docker cp <容器id>:/tmp/testdb /tmp/本地目录导入数据库
上传上面的本地目录的文件到线上的服务器。
mongodb恢复数据库的命令:
docker cp /home/docker/testdb <容器id>:/tmp/
docker exec -it mongodb mongorestore -u test -d testdb /tmp/testdb
请求测试:

微信小程序配置安全域名
开发 -> 开发设置 -> 安全域名

添加域名:

下面即可以在微信开发者工具中进行测试了,取消不校验合法域名:

#订阅消息
#介绍
消息能力是小程序能力中的重要组成,我们为开发者提供了订阅消息能力,以便实现服务的闭环和更优的体验。
- 订阅消息推送位置:服务通知
- 订阅消息下发条件:用户自主订阅
- 订阅消息卡片跳转能力:点击查看详情可跳转至该小程序的页面

#消息类型
1. 一次性订阅消息
一次性订阅消息用于解决用户使用小程序后,后续服务环节的通知问题。用户自主订阅后,开发者可不限时间地下发一条对应的服务消息;每条消息可单独订阅或退订。
2. 长期订阅消息
一次性订阅消息可满足小程序的大部分服务场景需求,但线下公共服务领域存在一次性订阅无法满足的场景,如航班延误,需根据航班实时动态来多次发送消息提醒。为便于服务,我们提供了长期性订阅消息,用户订阅一次后,开发者可长期下发多条消息。
目前长期性订阅消息仅向政务民生、医疗、交通、金融、教育等线下公共服务开放,后期将逐步支持到其他线下公共服务业务。
#使用步骤
步骤一:获取模板 ID
在微信公众平台手动配置获取模板 ID: 登录 https://mp.weixin.qq.com (opens new window)获取模板,如果没有合适的模板,可以申请添加新模板,审核通过后可使用。

步骤二:获取下发权限
详见小程序端消息订阅接口 wx.requestSubscribeMessage(opens new window)
步骤三:调用接口下发订阅消息
详见服务端消息发送接口 subscribeMessage.send(opens new window)
注意事项:用户勾选 “总是保持以上选择,不再询问” 之后,下次订阅调用 wx.requestSubscribeMessage 不会弹窗,保持之前的选择,修改选择需要打开小程序设置进行修改。
#封装订阅消息工具js
作用:
- 发起订阅请求;
- 判断用户是否开启了通知;
- 提示未开启通知的用户,并跳转设置页面;
|
#前端主动订阅
在App.vue中添加判断用户订阅逻辑:
|
在需要订阅的位置,加入订阅逻辑,比如在个人中心中,点击去登录时:
|
注意:订阅消息一次最多只可以订阅3条,所以需要加入一个splice方法截断未订阅的消息。
也可以自己在指定的业务部分,给
sub方法,传递指定的模板ID。
#订阅消息模板API
应用场景:
- 后台控制哪些消息可以被订阅;
- 当模板消息的id发生了变化时,如果保证订阅的准确性;
前端请求模板API:
|
调整App.vue中的模板IDs的判断逻辑,处理:
|
后端创建路由与接口:
路由:
|
接口:
|
#维护accessToken
后端发送订阅消息需要accessToken:
|
见官方文档:链接(opens new window)
由于acessToken的有效时间是2小时,而且每天微信请求的限制为2000次(链接 (opens new window)),如下图:

所以,需要自己定时维护accessToken:
创建
wxGetAccessToken方法:import log4js from '@/config/Log4j'
const logger = log4js.getLogger('error')
import { getValue, setValue } from '@/config/RedisConfig'
// flag 强制刷新,默认false - 不强制刷新
export const wxGetAccessToken = async (flag = false) => {
// https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
// 1.判断redis中是否有accessToken
// 2.有 & flag -> 则直接返回
// 3.没有 -> 请求新的token
let accessToken = await getValue('accessToken')
if (!accessToken || flag) {
try {
const result = await instance.get(`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${config.AppID}&secret=${config.AppSecret}`)
// console.log('🚀 ~ file: WxUtils.js ~ line 60 ~ wxGetAccessToken ~ result', result)
if (result.status === 200) {
await setValue('accessToken', result.data.access_token, result.data.expires_in)
accessToken = result.data.access_token
if (result.data.errcode && result.data.errmsg) {
logger.error(`wxGetAccessToken error${result.data.errcode} - ${result.data.errmsg}`)
}
}
} catch (error) {
logger.error(`wxGetAccessToken error: ${error.message}`)
}
}
return accessToken
}安装依赖
npm i cron配置定时任务:
// 每隔7200秒执行一次 刷新accessToken
import { CronJob } from 'cron'
import { wxGetAccessToken } from './WxUtils'
// Seconds: 0-59
// Minutes: 0-59
// Hours: 0-23
// Day of Month: 1-31
// Months: 0-11 (Jan-Dec)
// Day of Week: 0-6 (Sun-Sat)
const job = new CronJob('* * */1 * * *', () => {
wxGetAccessToken(true)
})
job.start()在入口文件处理,添加该文件:
import './common/Cron'
常见问题,如果不小心accessToken请求超限怎么办:

#后台发送订阅消息
发送订阅消息:官方文档(opens new window)
调用方式:
#HTTPS调用接口说明
|
请求参数:
| 属性 | 类型 | 默认值 | 必填 | 说明 |
|---|---|---|---|---|
| access_token / cloudbase_access_token | string | 是 | 接口调用凭证(opens new window) | |
| touser | string | 是 | 接收者(用户)的 openid | |
| template_id | string | 是 | 所需下发的订阅模板id | |
| page | string | 否 | 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转。 | |
| data | Object | 是 | 模板内容,格式形如 { “key1”: { “value”: any }, “key2”: { “value”: any } } | |
| miniprogram_state | string | 否 | 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版 | |
| lang | string | 否 | 进入小程序查看”的语言类型,支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN |
说明一下关于data:
例如,模板的内容为
|
则对应的json为
|
这里的data有内容限制(举例如下表),更多查看官方文档详情:
| 参数类别 | 参数说明 | 参数值限制 | 说明 |
|---|---|---|---|
| thing.DATA | 事物 | 20个以内字符 | 可汉字、数字、字母或符号组合 |
| number.DATA | 数字 | 32位以内数字 | 只能数字,可带小数 |
| letter.DATA | 字母 | 32位以内字母 | 只能字母 |
| symbol.DATA | 符号 | 5位以内符号 | 只能符号 |
| character_string.DATA | 字符串 | 32位以内数字、字母或符号 | 可数字、字母或符号组合 |
| time.DATA | 时间 | 24小时制时间格式(支持+年月日),支持填时间段,两个时间点之间用“~”符号连接 | 例如:15:01,或:2019年10月1日 15:01 |
| date.DATA | 日期 | 年月日格式(支持+24小时制时间),支持填时间段,两个时间点之间用“~”符号连接 | 例如:2019年10月1日,或:2019年10月1日 15:01 |
| amount.DATA | 金额 | 1个币种符号+10位以内纯数字,可带小数,结尾可带“元” | 可带小数 |
| phone_number.DATA | 电话 | 17位以内,数字、符号 | 电话号码,例:+86-0766-66888866 |
| car_number.DATA | 车牌 | 8位以内,第一位与最后一位可为汉字,其余为字母或数字 | 车牌号码:粤A8Z888挂 |
| name.DATA | 姓名 | 10个以内纯汉字或20个以内纯字母或符号 | 中文名10个汉字内;纯英文名20个字母内;中文和字母混合按中文名算,10个字内 |
| phrase.DATA | 汉字 | 5个以内汉字 | 5个以内纯汉字,例如:配送中 |
返回的 JSON 数据包
| 属性 | 类型 | 说明 |
|---|---|---|
| errcode | number | 错误码 |
| errmsg | string | 错误信息 |
errcode 的合法值
| 值 | 说明 |
|---|---|
| 40003 | touser字段openid为空或者不正确 |
| 40037 | 订阅模板id为空不正确 |
| 43101 | 用户拒绝接受消息,如果用户之前曾经订阅过,则表示用户取消了订阅关系 |
| 47003 | 模板参数不准确,可能为空或者不满足规则,errmsg会提示具体是哪个字段出错 |
| 41030 | page路径不正确,需要保证在现网版本小程序中存在,与app.json保持一致 |
#创建发送消息工具js
|
举例:
在用户登录之后,发送订阅消息通知:
|
#前后端联调
联调需要注意的点:
- 设置
miniprogram_state属性; - 使用真机进行调试 -> 配置局域网的访问的IP -> 让电脑与手机处于同一个网段;
- 检查发送订阅消息的数据格式、模板id、用户openid数据是否正确;
- 使用单步调试:发送订阅消息的接口,查看返回的errcode如果是0,说明发送成功;如果失败,则根据errcode来调整程序代码;

#内容安全
#微信安全检测
微信官方提供了可疑用户与危险接口的检查:

接口安全扫描:

这两个功能在后台即可操作,平坦可以交由运营小姐姐来进行操作一下。
#第三方内容安全推荐
- 各家云服务商:云盾 (opens new window)(阿里),易盾 (opens new window)(网易),天御 (opens new window)(腾讯)
- 第三方:图普 (opens new window)、国信网安、梵为科技(opens new window)
#文本内容安全
#整体思路
- 下载网络上的一些敏感词的数据库,使用正则进行匹配过滤一次,减少成本;
- 使用官方的限额的api对文本进行查验;
- 使用第三方的api对内容进行查验;
#官方介绍
检查一段文本是否含有违法违规内容,下面介绍的接口:官方链接(opens new window)
1.0 版本接口文档【点击查看】(opens new window)
应用场景举例:
- 用户个人资料违规文字检测;
- 媒体新闻类用户发表文章,评论内容检测;
- 游戏类用户编辑上传的素材(如答题类小游戏用户上传的问题及答案)检测等。 *频率限制:单个 appId 调用上限为 4000 次/分钟,2,000,000 次/天**
调用方式:
#工具js封装
请求地址
|
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 |
|---|---|---|---|---|
| access_token / cloudbase_access_token | string | 是 | 接口调用凭证(opens new window) | |
| version | string | 是 | 接口版本号,2.0版本为固定值2 | |
| openid | string | 是 | 用户的openid(用户需在近两小时访问过小程序) | |
| scene | number | 是 | 场景枚举值(1 资料;2 评论;3 论坛;4 社交日志) | |
| content | string | 是 | 需检测的文本内容,文本字数的上限为2500字 | |
| nickname | string | 否 | 用户昵称 | |
| title | string | 否 | 文本标题 | |
| signature | string | 否 | 个性签名,该参数仅在资料类场景有效(scene=1) |
返回的 JSON 数据包
| 属性 | 类型 | 说明 |
|---|---|---|
| errcode | number | 错误码 |
| errmsg | string | 错误信息 |
| trace_id | string | 唯一请求标识,标记单次请求 |
| result | object | 综合结果 |
| detail | array | 详细检测结果 |
逻辑:
- 用户只用传文本内容,其他的openid等信息可以不传,设置一个默认值,方便其他的平台使用;
- 使用正则匹配掉一些无用的内容;
- 长度判断,分2500词进行多次判断;
- 判断结果非pass的全部进入risky部分;
|
#自定义安全敏感词汇
在小程序管理后台,开发管理 -> 安全中心 -> 内容风控:

在这里添加的分值越高,越可能被屏蔽。
#测试接口工具js
加入accessToken失效判断
instance.interceptors.response.use(async (res) => {
const { data } = res
if (data.errcode === 40001) {
// 重新获取新的accessToken
const accessToken = await wxGetAccessToken(true)
const { url } = res.config
// 重新发起请求 -> res
if (url.indexOf('access_token') !== -1) {
const arr = url.split('?') // ?key=value&key1=value1... -> ['域名', 'key=value&key1=value1...']
const params = qs.parse(arr[1])
const newParams = {
...params,
access_token: accessToken
}
const newUrl = arr[0] + '?' + qs.stringify(newParams)
const config = { ...res.config, url: newUrl }
const result = await axios(config)
return result
}
}
return res
})有风险的词汇:

#图片内容安全
#整体思路
- 图片上传之后,创建tmp目录(使用make-dir (opens new window)),获取图片的基本信息;
- 判断图片的尺寸,如果超过了微信接口的分辨率要求750x1336,那需要使用sharp (opens new window)来进行压缩;
- nodejs侧对接微信接口需要使用formData数据类型,所以还需要安装form-data(opens new window)
- 最后还需要删除临时文件,使用del (opens new window)库,删除之前使用fs.access (opens new window)进行判断
#官方介绍
请求地址
|
请求参数
| 属性 | 类型 | 默认值 | 必填 | 说明 |
|---|---|---|---|---|
| access_token / cloudbase_access_token | string | 是 | 接口调用凭证(opens new window) | |
| media | FormData | 是 | 要检测的图片文件,格式支持PNG、JPEG、JPG、GIF,图片尺寸不超过 750px x 1334px |
返回的 JSON 数据包
| 属性 | 类型 | 说明 |
|---|---|---|
| errcode | number | 错误码 |
| errmsg | string | 错误信息 |
errcode 的合法值
| 值 | 说明 | 最低版本 |
|---|---|---|
| 0 | 内容正常 | |
| 87014 | 内容可能潜在风险 |
#工具js封装
安装依赖:
|
其中sharp (opens new window)需要配置加速:
|
工具js:
|
#测试接口工具js
注意:
- 自行找一下网上可疑的图片进行测试;
- errcode为0说明校验通过,反之不通过;
- 通过之后,删除图片临时目录中的文件;
- 一般上传图片的接口需要对接云上的存储,所以采用了本地缓存的方式校验图片;

#发贴评论功能
#创建分包
在uniapp中添加分包,减少主包体积,提升页面加载的性能:
新增页面,
/subcom-pkg/post/post加入基本的vue页面的结构配置
pages.json
pages.json中配置分包:
|
#表单校验
使用uview中的form (opens new window)表单组件:
|
加入表单校验:
- 配置
u-form中的model属性,ref用于校验整个表单; - 配置
u-form-item中的prop属性,配置对应的rules规则 - 在script的
onReady回调中配置this.$refs.uForm.setRules(this.rules)
|
#图片上传
#上传接口
上传formdata类型的数据,需要使用uni.uploadFile API,封装成一个promise谅,在complete中加入callback,方便后续的处理:
|
#页面结构
可以使用u-upload (opens new window)组件来轻松实现图片上传:
|
#完成效果
|
完成效果:

#发帖服务端逻辑
#图片上传
|
#发帖接口
调整整体的逻辑,删除验证码的逻辑部分,并添加内容安全校验:
|
添加路由:
|
#评论功能
#创建接口
|
#fixed底部定位问题
在小程序中,使用了fixed定位的底部元素可能被遮挡:
解决方案:
|
推荐:使用cursor-spacing,官方链接(opens new window)

如果不设置,默认是0,也是为什么会浮动不准确的原因。
#页面样式与事件
|
#评论服务端逻辑
#需求分析
- 用户是否被禁言
- 内容安全检查
- 评论 -> 发送订阅消息给用户
#评论接口
路由:
|
逻辑代码:
|
#发布上线
#分包机制
某些情况下,开发者需要将小程序划分成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载。

#普通分包
在构建小程序分包项目时,构建会输出一个或多个分包。
每个使用分包小程序必定含有一个主包。
所谓的主包,即放置默认启动页面/TabBar 页面,以及一些所有分包都需用到公共资源/JS 脚本;而分包则是根据开发者的配置进行划分。
在小程序启动时,默认会下载主包并启动主包内页面,当用户进入分包内某个页面时,客户端会把对应分包下载下来,下载完成后再进行展示。
目前小程序分包大小有以下限制:
- 整个小程序所有分包大小不超过 20M
- 单个分包/主包大小不能超过 2M
对小程序进行分包,可以优化小程序首次启动的下载时间,以及在多团队共同开发时可以更好的解耦协作。
#独立分包
独立分包是小程序中一种特殊类型的分包,可以独立于主包和其他分包运行。从独立分包中页面进入小程序时,不需要下载主包。当用户进入普通分包或主包内页面时,主包才会被下载。
独立分包属于分包的一种,普通分包的所有限制都对独立分包有效(2M大小)。独立分包中插件、自定义组件的处理方式同普通分包。
此外,使用独立分包时要注意:
独立分包中不能依赖主包和其他分包中的内容,包括 js 文件、template、wxss、自定义组件、插件等。
主包中的
app.wxss对独立分包无效,应避免在独立分包页面中使用app.wxss中的样式;App只能在主包内定义,独立分包中不能定义App,会造成无法预期的行为;与普通分包不同,独立分包运行时,
App并不一定被注册,因此getApp()也不一定可以获得App对象;独立分包中暂时不支持使用插件。
由于独立分包中无法定义
App,小程序生命周期的监听可以使用 wx.onAppShow (opens new window),wx.onAppHide (opens new window)完成。App上的其他事件可以使用 wx.onError (opens new window),wx.onPageNotFound (opens new window)监听。
应用场景:活动页面、登录注册相关页面…
大多数独立分包的场景,使用分包也可以很好的完成对应的功能,而且可以共享主包的样式。
#分包预加载
开发者可以通过配置,在进入小程序某个页面时,由框架自动预下载可能需要的分包,提升进入后续分包页面时的启动速度。
预下载分包行为在进入某个页面时触发,通过在 app.json 增加 preloadRule 配置来控制。
|
preloadRule 中,key 是页面路径,value 是进入此页面的预下载配置,每个配置有以下几项:
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| packages | StringArray | 是 | 无 | 进入页面后预下载分包的 root 或 name。__APP__ 表示主包。 |
| network | String | 否 | wifi | 在指定网络下预下载,可选值为: all: 不限网络 wifi: 仅wifi下预下载 |
说明:
network建议使用all;- 所有预下载的分包的总体积要小于2m,限额会打包自动校验;
不是必要,不是非常影响用户的交互体验,不要占用预下载分包的份额,不要使用大于40k的图片与资源。
#检查分包大小
- 使用HBuilder中的发布方式打包;
- 在详情中进行查看:
点击查看详情,用于排查哪些比较大的资源:

#基本流程

- 测试-检查-打包-配置
- 在开发工具中上传
- 管理后台中,提交审核,通过后发布
#成员说明
小程序成员管理包括对小程序项目成员及体验成员的管理。
- 项目成员:表示参与小程序开发、运营的成员,可登录小程序管理后台,包括运营者、开发者及数据分析者。管理员可在“成员管理”中添加、删除项目成员,并设置项目成员的角色。
- 体验成员:表示参与小程序内测体验的成员,可使用体验版小程序,但不属于项目成员。管理员及项目成员均可添加、删除体验成员。
不同项目成员拥有不同的权限,从而保证小程序开发安全有序。
| 权限 | 运营者 | 开发者 | 数据分析者 |
|---|---|---|---|
| 开发者权限 | √ | ||
| 体验者权限 | √ | √ | √ |
| 登录 | √ | √ | √ |
| 数据分析 | √ | ||
| 微信支付 | √ | ||
| 推广 | √ | ||
| 开发管理 | √ | ||
| 开发设置 | √ | ||
| 暂停服务 | √ | ||
| 解除关联公众号 | √ | ||
| 腾讯云管理 | √ | ||
| 小程序插件 | √ | ||
| 游戏运营管理 | √ |
配置路径:登录https://mp.weixin.qq.com/,选择管理 -> 成员管理 -> 添加项目成员,最多100个;体验成员->最多100个(已认证);

#版本说明
| 权限 | 说明 |
|---|---|
| 开发版本 | 使用开发者工具,可将代码上传到开发版本中。 开发版本只保留每人最新的一份上传的代码。 点击提交审核,可将代码提交审核。开发版本可删除,不影响线上版本和审核中版本的代码。 |
| 体验版本 | 可以选择某个开发版本作为体验版,并且选取一份体验版。 |
| 审核中版本 | 只能有一份代码处于审核中。有审核结果后可以发布到线上,也可直接重新提交审核,覆盖原审核版本。 |
| 线上版本 | 线上所有用户使用的代码版本,该版本代码在新版本代码发布后被覆盖更新。 |
#注意事项
#配置BaseURL
|
uniapp内置的打包工具即是webpack
说明:
uniapp中,使用发布方式打包,即
process.env.NODE_ENV会自动设置成production;在上线之前,请确保配置了api后台项目,并申请了域名,配置好了HTTPS服务;
在小程序后台中,添加安全域名:
#使用HBuilder生产打包方式
一定要注意,在uniapp中开发的时候,启动的是调试进程,这时的代码中有很多调试代码,也未进行压缩。
步骤:
打开项目的
pages.json文件;点击顶部的菜单:
发行->小程序-微信
#打包上线
#小程序打包
见 检查分包大小,注意这里的项目名称并非小程序的项目名称。
小程序的名称在注册小程序的时候,就已经确定下来了,这里只是一个项目别名。
请检查小程序的AppID。
#上传代码

上传代码是用于提交体验或者审核使用的。
点击开发者工具顶部操作栏的上传按钮,填写版本号以及项目备注,需要注意的是,这里版本号以及项目备注是为了方便管理员检查版本使用的,开发者可以根据自己的实际要求来填写这两个字段。

#审核与发布
#审核版本
上传成功之后,登录小程序管理后台 (opens new window)- 开发管理 - 开发版本 就可以找到刚提交上传的版本了。
可以将这个版本设置 体验版 或者是 提交审核。

然后点击下一步:

说明:
- 加急的情况:与用户的钱有关,与平台的稳定有关,与自身的利益有关,才加急——慎用;
- 上面一般要写一个注释,说明版本的升级情况;
审核中:

#关于灰度发布
审核通过后,需要在页面中点击发布,选择全量发布或者指定用户进行发布(灰度测试)。
点击发布后,即可发布小程序。小程序提供了两种发布模式:全量发布和分阶段发布。全量发布是指当点击发布之后,所有用户访问小程序时都会使用当前最新的发布版本。
分阶段发布是指分不同时间段来控制部分用户使用最新的发布版本,分阶段发布我们也称为灰度发布。一般来说,普通小程序发布时采用全量发布即可,当小程序承载的功能越来越多,使用的用户数越来越多时,采用分阶段发布是一个非常好的控制风险的办法。
#支付专题
支付作为工作中最常见的一个核心业务场景,前端同学其实需要做的东西有限,基本的流程、逻辑与难点都是在服务端。为了让前端同学,也能自己后续开发支付功能(Node.js),在本篇介绍了详细的从企业主体 -> 支付必要条件 ->微信小程序支付完整的闭环。
支付业务支撑:
- 商城应用
- 金融产品
- 充值应用(会员、点券)
- …
支付应用场景:
- H5支付 -> 扫码、跳转App
- 小程序
- 移动App
支付的技术难点:
- 多平台(微信、支付宝、银联)
- 安全性(HTTPS、全流程日志、交易备案、数据安全性如灾备…)
常见的问题:
- 支付的开发流程,是不是前端不用管?只用调接口——是的,举例:小程序前端,其实上只是wx.requestPayment调起支付即可;
- 支付功能的开通容易吗?——容易,但是针对于个人,无法开通微信支付;个人能开通吗?——不行,但是可以使用第三方服务,比如JSAPI;
- 服务端对接常见的支付功能的流程是怎样的?——基本步骤如下:
- 申请微信小程序账号
- 微信小程序认证
- 申请商户平台账号
- 信小程序关联商户号
- 接入微信支付
#企业注册与税务
开办企业需要注意:
财税问题,如果零报税,则需要企业年报每年需要申报。
开办企业不是玩,一定要负责任。而且,不良的企业负债,可能会影响到个人的征信,最终无法贷款、乘车等。
股份问题:如果多人开办,千万不要对半
多人入股不能均分,不能0资金入股,分股用期权;
设计退股机制,分红机制,以及债务问题解决办法;
股权主要的目的是激励;
利益分配问题:天下熙熙皆为利来,天下攘攘皆为利往;
无论是支付宝还是微信,能够支持的支付主体只有企业、个体工商户和政府及事业单位等,共同点是非个人。
个人开发者,如果需要接入支付功能,也可以选择第三方服务商:
- PayJS:开通费用300+手续费0.38%+服务费2%
- PaysApi:月付手续费30-199+单笔费率从0.3~0.1%不等
- PayBob:开通费用300+手续费0.38%+服务费1-2%
- xorpay:月付手续费0-60+手续费0.38%+单笔手续费1.2%~0.5%不等
了解企业的注册流程及税务相关的知识,有助于学习支付相关的内容,扩展知识面。
企业注册比较麻烦的地方:
- 设立登记可能要跑几躺
- 税务登记 + 银行开户 折腾个10几天
- 报税:有季报+年报(工商年报次年6月前、汇缴清算不定)
但是,开办企业也是有好处的,举例:
- 作为团队出去接项目
- 大多数网上的服务针对的是企业
- 国家政府对于小微企业现在有大力的扶持
#注册企业流程

企业注册流程中,需要注意的点:
准备工作(场地、人员、名称)
公司查名各地有各地的查询网址或者在工商管理部门办事大厅进行现场查重(因为可能会重名,所以需要多准备几个)
湖北企业查名查重:http://scjg.hubei.gov.cn/ICPSP/newNamecheck/nameCheck.action
企业经营范围查询网址:https://jyfwyun.com/
提交资料(章程、住所证明)
可以在当地的区(县)政务中心领取材料 或者在其网站上下载对应的模板文件,自行打印与复印。
办理税务+银行开户
当办理完企业登记,并核发营业执照之后,可以输税务与企业银行开户。
各家银行的开户费用大体相同,只是服务可能不一样,大家可以选择几家对比一下。推荐:招商与中兴。
选择银行需要注意:
- 服务问题;
- 便利性——不要选择一家离自己办公点或者家很远的地方;
#个人工商户与企业区别
共同点:
- 流程:个体要记账、交税、年检、审批,也会被抽查
- 税费:与小微企业无异
不同点:
- 债务:个体负责到底-无限,公司申请破产保护-有限责任
- 规模:个体8人不能上市,公司可以设立分机构、上市
- 业务:个体不能做进出口业务
- 经营 :个体限制经营范围
- 税费:税种不一样,征收方式不一样
#自己注册vs代注册
自己注册:
优点:
- 没有费用
缺点:
- 需要花时间
- 需要了解整体流程
- 需要去税务、银行
代注册:
优点:
- 省时间
- 省事(办证、税务、企业银行、注册地、资料准备一条龙)
缺点:
- 费用不等,从3000-6000,甚至更多;
- 后续可能会有代账、年租费用等;
- 开票&办税也会收费;
#报税&开票
大多数企业会找一个代账会计或者找一个代账公司,而企业前期是无需会计的,完全可以零报税,操作流程非常的简单。
如果不清楚报税的流程,可以在税务机关处进行现场报税。
代账费用:从200元到300元/月不等。
找了代账的企业需要会看如下的几个表格,了解概念:
- 利润表:你赚了多少钱!
- 资产负债表:有没有欠你的钱、你欠的钱,余下的钱
除了找代账公司以外,Brain更推荐自己在前期进行记账与报税,流程不复杂,而且软件非常的智能,只用填入自己的收入与支出,财务软件可以自动形成报表。
财务软件推荐:
- 金碟
- 用友
云平台推荐:
- 柠檬云
季度+年度报税:3个月报季报,照着软件填
#企业日常的开销
主要分为如下几类:
- 资产:办公设备如打印机、办公桌椅、空调等
- 费用:租赁场地、水+电+网
- 费用:人员(会计、开发、产品等)
- …
从上面的分类来看,注册企业没有什么费用,反而是维持一个企业的运转是需要大量费用的。大家在准备企业开办之前,需要有一定的准备。
不打没有准备的仗,也不要什么都准备好了,才开始!!
#支付前置必要条件
下面以微信支付为例,来介绍,开发支付功能需要准备的前置条件:
- 选择合适的主体&场景(企业服务号)微信(支付宝)认证;
- 域名+公网IP+HTTPS;
- 云服务器ICP备案;
微信支付:
- 主体问题:订阅号非媒体号无法支付,推荐企业服务号;
- 小程序:必须HTTPS + ICP备案;
- 开通商户号,也需要企业主体,与公众号&小程序同一主体;
支付宝支付:
主体问题:企业主体;
行业类目及资质要求,文档 (opens new window);
而且对于实体店铺审核要严格一些。

说明:
- 关于ICP备案,可以参考:阿里云 (opens new window)、腾讯云 (opens new window)(前置条件:域名、云服务器);
- HTTPS:必须要有域名,必须要有一台云服务器;
- 微信商户号,对应的网址:https://pay.weixin.qq.com/
#小程序支付流程
#开发技巧
- 技术问题,先读文档,查找官方的社区;
- 比较主流的支付方案,可以搜索一下有没有Node.js侧的npm包,例如:wechatpay-axios-plugin (opens new window)是一个非常不错的支持v2/v3的npm包,ts风格;
- 问stackoverflow、知乎、csdn等;
- 最后,才考虑自己开发轮子;
如果是学习,也可以从0到1的开发,了解整个支付的流程。
#流程图
学会看时序图:
- 最上面的是角色
- 从左往右看,按照箭头的方向走
- 从上往下看,是时间关系流程的流转

重点步骤说明:
步骤3:用户下单发起支付,商户可通过JSAPI下单 (opens new window)创建支付订单。
步骤8: 用户可通过小程序调起支付API (opens new window)调起微信支付,发起支付请求。
步骤15:用户支付成功后,商户可接收到微信支付支付结果通知支付通知API (opens new window)。
步骤20:商户在没有接收到微信支付结果通知的情况下需要主动调用查询订单API (opens new window)查询支付结果。
#搭建开发环境
#支付准备

按照上面的流程,准备相关的开发环境,参考指引:官方链接(opens new window)
#配置Https+域名解析
说明:
前置的章节有介绍HTTPS介绍,文章
域名解析以阿里云为例:

#配置API密钥
商户平台 (opens new window)-> 账户中心 -> API安全,分别配置API商户证书 + APIv3密钥

#配置frp内网穿透
为了方便测试微信支付通知,有两种方案:
上传API服务到测试服务器:

使用本地的API服务,需要使用frp工具把远程的通知转发到本地:

配置过程如下:
下载frp包到本地,见release (opens new window)(darwin是mac系统、windows、Linux要分arm与amd);
SSH到远程服务端,新建配置文件
/home/frp/frps.ini:[common]
bind_port = 10010
vhost_https_port = 443在服务器上运行frps,使用docker镜像:
docker run --restart=always --network host -d -v /home/frp/frps.ini:/etc/frp/frps.ini --name frps snowdreamtech/frps下载let’s encrypt创建的SSL证书 -> 一般在acme生成的目录中->放置到本地解压的frp目录中;

在本地创建
frpc.ini文件[common]
server_addr = 121.36.194.226
server_port = 10010
[test_htts2http]
type = https
custom_domains = test1.toimc.com
plugin = https2http
plugin_local_addr = 127.0.0.1:3000
# HTTPS 证书相关的配置
plugin_crt_path = ./fullchain.pem
plugin_key_path = ./key.pem
plugin_host_header_rewrite = 127.0.0.1
plugin_header_X-From-Where = frp
; test1.toimc.com -> vhost_https_port 443
; test1.toimc.com:443 -> frp -> 127.0.0.1:3000使用frpc运行该配置文件:
chmod +x frpc
./frpc -c ./frpc.ini运行成功的提示:

如果运行失败,可以在官方网站 (opens new window)上查询失败的原因:
#APIv3 vs APIv2
为了在 保证支付 安全的前提下,带给商户 简单、一致且易用的开发体验,我们推出了全新的微信支付API v3。
相较于之前的微信支付API,主要区别是:
- 遵循统一的REST 的设计风格
- 使用JSON作为数据交互的格式,不再使用XML
- 使用基于非对称密钥的SHA256-RSA的数字签名算法,不再使用MD5或HMAC-SHA256
- 不再要求HTTPS客户端证书
- 使用AES-256-GCM,对回调中的关键信息进行加密保护
APIv3的文档:官方链接(opens new window)
两个接口的对接图:
| V3 | 规则差异 | V2 |
|---|---|---|
| JSON | 参数格式 | XML |
| POST、GET 或 DELETE | 提交方式 | POST |
| AES-256-GCM加密 | 回调加密 | 无需加密 |
| RSA 加密 | 敏感加密 | 无需加密 |
| UTF-8 | 编码方式 | UTF-8 |
| 非对称密钥SHA256-RSA | 签名方式 | MD5 或 HMAC-SHA256 |
推荐:在没有接触v2的情况下,直接上手v3;如果有老旧业务,也可以对接到v3,或者不动原有的业务,直至v2的证书快过期,需要更换时,再切换到v3。
#JSAPI统一下单
#签名生成
商户可以按照下述步骤生成请求的签名,微信支付API v3 要求商户对请求进行签名,微信支付会在收到请求后进行签名的验证。
如果签名验证不通过,微信支付API v3将会拒绝处理请求,并返回401 Unauthorized。
签名生成的步骤有:
构造签名串
签名串一共有五行,每一行为一个参数。行尾以
\n(换行符,ASCII编码值为0x0A)结束,包括最后一行。如果参数本身以\n结束,也需要附加一个\n。HTTP请求方法\n
URL\n
请求时间戳\n
请求随机串\n
请求报文主体\n计算签名值
使用商户私钥对待签名串进行SHA256 with RSA签名,并对签名结果进行Base64编码得到签名值,示例:
$ echo -n -e \
"GET\n/v3/certificates\n1554208460\n593BEC0C930BF1AFEB40B4A08C8FB242\n\n" \
| openssl dgst -sha256 -sign apiclient_key.pem \
| openssl base64 -A
uOVRnA4qG/MNnYzdQxJanN+zU+lTgIcnU9BxGw5dKjK+VdEUz2FeIoC+D5sB/LN+nGzX3hfZg6r5wT1pl2ZobmIc6p0ldN7J6yDgUzbX8Uk3sD4a4eZVPTBvqNDoUqcYMlZ9uuDdCvNv4TM3c1WzsXUrExwVkI1XO5jCNbgDJ25nkT/c1gIFvqoogl7MdSFGc4W4xZsqCItnqbypR3RuGIlR9h9vlRsy7zJR9PBI83X8alLDIfR1ukt1P7tMnmogZ0cuDY8cZsd8ZlCgLadmvej58SLsIkVxFJ8XyUgx9FmutKSYTmYtWBZ0+tNvfGmbXU7cob8H/4nLBiCwIUFluw==
第一步比较好实现:
|
第二步的RSA的算法实现思路:
- 找一下微信社区有没有类似实现
- 找一下网上有没有类似实现
- 找一下有没有npm开源包
- 找一下stackoverflow或者百度
- …
如果 以上都没有,那么就要自己造轮子了。但是这样的场景少之有少,哈哈,还轮不上大家自己上。
|
然后使用上面合成的message进行签名即可:
|
如何验证呢?
方案一:
Linux或者mac直接使用openssl来进行验证
|
方案二:
使用老师给大家准备的docker镜像进行验证lw96/libressl:
|
#Authentication头部密钥串
微信支付商户API v3要求请求通过HTTP Authorization头来传递签名。Authorization由认证类型和签名信息两个部分组成。
下面我们使用命令行演示如何生成签名。
|
具体组成为:
1.认证类型,目前为WECHATPAY2-SHA256-RSA2048
2.签名信息
- 发起请求的商户(包括直连商户、服务商或渠道商)的商户号
mchid - 商户API证书 (opens new window)
serial_no,用于声明所使用的证书 (opens new window)(管理员账号登录微信商户管理后台,在API安全里面点击查看证书可以获取。) - 请求随机串
nonce_str - 时间戳
timestamp - 签名值
signature - 注:以上五项签名信息,无顺序要求。
Authorization 头的示例如下:(注意,示例因为排版可能存在换行,实际数据应在一行)
|
最终我们可以组一个包含了签名的HTTP请求了。
|
代码示例:
|
#接口说明
请求URL:https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi
请求方式:POST
形成随机的商户订单号:
|
最终,统一订单的接口:
|
#用户支付
#支付步骤
前端步骤:
- 用户下单 -> 发起请求,让后端生成支付参数
- 用户支付 -> wx.requestPayment调起支付
后端步骤:
- 使用JSAPI统一下单,产生的prepay_id(预支付id)
- 构造签名串 -> 计算签名
- 返回前端支付参数
#准备支付参数
第一步:
造签名串
|
参与签名字段及格式:
|
第二步:
计算签名:
|
参数:
| 参数名 | 变量 | 类型[长度限制] | 必填 | 描述 |
|---|---|---|---|---|
| 时间戳 | timeStamp | string[1,32] | 是 | 当前的时间,其他详见时间戳规则 (opens new window)。 示例值:1414561699 |
| 随机字符串 | nonceStr | string[1,32] | 是 | 随机字符串,不长于32位。 示例值:5K8264ILTKCH16CQ2502SI8ZNMTM67VS |
| 订单详情扩展字符串 | package | string[1,128] | 是 | 小程序下单接口返回的prepay_id参数值,提交格式如:prepay_id=*** 示例值:prepay_id=wx201410272009395522657a690389285100 |
| 签名方式 | signType | string[1,32] | 是 | 签名类型,默认为RSA,仅支持RSA。 示例值:RSA |
| 签名 | paySign | string[1,512] | 是 | 签名,使用字段appId、timeStamp、nonceStr、package计算得出的签名值 示例值 |
#后端接口开发
创建路由:
|
创建wxOrder下单方法:
|
#前端模拟下单&支付
|
#订单查询
用户支付成功后,需要接受微信平台的被动通知或者是主动查询订单的支付状态。
原因:可能用户支付成功后,微信后台已经给了用户侧反馈,但是由于网络问题,商户平台可能未收到通知。

#微信主动通知
frp转发通知,只需要创建对应的接口,然后在JSAPI统一下单的接口中指定回调的域名即可。
请求方式:POST
回调URL:该链接是通过基础下单接口中的请求参数“notify_url”来设置的,要求必须为https地址。请确保回调URL是外部可正常访问的,且不能携带后缀参数,否则可能导致商户无法接收到微信的回调通知信息。回调URL示例: “https://pay.weixin.qq.com/wxpay/pay.action”
通知规则
用户支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理该消息,并返回应答。
对后台通知交互时,如果微信收到商户的应答不符合规范或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。(通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m)
通知报文
支付结果通知是以POST 方法访问商户设置的通知url,通知的数据以JSON 格式通过请求主体(BODY)传输。通知的数据包括了加密的支付结果详情。
下面详细描述对通知数据进行解密的流程:
- 用商户平台上设置的APIv3密钥【微信商户平台 (opens new window)—>账户设置—>API安全—>设置APIv3密钥】,记为key;
- 针对resource.algorithm中描述的算法(目前为AEAD_AES_256_GCM),取得对应的参数nonce和associated_data;
- 使用key、nonce和associated_data,对数据密文resource.ciphertext进行解密,得到JSON形式的资源对象;
注: AEAD_AES_256_GCM算法的接口细节,请参考rfc5116 (opens new window)。微信支付使用的密钥key长度为32个字节,随机串nonce长度12个字节,associated_data长度小于16个字节并可能为空。
#主动通知后端接口
解密方法:
|
创建接口:
|
接口详情wxNotify:
|
收到订单通知后,需要保存通知中的状态与数据(微信订单号)。
#订单查询接口
有两种方案:
- 微信支付订单号查询
- 商户订单号查询(一般采用这种)
请求URL: https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}
请求方式:GET
请求参数
| 参数名 | 变量 | 类型[长度限制] | 必填 | 描述 |
|---|---|---|---|---|
| 直连商户号 | mchid | string[1,32] | 是 | query 直连商户的商户号,由微信支付生成并下发。 示例值:1230000109 |
| 商户订单号 | out_trade_no | string[6,32] | 是 | path 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一。 特殊规则:最小字符长度为6 示例值:1217752501201407033233368018 |
示例:
|
这里要特别注意,url中有路径参数与query参数
后端代码:
|
至此,完成的小程序支付的一个完整的闭环。
关于退款与退款通知与下单&订单通知是类似,不再提供示例。
