知識
不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價值,我們在追求其視覺表現(xiàn)的同時,更側(cè)重于功能的便捷,營銷的便利,運(yùn)營的高效,讓網(wǎng)站成為營銷工具,讓軟件能切實(shí)提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序?yàn)楹笃谏壧峁┍憬莸闹С郑?
您當(dāng)前位置>首頁 » 新聞資訊 » 小程序相關(guān) >
詳解微信小程序支付流程
發(fā)表時間:2020-10-19
發(fā)布人:葵宇科技
瀏覽次數(shù):64
首先先把小程序微信支付的圖搬過來:
相信會來查百度的同學(xué)們基本都是對文檔的說明不是很理解。我下面大概總結(jié)一下整個業(yè)務(wù)邏輯的過程。
微信小程序的商戶系統(tǒng)一般是以接口的形式開發(fā)的,小程序通過調(diào)用與后端約定好的接口進(jìn)行參數(shù)的傳遞以及數(shù)據(jù)的接收。在小程序支付這塊,還需要跟微信服務(wù)器進(jìn)行交互。過程大致是這樣的:
一.小程序調(diào)用登錄接口獲取code,傳遞給商戶服務(wù)器用來獲取用戶的openID
我們知道在微信平臺中,同一個公眾號的openID都是不同的,它是用戶身份識別的id,也就是說,我們通過openID來區(qū)分不同的用戶,這個有微信開發(fā)基礎(chǔ)的應(yīng)該都很熟悉。為了知道誰在支付,我們需要先獲取當(dāng)前用戶的openid,那么openID應(yīng)該怎么獲取呢?看下圖:
小程序調(diào)用wx.login() 獲取 臨時登錄憑證code ,并回傳到開發(fā)者服務(wù)器。
開發(fā)者服務(wù)器以code換取 用戶唯一標(biāo)識openid 和 會話密鑰session_key。
看不懂嗎?不急,聽我慢慢解釋,這個業(yè)務(wù)流程大致就是首先你得先在小程序的代碼中調(diào)用wx.login()來向微信獲取到code,拿到了之后把code通過request傳給商戶服務(wù)器,再由商戶服務(wù)器通過騷操作來跟微信服務(wù)器要session_key和openID。
偽代碼如下(小程序端):
getToken: function () {
//調(diào)用登錄接口
wx.login({
success: function (res) {
var code = res.code;
wx.request({
url: 商戶服務(wù)器接口地址,
data: {
code: code
},
method: ‘POST’,
success: function (res) {
wx.setStorageSync(‘token’, res.data.token); //存在小程序緩存中
},
fail: function (res) {
console.log(res.data);
}
})
}
})
}
調(diào)用這幾行代碼就可以向跟微信服務(wù)器要code,并且將code傳到商戶服務(wù)器中,記住這里最好使用post發(fā)送請求,安全性的東西我應(yīng)該不用講了,因?yàn)楸苊馄渌藶E用接口,于是我們使用token來進(jìn)行驗(yàn)證。并將商戶服務(wù)器返回的token存在小程序緩存中。
那么服務(wù)器端應(yīng)該怎么做呢?
我門通過小程序提交的code,和小程序的APPID以及APPSECRET和拼接下列的url,并用curl進(jìn)行g(shù)et請求。
https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
返回的數(shù)據(jù)是一個json對象,我門通過使用json_decode(JSON,true)解析為數(shù)組,數(shù)據(jù)包括用戶的openID以及session_key,獲取到了后我們應(yīng)該將openID存入數(shù)據(jù)庫中,它代表著用戶的身份,那么令牌應(yīng)該怎么生成呢。
二.token的生成以及緩存
我們根據(jù)一個用戶表將id和openid聯(lián)系起來,對應(yīng)openID的id則是用戶的uid,我們可以這么封裝
//要緩存的數(shù)據(jù)數(shù)組
$cacheValue = $result; //包含openID和session_key
$cacheValue['uid'] =$uid; //用戶id
$cacheValue['scope'] =ScopeEnum::User; //用戶權(quán)限級別
緩存的方式我們可以選擇redis,memcache, 文件緩存等等,采用鍵值對(key-value)的方式進(jìn)行存儲,記得設(shè)置好過期時間。這里的key我們用token來賦值,token可以通過這樣的方式進(jìn)行生成:
//獲取32位隨機(jī)字符串
$str = getRandChar(32); //自定義方法生成32位隨機(jī)串
//三組字符串進(jìn)行md5加密
$timeStamp =$_SERVER['REQUEST_TIME_FLOAT'];
//salt
$salt = config('secure.token_salt'); //隨機(jī)字符串
//返回token
return md5($str.$timeStamp.$salt);
這種算法基本保障了token的唯一性。因?yàn)橹凳俏覀儷@取到的openID和session_key所在的數(shù)組,所以需要將數(shù)組轉(zhuǎn)成json才能存進(jìn)去。以后的代碼當(dāng)我們需要openID或者uid等時可以直接通過取緩存的方式來取。
三,調(diào)用統(tǒng)一下單接口,獲取prepay_id,再次簽名
在你寫完了訂單操作后,如何讓用戶支付訂單費(fèi)用呢?這里就是重點(diǎn)了,我一步一步來說:
1.下載微信JS-SDK:
(https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1)
解壓打開進(jìn)入lib文件夾中:
我們需要將lib中的文件放到我們的框架中,例如我使用的是tp5,就放到extend下,最好是在extend下建個子文件夾。其中WxPay.Api.php是入口,WxPay.Config.php是配置文件。下好后需要改動一些地方。在WxPay.Config.php中修改下列的東西改成你的。
然后在WxPay.Api.php中require一下WxPay.Notify.php,如圖:
在某個控制器或者服務(wù)層的代碼先是用Loader::import()引入WxPay.Api.php,相當(dāng)于五個都引入了。
2.調(diào)用統(tǒng)一下單api
這里要啰嗦的是,如何你寫的是有關(guān)商品買賣的小程序,那么需要在支付前再次檢測一下庫存量,因?yàn)橛脩粝峦暧唵魏蟛灰欢R上就會付款,如果在付款的期間庫存量沒了便會出現(xiàn)問題。業(yè)務(wù)邏輯我就不說太多了,這取決于你寫代碼的嚴(yán)謹(jǐn)性。
在我們引入了上面那個文件后,先實(shí)例化這個類WxPayUnifiedOrder,把需要的參數(shù)通過調(diào)用對應(yīng)的方法傳入。
偽代碼如下:
//調(diào)用微信支付統(tǒng)一下單接口
$wxOrderData = new \WxPayUnifiedOrder();
//設(shè)置相關(guān)參數(shù)
$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'));//支付回調(diào)
其中第一個是你的訂單號,訂單號的生成方法可以自定義,第二個是死參數(shù),第三個是總訂單價格,第四個是名稱如果是中文的話要轉(zhuǎn)碼,第四個是openID,這個這時候就可以從緩存中取了。最后一個是支付回調(diào),就是支付成功后微信要訪問的地址。必須是公網(wǎng)能訪問的,或者你使用ngrok來進(jìn)行反向代理轉(zhuǎn)發(fā)本地的服務(wù)器。
參數(shù)設(shè)置好了之后,就直接調(diào)用SDK的方法了
$wxOrder = \WxPayApi::unifiedOrder($wxOrderData);
如果參數(shù)沒有錯誤的話,返回的數(shù)據(jù)中會含有prepay_id,這個是我們需要的參數(shù)。
3.再次簽名
// 提交JSAPI輸入對象
$jsApiPayData = new \WxPayJsApiPay();
//設(shè)置appid
$jsApiPayData->SetAppid(config('wx.app_id'));
//timeStamp
$jsApiPayData->SetTimeStamp((string)time());
//隨機(jī)串
$randStr = md5(time().mt_rand(0,1000));
$jsApiPayData->SetNonceStr($randStr);
//數(shù)據(jù)報
$jsApiPayData->SetPackage('prepay_id='.$wxOrder['prepay_id']);
//類型
$jsApiPayData->SetSignType('MD5');
//生成簽名
$sign = $jsApiPayData->MakeSign();
//獲得簽名數(shù)組
$signData = $jsApiPayData->GetValues();
//增加字段paySign
$signData['paySign']=$sign;
//刪除signData中的app_Id字段
unset($signData['appId']);
return $signData;
再次簽名完成后,就把五個參數(shù)返回給小程序。
四,小程序獲取五個參數(shù)后,鑒權(quán)調(diào)起支付
偽代碼(小程序端)
pay: function () {
var token = wx.getStorageSync('token');
var that = this;
wx.request({
url: baseUrl + '/order',
header: {
token: token
},
data: { //產(chǎn)品的數(shù)據(jù)
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); //調(diào)用getPreOrder
}
else {
console.log('訂單未創(chuàng)建成功');
}
}
})
},
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);
}
})
}
})
}
},
如果一切正常的話,在微信開發(fā)者工具就會顯示這個二維碼,
如果在真機(jī)上測試的話,就會直接彈出支付頁面。小程序會直接顯示支付成功或者失敗的頁面,然后微信服務(wù)器就會開始訪問我們之前設(shè)置的支付回調(diào)地址來推送支付結(jié)果,根據(jù)結(jié)果可以來更新訂單的狀態(tài)。這里我就不寫業(yè)務(wù)邏輯了,大概講一下就好。
五,支付回調(diào)
實(shí)際上我們需要重寫WxPayNotify類的NotifyProcess方法,這里記得
Loader::impor()引入那個入口類。
/**
*
* 回調(diào)方法入口,子類可重寫該方法
* 注意:
* 1、微信回調(diào)超時時間為2s,建議用戶使用異步處理流程,確認(rèn)成功之后立刻回復(fù)微信服務(wù)器
* 2、微信服務(wù)器在調(diào)用失敗或者接到回包為非確認(rèn)包的時候,會發(fā)起重試,需確保你的回調(diào)是可以重入
* @param array $data 回調(diào)解釋出的參數(shù)
* @param string $msg 如果回調(diào)處理失敗,可以將錯誤信息輸出到該方法
* @return true 回調(diào)出來完成不需要繼續(xù)回調(diào),false回調(diào)處理未完成需要繼續(xù)回調(diào)
*/
public function NotifyProcess($data, &$msg)
{
//TODO 用戶基礎(chǔ)該類之后需要重寫該方法,成功的時候返回true,失敗返回false
return true;
}
也就是說你需要寫個新類繼承WxPayNotify,再重寫NotifyProcess方法,根據(jù)檢查 d a t a [ ′ r e s u l t c o d e ′ ] 是 否 為 S U C C E S S 可 以 判 斷 成 功 與 否 , 成 功 的 話 你 可 以 根 據(jù) 業(yè) 務(wù) 需 求 寫 業(yè) 務(wù) 邏 輯 , 最 后 r e t u r n t r u e 即 可 。 這 時 候 會 想 , 我 重 寫 了 這 個 方 法 后 微 信 怎 么 調(diào) 用 呢 , 其 實(shí) 這 里 微 信 不 是 要 直 接 調(diào) 用 這 個 方 法 , 你 應(yīng) 該 在 微 信 支 付 回 調(diào) 的 方 法 中 實(shí) 例 化 這 個 新 類 , 然 后 根 據(jù) 獲 得 的 對 象 去 調(diào) 用 H a n d l e ( ) 方 法 。 data['result_code']是否為SUCCESS可以判斷成功與否,成功的話你可以根據(jù)業(yè)務(wù)需求寫業(yè)務(wù)邏輯,最后return true 即可。這時候會想,我重寫了這個方法后微信怎么調(diào)用呢,其實(shí)這里微信不是要直接調(diào)用這個方法,你應(yīng)該在微信支付回調(diào)的方法中實(shí)例化這個新類,然后根據(jù)獲得的對象去調(diào)用Handle()方法。 data[′resultc?ode′]是否為SUCCESS可以判斷成功與否,成功的話你可以根據(jù)業(yè)務(wù)需求寫業(yè)務(wù)邏輯,最后returntrue即可。這時候會想,我重寫了這個方法后微信怎么調(diào)用呢,其實(shí)這里微信不是要直接調(diào)用這個方法,你應(yīng)該在微信支付回調(diào)的方法中實(shí)例化這個新類,然后根據(jù)獲得的對象去調(diào)用Handle()方法。obj = new 新類(),$obj->Handle()。
相關(guān)案例查看更多
相關(guān)閱讀
- 小程序被攻擊
- 小程序開發(fā)排名前十名
- 小程序設(shè)計
- 網(wǎng)站沒排名
- 云南軟件開發(fā)
- php網(wǎng)站
- 云南網(wǎng)站制作哪家好
- 網(wǎng)站開發(fā)公司哪家好
- 云南微信小程序開發(fā)
- 云南軟件定制
- 報廢車拆解軟件
- 云南網(wǎng)站建設(shè)招商
- 云南網(wǎng)站建設(shè)專家
- web前端
- 網(wǎng)絡(luò)公司電話
- 云南小程序設(shè)計
- 百度小程序開發(fā)公司
- 貴州小程序開發(fā)
- 排名
- 網(wǎng)站小程序
- 楚雄網(wǎng)站建設(shè)公司
- 買小程序被騙
- 小程序開發(fā)公司
- 全國前十名小程序開發(fā)公司
- 支付寶小程序被騙
- 云南旅游網(wǎng)站建設(shè)
- 關(guān)鍵詞快速排名
- 報廢車拆解管理系統(tǒng)
- 前端
- 云南小程序開發(fā)課程