一、官方流程圖
小程序支付的交互圖如下:
二、小程序調用登錄接口獲取code,傳遞給商戶服務器獲取用戶的openID
1.小程序調用wx.login() 獲取 臨時登錄憑證code ,并回傳到開發者服務器。
2.開發者服務器以code換取 用戶唯一標識openid 和 會話密鑰session_key。
小程序端:
getToken: function () {
//調用登錄接口
wx.login({
success: function (res) {
var code = res.code;
wx.request({
url: 商戶服務器接口地址,
data: {
code: code
},
method: 'POST',
success: function (res) {
wx.setStorageSync('token', res.data.token); //存在小程序緩存中
},
fail: function (res) {
console.log(res.data);
}
})
}
})
}
服務端:
我們(men)通過小程序(xu)提交的code,和小程序(xu)的APPID以(yi)及APPSECRET和拼(pin)接下(xia)列的url,并用curl進行get請求。
//api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
返回的(de)數(shu)(shu)(shu)據(ju)是(shi)一個json對象(xiang),我門通過(guo)使用(yong)json_decode(JSON,true)解析為(wei)數(shu)(shu)(shu)組,數(shu)(shu)(shu)據(ju)包括用(yong)戶的(de)openID以及(ji)session_key,獲取到了后我們(men)應該(gai)將openID存入數(shu)(shu)(shu)據(ju)庫中,它代表著用(yong)戶的(de)身份,那么令牌(pai)應該(gai)怎么生(sheng)成(cheng)呢。
三、token的生成以及緩存
我(wo)們根據一個用(yong)戶(hu)表將id和openid聯(lian)系起(qi)來,對應openID的id則是用(yong)戶(hu)的uid,我(wo)們可以這么封裝
//要緩存的數據數組
$cacheValue = $result; //包含openID和session_key
$cacheValue['uid'] =$uid; //用戶id
$cacheValue['scope'] =ScopeEnum::User; //用戶權限級別
緩存(cun)的(de)方式(shi)我們(men)可(ke)以選擇redis,memcache, 文件緩存(cun)等等,采(cai)用(yong)鍵值(zhi)對(key-value)的(de)方式(shi)進(jin)行存(cun)儲(chu),記(ji)得設置好過期(qi)時間。這里的(de)key我們(men)用(yong)token來賦(fu)值(zhi),token可(ke)以通過這樣的(de)方式(shi)進(jin)行生成:
//獲取32位隨機字符串
$str = getRandChar(32); //自定義方法生成32位隨機串
//三組字符串進行md5加密
$timeStamp =$_SERVER['REQUEST_TIME_FLOAT'];
//salt
$salt = config('secure.token_salt'); //隨機字符串
//返回token
return md5($str.$timeStamp.$salt);
這種算法基本(ben)保障了token的(de)(de)(de)唯(wei)一(yi)性。因為值(zhi)是(shi)我們(men)獲(huo)取到(dao)的(de)(de)(de)openID和session_key所(suo)在的(de)(de)(de)數組,所(suo)以(yi)需要(yao)將數組轉成json才能存進去(qu)。以(yi)后的(de)(de)(de)代(dai)碼當我們(men)需要(yao)openID或者(zhe)uid等時可(ke)以(yi)直接(jie)通(tong)過取緩存的(de)(de)(de)方式來取。
四、調用統一下單接口,獲取prepay_id,再次簽名
4.1 下載微信JS-SDK
(//pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1)
解壓打開進入lib文件夾中。
我們需要將lib中的文件放到我們的框架中,例如我使用的是tp5,就放到extend下,最好是在extend下建個子文件夾。其中WxPay.Api.php是入口,WxPay.Config.php是配置文件。下好后需要改動一些地方。在WxPay.Config.php中修改下列的東西改成你的。
然后在WxPay.Api.php中require一下WxPay.Notify.php。
在某個控制器或者服務層的代碼(ma)先是用Loader::import()引(yin)入WxPay.Api.php,相當于五個都引(yin)入了。
4.2 調用統一下單api
在我們引入了(le)上面(mian)那個文件后,先實例化這個類WxPayUnifiedOrder,把需(xu)要的參數通過(guo)調用對應的方法(fa)傳入。
//調用微信支付統一下單接口
$wxOrderData = new \WxPayUnifiedOrder();
//設置相關參數
$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'));//支付回調
參數設(she)置好了之后(hou),就直接調用(yong)SDK的方法了
$wxOrder = \WxPayApi::unifiedOrder($wxOrderData);
4.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中的app_Id字段
unset($signData['appId']);
return $signData;
再次簽名完成后,就把(ba)五個參(can)數返(fan)回給(gei)小程序(xu)。
五、小程序獲取五個參數后,鑒權調起支付
小程序端:
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);
}
})
}
})
}
},
六、支付回調
我們需要重寫WxPayNotify類(lei)的NotifyProcess方法,這(zhe)里(li)記(ji)得Loader::impor()引入(ru)那個入(ru)口類(lei)。
/**
*
* 回調方法入口,子類可重寫該方法
* 注意:
* 1、微信回調超時時間為2s,建議用戶使用異步處理流程,確認成功之后立刻回復微信服務器
* 2、微信服務器在調用失敗或者接到回包為非確認包的時候,會發起重試,需確保你的回調是可以重入
* @param array $data 回調解釋出的參數
* @param string $msg 如果回調處理失敗,可以將錯誤信息輸出到該方法
* @return true 回調出來完成不需要繼續回調,false回調處理未完成需要繼續回調
*/
public function NotifyProcess($data, &$msg)
{
//TODO 用戶基礎該類之后需要重寫該方法,成功的時候返回true,失敗返回false
return true;
}