如何使用Koa2开发微信二维码扫码支付
这次给大家带来如何使用Koa2开发微信二维码扫码支付,使用Koa2开发微信二维码扫码支付的注意事项有哪些,下面就是实战案例,一起来看一下。
前段时间在开发一个功能,要求是通过微信二维码进行扫码支付。这个情景我们屡见不鲜了,各种电子商城、线下的自动贩卖机等等都会有这个功能。平时只是使用者,如今变为开发者,也是有不小的坑。所以特此写一篇博客记录一下。
注: 要开发微信二维码支付,你必须要有相应的商户号的权限,否则你是无法开发的。若无相应权限,本文不推荐阅读。
两种模式
打开微信支付的文档,我们可以看到两种支付模式:模式一和模式二。这二者的流程图微信的文档里都给出了(不过说实话画得真的有点丑)。
文档里指出了二者的区别:
模式一开发前,商户必须在公众平台后台设置支付回调URL。URL实现的功能:接收用户扫码后微信支付系统回调的productid和openid。
模式二与模式一相比,流程更为简单,不依赖设置的回调支付URL。商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数code_url,商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。注意:code_url有效期为2小时,过期后扫码不能再发起支付。
模式一是我们平时在网购的时候比较常见的,会弹出一个专门的页面用于扫码支付,然后支付成功后这个页面会再次跳转回回调页面,通知你支付成功。第二种的话想对少一些,不过第二种开发起来相对简单点。 本文主要介绍模式二的开发 。
搭建Koa2的简单开发环境
快速搭建Koa2的开发环境我推荐可以使用koa-generator 。脚手架能帮我们省去Koa项目一开始的一些基本中间件的书写步骤。(如果你想学习Koa最好自己搭建一个。如果你已经会Koa了就可以使用一些快速脚手架了。)
首先全局安装 koa-generator
:
npm install -g koa-generator #or yarn global add koa-generator
然后找一个目录用来存放Koa项目,我们打算给这个项目取个名字叫做 koa-wechatpay
,然后就可以输入 koa2 koa-wechatpay
。然后脚手架会自动创建相应文件夹 koa-wechatpay
,并生成基本骨架。进入这个文件夹,安装相应的插件。输入:
npm install #or yarn
接着你可以输入 npm start
或者 yarn start
来运行项目(默认监听在3000端口)。
如果不出意外,你的项目跑起来了,然后我们用postman测试一下:
这条路由是在 routes/index.js
里。
如果你看到了
{ "title": "koa2 json" }
就说明没问题。(如果有问题,检查一下是不是端口被占用了等等。)
接下来在 routes
文件夹里我们新建一个 wechatpay.js
的文件用来书写我们的流程。
签名
跟微信的服务器交流很关键的一环是签名必须正确,如果签名不正确,那么一切都白搭。
首先我们需要去公众号的后台获取我们所需要的如下相应的id或者key的信息。其中 notify_url
和 server_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的充值支付' // 用于显示在支付界面的提示词
然后开始写签名函数:
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是个比较随意的选项,只要符合规则的ip经过测试都是可以的,下文里我用的是 server_ip
。 nonce
就是微信要求的不重复的32位以内的字符串,通常可以使用订单号等唯一标识的字符串。
由于跟微信的服务器交流都是用xml来交流,所以现在我们要手动组装一下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
串有问题,可以提前在微信提供的签名校验工具里先校验一遍,看看是否能通过。
发送请求
因为需要跟微信服务端发请求,所以我选择了 axios
这个在浏览器端和node端都能发起ajax请求的库。
安装过程不再赘述。继续在 wechatpay.js
写发请求的逻辑。
由于微信给我们返回的也将是一个xml格式的字符串。所以我们需要预先写好解析函数,将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>
我们的目标是转为如下的js对象,好让我们用js来操作数据:
{ 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' // 用于生成支付二维码的链接 }
于是我们写一个函数,调用 xml2js
来解析xml:
// 将XML转为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
的操作是在回调函数里返回的结果,所以为了配合Koa2的 async
、 await
,我们可以将其封装成一个 Promise
对象,将解析完的结果通过 resolve
返回回去。这样就能用 await
来取数据了:
const axios = require('axios') const url = 'https://api.mch.weixin.qq.com/pay/unifiedorder' // 微信服务端地址 const pay = async (ctx) => { const form = ctx.request.body // 通过前端传来的数据 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 // 拿到返回的数据 }).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()) // 加在这里
于是你就可以通过发送 /api/pay
来请求二维码数据啦。(如果有跨域需要自己考虑解决跨域方案,可以跟Koa放在同域里,也可以开一层proxy来转发,也可以开CORS头等等)
注意, 本例里是用前端来生成二维码,其实也可以通过后端生成二维码,然后再返回给前端。不过为了简易演示,本例采用前端通过获取 code_url
后,在前端生成二维码。
展示支付二维码
前端我用的是 Vue
,当然你可以选择你喜欢的前端框架。这里关注点在于通过拿到刚才后端传过来的 code_url
来生成二维码。
在前端,我使用的是 @xkeshi/vue-qrcode 这个库来生成二维码。它调用特别简单:
import VueQrcode from '@xkeshi/vue-qrcode' export default { components: { VueQrcode }, // ...其他代码 }
然后就可以在前端里用 <vue-qrcode>
的组件来生成二维码了:
<vue-qrcode :value="codeUrl" :options="{ size: 200 }">
放到Dialog里就是这样的效果:
文本是我自己添加的
付款成功自动刷新页面
有两种将支付成功写入数据库的办法。
一种是在打开了扫码对话框后,不停向微信服务端轮询支付结果,如果支付成功,那么就向后端发起请求,告诉后端支付成功,让后端写入数据库。
一种是后端一直开着接口,等微信主动给后端的 notify_url
发起post请求,告诉后端支付结果,让后端写入数据库。然后此时前端向后端轮询的时候应该是去数据库取轮询该订单的支付结果,如果支付成功就关闭Dialog。
第一种比较简单但是不安全:试想万一用户支付成功的同时关闭了页面,或者用户支付成功了,但是网络有问题导致前端没法往后端发支付成功的结果,那么后端就一直没办法写入支付成功的数据。
第二种虽然麻烦,但是保证了安全。所有的支付结果都必须等微信主动向后端通知,后端存完数据库后再返回给前端消息。这样哪怕用户支付成功的同时关闭了页面,下次再打开的时候,由于数据库已经写入了,所以拿到的也是支付成功的结果。
所以 付款成功自动刷新页面
这个部分我们分为两个部分来说:
前端部分
Vue的data部分
data: { payStatus: false, // 未支付成功 retryCount: 0, // 轮询次数,从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后如果还是没有付款信息也自动刷新页面。实际上你可以自己根据项目的需要来定义这个时间。
后端部分
前端到后端只有一个接口,但是后端有两个接口。一个是用来接收微信的推送,一个是用来接收前端的查询请求。
先来写最关键的微信的推送请求处理。由于我们接收微信的请求是在Koa的路由里,并且是以流的形式传输的。需要让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代表支付成功 // ... 这里是写入数据库的相关操作 // 开始回传微信 ctx.type = 'application/xml' // 指定发送的请求类型是xml // 回传微信,告诉已经收到 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
的形式回传的,会调试半天。。
接下来这个就是比较简单的给前端回传的了。
const checkBill = async (ctx) => { const form = ctx.request.body const orderNo = form.orderNo const result = await 数据库操作 if (result) { // 如果订单支付成功 return ctx.body = { success: true } } ctx.status = 400 ctx.body = { success: false } } router.post('/api/check-bill', checkBill)
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
Atas ialah kandungan terperinci 如何使用Koa2开发微信二维码扫码支付. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

AI Hentai Generator
Menjana ai hentai secara percuma.

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas



Alat pengaturcaraan berbantukan AI ini telah menemui sejumlah besar alat pengaturcaraan berbantukan AI yang berguna dalam peringkat pembangunan AI yang pesat ini. Alat pengaturcaraan berbantukan AI boleh meningkatkan kecekapan pembangunan, meningkatkan kualiti kod dan mengurangkan kadar pepijat Ia adalah pembantu penting dalam proses pembangunan perisian moden. Hari ini Dayao akan berkongsi dengan anda 4 alat pengaturcaraan berbantukan AI (dan semua menyokong bahasa C# saya harap ia akan membantu semua orang). https://github.com/YSGStudyHards/DotNetGuide1.GitHubCopilotGitHubCopilot ialah pembantu pengekodan AI yang membantu anda menulis kod dengan lebih pantas dan dengan sedikit usaha, supaya anda boleh lebih memfokuskan pada penyelesaian masalah dan kerjasama. Git

Pada 3 Mac 2022, kurang daripada sebulan selepas kelahiran pengaturcara AI pertama di dunia, Devin, pasukan NLP Universiti Princeton membangunkan pengaturcara AI sumber terbuka ejen SWE. Ia memanfaatkan model GPT-4 untuk menyelesaikan isu secara automatik dalam repositori GitHub. Prestasi ejen SWE pada set ujian bangku SWE adalah serupa dengan Devin, mengambil purata 93 saat dan menyelesaikan 12.29% masalah. Dengan berinteraksi dengan terminal khusus, ejen SWE boleh membuka dan mencari kandungan fail, menggunakan semakan sintaks automatik, mengedit baris tertentu dan menulis serta melaksanakan ujian. (Nota: Kandungan di atas adalah sedikit pelarasan bagi kandungan asal, tetapi maklumat utama dalam teks asal dikekalkan dan tidak melebihi had perkataan yang ditentukan.) SWE-A

Tutorial aplikasi mudah alih pembangunan bahasa Go Memandangkan pasaran aplikasi mudah alih terus berkembang pesat, semakin ramai pembangun mula meneroka cara menggunakan bahasa Go untuk membangunkan aplikasi mudah alih. Sebagai bahasa pengaturcaraan yang mudah dan cekap, bahasa Go juga telah menunjukkan potensi yang kukuh dalam pembangunan aplikasi mudah alih. Artikel ini akan memperkenalkan secara terperinci cara menggunakan bahasa Go untuk membangunkan aplikasi mudah alih dan melampirkan contoh kod khusus untuk membantu pembaca bermula dengan cepat dan mula membangunkan aplikasi mudah alih mereka sendiri. 1. Persediaan Sebelum memulakan, kita perlu menyediakan persekitaran dan alatan pembangunan. kepala

Ringkasan lima pustaka bahasa Go yang paling popular: alatan penting untuk pembangunan, memerlukan contoh kod khusus Sejak kelahirannya, bahasa Go telah mendapat perhatian dan aplikasi yang meluas. Sebagai bahasa pengaturcaraan yang cekap dan ringkas, pembangunan pesat Go tidak dapat dipisahkan daripada sokongan perpustakaan sumber terbuka yang kaya. Artikel ini akan memperkenalkan lima perpustakaan bahasa Go yang paling popular. Perpustakaan ini memainkan peranan penting dalam pembangunan Go dan menyediakan pembangun dengan fungsi yang berkuasa dan pengalaman pembangunan yang mudah. Pada masa yang sama, untuk lebih memahami kegunaan dan fungsi perpustakaan ini, kami akan menerangkannya dengan contoh kod khusus.

Pembangunan Android ialah kerja yang sibuk dan menarik, dan amat penting untuk memilih pengedaran Linux yang sesuai untuk pembangunan. Di antara banyak pengedaran Linux, yang manakah paling sesuai untuk pembangunan Android? Artikel ini akan meneroka isu ini dari beberapa aspek dan memberikan contoh kod khusus. Mula-mula, mari kita lihat beberapa pengedaran Linux yang popular pada masa ini: Ubuntu, Fedora, Debian, CentOS, dll. Mereka semua mempunyai kelebihan dan ciri tersendiri.

"Memahami VSCode: Untuk apa alat ini digunakan?" 》Sebagai pengaturcara, sama ada anda seorang pemula atau pembangun berpengalaman, anda tidak boleh melakukannya tanpa menggunakan alat penyuntingan kod. Di antara banyak alat penyuntingan, Kod Visual Studio (pendek kata VSCode) sangat popular di kalangan pembangun sebagai penyunting kod sumber terbuka, ringan dan berkuasa. Jadi, untuk apa sebenarnya VSCode digunakan? Artikel ini akan menyelidiki fungsi dan kegunaan VSCode dan menyediakan contoh kod khusus untuk membantu pembaca

Sebagai bahasa pengaturcaraan yang pantas dan cekap, bahasa Go popular secara meluas dalam bidang pembangunan bahagian belakang. Walau bagaimanapun, beberapa orang mengaitkan bahasa Go dengan pembangunan bahagian hadapan. Malah, menggunakan bahasa Go untuk pembangunan bahagian hadapan bukan sahaja boleh meningkatkan kecekapan, tetapi juga membawa ufuk baharu kepada pembangun. Artikel ini akan meneroka kemungkinan menggunakan bahasa Go untuk pembangunan bahagian hadapan dan memberikan contoh kod khusus untuk membantu pembaca memahami dengan lebih baik bahagian ini. Dalam pembangunan front-end tradisional, JavaScript, HTML dan CSS sering digunakan untuk membina antara muka pengguna

PHP tergolong dalam bahagian belakang dalam pembangunan web. PHP ialah bahasa skrip sebelah pelayan, terutamanya digunakan untuk memproses logik sebelah pelayan dan menjana kandungan web dinamik. Berbanding dengan teknologi bahagian hadapan, PHP lebih banyak digunakan untuk operasi bahagian belakang seperti berinteraksi dengan pangkalan data, memproses permintaan pengguna dan menjana kandungan halaman. Seterusnya, contoh kod khusus akan digunakan untuk menggambarkan aplikasi PHP dalam pembangunan back-end. Mula-mula, mari kita lihat contoh kod PHP mudah untuk menyambung ke pangkalan data dan menanyakan data:
