在我上学那会当时写过一个小网站,初衷是分享自己的学习经验。后台随机学习的不断深入,有把网站做一个支付功能。当时这是想但是一直没有实现这个想法。那时认为支付是一个很复杂的功能,今天我就想简单来了解一下支付的一个实现过程,案例中我采用的是沙箱,之前只是听过,编码使用Vue和Node想结合。写的不好,大家多多批评。
下边是我找的两个对我们开发有帮助的网址
这里我不做过多的介绍,我们在电脑上安装好脚手架工具进行对应的项目模板生成就好了,后端我采用的是express,如果我们想快速生成一个node项目,我们需要安装一下
express-generator
,通过执行express --view=ejs 项目名
生成项目。
由于我们是开启了两个服务,所有在进行ajax请求时,会存在跨域问题。这里并不是我们讨论的重点!
连接办法有两种:
一:是通过Vue
解决(可以参考我之前的文章使用Vue解决跨域问题)
二:通过配置node
解决。
node解决跨域问题(需要在app.js
添加如下代码):
app.all('*', function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Headers', ['mytoken','Content-Type']);
next();
});
我们需要使用
axios
向后端发起接口请求,使用qs
对数据进行封装。前端大概的思路就是我们发起一个网络请求,给到后端一个订单号,后端返回给我们要跳转的支付地址。
yarn add axios qs -S
# 或
npm install axios qs -S
为了方便,我直接在·
App.vue
进行操作,正常我们是要封装http``请求和
api`的(Vue 之 Axios请求封装)
// 这里我是用的是语法糖
<script setup>
// 1. 引入axios和qs插件
import axios from 'axios'
import qs from 'qs'
// 2. 我们在template中定义一个
// 3. 实现goPay函数
const goPay = function () {
let data = {
orderId: '20220529001' // 比如这个我们前端传递的订单号
}
// 4. 发送ajax请求
axios({
url: 'http://localhost:3000/api/payment', // 这是我们要访问的接口
method: 'post',
headers: {
'content-type': 'application/x-www-form-urlencoded'
},
data: qs.stringify(data)
}).then(res => {
window.location.href = res.data.result // result是后端回传给我们的回调成功的地址
})
}
<script>
我们在
src
目录下创建一个alipayUtil.js
文件
const AlipaySdk = require('alipay-sdk').default
const alipaysdk = new AlipaySdk({
appId, // 在啥感想应用中可以找到
signType, // 算法签名(一般为RSA20)
gateway, // 支付宝的网关地址
alipayPublicKey, // 公钥
privateKey // 私钥
})
module.exports = alipaysdk
var express = require('express');
var router = express.Router();
// 1. 引入alipayUtil文件
const alipaySdk = require('../db/alipayUtil')
const AlipayFormData = require('alipay-sdk/lib/form').default
/* GET home page. 这是根目录,项目自动生成的*/
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
router.post('/api/payment', (req, res, next) => {
// 2. 获取前端传递过来的数据
let orderId = req.body.orderId
// 对接支付宝
const formData = new AlipayFormData()
// 下面是官网的测试代码
formData.setMethod('get')
formData.addField('returnUrl', 'http://localhost:3001/home'); //支付成功的回调
formData.addField('bizContent', {
outTradeNo: orderId, // 因为测试原因我们可以把订单号改为Math.random()
productCode: 'FAST_INSTANT_TRADE_PAY', // 产品码
totalAmount: '2', // 商品金额
subject: '打赏', // 出售商品的标题
body: '您的支持是对我最大的鼓励' // 出售商品的内容
});
// 'alipay.trade.page.pay': 这个接口来下单与付款
const reult = alipaySdk.exec('alipay.trade.page.pay',{},{ formData: formData })
reult.then(resp => {
res.send({
code : 200,
success: 'true',
result: resp // resp包含我们成功跳转的地址
})
})
})
module.exports = router;
这里我定义的跳转路径为
http://localhost:3001/home
,
在我们付款操作之后,后端会返回我们一个跳转的地址,我们需要在前端定义这个路由,当我们注册这个
vue
文件时,我们可以在created
生命周期函数中再次请求我们的后端接口,返回我们status
,我们需要在地址中获取两个我们需要请求携带的参数:分别是out_trade_no
、trade_no
。
<script>
created() {
let data = {
out_trade_no: this.$route.query.out_trade_no,
trade_no: this.$route.query.trade_no
}
axios({
url: '/api/getStatus',
method: 'post',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
data: qs.stringify(data)
}).then(res => {
console.log(res)
})
}
<script />
router.post('/api/getStatus', (req, res, next) => {
let { out_trade_no, trade_no } = req.body
// 对接支付宝
const formData = new AlipayFormData()
// 下面是官网的测试代码
formData.setMethod('get')
formData.addField('bizContent', {
out_trade_no,
trade_no
});
// 'alipay.trade.page.pay': 这个接口来下单与付款
const reult = alipaySdk.exec('alipay.trade.query',{},{ formData: formData })
// 返回的result是一个Promise
result.then(resData => {
// 安装一下axios请求支付宝
axios({
url: resData,
method: 'get'
}).then(data => {
// 获取调取成功的状态码
let r = data.data.alipay_trade_query_response
if (r.code === '10000') { // 接口调用成功
switch (r.trade_status) {
case 'WAIT_BUYER_PAY':
res.send(
{
"success": true,
"message": "success",
"code": 200,
"timestamp": (new Date()).getTime(),
"result": {
"status": 0,
"massage": '交易创建,等待买家付款'
}
}
)
break;
case 'TRADE_CLOSED':
res.send(
{
"success": true,
"message": "success",
"code": 200,
"timestamp": (new Date()).getTime(),
"result": {
"status": 1,
"massage": '未付款交易超时关闭,或支付完成后全额退款'
}
}
)
break;
case 'TRADE_SUCCESS':
res.send(
{
"success": true,
"message": "success",
"code": 200,
"timestamp": (new Date()).getTime(),
"result": {
"status": 2,
"massage": '交易支付成功'
}
}
)
break;
case 'TRADE_FINISHED':
res.send(
{
"success": true,
"message": "success",
"code": 200,
"timestamp": (new Date()).getTime(),
"result": {
"status": 3,
"massage": '交易结束,不可退款'
}
}
)
break;
}
} else if (r.code === '40004') {
res.send('交易不存在');
}
}).catch(err => {
res.json({
msg: '查询失败'
})
})
})
})
到此支付功能开发完成,希望对你有所帮助。写的不是很规范,请求没有进行二次封装,正常工作的话肯定是不太友好的。通过这个小demo,使我对支付功能有一个更深刻的理解,以便于下次做类似的开发不至于无从下手。欢迎你的点赞和支持!!!