詳解微信小程序支付流程 - 新聞資(zī)訊 - 雲南小程序開發|雲南軟件開發|雲南網站(zhàn)建設-西山區知普網絡科技工作室

159-8711-8523

雲南網建設/小程序開發/軟件開發

知識

不管是網站(zhàn),軟件還是小程序,都要直接或間接能為您産生價值,我們在追求其視覺表現的同時,更側重于功能的便捷,營銷的便利,運營的高效,讓網站(zhàn)成為營銷工具,讓軟件能切實提升企業(yè)内部管理水平和(hé)效率。優秀的程序為後期升級提供便捷的支持!

詳解微信小程序支付流程

發表時間:2020-10-19

發布人:葵宇科技

浏覽次數:41

首先先把小程序微信支付的圖搬過來:在這裡插入圖片描述
相信會來查百度的同學們基本都是對文(wén)檔的說明不是很理解。我下(xià)面大概總結一下(xià)整個(gè)業(yè)務邏輯的過程。

微信小程序的商(shāng)戶系統一般是以接口的形式開發的,小程序通(tōng)過調用與後端約定好的接口進行參數的傳遞以及數據的接收。在小程序支付這塊,還需要跟微信服務器(qì)進行交互。過程大緻是這樣的:

一.小程序調用登錄接口獲取code,傳遞給商(shāng)戶服務器(qì)用來獲取用戶的openID
我們知道在微信平台中(zhōng),同一個(gè)公衆号的openID都是不同的,它是用戶身份識别的id,也就是說,我們通(tōng)過openID來區分不同的用戶,這個(gè)有微信開發基礎的應該都很熟悉。為了知道誰在支付,我們需要先獲取當前用戶的openid,那麼openID應該怎麼獲取呢(ne)?看下(xià)圖:
在這裡插入圖片描述
小程序調用wx.login() 獲取 臨時登錄憑證code ,并回傳到開發者服務器(qì)。

開發者服務器(qì)以code換取 用戶唯一标識openid 和(hé) 會話密鑰session_key。

看不懂嗎?不急,聽我慢慢解釋,這個(gè)業(yè)務流程大緻就是首先你(nǐ)得先在小程序的代碼中(zhōng)調用wx.login()來向微信獲取到code,拿到了之後把code通(tōng)過request傳給商(shāng)戶服務器(qì),再由商(shāng)戶服務器(qì)通(tōng)過騷操作來跟微信服務器(qì)要session_key和(hé)openID。

僞代碼如(rú)下(xià)(小程序端):

getToken: function () {
//調用登錄接口
wx.login({
success: function (res) {
var code = res.code;
wx.request({
url: 商(shāng)戶服務器(qì)接口地址,
data: {
code: code
},
method: ‘POST’,
success: function (res) {
wx.setStorageSync(‘token’, res.data.token); //存在小程序緩存中(zhōng)
},
fail: function (res) {
console.log(res.data);
}
})
}
})
}
調用這幾行代碼就可(kě)以向跟微信服務器(qì)要code,并且将code傳到商(shāng)戶服務器(qì)中(zhōng),記住這裡最好使用post發送請求,安全性的東西我應該不用講了,因為避免其他人濫用接口,于是我們使用token來進行驗證。并将商(shāng)戶服務器(qì)返回的token存在小程序緩存中(zhōng)。

那麼服務器(qì)端應該怎麼做呢(ne)?

我門通(tōng)過小程序提交的code,和(hé)小程序的APPID以及APPSECRET和(hé)拼接下(xià)列的url,并用curl進行get請求。

https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code

返回的數據是一個(gè)json對象,我門通(tōng)過使用json_decode(JSON,true)解析為數組,數據包括用戶的openID以及session_key,獲取到了後我們應該将openID存入數據庫中(zhōng),它代表着用戶的身份,那麼令牌應該怎麼生成呢(ne)。

二.token的生成以及緩存
我們根據一個(gè)用戶表将id和(hé)openid聯系起來,對應openID的id則是用戶的uid,我們可(kě)以這麼封裝

//要緩存的數據數組
$cacheValue = $result;   //包含openID和(hé)session_key
$cacheValue['uid'] =$uid;   //用戶id
 
$cacheValue['scope'] =ScopeEnum::User;   //用戶權限級别

緩存的方式我們可(kě)以選擇redis,memcache, 文(wén)件緩存等等,采用鍵值對(key-value)的方式進行存儲,記得設置好過期時間。這裡的key我們用token來賦值,token可(kě)以通(tōng)過這樣的方式進行生成:

//獲取32位随機字符串
$str = getRandChar(32);   //自定義方法生成32位随機串
//三組字符串進行md5加密
$timeStamp =$_SERVER['REQUEST_TIME_FLOAT'];
//salt
$salt = config('secure.token_salt'); //随機字符串
//返回token
 
