微信小程序推出以后,我在公司內部開過幾場小程序開發(fā)培訓課。出于培訓課程需要,以公眾號“贊賞 ...
微信小程序推出以后,我在公司內部開過幾場小程序開發(fā)培訓課。出于培訓課程需要,以公眾號“贊賞”功能作為需求,制作出一個“贊賞”小程序DEMO。
本案例涵蓋前后端完整設計與代碼講解,其中包括2個小程序前端頁面,3個后端JSON接口,涉及到的相關知識要點包括了登錄、后端信息獲?。ˋES-128-CBC解密算法)、微信支付、后端數(shù)據(jù)存儲等,是個完整的麻雀案例。
案例借助開源NAMI框架( https://github.com/wodenwang/nami ),十分鐘即可完成前后端本地部署。
微信公眾平臺(包括公眾號、小程序)相比APP的一個明顯優(yōu)勢,是開發(fā)者可以直接利用微信的用戶鑒權體系,免去注冊、密碼登錄的步驟。微信小程序對于登錄的設計,更是用之于無形,在整個用戶使用過程中都是無感知的,一進小程序其實就已經登錄了。
也許有讀者會說,小程序登錄的時候不是會彈出一個用戶信息的詢問框嗎?錯了,這個詢問框是調用wx.getUserInfo獲取“用戶資料”時候彈出的,調用wx.login的“登錄”操作是不會彈出任何詢問框的。
關于微信小程序登錄,官方文檔的流程實現(xiàn)起來挺不容易的(如下圖),需要服務端維護一個保存OpenID等用戶資料的緩存池,同時小程序客戶端還需要維護一個3rd session key,并且在之后每次做服務端請求的時候都要帶入這個3rd session key。
所幸NAMI框架已經把這塊封裝好,下面我們用一點篇幅來看看案例是如何做Login。
import nami from '/nami/index';
App({
data: {
userInfo: null,
rankLoaded: false //排行榜已加載
},
onLaunch: function () {
var app = this;
//登錄
nami.login(() => {
nami.getUserInfo((userInfo) => {
console.log("已獲取數(shù)據(jù)", userInfo);
app.data.userInfo = userInfo;
}, () => {
console.log("用戶拒絕提供信息");
});
});
}
})
案例在小程序入口的時候實現(xiàn)nami封裝的登錄,登錄成功之后可以看到localstorage中保存了一個叫做NAMI_TOKEN的字符,這個就是小程序登錄之后要求前端保存的3rd session key了。
/**
*下拉刷新
*/
onPullDownRefresh: function () {
wx.stopPullDownRefresh();
var that = this;
//這里使用了nami封裝的request而不是wx.request
nami.request({
loading: true,
url: '/request/scholes_pay/rank.groovy',
success: function (res) {
that.setData({
dataList: res.data.list
});
app.data.rankLoaded = true;//加載完成
}
});
},
截取一個服務端請求的代碼片段做分析,發(fā)現(xiàn)NAMI框架將request也做了封裝,其主要目的是在每次服務端請求的時候將localstorage中的NAMI_TOKEN帶入,如圖。
/**
* 排行榜
* @author woden
*/
//重點是這一句獲取已登錄的用戶信息
//NAMI框架中通過nami_token在服務端緩存池找到對應的用戶資料,并返回給邏輯代碼
//這里的session指的是NAMI框架中封裝的會話上下文
def user = session.appUser();
//根據(jù)被贊賞金額排名的用戶列表
def list = db.query("""
select b.*,a.TOTAL_FEE from
(
select sum(TOTAL_FEE) TOTAL_FEE,OPEN_ID from PAY_DETAIL group by OPEN_ID
) a
left join PAY_USER b on a.OPEN_ID = b.OPEN_ID
order by a.TOTAL_FEE desc,b.UPDATE_TIME asc
limit 10
""".toString());
def result = [];
def i=1;
for(def o:list){
def vo = [:];
vo.avatar = o.AVATAR_URL;
vo.nickName = o.NICK_NAME;
vo.fee = fmt.formatPrice(o.TOTAL_FEE/100);
if(user.openId == o.OPEN_ID){
vo.my = true;
}
vo.rank = i++;
result += vo;
}
return [list:result];
從這個案例看出,NAMI框架遵循規(guī)范推薦的技術原則,將緩存池、3rd key等業(yè)務無關的技術細節(jié)封裝在框架中,讓開發(fā)人員可以更專注于業(yè)務邏輯本身。
小程序推出之初,許多開發(fā)人員對于小程序支付的實現(xiàn)也是焦頭爛額,官方給出的時序圖如下,讀者可以自己感受一下。
微信小程序的支付對于前端的接口倒是簡單,只需事先一個wx.requestPayment接口,至于API里面指定的參數(shù),填空就是了。先看看案例中前端支付是怎么寫的。
/**
* 綁定選中贊賞金額按鈕
*/
selectItem: function (event) {
//獲取待支付金額,單位:元
var total = event.currentTarget.dataset.item;
var that = this;
that.setData({ selected: total });
//向服務端發(fā)起支付請求,獲取wx.requestPayment需要的信息
nami.request({
loading: true,
url: '/request/scholes_pay/pay.groovy',
data: {
total: total * 100 //元轉為分
},
success: function (res) {
console.log("獲取支付密匙", res);
//發(fā)起支付,根據(jù)服務端的返回填空
wx.requestPayment({
timeStamp: '' + res.data.signature.timestamp,
nonceStr: res.data.signature.nonce,
package: res.data.signature.pack,
signType: 'MD5',
paySign: res.data.signature.signature,
success: function (res) {
app.data.rankLoaded = false;//通知排行榜重新加載
wx.showToast({
title: '支付成功,感謝',
icon: 'success'
});
},
fail: function (res) {
wx.showToast({
title: '已取消支付',
icon: 'success'
});
},
complete: function () {
that.setData({ selected: 0 });//取消選中
}
});
},
fail: function (res) {
//do anything
}
});
}
上面的代碼看出,調用wx.requestPayment之前需先請求服務端下單,并返回對應的支付密匙信息。好,接下來我們看看服務端下單的邏輯怎么寫。
/**
* 下單支付
*
* @author woden
*
*/
//獲取當前用戶
def user = session.appUser();
log.debug("user:{}",user);
if(!user?.nickName){
nami.error("用戶拒絕提供資料,無法支付.");
}
//更新用戶信息
if(db.find("select OPEN_ID from PAY_USER where OPEN_ID=?",user.openId)){//找得到則更新
db.exec("update PAY_USER set NICK_NAME=?,AVATAR_URL=?,UPDATE_TIME=? where OPEN_ID=?"
,user.nickName
,user.avatarUrl
,now
,user.openId
);
}else{//否則新增
db.exec("insert into PAY_USER (OPEN_ID,NICK_NAME,AVATAR_URL,UPDATE_TIME) values (?,?,?,?)"
,user.openId
,user.nickName
,user.avatarUrl
,now
);
}
//支付下單
//調用NAMI框架的app.pay.order接口(封裝了微信支付下單),直接獲取訂單order對象
def total = request.getInteger("total");
def order =app.pay.order([
openId : user.openId,
total : total,
body : '多謝贊賞',
notify : nami.invoke('host.groovy')+'/request/scholes_pay/pay_callback.groovy' //回調
]);
//向客戶端生成支付加密串
//調用NAMI框架的app.pay.signatur接口(根據(jù)prepayId換取支付密匙)
return [tradeNumber:order.tradeNumber,signature:app.pay.signature(order.prepayId)];
這里NAMI有兩個接口,分別是:
NAMI框架已將接口調用、基于支付安全考慮的簽名加密算法都封裝好,讓開發(fā)人員關注業(yè)務邏輯本身而非這些技術細節(jié),這在實際項目開發(fā)的時候可以大大提升效率。
即使開發(fā)這么小的案例,數(shù)數(shù)也踩了不少坑,除了像兼容性問題、工具BUG、各種組件BUG這種已經讓開發(fā)者們“習以為常”的坑以外,我重點列幾個被官方升級“升”出來的坑。
最后說個血淚小教訓,調試微信支付的時候千萬千萬不要用銀行卡或信用卡去測試,頻繁的支付1分錢1毛錢1塊錢,會導致卡被銀行封掉。
另外用內置的DB console可以查看案例的運營數(shù)據(jù),觀摩一下。讀者可以看出誰贊賞了最多,最多又是多少錢嗎?
下載源碼:大家可以從 Github獲取“贊賞”案例源碼 。