微信小程序踩坑指南【一】

最近由于公司业务一直在作微信小程序的项目,趁此机会将最近踩过的一些坑总结记录下。前端

微信小程序登录相关

登陆流程时序

  1. 前端调用 wx.login(),获取临时登陆凭证 code
  2. 经过 wx.request()将 code 发给服务器(须要后端建立接口接收 code)
  3. 后端进行登陆凭证校验,入参为(appid,secret,js_code,grant_type)

附常见名词解释:ios

appid 小程序惟一标识 secret 小程序的 app secret js_code 登陆时获取的 code grant_type 填写为 authorization_codenpm

  1. 登录凭证校验经过,从微信服务器换取 openid 和 session_key

openid 用户惟一标识 session_key 会话密钥json

openid 是用户惟一标识,但不建议直接用作后端服务器的各标示符。 session_key 是针对用户数据进行加密签名的密 session_key 在文件校验,获取用户具体信息时均需使用小程序

通常为了安全起见,这两个数据都不会发往客户端。后端

  1. 后端将 session_key 处理以后,返回前端一个处理后的一个字符串做为用户的登录标识,通常以 token 的形式。(自定义登录态与 openid session_key 相关)
  2. 前端接收到 token,储存到 localStorage 中,每次向服务器请求数据的时候带上,做为服务器识别用户的凭证。
  3. 后续用户进入小程序时,首先调用 wx.checkSession() 检测登录态,若是失败,从新发起登录流程。
//app.js
const NOLOGINCODE = 1000003 //未登陆
const SUCCESS = 1000001 //成功
App({
  onLaunch: function() {
    var loginFlag = wx.getStorageSync('sessionId')
    var that = this
    if (loginFlag) {
      // 检查 session_key 是否过时
      wx.checkSession({
        // session_key 有效(未过时)
        success: function() {
          var userInfo = wx.getStorageSync('wxUserInfo')
          if (userInfo) {
            that.globalData.hasUserInfo = true
          }
        },
        // session_key 过时
        fail: function() {
          // session_key过时,从新登陆
          that.doLogin()
        }
      })
    } else {
      // 无skey,做为首次登陆
      this.doLogin()
    }
  },
  doLogin() {
    this.log().then(res => {
      this.$post('/auth', { code: res.code }, false).then(data => {
        wx.setStorageSync('sessionId', data.sessionId)
      })
    })
  },
  /** *微信登陆 获取code值,并将code传递给服务器 * @returns */
  log() {
    return new Promise(resolve => {
      wx.login({
        success(res) {
          if (res.errMsg === 'login:ok') {
            resolve(res)
          } else {
            wx.showToast({
              title: '微信登陆失败',
              icon: 'none',
              duration: 1200
            })
          }
        },
        fail() {
          wx.showToast({
            title: '微信登陆接口调用失败',
            icon: 'none',
            duration: 1200
          })
        }
      })
    })
  },
  globalData: {
    baseurl: 'https://www.fake.shop'
  }
})
复制代码

网络请求封装

微信小程序中网络请求的 api 是 wx.request(),可是这个请求是个异步回调的形式,每次发请求都要写好长一串,并且若是是嵌套的发请求,就会发现代码写的及其臃肿,因此将其 Promisefy 是及其有必要的。 代码以下:微信小程序

