Vue3+Node/Express支付宝沙箱支付与确认支付

Vue3+Node/Express支付宝沙箱支付与确认支付

支付宝沙箱配置

支付宝开放平台,进行登录,进入控制台

进入沙箱

选择自定义密钥

密钥工具下载

密钥工具下载

生成密钥


格式转换

将应用私钥复制至格式转换

自定义密钥设置

将生成的应用公钥 复制到自定义密钥设置中

保存后如下图所示

最后点击确认 即可,此时公钥模式已启用

Express

安装依赖

json 复制代码
    "alipay-sdk": "^3.6.1",
    "axios": "^1.8.1",

本篇文章使用的sdk^3.6.1axios是为寻求支付结果需要使用到的

项目目录

复制代码
├─db
├─jwt_config
├─limit
├─public
│  └─upload
├─router
├─router_handler
└─utils
   └─alipay.js

创建alipay.js


utils目录下新建alipay.js文件,代码如下:

js 复制代码
const AlipaySdk = require("alipay-sdk").default;

const alipaySdk = new AlipaySdk({
  appId: "",
  signType: "RSA2",
  gateway: "https://openapi-sandbox.dl.alipaydev.com/gateway.do",
  alipayPublicKey:
    "",
    privateKey:
    ""
});
module.exports = alipaySdk;

这里的appId可在支付宝开放平台的沙盒里看到,如下图所示:

signTypegateway分别是签名算法网关,这个默认和上述代码即可

alipayPublicKey就是自定义密钥设置 一节中的支付宝公钥
privateKey则为格式转换 后输出的密钥

请求(打开支付)代码

router_handler下新建pay.js路由处理程序文件,代码如下:

js 复制代码
const alipaySdk = require("../utils/alipay");
const AlipayFormData = require("alipay-sdk/lib/form").default;

exports.pay = (req, res) => {
  console.log(req.body);
  (async () => {
    // 调用 setMethod 并传入 get,会返回可以跳转到支付页面的 url
    const formData = new AlipayFormData();
    formData.setMethod("get");
    // 通过 addField 增加参数
    // 在用户支付完成之后,支付宝服务器会根据传入的 notify_url,以 POST 请求的形式将支付结果作为参数通知到商户系统。
    formData.addField("returnUrl", "http://localhost:8080/#/home"); // 支付成功回调地址,必须为可以直接访问的地址,不能带参数
    formData.addField("bizContent", {
      outTradeNo: req.body.outTradeNo, // 商户订单号,64个字符以内、可包含字母、数字、下划线,且不能重复
      productCode: "FAST_INSTANT_TRADE_PAY", // 销售产品码,与支付宝签约的产品码名称,仅支持FAST_INSTANT_TRADE_PAY
      totalAmount: req.body.all_money, // 订单总金额,单位为元,精确到小数点后两位
      subject: "商品", // 订单标题
      body: "商品详情", // 订单描述
    }); // 如果需要支付后跳转到商户界面,可以增加属性"returnUrl"
    const result = await alipaySdk.exec(
      "alipay.trade.page.pay", // 统一收单下单并支付页面接口
      {}, // api 请求的参数(包含"公共请求参数"和"业务参数")
      {
        formData: formData,
      }
    ); // result 为可以跳转到支付链接的 url
    res.json({
      url: result,
    });
  })();
};

这里需要注意的是returnUrl对应的地址,这是当支付成功后跳转的页面,一般来说都是跳转回商城的首页

bizContent里面的内容则是与支付有关,outTradeNo是订单号(可以使用随机函数唯一生成,用于查询结果,非常重要 ),productCode默认为FAST_INSTANT_TRADE_PAY无需改变,totalAmount是商品的总价,也就是需要支付的价格,需要注意的是要精确到小数点后两位,可以使用fixed(2)实现,subjectbody则是与商品的基本信息有关,可以通过前端传值进行赋值

router/pay.js

js 复制代码
// router/pay.js
const express = require("express");
const router = express.Router();
const PayHandler = require("../router_handler/pay");
router.post("/pay", PayHandler.pay);

module.exports = router;

app.js

js 复制代码
// app.js
const PayRouter = require("./router/pay");
app.use("/pay", PayRouter);

至此基础的就完成了,可以请求当前接口进行支付

前端代码

前端封装接口

js 复制代码
// 订单号和总价
export const pay = (outTradeNo, all_money) => {
  return instance({
    url: "/pay/pay",
    method: "POST",
    data: {
      outTradeNo,
      all_money,
    },
  });
};

前端调用

js 复制代码
const res = await pay(outTradeNo, totalPrice1.toFixed(2));
if (res.url) {
	window.open(res.url, "_blank"); // 在新窗口中打开支付链接
} else {
 alert("未获取到支付链接!");
return;
}

实现支付