return md5($str.$timeStamp.$salt);

這種算法基本保障了token的唯一性。因為值是我們獲取到的openID和(hé)session_key所在的數組,所以需要将數組轉成json才能存進去。以後的代碼當我們需要openID或者uid等時可(kě)以直接通(tōng)過取緩存的方式來取。

三,調用統一下(xià)單接口,獲取prepay_id,再次簽名
在你(nǐ)寫完了訂單操作後,如(rú)何讓用戶支付訂單費用呢(ne)?這裡就是重點了,我一步一步來說:

1.下(xià)載微信JS-SDK:
(https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1)

解壓打開進入lib文(wén)件夾中(zhōng):

在這裡插入圖片描述
我們需要将lib中(zhōng)的文(wén)件放到我們的框架中(zhōng),例如(rú)我使用的是tp5,就放到extend下(xià),最好是在extend下(xià)建個(gè)子(zǐ)文(wén)件夾。其中(zhōng)WxPay.Api.php是入口,WxPay.Config.php是配置文(wén)件。下(xià)好後需要改動(dòng)一些地方。在WxPay.Config.php中(zhōng)修改下(xià)列的東西改成你(nǐ)的。
在這裡插入圖片描述
然後在WxPay.Api.php中(zhōng)require一下(xià)WxPay.Notify.php,如(rú)圖:
在這裡插入圖片描述
在某個(gè)控制器(qì)或者服務層的代碼先是用Loader::import()引入WxPay.Api.php,相當于五個(gè)都引入了。

2.調用統一下(xià)單api
這裡要啰嗦的是,如(rú)何你(nǐ)寫的是有關(guān)商(shāng)品買賣的小程序,那麼需要在支付前再次檢測一下(xià)庫存量,因為用戶下(xià)完訂單後不一定馬上就會付款,如(rú)果在付款的期間庫存量沒了便會出現問(wèn)題。業(yè)務邏輯我就不說太多了,這取決于你(nǐ)寫代碼的嚴謹性。

在我們引入了上面那個(gè)文(wén)件後,先實例化這個(gè)類WxPayUnifiedOrder,把需要的參數通(tōng)過調用對應的方法傳入。
僞代碼如(rú)下(xià):

 //調用微信支付統一下(xià)單接口
        $wxOrderData = new \WxPayUnifiedOrder();
        //設置相關(guān)參數
        $wxOrderData->SetOut_trade_no($this->orderNO);
        $wxOrderData->SetTrade_type('JSAPI');
        $wxOrderData->SetTotal_fee($totalPrice * 100); //這裡的價格單位是分
        $wxOrderData->SetBody('Mc');
        $wxOrderData->SetOpenid($openid);
        $wxOrderData->SetNotify_url(config('secure.pay_back_url'));//支付回調

其中(zhōng)第一個(gè)是你(nǐ)的訂單号,訂單号的生成方法可(kě)以自定義,第二個(gè)是死參數,第三個(gè)是總訂單價格,第四個(gè)是名稱如(rú)果是中(zhōng)文(wén)的話要轉碼,第四個(gè)是openID,這個(gè)這時候就可(kě)以從緩存中(zhōng)取了。最後一個(gè)是支付回調,就是支付成功後微信要訪問(wèn)的地址。必須是公網能訪問(wèn)的,或者你(nǐ)使用ngrok來進行反向代理轉發本地的服務器(qì)。

參數設置好了之後,就直接調用SDK的方法了

 $wxOrder = \WxPayApi::unifiedOrder($wxOrderData);

如(rú)果參數沒有錯誤的話,返回的數據中(zhōng)會含有prepay_id,這個(gè)是我們需要的參數。

3.再次簽名

//  提交JSAPI輸入對象
        $jsApiPayData = new \WxPayJsApiPay();
        //設置appid
        $jsApiPayData->SetAppid(config('wx.app_id'));
        //timeStamp
        $jsApiPayData->SetTimeStamp((string)time());
        //随機串
        $randStr = md5(time().mt_rand(0,1000));
        $jsApiPayData->SetNonceStr($randStr);
        //數據報
        $jsApiPayData->SetPackage('prepay_id='.$wxOrder['prepay_id']);
        //類型
        $jsApiPayData->SetSignType('MD5');
        //生成簽名
        $sign  = $jsApiPayData->MakeSign();
        //獲得簽名數組
        $signData = $jsApiPayData->GetValues();
        //增加字段paySign
        $signData['paySign']=$sign;
        //删除signData中(zhōng)的app_Id字段
        unset($signData['appId']);
        return $signData;

再次簽名完成後,就把五個(gè)參數返回給小程序。

