前端必知必会(一):vue3+node实现网站支付功能

在我上学那会当时写过一个小网站,初衷是分享自己的学习经验。后台随机学习的不断深入,有把网站做一个支付功能。当时这是想但是一直没有实现这个想法。那时认为支付是一个很复杂的功能,今天我就想简单来了解一下支付的一个实现过程,案例中我采用的是沙箱,之前只是听过,编码使用Vue和Node想结合。写的不好,大家多多批评。
下边是我找的两个对我们开发有帮助的网址

  • 支付宝开放平台:https://open.alipay.com/
  • 沙箱对接文档:https://www.yuque.com/chenqiu/alipay-node-sdk/guide

1. 创建项目Vue和Node项目

这里我不做过多的介绍,我们在电脑上安装好脚手架工具进行对应的项目模板生成就好了,后端我采用的是express,如果我们想快速生成一个node项目,我们需要安装一下express-generator,通过执行express --view=ejs 项目名生成项目。

2. 解决跨域

由于我们是开启了两个服务,所有在进行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();
});

3. 前端实现步骤

我们需要使用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>

4.后端代码实现(重点)

1. 配置AlipaySdk

我们在src目录下创建一个alipayUtil.js文件

const AlipaySdk = require('alipay-sdk').default
const alipaysdk = new AlipaySdk({
  appId,  // 在啥感想应用中可以找到
  signType, // 算法签名(一般为RSA20)
  gateway,  // 支付宝的网关地址
  alipayPublicKey, // 公钥
  privateKey // 私钥
})

module.exports = alipaysdk

2. 定义路由(打开routes/index.js)

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

5. 获取支付状态

1. 前端操作编码(Home.vue)

在我们付款操作之后,后端会返回我们一个跳转的地址,我们需要在前端定义这个路由,当我们注册这个vue文件时,我们可以在created生命周期函数中再次请求我们的后端接口,返回我们status,我们需要在地址中获取两个我们需要请求携带的参数:分别是out_trade_notrade_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 />

2. 后端操作编码

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,使我对支付功能有一个更深刻的理解,以便于下次做类似的开发不至于无从下手。欢迎你的点赞和支持!!!

你可能感兴趣的:(Vue,前端,vue.js,javascript)