<em id="6dylr"></em>

<abbr id="6dylr"></abbr>

      ${body}<\/body>\r\n?${mch_id}<\/mch_id>\r\n?${nonce_str}<\/nonce_str>\r\n?${notify_url}<\/notify_url>\r\n?${nonce_str}<\/out_trade_no>\r\n?${fee}<\/total_fee>\r\n?${server_ip}<\/spbill_create_ip>\r\n?NATIVE<\/trade_type>\r\n?${signString(fee,?server_ip,?nonce_str)}<\/sign>\r\n?<\/xml>\r\n?`\r\n?return?{\r\n?xml,\r\n?out_trade_no:?nonce_str\r\n?}\r\n}<\/pre>\n

      如果你怕自己的簽名的 xml<\/code> 串有問題,可以提前在微信提供的簽名校驗工具里先校驗一遍,看看是否能通過。<\/p>\n

      發(fā)送請求<\/strong><\/span><\/p>\n

      因為需要跟微信服務端發(fā)請求,所以我選擇了 axios<\/code> 這個在瀏覽器端和node端都能發(fā)起ajax請求的庫。<\/p>\n

      安裝過程不再贅述。繼續(xù)在 wechatpay.js<\/code> 寫發(fā)請求的邏輯。<\/p>\n

      由于微信給我們返回的也將是一個xml格式的字符串。所以我們需要預先寫好解析函數(shù),將xml解析成js對象。為此你可以安裝一個 xml2js 。安裝過程跟上面的類似,不再贅述。<\/p>\n

      微信會給我們返回一個諸如下面格式的 xml<\/code> 字符串:<\/p>\n

      <\/return_code>\r\n<\/return_msg>\r\n<\/appid>\r\n<\/mch_id>\r\n<\/nonce_str>\r\n<\/sign>\r\n<\/result_code>\r\n<\/prepay_id>\r\n<\/trade_type>\r\n<\/code_url>\r\n<\/xml><\/pre>\n

      我們的目標是轉(zhuǎn)為如下的js對象,好讓我們用js來操作數(shù)據(jù):<\/p>\n

      {\r\n?return_code:?'SUCCESS',?\/\/?SUCCESS?或者?FAIL\r\n?return_msg:?'OK',\r\n?appid:?'wx742xxxxxxxxxxxxx',\r\n?mch_id:?'14899xxxxx',\r\n?nonce_str:?'R69QXXXXXXXX6O',\r\n?sign:?'79F0891XXXXXX189507A184XXXXXXXXX',\r\n?result_code:?'SUCCESS',\r\n?prepay_id:?'wx152316xxxxxxxxxxxxxxxxxxxxxxxxxxx',\r\n?trade_type:?'NATIVE',\r\n?code_url:?'weixin:\/\/wxpay\/xxxurl?pr=dQNakHH'?\/\/?用于生成支付二維碼的鏈接\r\n}<\/pre>\n

      于是我們寫一個函數(shù),調(diào)用 xml2js<\/code> 來解析xml:<\/p>\n

      \/\/?將XML轉(zhuǎn)為JS對象\r\nconst?parseXML?=?(xml)?=>?{\r\n?return?new?Promise((res,?rej)?=>?{\r\n?xml2js.parseString(xml,?{trim:?true,?explicitArray:?false},?(err,?json)?=>?{\r\n?if?(err)?{\r\n?rej(err)\r\n?}?else?{\r\n?res(json.xml)\r\n?}\r\n?})\r\n?})\r\n}<\/pre>\n

      上面的代碼返回了一個 Promise<\/code> 對象,因為 xml2js<\/code> 的操作是在回調(diào)函數(shù)<\/a>里返回的結(jié)果,所以為了配合Koa2的 async<\/code> 、 await<\/code> ,我們可以將其封裝成一個 Promise<\/code> 對象,將解析完的結(jié)果通過 resolve<\/code> 返回回去。這樣就能用 await<\/code> 來取數(shù)據(jù)了:<\/p>\n

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

      然后我們要將這個router掛載到根目錄的 app.js<\/code> 里去。<\/p>\n

      找到之前默認的兩個路由,一個 index<\/code> ,一個 user<\/code> :<\/p>\n

      const?index?=?require('.\/routes\/index')\r\nconst?users?=?require('.\/routes\/users')\r\nconst?wechatpay?=?require('.\/routes\/wechatpay')?\/\/?加在這里<\/pre>\n

      然后到頁面底下掛載這個路由:<\/p>\n

      \/\/?routes\r\napp.use(index.routes(),?index.allowedMethods())\r\napp.use(users.routes(),?users.allowedMethods())\r\napp.use(wechatpay.routes(),?users.allowedMethods())?\/\/?加在這里<\/pre>\n

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

      注意, 本例里是用前端來生成二維碼,其實也可以通過后端生成二維碼,然后再返回給前端。不過為了簡易演示,本例采用前端通過獲取 code_url<\/code> 后,在前端生成二維碼。<\/p>\n

      展示支付二維碼<\/strong><\/span><\/p>\n

      前端我用的是 Vue<\/code> ,當然你可以選擇你喜歡的前端框架。這里關(guān)注點在于通過拿到剛才后端傳過來的 code_url<\/code> 來生成二維碼。<\/p>\n

      在前端,我使用的是 @xkeshi\/vue-qrcode 這個庫來生成二維碼。它調(diào)用特別簡單:<\/p>\n

      import?VueQrcode?from?'@xkeshi\/vue-qrcode'\r\nexport?default?{\r\n?components:?{\r\n?VueQrcode\r\n?},\r\n?\/\/?...其他代碼\r\n}<\/pre>\n

      然后就可以在前端里用 <\/code> 的組件來生成二維碼了:<\/p>\n

      <\/pre>\n

      放到Dialog里就是這樣的效果:<\/p>\n

      文本是我自己添加的<\/p>\n

      \"\"?<\/p>\n

      付款成功自動刷新頁面<\/strong><\/span><\/p>\n

      有兩種將支付成功寫入數(shù)據(jù)庫的辦法。<\/p>\n

      一種是在打開了掃碼對話框后,不停向微信服務端輪詢支付結(jié)果,如果支付成功,那么就向后端發(fā)起請求,告訴后端支付成功,讓后端寫入數(shù)據(jù)庫。<\/p>\n

      一種是后端一直開著接口,等微信主動給后端的 notify_url<\/code> 發(fā)起post請求,告訴后端支付結(jié)果,讓后端寫入數(shù)據(jù)庫。然后此時前端向后端輪詢的時候應該是去數(shù)據(jù)庫取輪詢該訂單的支付結(jié)果,如果支付成功就關(guān)閉Dialog。<\/p>\n

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

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

      所以 付款成功自動刷新頁面<\/code> 這個部分我們分為兩個部分來說:<\/p>\n

      前端部分<\/strong><\/span><\/p>\n

      Vue的data部分<\/p>\n

      data:?{\r\n?payStatus:?false,?\/\/?未支付成功\r\n?retryCount:?0,?\/\/?輪詢次數(shù),從0-200\r\n?orderNo:?'xxx',?\/\/?從后端傳來的order_no\r\n?codeUrl:?'xxx'?\/\/?從后端傳來的code_url\r\n}<\/pre>\n

      在methods里寫一個查詢訂單信息的方法:<\/p>\n

      \/\/?...\r\nhandleCheckBill?()?{\r\n?return?setTimeout(()?=>?{\r\n?if?(!this.payStatus?&&?this.retryCount?< 120) {\r\n this.retryCount += 1\r\n axios.post('\/api\/check-bill', { \/\/ 向后端請求訂單支付信息\r\n orderNo: this.orderNo\r\n })\r\n .then(res =>?{\r\n?if?(res.data.success)?{\r\n?this.payStatus?=?true\r\n?location.reload()?\/\/?偷懶就用reload重新刷新頁面\r\n?}?else?{\r\n?this.handleCheckBill()\r\n?}\r\n?}).catch(err?=>?{\r\n?console.log(err)\r\n?})\r\n?}?else?{\r\n?location.reload()\r\n?}\r\n?},?1000)\r\n}<\/pre>\n

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

      后端部分<\/strong><\/span><\/p>\n

      前端到后端只有一個接口,但是后端有兩個接口。一個是用來接收微信的推送,一個是用來接收前端的查詢請求。<\/p>\n

      先來寫最關(guān)鍵的微信的推送請求處理。由于我們接收微信的請求是在Koa的路由里,并且是以流的形式傳輸?shù)?。需要讓Koa支持解析xml格式的body,所以需要安裝一個rawbody 來獲取xml格式的body。<\/p>\n

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

      這里的坑就是Koa處理微信回傳的xml。如果不知道是以 raw-body<\/code> 的形式回傳的,會調(diào)試半天。。<\/p>\n

      接下來這個就是比較簡單的給前端回傳的了。<\/p>\n

      const?checkBill?=?async?(ctx)?=>?{\r\n?const?form?=?ctx.request.body\r\n?const?orderNo?=?form.orderNo\r\n?const?result?=?await?數(shù)據(jù)庫操作\r\n?if?(result)?{?\/\/?如果訂單支付成功\r\n?return?ctx.body?=?{\r\n?success:?true\r\n?}\r\n?}\r\n?ctx.status?=?400\r\n?ctx.body?=?{\r\n?success:?false\r\n?}\r\n}\r\nrouter.post('\/api\/check-bill',?checkBill)<\/pre>\n

      相信看了本文案例你已經(jīng)掌握了方法,更多精彩請關(guān)注php中文網(wǎng)其它相關(guān)文章!<\/p>\n

      推薦閱讀:<\/p>\n

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

      如何操作Koa2微信公眾號實現(xiàn)消息管理<\/a>
      <\/p>"}

      亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

      首頁 微信小程式 微信開發(fā) 如何使用Koa2開發(fā)微信二維碼掃碼支付

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

      May 29, 2018 am 11:23 AM
      koa2 開發(fā)

      這次帶給大家如何使用Koa2開發(fā)微信二維碼掃碼支付,使用Koa2開發(fā)微信二維碼掃碼支付的注意事項有哪些,下面就是實戰(zhàn)案例,一起來看一下。

      前段時間在開發(fā)一個功能,要求是透過微信二維碼進行掃碼支付。這個情景我們屢見不鮮了,各種電子商城、線下的自動販賣機等等都會有這個功能。平時只是使用者,如今變成開發(fā)者,也是有不小的坑。所以特此寫一篇部落格記錄一下。

      附註: 要開發(fā)微信二維碼支付,你必須要有對應的商家號碼的權(quán)限,否則你是無法開發(fā)的。若無相應權(quán)限,本文不建議閱讀。

      兩種模式

      開啟微信支付的文檔,我們可以看到兩種支付模式:模式一和模式二。這二者的流程圖微信的文檔裡都給了(不過說實話畫得真的有點醜)。

      文件裡指出了二者的差異:

      模式一開發(fā)前,商家必須在公眾平臺後臺設定支付回呼URL。 URL實現(xiàn)的功能:接收用戶掃碼後微信支付系統(tǒng)回呼的productid和openid。

      模式二與模式一相比,流程更為簡單,不依賴設定的回呼支付URL。商家後臺系統(tǒng)先呼叫微信支付的統(tǒng)一下單接口,微信後臺系統(tǒng)返回連結(jié)參數(shù)code_url,商家後臺系統(tǒng)將code_url值產(chǎn)生二維碼圖片,使用者使用微信客戶端掃碼後發(fā)起付款。注意:code_url有效期限為2小時,過期後掃碼不能再發(fā)起付款。

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

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

      #快速搭建Koa2的開發(fā)環(huán)境我推薦可以使用koa-generator 。鷹架能幫我們省去Koa專案一開始的一些基本中間件的書寫步驟。 (如果你想學習Koa最好自己搭建一個。如果你已經(jīng)會Koa了就可以使用一些快速腳手架了。)

      首先全局安裝koa-generator

      npm?install?-g?koa-generator
      #or
      yarn?global?add?koa-generator

      接著找一個目錄用來存放Koa項目,我們打算給這個項目取個名字叫做koa-wechatpay ,然後就可以輸入koa2 koa-wechatpay 。然後腳手架會自動建立對應資料夾 koa-wechatpay ,並產(chǎn)生基本骨架。進入這個資料夾,安裝對應的插件。輸入:

      npm?install
      #or
      yarn

      接著你可以輸入 npm startyarn start 來執(zhí)行專案(預設監(jiān)聽在3000埠)。

      如果不出意外,你的專案跑起來了,然後我們用postman測試一下:

      這條路由是在 routes/index.js 裡。

      如果你看到了

      {
      ?"title":?"koa2?json"
      }

      就表示沒問題。 (如果有問題,檢查一下是不是連接埠被佔用了等等。)

      接下來在routes 資料夾裡我們新建一個wechatpay.js 的文件用來書寫我們的流程。

      簽名

      跟微信的伺服器交流很關(guān)鍵的一環(huán)是簽章必須正確,如果簽章不正確,那麼一切都白搭。

      首先我們需要去公眾號的後臺取得我們所需要的以下對應的id或key的資訊。其中 notify_urlserver_ip 是用來當我們支付成功後,微信會主動往這個url post 支付成功的訊息。

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

      為了簽章正確,我們需要安裝一下md5

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

      然後開始寫簽名函數(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()
      }

      其中 fee 是要充值的費用,以分為單位。比如要充值1塊錢, fee 就是100。ip是個比較隨意的選項,只要符合規(guī)則的ip經(jīng)過測試都是可以的,下文里我用的是 server_ipnonce 就是微信要求的不重復的32位以內(nèi)的字符串,通??梢允褂糜唵翁柕任ㄒ粯俗R的字符串。

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

      const?xmlBody?=?(fee,?nonce_str)?=>?{
      ?const?xml?=?`
      ?<xml>
      ?<appid>${appid}</appid>
      ?<body>${body}</body>
      ?<mch_id>${mch_id}</mch_id>
      ?<nonce_str>${nonce_str}</nonce_str>
      ?<notify_url>${notify_url}</notify_url>
      ?<out_trade_no>${nonce_str}</out_trade_no>
      ?<total_fee>${fee}</total_fee>
      ?<spbill_create_ip>${server_ip}</spbill_create_ip>
      ?<trade_type>NATIVE</trade_type>
      ?<sign>${signString(fee,?server_ip,?nonce_str)}</sign>
      ?</xml>
      ?`
      ?return?{
      ?xml,
      ?out_trade_no:?nonce_str
      ?}
      }

      如果你怕自己的簽名的 xml 串有問題,可以提前在微信提供的簽名校驗工具里先校驗一遍,看看是否能通過。

      發(fā)送請求

      因為需要跟微信服務端發(fā)請求,所以我選擇了 axios 這個在瀏覽器端和node端都能發(fā)起ajax請求的庫。

      安裝過程不再贅述。繼續(xù)在 wechatpay.js 寫發(fā)請求的邏輯。

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

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

      <xml><return_code><![CDATA[SUCCESS]]></return_code>
      <return_msg><![CDATA[OK]]></return_msg>
      <appid><![CDATA[wx742xxxxxxxxxxxxx]]></appid>
      <mch_id><![CDATA[14899xxxxx]]></mch_id>
      <nonce_str><![CDATA[R69QXXXXXXXX6O]]></nonce_str>
      <sign><![CDATA[79F0891XXXXXX189507A184XXXXXXXXX]]></sign>
      <result_code><![CDATA[SUCCESS]]></result_code>
      <prepay_id><![CDATA[wx152316xxxxxxxxxxxxxxxxxxxxxxxxxxx]]></prepay_id>
      <trade_type><![CDATA[NATIVE]]></trade_type>
      <code_url><![CDATA[weixin://wxpay/xxxurl?pr=dQNakHH]]></code_url>
      </xml>

      我們的目標是轉(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'?//?用于生成支付二維碼的鏈接
      }

      于是我們寫一個函數(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)
      ?}
      ?})
      ?})
      }

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

      const?axios?=?require('axios')
      const?url?=?'https://api.mch.weixin.qq.com/pay/unifiedorder'?//?微信服務端地址
      const?pay?=?async?(ctx)?=>?{
      ?const?form?=?ctx.request.body?//?通過前端傳來的數(shù)據(jù)
      ?const?orderNo?=?'XXXXXXXXXXXXXXXX'?//?不重復的訂單號
      ?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

      然后我們要將這個router掛載到根目錄的 app.js 里去。

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

      const?index?=?require('./routes/index')
      const?users?=?require('./routes/users')
      const?wechatpay?=?require('./routes/wechatpay')?//?加在這里

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

      //?routes
      app.use(index.routes(),?index.allowedMethods())
      app.use(users.routes(),?users.allowedMethods())
      app.use(wechatpay.routes(),?users.allowedMethods())?//?加在這里

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

      注意, 本例里是用前端來生成二維碼,其實也可以通過后端生成二維碼,然后再返回給前端。不過為了簡易演示,本例采用前端通過獲取 code_url 后,在前端生成二維碼。

      展示支付二維碼

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

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

      import?VueQrcode?from?'@xkeshi/vue-qrcode'
      export?default?{
      ?components:?{
      ?VueQrcode
      ?},
      ?//?...其他代碼
      }

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

      <vue-qrcode :value="codeUrl" :options="{ size: 200 }">

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

      文本是我自己添加的

      ?

      付款成功自動刷新頁面

      有兩種將支付成功寫入數(shù)據(jù)庫的辦法。

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

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

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

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

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

      前端部分

      Vue的data部分

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

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

      //?...
      handleCheckBill?()?{
      ?return?setTimeout(()?=>?{
      ?if?(!this.payStatus?&&?this.retryCount?< 120) {
       this.retryCount += 1
       axios.post(&#39;/api/check-bill&#39;, { // 向后端請求訂單支付信息
       orderNo: this.orderNo
       })
       .then(res =>?{
      ?if?(res.data.success)?{
      ?this.payStatus?=?true
      ?location.reload()?//?偷懶就用reload重新刷新頁面
      ?}?else?{
      ?this.handleCheckBill()
      ?}
      ?}).catch(err?=>?{
      ?console.log(err)
      ?})
      ?}?else?{
      ?location.reload()
      ?}
      ?},?1000)
      }

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

      后端部分

      前端到后端只有一個接口,但是后端有兩個接口。一個是用來接收微信的推送,一個是用來接收前端的查詢請求。

      先來寫最關(guān)鍵的微信的推送請求處理。由于我們接收微信的請求是在Koa的路由里,并且是以流的形式傳輸?shù)?。需要讓Koa支持解析xml格式的body,所以需要安裝一個rawbody 來獲取xml格式的body。

      //?處理微信支付回傳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?=?`<xml>
      ?<return_code><![CDATA[SUCCESS]]></return_code>
      ?<return_msg><![CDATA[OK]]></return_msg>
      ?</xml>
      ?`
      ?}
      ?}
      ?//?如果支付失敗,也回傳微信
      ?ctx.status?=?400
      ?ctx.type?=?'application/xml'
      ?ctx.body?=?`<xml>
      ?<return_code><![CDATA[FAIL]]></return_code>
      ?<return_msg><![CDATA[OK]]></return_msg>
      ?</xml>
      ?`
      }
      router.post('/api/notify',?handleNotify)

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

      接下來這個就是比較簡單的給前端回傳的了。

      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)

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

      推薦閱讀:

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

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

      以上是如何使用Koa2開發(fā)微信二維碼掃碼支付的詳細內(nèi)容。更多資訊請關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

      本網(wǎng)站聲明
      本文內(nèi)容由網(wǎng)友自願投稿,版權(quán)歸原作者所有。本站不承擔相應的法律責任。如發(fā)現(xiàn)涉嫌抄襲或侵權(quán)的內(nèi)容,請聯(lián)絡admin@php.cn

      熱AI工具

      Undress AI Tool

      Undress AI Tool

      免費脫衣圖片

      Undresser.AI Undress

      Undresser.AI Undress

      人工智慧驅(qū)動的應用程序,用於創(chuàng)建逼真的裸體照片

      AI Clothes Remover

      AI Clothes Remover

      用於從照片中去除衣服的線上人工智慧工具。

      Clothoff.io

      Clothoff.io

      AI脫衣器

      Video Face Swap

      Video Face Swap

      使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

      熱工具

      記事本++7.3.1

      記事本++7.3.1

      好用且免費的程式碼編輯器

      SublimeText3漢化版

      SublimeText3漢化版

      中文版,非常好用

      禪工作室 13.0.1

      禪工作室 13.0.1

      強大的PHP整合開發(fā)環(huán)境

      Dreamweaver CS6

      Dreamweaver CS6

      視覺化網(wǎng)頁開發(fā)工具

      SublimeText3 Mac版

      SublimeText3 Mac版

      神級程式碼編輯軟體(SublimeText3)

      四款值得推薦的AI輔助程式工具 四款值得推薦的AI輔助程式工具 Apr 22, 2024 pm 05:34 PM

      這個AI輔助程式工具在這個AI快速發(fā)展的階段,挖掘出了一大批好用的AI輔助程式工具。 AI輔助程式設計工具能夠提升開發(fā)效率、提升程式碼品質(zhì)、降低bug率,是現(xiàn)代軟體開發(fā)過程中的重要助手。今天大姚給大家分享4款AI輔助程式工具(而且都支援C#語言),希望對大家有幫助。 https://github.com/YSGStudyHards/DotNetGuide1.GitHubCopilotGitHubCopilot是一款AI編碼助手,可幫助你更快、更省力地編寫程式碼,從而將更多精力集中在問題解決和協(xié)作上。 Git

      AI程式設計師哪家強?探索Devin、通靈靈碼和SWE-agent的潛力 AI程式設計師哪家強?探索Devin、通靈靈碼和SWE-agent的潛力 Apr 07, 2024 am 09:10 AM

      2022年3月3日,距離世界首個AI程式設計師Devin誕生不足一個月,普林斯頓大學的NLP團隊開發(fā)了一個開源AI程式設計師SWE-agent。它利用GPT-4模型在GitHub儲存庫中自動解決問題。 SWE-agent在SWE-bench測試集上的表現(xiàn)與Devin相似,平均耗時93秒,解決了12.29%的問題。 SWE-agent透過與專用終端交互,可以開啟、搜尋文件內(nèi)容,使用自動語法檢查、編輯特定行,以及編寫和執(zhí)行測試。 (註:以上內(nèi)容為原始內(nèi)容微調(diào),但保留了原文中的關(guān)鍵訊息,未超過指定字數(shù)限制。)SWE-A

      學習如何利用Go語言開發(fā)行動應用程式 學習如何利用Go語言開發(fā)行動應用程式 Mar 28, 2024 pm 10:00 PM

      Go語言開發(fā)行動應用程式教學隨著行動應用程式市場的不斷蓬勃發(fā)展,越來越多的開發(fā)者開始探索如何利用Go語言開發(fā)行動應用程式。作為一種簡潔高效的程式語言,Go語言在行動應用開發(fā)中也展現(xiàn)了強大的潛力。本文將詳細介紹如何利用Go語言開發(fā)行動應用程序,並附上具體的程式碼範例,幫助讀者快速入門並開始開發(fā)自己的行動應用程式。一、準備工作在開始之前,我們需要準備好開發(fā)環(huán)境和工具。首

      Go語言前端技術(shù)探秘:前端開發(fā)新視野 Go語言前端技術(shù)探秘:前端開發(fā)新視野 Mar 28, 2024 pm 01:06 PM

      Go語言作為一種快速、高效的程式語言,在後端開發(fā)領(lǐng)域廣受歡迎。然而,很少有人將Go語言與前端開發(fā)聯(lián)繫起來。事實上,使用Go語言進行前端開發(fā)不僅可以提高效率,還能為開發(fā)者帶來全新的視野。本文將探討使用Go語言進行前端開發(fā)的可能性,並提供具體的程式碼範例,幫助讀者更了解這一領(lǐng)域。在傳統(tǒng)的前端開發(fā)中,通常會使用JavaScript、HTML和CSS來建立使用者介面

      五大熱門Go語言庫總表:開發(fā)必備利器 五大熱門Go語言庫總表:開發(fā)必備利器 Feb 22, 2024 pm 02:33 PM

      五大熱門Go語言庫總結(jié):開發(fā)必備利器,需要具體程式碼範例Go語言自從誕生以來,受到了廣泛的關(guān)注和應用。作為一門新興的高效、簡潔的程式語言,Go的快速發(fā)展離不開豐富的開源程式庫的支援。本文將介紹五大熱門的Go語言庫,這些庫在Go開發(fā)中扮演了至關(guān)重要的角色,為開發(fā)者提供了強大的功能和便利的開發(fā)體驗。同時,為了更好地理解這些庫的用途和功能,我們會結(jié)合具體的程式碼範例進行講

      Android開發(fā)最適合的Linux發(fā)行版是哪一個? Android開發(fā)最適合的Linux發(fā)行版是哪一個? Mar 14, 2024 pm 12:30 PM

      Android開發(fā)是一項繁忙而又令人興奮的工作,而選擇適合的Linux發(fā)行版來進行開發(fā)則顯得尤為重要。在眾多的Linux發(fā)行版中,究竟哪一個最適合Android開發(fā)呢?本文將從幾個方面來探討這個問題,並給出具體的程式碼範例。首先,我們來看看目前流行的幾個Linux發(fā)行版:Ubuntu、Fedora、Debian、CentOS等,它們都有各自的優(yōu)點和特點。

      VSCode最適合哪種框架開發(fā)? VSCode最適合哪種框架開發(fā)? Mar 25, 2024 pm 02:03 PM

      VSCode是一款功能強大、靈活、易於擴充的開源程式碼編輯器,廣受開發(fā)者青睞。它支援眾多程式語言和框架,能夠滿足不同專案需求。但是,針對不同的框架,VSCode的優(yōu)勢可能有所不同。本文將討論VSCode在不同框架開發(fā)中的適用性,並提供具體的程式碼範例。 1.ReactReact是一款流行的JavaScript庫,用於建立使用者介面。在使用React開發(fā)專案時,

      全面指南:詳解Java虛擬機器安裝過程 全面指南:詳解Java虛擬機器安裝過程 Jan 24, 2024 am 09:02 AM

      Java開發(fā)必備:詳細解讀Java虛擬機器安裝步驟,需要具體程式碼範例隨著電腦科學和技術(shù)的發(fā)展,Java語言已成為廣泛使用的程式語言之一。它具有跨平臺、物件導向等優(yōu)點,逐漸成為開發(fā)人員的首選語言。在使用Java進行開發(fā)之前,首先需要安裝Java虛擬機器(JavaVirtualMachine,JVM)。本文將詳細解讀Java虛擬機器的安裝步驟,並提供具體的程式碼示

      See all articles