如何使用Koa2開發(fā)微信二維碼掃碼支付

    這次給大家?guī)砣绾问褂肒oa2開發(fā)微信二維碼掃碼支付,使用Koa2開發(fā)微信二維碼掃碼支付的注意事項有哪些,下面就是實戰(zhàn)案例,一起來看一下。愛掏網(wǎng) - it200.com

    前段時間在開發(fā)一個功能,要求是通過微信二維碼進行掃碼支付。愛掏網(wǎng) - it200.com這個情景我們屢見不鮮了,各種電子商城、線下的自動販賣機等等都會有這個功能。愛掏網(wǎng) - it200.com平時只是使用者,如今變?yōu)殚_發(fā)者,也是有不小的坑。愛掏網(wǎng) - it200.com所以特此寫一篇博客記錄一下。愛掏網(wǎng) - it200.com

    注: 要開發(fā)微信二維碼支付,你必須要有相應(yīng)的商戶號的權(quán)限,否則你是無法開發(fā)的。愛掏網(wǎng) - it200.com若無相應(yīng)權(quán)限,本文不推薦閱讀。愛掏網(wǎng) - it200.com

    兩種模式

    打開微信支付的文檔,我們可以看到兩種支付模式:模式一和模式二。愛掏網(wǎng) - it200.com這二者的流程圖微信的文檔里都給出了(不過說實話畫得真的有點丑)。愛掏網(wǎng) - it200.com

    文檔里指出了二者的區(qū)別:

    模式一開發(fā)前,商戶必須在公眾平臺后臺設(shè)置支付回調(diào)URL。愛掏網(wǎng) - it200.comURL實現(xiàn)的功能:接收用戶掃碼后微信支付系統(tǒng)回調(diào)的productid和openid。愛掏網(wǎng) - it200.com

    模式二與模式一相比,流程更為簡單,不依賴設(shè)置的回調(diào)支付URL。愛掏網(wǎng) - it200.com商戶后臺系統(tǒng)先調(diào)用微信支付的統(tǒng)一下單接口,微信后臺系統(tǒng)返回鏈接參數(shù)code_url,商戶后臺系統(tǒng)將code_url值生成二維碼圖片,用戶使用微信客戶端掃碼后發(fā)起支付。愛掏網(wǎng) - it200.com注意:code_url有效期為2小時,過期后掃碼不能再發(fā)起支付。愛掏網(wǎng) - it200.com

    模式一是我們平時在網(wǎng)購的時候比較常見的,會彈出一個專門的頁面用于掃碼支付,然后支付成功后這個頁面會再次跳轉(zhuǎn)回回調(diào)頁面,通知你支付成功。愛掏網(wǎng) - it200.com第二種的話想對少一些,不過第二種開發(fā)起來相對簡單點。愛掏網(wǎng) - it200.com 本文主要介紹模式二的開發(fā)愛掏網(wǎng) - it200.com

    搭建Koa2的簡單開發(fā)環(huán)境

    快速搭建Koa2的開發(fā)環(huán)境我推薦可以使用koa-generator 。愛掏網(wǎng) - it200.com腳手架能幫我們省去Koa項目一開始的一些基本中間件的書寫步驟。愛掏網(wǎng) - it200.com(如果你想學(xué)習(xí)Koa最好自己搭建一個。愛掏網(wǎng) - it200.com如果你已經(jīng)會Koa了就可以使用一些快速腳手架了。愛掏網(wǎng) - it200.com

    首先全局安裝 koa-generator

    npm?install?-g?koa-generator
    #or
    yarn?global?add?koa-generator
    登錄后復(fù)制

    然后找一個目錄用來存放Koa項目,我們打算給這個項目取個名字叫做 koa-wechatpay ,然后就可以輸入 koa2 koa-wechatpay愛掏網(wǎng) - it200.com然后腳手架會自動創(chuàng)建相應(yīng)文件夾 koa-wechatpay ,并生成基本骨架。愛掏網(wǎng) - it200.com進入這個文件夾,安裝相應(yīng)的插件。愛掏網(wǎng) - it200.com輸入:

    npm?install
    #or
    yarn
    登錄后復(fù)制

    接著你可以輸入 npm start 或者 yarn start 來運行項目(默認監(jiān)聽在3000端口)。愛掏網(wǎng) - it200.com

    如果不出意外,你的項目跑起來了,然后我們用postman測試一下:

    這條路由是在 routes/index.js 里。愛掏網(wǎng) - it200.com

    如果你看到了

    {
    ?"title":?"koa2?json"
    }
    登錄后復(fù)制

    就說明沒問題。愛掏網(wǎng) - it200.com(如果有問題,檢查一下是不是端口被占用了等等。愛掏網(wǎng) - it200.com

    接下來在 routes 文件夾里我們新建一個 wechatpay.js 的文件用來書寫我們的流程。愛掏網(wǎng) - it200.com

    簽名

    跟微信的服務(wù)器交流很關(guān)鍵的一環(huán)是簽名必須正確,如果簽名不正確,那么一切都白搭。愛掏網(wǎng) - it200.com

    首先我們需要去公眾號的后臺獲取我們所需要的如下相應(yīng)的id或者key的信息。愛掏網(wǎng) - it200.com其中 notify_urlserver_ip 是用于當(dāng)我們支付成功后,微信會主動往這個url post 支付成功的信息。愛掏網(wǎng) - it200.com

    簽名算法如下:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_3

    為了簽名正確,我們需要安裝一下 md5愛掏網(wǎng) - it200.com

    npm?install?md5?--save
    #or
    yarn?add?md5
    登錄后復(fù)制
    const?md5?=?require('md5')
    const?appid?=?'xxx'
    const?mch_id?=?'yyy'
    const?mch_api_key?=?'zzz'
    const?notify_url?=?'http://xxx/api/notify'?//?服務(wù)端可訪問的域名和接口
    const?server_ip?=?'xx.xx.xx.xx'?//?服務(wù)端的ip地址
    const?trade_type?=?'NATIVE'?//?NATIVE對應(yīng)的是二維碼掃碼支付
    let?body?=?'XXX的充值支付'?//?用于顯示在支付界面的提示詞
    登錄后復(fù)制

    然后開始寫簽名函數(shù):

    const?signString?=?(fee,?ip,?nonce)?=>?{
    ?let?tempString?=?`appid=${appid}&body=${body}&mch_id=${mch_id}&nonce_str=${nonce}&notify_url=${notify_url}&out_trade_no=${nonce}&spbill_create_ip=${ip}&total_fee=${fee}&trade_type=${trade_type}&key=${mch_api_key}`
    ?return?md5(tempString).toUpperCase()
    }
    登錄后復(fù)制

    其中 fee 是要充值的費用,以分為單位。愛掏網(wǎng) - it200.com比如要充值1塊錢, fee 就是100。愛掏網(wǎng) - it200.comip是個比較隨意的選項,只要符合規(guī)則的ip經(jīng)過測試都是可以的,下文里我用的是 server_ip愛掏網(wǎng) - it200.com nonce 就是微信要求的不重復(fù)的32位以內(nèi)的字符串,通常可以使用訂單號等唯一標(biāo)識的字符串。愛掏網(wǎng) - it200.com

    由于跟微信的服務(wù)器交流都是用xml來交流,所以現(xiàn)在我們要手動組裝一下post請求的 xml :

    const?xmlBody?=?(fee,?nonce_str)?=>?{
    ?const?xml?=?`
    ?
    ?${appid}
    ?${body}
    ?${mch_id}
    ?${nonce_str}
    ?${notify_url}
    ?${nonce_str}
    ?${fee}
    ?${server_ip}
    ?NATIVE
    ?${signString(fee,?server_ip,?nonce_str)}
    ?
    ?`
    ?return?{
    ?xml,
    ?out_trade_no:?nonce_str
    ?}
    }
    登錄后復(fù)制

    如果你怕自己的簽名的 xml 串有問題,可以提前在微信提供的簽名校驗工具里先校驗一遍,看看是否能通過。愛掏網(wǎng) - it200.com

    發(fā)送請求

    因為需要跟微信服務(wù)端發(fā)請求,所以我選擇了 axios 這個在瀏覽器端和node端都能發(fā)起ajax請求的庫。愛掏網(wǎng) - it200.com

    安裝過程不再贅述。愛掏網(wǎng) - it200.com繼續(xù)在 wechatpay.js 寫發(fā)請求的邏輯。愛掏網(wǎng) - it200.com

    由于微信給我們返回的也將是一個xml格式的字符串。愛掏網(wǎng) - it200.com所以我們需要預(yù)先寫好解析函數(shù),將xml解析成js對象。愛掏網(wǎng) - it200.com為此你可以安裝一個 xml2js 。愛掏網(wǎng) - it200.com安裝過程跟上面的類似,不再贅述。愛掏網(wǎng) - it200.com

    微信會給我們返回一個諸如下面格式的 xml 字符串:

    登錄后復(fù)制

    我們的目標(biāo)是轉(zhuǎn)為如下的js對象,好讓我們用js來操作數(shù)據(jù):

    {
    ?return_code:?'SUCCESS',?//?SUCCESS?或者?FAIL
    ?return_msg:?'OK',
    ?appid:?'wx742xxxxxxxxxxxxx',
    ?mch_id:?'14899xxxxx',
    ?nonce_str:?'R69QXXXXXXXX6O',
    ?sign:?'79F0891XXXXXX189507A184XXXXXXXXX',
    ?result_code:?'SUCCESS',
    ?prepay_id:?'wx152316xxxxxxxxxxxxxxxxxxxxxxxxxxx',
    ?trade_type:?'NATIVE',
    ?code_url:?'weixin://wxpay/xxxurl?pr=dQNakHH'?//?用于生成支付二維碼的鏈接
    }
    登錄后復(fù)制

    于是我們寫一個函數(shù),調(diào)用 xml2js 來解析xml:

    //?將XML轉(zhuǎn)為JS對象
    const?parseXML?=?(xml)?=>?{
    ?return?new?Promise((res,?rej)?=>?{
    ?xml2js.parseString(xml,?{trim:?true,?explicitArray:?false},?(err,?json)?=>?{
    ?if?(err)?{
    ?rej(err)
    ?}?else?{
    ?res(json.xml)
    ?}
    ?})
    ?})
    }
    登錄后復(fù)制

    上面的代碼返回了一個 Promise 對象,因為 xml2js 的操作是在回調(diào)函數(shù)里返回的結(jié)果,所以為了配合Koa2的 asyncawait ,我們可以將其封裝成一個 Promise 對象,將解析完的結(jié)果通過 resolve 返回回去。愛掏網(wǎng) - it200.com這樣就能用 await 來取數(shù)據(jù)了:

    const?axios?=?require('axios')
    const?url?=?'https://api.mch.weixin.qq.com/pay/unifiedorder'?//?微信服務(wù)端地址
    const?pay?=?async?(ctx)?=>?{
    ?const?form?=?ctx.request.body?//?通過前端傳來的數(shù)據(jù)
    ?const?orderNo?=?'XXXXXXXXXXXXXXXX'?//?不重復(fù)的訂單號
    ?const?fee?=?form.fee?//?通過前端傳來的費用值
    ?const?data?=?xmlBody(fee,?orderNo)?//?fee是費用,orderNo是訂單號(唯一)
    ?const?res?=?await?axios.post(url,?{
    ?data:?data.xml
    ?}).then(async?res?=>?{
    ?const?resJson?=?await?parseXML(res.data)
    ?return?resJson?//?拿到返回的數(shù)據(jù)
    ?}).catch(err?=>?{
    ?console.log(err)
    ?})
    ?if?(res.return_code?===?'SUCCESS')?{?//?如果返回的
    ?return?ctx.body?=?{
    ?success:?true,
    ?message:?'請求成功',
    ?code_url:?res.code_url,?//?code_url就是用于生成支付二維碼的鏈接
    ?order_no:?orderNo?//?訂單號
    ?}
    ?}
    ?ctx.body?=?{
    ?success:?false,
    ?message:?'請求失敗'
    ?}
    }
    router.post('/api/pay',?pay)
    module.exports?=?router
    登錄后復(fù)制

    然后我們要將這個router掛載到根目錄的 app.js 里去。愛掏網(wǎng) - it200.com

    找到之前默認的兩個路由,一個 index ,一個 user

    const?index?=?require('./routes/index')
    const?users?=?require('./routes/users')
    const?wechatpay?=?require('./routes/wechatpay')?//?加在這里
    登錄后復(fù)制

    然后到頁面底下掛載這個路由:

    //?routes
    app.use(index.routes(),?index.allowedMethods())
    app.use(users.routes(),?users.allowedMethods())
    app.use(wechatpay.routes(),?users.allowedMethods())?//?加在這里
    登錄后復(fù)制

    于是你就可以通過發(fā)送 /api/pay 來請求二維碼數(shù)據(jù)啦。愛掏網(wǎng) - it200.com(如果有跨域需要自己考慮解決跨域方案,可以跟Koa放在同域里,也可以開一層proxy來轉(zhuǎn)發(fā),也可以開CORS頭等等)

    注意, 本例里是用前端來生成二維碼,其實也可以通過后端生成二維碼,然后再返回給前端。愛掏網(wǎng) - it200.com不過為了簡易演示,本例采用前端通過獲取 code_url 后,在前端生成二維碼。愛掏網(wǎng) - it200.com

    展示支付二維碼

    前端我用的是 Vue ,當(dāng)然你可以選擇你喜歡的前端框架。愛掏網(wǎng) - it200.com這里關(guān)注點在于通過拿到剛才后端傳過來的 code_url 來生成二維碼。愛掏網(wǎng) - it200.com

    在前端,我使用的是 @xkeshi/vue-qrcode 這個庫來生成二維碼。愛掏網(wǎng) - it200.com它調(diào)用特別簡單:

    import?VueQrcode?from?'@xkeshi/vue-qrcode'
    export?default?{
    ?components:?{
    ?VueQrcode
    ?},
    ?//?...其他代碼
    }
    登錄后復(fù)制

    然后就可以在前端里用 的組件來生成二維碼了:

    登錄后復(fù)制

    放到Dialog里就是這樣的效果:

    文本是我自己添加的

    ?

    付款成功自動刷新頁面

    有兩種將支付成功寫入數(shù)據(jù)庫的辦法。愛掏網(wǎng) - it200.com

    一種是在打開了掃碼對話框后,不停向微信服務(wù)端輪詢支付結(jié)果,如果支付成功,那么就向后端發(fā)起請求,告訴后端支付成功,讓后端寫入數(shù)據(jù)庫。愛掏網(wǎng) - it200.com

    一種是后端一直開著接口,等微信主動給后端的 notify_url 發(fā)起post請求,告訴后端支付結(jié)果,讓后端寫入數(shù)據(jù)庫。愛掏網(wǎng) - it200.com然后此時前端向后端輪詢的時候應(yīng)該是去數(shù)據(jù)庫取輪詢該訂單的支付結(jié)果,如果支付成功就關(guān)閉Dialog。愛掏網(wǎng) - it200.com

    第一種比較簡單但是不安全:試想萬一用戶支付成功的同時關(guān)閉了頁面,或者用戶支付成功了,但是網(wǎng)絡(luò)有問題導(dǎo)致前端沒法往后端發(fā)支付成功的結(jié)果,那么后端就一直沒辦法寫入支付成功的數(shù)據(jù)。愛掏網(wǎng) - it200.com

    第二種雖然麻煩,但是保證了安全。愛掏網(wǎng) - it200.com所有的支付結(jié)果都必須等微信主動向后端通知,后端存完數(shù)據(jù)庫后再返回給前端消息。愛掏網(wǎng) - it200.com這樣哪怕用戶支付成功的同時關(guān)閉了頁面,下次再打開的時候,由于數(shù)據(jù)庫已經(jīng)寫入了,所以拿到的也是支付成功的結(jié)果。愛掏網(wǎng) - it200.com

    所以 付款成功自動刷新頁面 這個部分我們分為兩個部分來說:

    前端部分

    Vue的data部分

    data:?{
    ?payStatus:?false,?//?未支付成功
    ?retryCount:?0,?//?輪詢次數(shù),從0-200
    ?orderNo:?'xxx',?//?從后端傳來的order_no
    ?codeUrl:?'xxx'?//?從后端傳來的code_url
    }
    登錄后復(fù)制

    在methods里寫一個查詢訂單信息的方法:

    //?...
    handleCheckBill?()?{
    ?return?setTimeout(()?=>?{
    ?if?(!this.payStatus?&&?this.retryCount??{
    ?if?(res.data.success)?{
    ?this.payStatus?=?true
    ?location.reload()?//?偷懶就用reload重新刷新頁面
    ?}?else?{
    ?this.handleCheckBill()
    ?}
    ?}).catch(err?=>?{
    ?console.log(err)
    ?})
    ?}?else?{
    ?location.reload()
    ?}
    ?},?1000)
    }
    登錄后復(fù)制

    在打開二維碼Dialog的時候,這個方法就啟用了。愛掏網(wǎng) - it200.com然后就開始輪詢。愛掏網(wǎng) - it200.com我訂了一個時間,200s后如果還是沒有付款信息也自動刷新頁面。愛掏網(wǎng) - it200.com實際上你可以自己根據(jù)項目的需要來定義這個時間。愛掏網(wǎng) - it200.com

    后端部分

    前端到后端只有一個接口,但是后端有兩個接口。愛掏網(wǎng) - it200.com一個是用來接收微信的推送,一個是用來接收前端的查詢請求。愛掏網(wǎng) - it200.com

    先來寫最關(guān)鍵的微信的推送請求處理。愛掏網(wǎng) - it200.com由于我們接收微信的請求是在Koa的路由里,并且是以流的形式傳輸?shù)摹?b class="xhide">愛掏網(wǎng) - it200.com需要讓Koa支持解析xml格式的body,所以需要安裝一個rawbody 來獲取xml格式的body。愛掏網(wǎng) - it200.com

    //?處理微信支付回傳notify
    //?如果收到消息要跟微信回傳是否接收到
    const?handleNotify?=?async?(ctx)?=>?{
    ?const?xml?=?await?rawbody(ctx.req,?{
    ?length:?ctx.request.length,
    ?limit:?'1mb',
    ?encoding:?ctx.request.charset?||?'utf-8'
    ?})
    ?const?res?=?await?parseXML(xml)?//?解析xml
    ?if?(res.return_code?===?'SUCCESS')?{
    ?if?(res.result_code?===?'SUCCESS')?{?//?如果都為SUCCESS代表支付成功
    ?//?...?這里是寫入數(shù)據(jù)庫的相關(guān)操作
    ?//?開始回傳微信
    ?ctx.type?=?'application/xml'?//?指定發(fā)送的請求類型是xml
    ?//?回傳微信,告訴已經(jīng)收到
    ?return?ctx.body?=?`
    ?
    ?
    ?
    ?`
    ?}
    ?}
    ?//?如果支付失敗,也回傳微信
    ?ctx.status?=?400
    ?ctx.type?=?'application/xml'
    ?ctx.body?=?`
    ?
    ?
    ?
    ?`
    }
    router.post('/api/notify',?handleNotify)
    登錄后復(fù)制

    這里的坑就是Koa處理微信回傳的xml。愛掏網(wǎng) - it200.com如果不知道是以 raw-body 的形式回傳的,會調(diào)試半天。愛掏網(wǎng) - it200.com愛掏網(wǎng) - it200.com

    接下來這個就是比較簡單的給前端回傳的了。愛掏網(wǎng) - it200.com

    const?checkBill?=?async?(ctx)?=>?{
    ?const?form?=?ctx.request.body
    ?const?orderNo?=?form.orderNo
    ?const?result?=?await?數(shù)據(jù)庫操作
    ?if?(result)?{?//?如果訂單支付成功
    ?return?ctx.body?=?{
    ?success:?true
    ?}
    ?}
    ?ctx.status?=?400
    ?ctx.body?=?{
    ?success:?false
    ?}
    }
    router.post('/api/check-bill',?checkBill)
    登錄后復(fù)制

    相信看了本文案例你已經(jīng)掌握了方法,更多精彩請關(guān)注愛掏網(wǎng) - it200.com其它相關(guān)文章!

    推薦閱讀:

    如何操作Koa2微信公眾號開發(fā)之本地開發(fā)調(diào)試環(huán)境搭建

    如何操作Koa2微信公眾號實現(xiàn)消息管理

    以上就是如何使用Koa2開發(fā)微信二維碼掃碼支付的詳細內(nèi)容,更多請關(guān)注愛掏網(wǎng) - it200.com其它相關(guān)文章!

    聲明:所有內(nèi)容來自互聯(lián)網(wǎng)搜索結(jié)果,不保證100%準(zhǔn)確性,僅供參考。如若本站內(nèi)容侵犯了原著者的合法權(quán)益,可聯(lián)系我們進行處理。
    發(fā)表評論
    更多 網(wǎng)友評論0 條評論)
    暫無評論

    返回頂部

    主站蜘蛛池模板: 伊人久久精品一区二区三区| 狠狠色综合一区二区| 日本成人一区二区三区| 亚欧成人中文字幕一区| 精品无码一区二区三区爱欲| 人妻体内射精一区二区三区| 中文字幕在线视频一区| 国产精品一区二区资源| 少妇激情一区二区三区视频| 久久精品一区二区三区中文字幕 | 国产嫖妓一区二区三区无码| 亚洲乱码国产一区三区| 精品一区二区三区免费观看| 久久无码人妻精品一区二区三区| 偷拍精品视频一区二区三区| 91午夜精品亚洲一区二区三区| 国产无码一区二区在线| 国产大秀视频一区二区三区| 精品欧美一区二区在线观看| 亚洲AV成人精品一区二区三区| 91久久精品国产免费一区| 亚洲AⅤ视频一区二区三区| 在线精品亚洲一区二区| 中文字幕人妻第一区| 麻豆一区二区免费播放网站| 国产福利一区二区三区视频在线| 2022年亚洲午夜一区二区福利| 无码av免费一区二区三区| 久久久精品人妻一区二区三区蜜桃| 无码人妻少妇色欲AV一区二区| 麻豆AV一区二区三区久久| 亚洲Av无码一区二区二三区| 色噜噜狠狠一区二区| 尤物精品视频一区二区三区| 国产在线步兵一区二区三区 | 久久99精品免费一区二区| 美女免费视频一区二区| 动漫精品第一区二区三区| 成人国内精品久久久久一区| 国产成人精品一区二区秒拍| 国产伦精品一区二区三区女|