$get(url, data = {}, needToken = true) {
    let SUCCESS = 200
    var that = this
    needToken ? (data.token = wx.getStorageSync('ToKen')) : ''
    return new Promise((resolve, reject) => {
      wx.request({
        url: that.globalData.baseurl + url,
        method: "GET",
        header: {
          'content-type': 'application/json'
        },
        data: data,
        success(e) {
          if (e.data.code == SUCCESS) {
            resolve(e.data)
            return
          }

        },
        fail(e) {
          wx.showModal({
            title: '提示',
            content: '请求失败',
            showCancel: false
          })
          reject(e)
        }
      })
    })
  },
  $post(url, data = {}, needToken = true) {
    let that = this
    let SUCCESS = 200
    let TimeOut = 1000
    var that = this
    needToken ? (data.token = wx.getStorageSync('ToKen')) : ''
    return new Promise((resolve, reject) => {
      wx.request({
        url: that.globalData.baseurl + url,
        method: "POST",
        //此处能够根据接口文档设置header头
        // header: {
        // 'content-type': 'application/x-www-form-urlencoded'
        // },
        data: data,
        success(e) {
          if (e.statusCode == SUCCESS) {
            if (e.data.code == SUCCESS) {
              resolve(e.data)
            }
            else {
              reject(e)
              wx.showModal({
                title: '提示',
                content: e.data.msg,
                showCancel: false,
                success: function (res) {
                  if (res.confirm) {
                    if (e.data.code == TimeOut) { //根据实际业务返回的code码判断是否过时
                      // 登陆过时
                      that.doLogin();
                    }
                  }
                }
              })
            }
          } else {
            wx.showModal({
              title: '提示',
              content: e.data.error,
              showCancel: false
            })
            reject(e)
          }
        },
        fail(e) {
          console.log(e)
          wx.showModal({
            title: '提示',
            content: '请求失败',
            showCancel: false
          })
          reject(e)
        },
        complete(e) {
        }
      })

    })
  },
复制代码

微信公共号支付(微信浏览器)

虽然是写小程序踩坑指南,可是在微信内的 H5 页面支付和小程序内掉起支付仍是有类似之处的,顺便记录一下。api

应用场景

  • 已有 H5 商城网站,用户经过消息或扫描二维码在微信内打开网页时,能够调用微信支付完成下单购买的流程。
准备

UnionID:为了识别用户,每一个用户针对每一个公众号会产生一个安全的 OpenID,若是须要在多公众号、移动应用之间作用户共通,则需前往微信开放平台,将这些公众号和应用绑定到一个开放平台帐号下,绑定后,一个用户虽然对多个公众号和应用有多个不一样的 OpenID,但他对全部这些同一开放平台帐号下的公众号和应用,只有一个 UnionID 网页受权: 一些复杂的业务场景下,须要以网页的形式提供服务,经过网页受权能够获取用户的 openid(注:获取用户的 OpenID 是无需用户赞成的,获取用户的基本信息则需用户赞成) 微信 JS-SDK:是开发者在网页上经过 JavaScript 代码使用微信原生功能的工具包,开发者可使用它在网页上录制和播放微信语音、监听微信分享、上传手机本地图片、拍照等许多能力。跨域

业务流程时序图

业务流程时序图

主要流程
  • 网页内引入 jssdk,主要有两种
    1. 在须要调用 JS 接口的页面引入以下 JS 文件:res.wx.qq.com/open/js/jwe… JSSDK 使用步骤
    2. 模块引入: 直接引入 npm 包weixin-js-sdk,能够经过 npm 直接安装,而后在须要的文件中直接引用便可。
  • 网页受权
    • 个人理解就是网页受权主要是为了使在微信浏览器里面打开的第三方网页,能够跟微信公共号以及用户的微信相关联的操做,最终获取用户在该公共号下的openid.
    • 网站应用微信登陆是基于 OAuth2.0 协议标准构建的微信 OAuth2.0 受权登陆系统。获取 openid 分为两步
      1. 前端经过跳转网址获取 code,而后将 code 发送给后端
      2. 后端而后根据 code 获取 openid。

code 的获取

  • 在微信公众号请求用户网页受权以前,开发者须要先到公众平台官网中的 “开发 - 接口权限 - 网页服务 - 网页账号 - 网页受权获取用户基本信息” 的配置选项中,修改受权回调域名。本例中回调域名为 www.foo.com
  • 业务流程 举例: 支付页面地址: payUrl => "www.foo.com/pay" 1. 要跳转到支付页面时,若是是微信浏览器直接跳转 href(办法有不少能够重定向也能够 location.href)到 "open.weixin.qq.com/connect/oau…"+ appid +"&redirect_uri="+ URLEncoder.encode(payUrl) +"&response_type=code&scope=snsapi_base&state=123#wechat_redirect" 2. 系统会自动跳转到 payUrl 而且返回一个参数 code 例如=> "www.aa.com/pay?code=aa…" 3. 而后读取下 code 发送后端就 ok 了,这个你们应该都会吧。