四,小程序獲取五個(gè)參數後,鑒權調起支付
僞代碼(小程序端)

  pay: function () {
    var token = wx.getStorageSync('token');
    var that = this;
  
    wx.request({
      url: baseUrl + '/order',
      header: {
        token: token
      },
      data: {   //産品的數據
        products:
        [
          {
            product_id: 1, count: 1
          },
          
          {
            product_id: 2, count: 1
          }
        ]
      },
      method: 'POST',
      success: function (res) {
        console.log(res.data);
        if (res.data.pass) {
          wx.setStorageSync('order_id', res.data.order_id);
          that.getPreOrder(token, res.data.order_id); //調用getPreOrder
        }
        else {
          console.log('訂單未創建成功');
        }
      }
    })
  },
 
  getPreOrder: function (token, orderID) {
    if (token) {
      wx.request({
        url: baseUrl + '/pay/pre_order',
        method: 'POST',
        header: {
          token: token
        },
        data: {
          id: orderID
        },
        success: function (res) {
          var preData = res.data;
          console.log(preData);
          
          wx.requestPayment({    //請求支付
            timeStamp: preData.timeStamp.toString(),
            nonceStr: preData.nonceStr,
            package: preData.package,
            signType: preData.signType,
            paySign: preData.paySign,
            success: function (res) {
              console.log(res.data);     
            },
            fail: function (error) {
              console.log(error);
            }
          })
        }
      })
    }
  },

如(rú)果一切正常的話,在微信開發者工具就會顯示這個(gè)二維碼,
在這裡插入圖片描述
如(rú)果在真機上測試的話,就會直接彈出支付頁面。小程序會直接顯示支付成功或者失敗的頁面,然後微信服務器(qì)就會開始訪問(wèn)我們之前設置的支付回調地址來推送支付結果,根據結果可(kě)以來更新訂單的狀态。這裡我就不寫業(yè)務邏輯了,大概講一下(xià)就好。

五,支付回調
實際上我們需要重寫WxPayNotify類的NotifyProcess方法,這裡記得

Loader::impor()引入那個(gè)入口類。

/**
	 * 
	 * 回調方法入口,子(zǐ)類可(kě)重寫該方法
	 * 注意:
	 * 1、微信回調超時時間為2s,建議用戶使用異步處理流程,确認成功之後立刻回複微信服務器(qì)
	 * 2、微信服務器(qì)在調用失敗或者接到回包為非确認包的時候,會發起重試,需确保你(nǐ)的回調是可(kě)以重入
	 * @param array $data 回調解釋出的參數
	 * @param string $msg 如(rú)果回調處理失敗,可(kě)以将錯誤信息輸出到該方法
	 * @return true 回調出來完成不需要繼續回調,false回調處理未完成需要繼續回調
	 */
	public function NotifyProcess($data, &$msg)
	{
		//TODO 用戶基礎該類之後需要重寫該方法,成功的時候返回true,失敗返回false
		return true;
	}

也就是說你(nǐ)需要寫個(gè)新類繼承WxPayNotify,再重寫NotifyProcess方法,根據檢查 d a t a [ ′ r e s u l t c o d e ′ ] 是 否 為 S U C C E S S 可(kě) 以 判 斷 成 功 與 否 , 成 功 的 話 你(nǐ) 可(kě) 以 根 據 業(yè) 務 需 求 寫 業(yè) 務 邏 輯 , 最 後 r e t u r n t r u e 即 可(kě) 。 這 時 候 會 想 , 我 重 寫 了 這 個(gè) 方 法 後 微 信 怎 麼 調 用 呢(ne) , 其 實 這 裡 微 信 不 是 要 直 接 調 用 這 個(gè) 方 法 , 你(nǐ) 應 該 在 微 信 支 付 回 調 的 方 法 中(zhōng) 實 例 化 這 個(gè) 新 類 , 然 後 根 據 獲 得 的 對 象 去 調 用 H a n d l e ( ) 方 法 。 data['result_code']是否為SUCCESS可(kě)以判斷成功與否,成功的話你(nǐ)可(kě)以根據業(yè)務需求寫業(yè)務邏輯,最後return true 即可(kě)。這時候會想,我重寫了這個(gè)方法後微信怎麼調用呢(ne),其實這裡微信不是要直接調用這個(gè)方法,你(nǐ)應該在微信支付回調的方法中(zhōng)實例化這個(gè)新類,然後根據獲得的對象去調用Handle()方法。 data[resultc?ode]SUCCESS可(kě)你(nǐ)可(kě)業(yè)業(yè)returntrue可(kě)個(gè)調呢(ne)調個(gè)你(nǐ)調中(zhōng)個(gè)調Handle()obj = new 新類(),$obj->Handle()。

相關(guān)案例查看更多