How to use Koa2 to develop WeChat QR code scanning payment
May 29, 2018 am 11:23 AMThis time I will show you how to use Koa2 to develop WeChat QR code scanning payment, and what are the precautions of using Koa2 to develop WeChat QR code scanning payment. The following is a practical case, let's come together take a look.
I was developing a function some time ago, and the requirement is to pay by scanning the WeChat QR code. This scenario is not uncommon for us. Various electronic shopping malls, offline vending machines, etc. all have this function. Usually I am just a user, but now I am a developer, which is not a small pitfall. So hereby write a blog to record it. Note: To develop WeChat QR code payment, you must have the permission of the corresponding merchant account, otherwise you will not be able to develop it. If you do not have the corresponding permissions, this article is not recommended to be read.Two modes
Open the WeChat payment document, we can see two payment modes: mode one and mode two. The flow charts of both are given in the WeChat documentation (but to be honest, the drawings are really ugly). The document points out the difference between the two: Before the development of Mode 1, merchants must set a payment callback URL in the backend of the public platform. Function implemented by URL: Receive the productid and openid called back by the WeChat payment system after the user scans the QR code. Compared with Mode 1, Mode 2 has a simpler process and does not rely on the set callback payment URL. The merchant's backend system first calls the unified ordering interface of WeChat payment. The WeChat backend system returns the link parameter code_url. The merchant's backend system generates a QR code image from the code_url value. The user uses the WeChat client to scan the code and initiate payment. Note: code_url is valid for 2 hours. After expiration, scanning the code cannot initiate payment. Mode 1 is more common when we shop online. A special page will pop up for scanning the QR code to pay. Then after the payment is successful, this page will jump back to the callback page again to notify you that the payment was successful. The second one is less likely to be correct, but the second one is relatively simple to develop.This article mainly introduces the development of mode two.
Build a simple development environment for Koa2
I recommend using koa-generator to quickly build a development environment for Koa2. Scaffolding can help us save some basic middleware writing steps at the beginning of the Koa project. (If you want to learn Koa, it’s best to build one yourself. If you already know Koa, you can use some quick scaffolding.) First install it globallykoa-generator:
npm?install?-g?koa-generator #or yarn?global?add?koa-generatorThen find a directory to store the Koa project. We plan to name this project
koa-wechatpay, and then enter
koa2 koa-wechatpay. Then the scaffolding will automatically create the corresponding folder
koa-wechatpay and generate the basic skeleton. Go into this folder and install the corresponding plug-in. Enter:
npm?install #or yarnThen you can enter
npm start or
yarn start to run the project (the default listening port is 3000).
routes/index.js.
{ ?"title":?"koa2?json" }, it means there is no problem. (If there is a problem, check whether the port is occupied, etc.) Next, we create a new file
wechatpay.js in the
routes folder Used to write our processes.
Signature
#The key to communicating with the WeChat server is that the signature must be correct. If the signature is incorrect, then everything is in vain. First we need to go to the backend of the official account to obtain the following corresponding id or key information we need. Among them,notify_url and
server_ip are used for when our payment is successful, WeChat will actively send the payment success information to this url
post.
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_3
In order for the signature to be correct , we need to installmd5.
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的充值支付'?//?用于顯示在支付界面的提示詞Then start writing the signature function:
const?signString?=?(fee,?ip,?nonce)?=>?{ ?let?tempString?=?`appid=${appid}&body=${body}&mch_id=${mch_id}&nonce_str=${nonce}¬ify_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_ip
。 nonce
就是微信要求的不重復的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('/api/check-bill', { // 向后端請求訂單支付信息 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ù)摹P枰孠oa支持解析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)境搭建
The above is the detailed content of How to use Koa2 to develop WeChat QR code scanning payment. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

This AI-assisted programming tool has unearthed a large number of useful AI-assisted programming tools in this stage of rapid AI development. AI-assisted programming tools can improve development efficiency, improve code quality, and reduce bug rates. They are important assistants in the modern software development process. Today Dayao will share with you 4 AI-assisted programming tools (and all support C# language). I hope it will be helpful to everyone. https://github.com/YSGStudyHards/DotNetGuide1.GitHubCopilotGitHubCopilot is an AI coding assistant that helps you write code faster and with less effort, so you can focus more on problem solving and collaboration. Git

On March 3, 2022, less than a month after the birth of the world's first AI programmer Devin, the NLP team of Princeton University developed an open source AI programmer SWE-agent. It leverages the GPT-4 model to automatically resolve issues in GitHub repositories. SWE-agent's performance on the SWE-bench test set is similar to Devin, taking an average of 93 seconds and solving 12.29% of the problems. By interacting with a dedicated terminal, SWE-agent can open and search file contents, use automatic syntax checking, edit specific lines, and write and execute tests. (Note: The above content is a slight adjustment of the original content, but the key information in the original text is retained and does not exceed the specified word limit.) SWE-A

Go language development mobile application tutorial As the mobile application market continues to boom, more and more developers are beginning to explore how to use Go language to develop mobile applications. As a simple and efficient programming language, Go language has also shown strong potential in mobile application development. This article will introduce in detail how to use Go language to develop mobile applications, and attach specific code examples to help readers get started quickly and start developing their own mobile applications. 1. Preparation Before starting, we need to prepare the development environment and tools. head

As a fast and efficient programming language, Go language is widely popular in the field of back-end development. However, few people associate Go language with front-end development. In fact, using Go language for front-end development can not only improve efficiency, but also bring new horizons to developers. This article will explore the possibility of using the Go language for front-end development and provide specific code examples to help readers better understand this area. In traditional front-end development, JavaScript, HTML, and CSS are often used to build user interfaces

Summary of the five most popular Go language libraries: essential tools for development, requiring specific code examples. Since its birth, the Go language has received widespread attention and application. As an emerging efficient and concise programming language, Go's rapid development is inseparable from the support of rich open source libraries. This article will introduce the five most popular Go language libraries. These libraries play a vital role in Go development and provide developers with powerful functions and a convenient development experience. At the same time, in order to better understand the uses and functions of these libraries, we will explain them with specific code examples.

Android development is a busy and exciting job, and choosing a suitable Linux distribution for development is particularly important. Among the many Linux distributions, which one is most suitable for Android development? This article will explore this issue from several aspects and give specific code examples. First, let’s take a look at several currently popular Linux distributions: Ubuntu, Fedora, Debian, CentOS, etc. They all have their own advantages and characteristics.

VSCode is a powerful, flexible, and easy-to-extend open source code editor that is widely favored by developers. It supports many programming languages ??and frameworks to meet different project needs. However, the advantages of VSCode may be different for different frameworks. This article will discuss the applicability of VSCode in the development of different frameworks and provide specific code examples. 1.ReactReact is a popular JavaScript library used for building user interfaces. When developing projects using React,

Essentials for Java development: Detailed explanation of Java virtual machine installation steps, specific code examples required. With the development of computer science and technology, the Java language has become one of the most widely used programming languages. It has the advantages of cross-platform and object-oriented, and has gradually become the preferred language for developers. Before using Java for development, you first need to install the Java Virtual Machine (JavaVirtualMachine, JVM). This article will explain in detail the installation steps of the Java virtual machine and provide specific code examples.