注:

URLEncoder.encode(payUrl)是很是有必要的 state 参数: 用于保持请求和回调的状态,受权请求后原样带回给第三方。该参数可用于防止 csrf 攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加 session 进行校验 后端获取 openid 的缘由: 由于我是前端,不想搞这个(开玩笑的 😜),其实主要多是由于这部分逻辑部分敏感的公众号的秘钥等以及为了不前端跨域的问题。 code 的是时限: code 做为换取 access_token 的票据,每次用户受权带上的 code 将不同,code 只能使用一次,5 分钟未被使用自动过时。 因此每次进行支付的时候都须要进行以上逻辑浏览器

微信内 H5 调起支付

  • 须要将 openid 和 商户订单号发给后端,后端调用 api 生成前端调用支付 jsapi 须要的配置(这个主要是后端的逻辑)
    配置
    不啰嗦,代码以下:
//this.wechaConfig 里面保存的是后端调用预支付api 之后传递给前端用来调用getBrandWCPayRequest 的配置项。
		let config = {
				appId: this.wechaConfig.appId + '', // 公众号名称,由商户传入
				timeStamp: this.wechaConfig.timeStamp + '', // 时间戳,自 1970 年以来的秒数
				nonceStr: this.wechaConfig.nonceStr + '', // 随机串
				package: this.wechaConfig.package + '', // 统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=***
				signType: this.wechaConfig.signType + '', // 微信签名方式:
				paySign: this.wechaConfig.paySign + '', // 微信签名
			}
			// config = JSON.parse(JSON.stringify(config))
			WeixinJSBridge.invoke(
				'getBrandWCPayRequest',
				config,
				function(res) {
					if (res.err_msg == 'get_brand_wcpay_request:ok') {
						// 使用以上方式判断前端返回, 微信团队郑重提示:res.err_msg 将在用户支付成功后返回 ok,但并不保证它绝对可靠。
						this.$router.push({
							name: 'payResult',
							query: {
								status: true,
								id: this.addOrder.orderId,
							},
						})
					} else {
						this.$router.push({
							name: 'payResult',
							query: {
								status: false,
							},
						})
					}
				}.bind(this)
			)
复制代码

注意:

  1. 若是是使用 wx.chooseWXPay(),那么配置字段中是 timestamp 而不是 timeStamp
  2. config 变量里面之因此每一个变量都加 '' 例如:this.wechaConfig.appId + '',由于没有加以前在安卓上面能够正常的唤起 微信支付,而在 ios 上面测试的时候,会报错 缺乏 jsapi appid 或者缺乏 jsapipackage (我当时内心面就是 什么鬼啊 (((m -__-)m 我明明都传了的),因此加上查资料好多都说是 json 格式的问题, 我推测多是因为很奇怪的缘由(有理清楚的大佬评论区说下 😂),appid 的值没有被当成 String 类型被解析,因此我加了这个来处理一下。

    查到的比较有用的一个是 问题在于支付的时候 JSON 参数,必须所有是字符串。 好比个人错误是参数中 {"timeStamp":12312312},时间戳的值为整型,虽然 Android 上能够支付,可是 IOS 上就不行了,必须严格按文档上说的,键和值所有是字符串!这样 {"timeStamp":"12312312"} >才对! 传送门

  3. 若是是进行本地调试的话,须要注意微信的接口默认使用 80 端口

以前写这篇文章的初衷是想着记录下本身踩过的坑,避免小伙伴们重复踩坑。如今看来内容仍是干货比较少,之后会持续更新的。。。

参考

相关文章
相关标签/搜索