当请求后发起后会在浏览器打开支付宝的支付窗口,如下所示:

账户名和支付密码在沙箱账号中,如下图所示:

支付成功,等待2~3秒会跳转至returnUrl指定的地址

查询结果代码

开箱即用,传入订单号outTradeNo 即可查询结果

js 复制代码
// router/pay.js
const express = require("express");
const router = express.Router();
const axios = require("axios");
const PayHandler = require("../router_handler/pay");
const alipaySdk = require("../utils/alipay");
router.post("/pay", PayHandler.pay);

const AlipayFormData = require("alipay-sdk/lib/form").default;
router.post("/query", function (req, res) {
  (async function () {
    const { outTradeNo } = req.body;
    console.log(outTradeNo);
    const formData = new AlipayFormData();
    formData.setMethod("get");
    formData.addField("bizContent", {
      outTradeNo,
    }); // 通过该接口主动查询订单状态const result = await alipaySdk.exec(          'alipay.trade.query',

    const result = await alipaySdk.exec(
      "alipay.trade.query", // 统一收单下单并支付页面接口
      {}, // api 请求的参数(包含"公共请求参数"和"业务参数")
      {
        formData: formData,
      }
    );
    axios({
      method: "GET",
      url: result,
    })
      .then((data) => {
        let r = data.data.alipay_trade_query_response;
        if (r.code === "10000") {
          // 接口调用成功
          switch (r.trade_status) {
            case "WAIT_BUYER_PAY":
              res.send("交易创建,等待买家付款");
              break;
            case "TRADE_CLOSED":
              res.send("未付款交易超时关闭,或支付完成后全额退款");
              break;
            case "TRADE_SUCCESS":
              res.send({
                status: 0,
                msg: "支付成功",
              });
              break;
            case "TRADE_FINISHED":
              res.send("交易结束,不可退款");
              break;
          }
        } else if (r.code === "40004") {
          res.send("交易不存在");
        }
      })
      .catch((err) => {
        res.json({
          msg: "查询失败",
          err,
        });
      });
  })();
});

module.exports = router;

优化和实现思路(重点)

支付需要结果

支付了需要查询结果,大部分文章仅仅讲到了支付,并没有谈到如何获取结果,这里参考了使用Node.js打通支付宝支付(沙箱环境),作者是字节跳动程序员

如何更新页面?

查询到结果如何更新页面?使用了ElMessageBox ElLoading

第一步是弹窗,如下图所示:

由于此时无法确定用户是否已支付未支付(打开了弹窗但未支付),所以不管是取消和确定都需要调用接口进行查询

请求时间增加健壮性

由于请求结果是由支付宝服务器 传回,所以需要最大程度的考虑请求的响应时间,经过测试,60s是个不错的选择,代码如下:

js 复制代码
// 二次封装axios
import axios from "axios";

const instance = axios.create({
  // 后端url地址
  baseURL: "http://127.0.0.1:3007",
  timeout: 60000, //设置超时
  headers: {
    "Content-Type": "application/x-www-form-urlencoded",
  },
});

当然,请求失败后再次进行请求也是一个不错的选择

请求过程优化

添加loading状态

直至结果返回后取消

一些相关的思路

①支付成功后需要连带更新库存销量

②支付成功后需要更新购物车 或当前页面(假设当前页面存在①的数据)

③支付成功后需要更新 用户的订单列表

④由于订单号唯一性,初次 支付过程中未支付 (生成了订单号),下次 支付时需要更新改商品已存在的订单号

⑤多个商品同时结算,利用多条记录的订单号相同的思路,便可通过订单号查询结果时一并更新用户订单,也就是订单号已经为支付状态了,把相同订单号的记录的信息更新至用户的订单表中

参考链接

Node对接支付宝沙箱完成支付完整流程
使用Node.js打通支付宝支付(沙箱环境)

相关推荐
小小小小宇14 分钟前
前端小tips
前端
小小小小宇23 分钟前
二维数组按顺时针螺旋顺序
前端
安木夕42 分钟前
C#-Visual Studio宇宙第一IDE使用实践
前端·c#·.net
努力敲代码呀~44 分钟前
前端高频面试题2:浏览器/计算机网络
前端·计算机网络·html
高山我梦口香糖1 小时前
[electron]预脚本不显示内联script
前端·javascript·electron
神探小白牙1 小时前
vue-video-player视频保活成功确无法推送问题
前端·vue.js·音视频
Angel_girl3192 小时前
vue项目使用svg图标
前端·vue.js
難釋懷2 小时前
vue 项目中常用的 2 个 Ajax 库
前端·vue.js·ajax
Qian Xiaoo2 小时前
Ajax入门
前端·ajax·okhttp
爱生活的苏苏2 小时前
vue生成二维码图片+文字说明
前端·vue.js