微信小程序踩坑指南

最近由於公司業務一直在作微信小程序的項目,趁此機會將最近踩過的一些坑總結記錄下前端


微信小程序登錄流程

登陸流程時序
  • 前端調用wx.login(),獲取臨時登陸憑證 code
  • 經過wx.request()將code發給服務器(須要後端建立接口接收code)
  • 後端進行登陸憑證校驗,入參爲(appid,secret,js_code,grant_type)
appid 小程序惟一標識
secret 小程序的 app secret
js_code 登陸時獲取的 code
grant_type 填寫爲 authorization_code
  • 登錄憑證校驗經過,從微信服務器換取openid和session_key

openid 用戶惟一標識
session_key 會話密鑰ios

openid 是用戶惟一標識,但不建議直接用作後端服務器的各用戶標示符。
session_key 是針對用戶數據進行加密簽名的密匙。session_key 在文件校驗,獲取用戶具體信息時均需使用

通常爲了安全起見,這兩個數據都不會發往客戶端。npm

  • 後端將session_key處理以後,返回前端一個處理後的一個字符串做爲用戶的登錄標識,通常以token的形式。(自定義登錄態與openid session_key相關)
  • 前端接收到token,儲存到localStorage中,每次向服務器請求數據的時候帶上,做爲服務器識別用戶的憑證。
  • 後續用戶進入小程序時,首先調用 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'
  }
})

補充json


微信小程序中網絡請求的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 商城網站,用戶經過消息或掃描二維碼在微信內打開網頁時,能夠調用微信支付完成下單購買的流程。

準備

UnionID:爲了識別用戶,每一個用戶針對每一個公衆號會產生一個安全的 OpenID,若是須要在多公衆號、移動應用之間作用戶共通,則需前往微信開放平臺,將這些公衆號和應用綁定到一個開放平臺帳號下,綁定後,一個用戶雖然對多個公衆號和應用有多個不一樣的 OpenID,但他對全部這些同一開放平臺帳號下的公衆號和應用,只有一個 UnionID
網頁受權: 一些複雜的業務場景下,須要以網頁的形式提供服務,經過網頁受權能夠獲取用戶的openid(注:獲取用戶的 OpenID 是無需用戶贊成的,獲取用戶的基本信息則需用戶贊成)
微信 JS-SDK:是開發者在網頁上經過 JavaScript 代碼使用微信原生功能的工具包,開發者可使用它在網頁上錄製和播放微信語音、監聽微信分享、上傳手機本地圖片、拍照等許多能力。

業務流程時序圖

業務流程時序圖

主要流程

  • 網頁內引入jssdk,主要有兩種api

  • 網頁受權跨域

    • 個人理解就是網頁受權主要是爲了使在微信瀏覽器裏面打開的第三方網頁,能夠跟微信公共號以及用戶的微信相關聯的操做,最終獲取用戶在該公共號下的openid.
    • 網站應用微信登陸是基於 OAuth2.0 協議標準構建的微信 OAuth2.0 受權登陸系統。獲取 openid 分爲兩步瀏覽器

      • 前端經過跳轉網址獲取 code,而後將code發送給後端
      • 後端而後根據 code 獲取 openid。
code的獲取
  • 在微信公衆號請求用戶網頁受權以前,開發者須要先到公衆平臺官網中的 「開發 - 接口權限 - 網頁服務 - 網頁賬號 - 網頁受權獲取用戶基本信息」 的配置選項中,修改受權回調域名。本例中回調域名爲www.foo.com
  • 業務流程 舉例: 支付頁面地址: payUrl => "http://www.foo.com/pay"

    1. 要跳轉到支付頁面時,若是是微信瀏覽器直接跳轉href(辦法有不少能夠重定向也能夠location.href)到 "https://open.weixin.qq.com/connect/oauth2/authorize?appid="+ appid +"&redirect_uri="+ URLEncoder.encode(payUrl) +"&response_type=code&scope=snsapi_base&state=123#wechat_redirect"
    2. 系統會自動跳轉到 payUrl 而且返回一個參數 code 例如=> "http://www.aa.com/pay?code=aaa"
    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"} >纔對! 傳送門
  1. 若是是進行本地調試的話,須要注意微信的接口默認使用80端口

參考

相關文章
相關標籤/搜索