vue+element模仿电商商城,前后端分离实现,下单微信扫码支付

news2025/1/11 10:09:28

1.前言

接上一篇《vue+element+SpringBoot+OAuth2+Spring Security+Redis+mybatis-plus+mysql+swagger模仿商城,前后端分离实现》。

上篇文章介绍了:

  1. 用户注册
  2. 用户登录
  3. 首页商品推荐展览
  4. 商品搜索
  5. 商品分类
  6. 按商品分类预览商品
  7. 商品详情预览
  8. 加入购物车

上一篇文章有说过已实现单一订单下单,即只能一个一个商品去下单购买,所以这次进行了优化完善,把单一订单改成了合并订单,即一个订单下面可包含多个订单行项目,也就是一次可购买多个商品,统一生成一个订单,然后进行扫码支付;

2.合并订单需要设计两个数据表:

大订单表:

  CREATE TABLE `product_order`
(
    `id` bigint NOT NULL COMMENT '主键',
    `order_num` varchar(100) DEFAULT NULL COMMENT '订单号',
    `user_id` bigint NOT NULL COMMENT '所属用户',
    `pay_amount` decimal(10, 2) NOT NULL DEFAULT 0 COMMENT '支付金额',
    `receiver_name` varchar(225) NOT NULL COMMENT '收货人姓名',
    `receiver_phone` varchar(225) NOT NULL COMMENT '收货人联系电话',
    `receiver_addr` varchar(225) NOT NULL COMMENT '收货人地址',
    `receiver_remark` varchar(225) DEFAULT NULL COMMENT '收货人备注',
    `pay_status`  TINYINT(1) DEFAULT 1 COMMENT '支付状态 1待支付 2待发货 3已发货 4已完成 5已取消',
    `deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除标记 是否已删除: 0否  1是',
    `create_time` datetime(0) COMMENT '创建时间',
    `update_time` datetime(0) COMMENT '更新时间',
    PRIMARY KEY (`id`) USING BTREE,
    UNIQUE KEY `order_num` (`order_num`) USING BTREE
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4 COMMENT ='订单';

订单行项目表:

    -- 订单行项目
  CREATE TABLE `product_order_item`
(
    `id` bigint NOT NULL COMMENT '主键',
    `parent_id` bigint NOT NULL COMMENT '订单主键',
    `order_num` varchar(100) DEFAULT NULL COMMENT '订单号',
    `product_id` bigint NOT NULL COMMENT '商品主键',
    `product_name` varchar(225) NOT NULL COMMENT '商品名称',
    `product_des` varchar(225) DEFAULT NULL COMMENT '商品描述',
    `price` decimal(10, 2) NOT NULL DEFAULT 0 COMMENT '商品单价',
    `original_price` decimal(10, 2) NOT NULL DEFAULT 0 COMMENT '商品原价',
    `cover_path` varchar(500) DEFAULT NULL COMMENT '商品封面图片',
    `product_num` int(11) NULL DEFAULT 0 COMMENT '商品购买数量',
    `deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除标记 是否已删除: 0否  1是',
    `create_time` datetime(0) COMMENT '创建时间',
    `update_time` datetime(0) COMMENT '更新时间',
    PRIMARY KEY (`id`) USING BTREE,
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4 COMMENT ='订单行项目';

3.生成支付二维码

controller:

/**
     * 微信支付->扫码支付(模式二)->统一下单->微信二维码
     */
    @GetMapping("/getQrcode")
    public void wxPayPay(@Param("订单号") @RequestParam("orderNum") String orderNum, HttpServletResponse response) {
        wxPayService.getPayQrcode(orderNum,response);
    }

service: 

 /**
     * 微信支付->扫码支付(模式二)->统一下单->微信二维码
     *
     * @param orderNum 订单号
     * @param response 返回
     */
    @Override
    public void getPayQrcode(String orderNum, HttpServletResponse response) {
        String urlCode;
        // 获取订单信息
        WxpayVO vo = new WxpayVO();
        String outTradeNo = UUID.randomUUID().toString().replace("-", "");
        vo.setOutTradeNo(outTradeNo);
        // 账号信息
        vo.setAppId(wxPayConfig.getAppId());
        vo.setMchId(wxPayConfig.getMchId());
        vo.setKey(wxPayConfig.getKey());
        String currTime = PayToolUtil.getCurrTime();
        vo.setCurrTime(currTime);
        String strTime = currTime.substring(8, currTime.length());
        vo.setStrTime(strTime);
        String strRandom = String.valueOf(PayToolUtil.buildRandom(4));
        vo.setStrRandom(strRandom);
        String nonceStr = strTime + strRandom;
        vo.setNonceStr(nonceStr);
        vo.setSpbillCreateIp(wxPayConfig.getSpbillCreateIp());
        vo.setNotifyUrl(wxPayConfig.getNotifyUrl());
        vo.setTradeType("NATIVE");
        String totalFee = "1";
        vo.setTotalFee(totalFee);//价格的单位为分

        SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
        packageParams.put("appid", wxPayConfig.getAppId());//公众账号ID
        packageParams.put("mch_id", wxPayConfig.getMchId());//商户号
        packageParams.put("nonce_str", wxPayConfig.getNonceStr());//随机字符串
        packageParams.put("body", "资源");  //商品描述
        packageParams.put("out_trade_no", wxPayConfig.getNonceStr());//商户订单号
        packageParams.put("total_fee", totalFee); //标价金额 订单总金额,单位为分
        packageParams.put("spbill_create_ip", wxPayConfig.getSpbillCreateIp());//终端IP APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP
        packageParams.put("notify_url", wxPayConfig.getNotifyUrl());//通知地址 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数
        packageParams.put("trade_type", "NATIVE");//交易类型 NATIVE 扫码支付
        // 签名
        String sign = PayToolUtil.createSign("UTF-8", packageParams, wxPayConfig.getKey());
        packageParams.put("sign", sign);

        // 将请求参数转换为xml格式的string
        String requestXML = PayToolUtil.getRequestXml(packageParams);
        log.info("requestXML:{}", requestXML);

        // 调用微信支付统一下单接口
        String resXml = HttpUtil.postData(PayConfigUtil.UFDODER_URL, requestXML);
        log.info("resXml: {}", resXml);

        // 解析微信支付结果
        Map map = null;
        try {
            map = XMLUtil4jdom.doXMLParse(resXml);
            log.info("map: {}", map);
        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 返回微信支付的二维码连接
        urlCode = (String) map.get("code_url");
        log.info("urlCode:{}", urlCode);
        if(urlCode == null){//测试
            urlCode = "http://192.168.50.231:8089/order/updatePayStatus?orderNum=" + orderNum + "&payStatus=" + PayStatus.WAIT_SEND.getValue();
        }

        try {
            int width = 300;
            int height = 300;
            //二维码的图片格式
            String format = "gif";
            Hashtable hints = new Hashtable();
            //内容所使用编码
            hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
            BitMatrix bitMatrix;
            bitMatrix = new MultiFormatWriter().encode(urlCode, BarcodeFormat.QR_CODE, width, height, hints);
            QRUtil.writeToStream(bitMatrix, format, response.getOutputStream());
        } catch (WriterException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

注意:

urlCode = "http://192.168.50.231:8089/order/updatePayStatus?orderNum=" + orderNum + "&payStatus=" + PayStatus.WAIT_SEND.getValue();

这一句是为了本地测试加的,因为微信支付首先要去微信申请开通微信支付:

微信支付文档

二维码代表的内容就是urlCode的具体值,扫码模拟微信支付就会直接请求路径:http://192.168.50.231:8089/order/updatePayStatus?orderNum=" + orderNum + "&payStatus=" + PayStatus.WAIT_SEND.getValue(),进行订单状态更新,所以接口要开放白名单,即是无需登录即可请求。

当然如果是真实的微信支付,则需要配置支付回调地址,由微信官方通知服务器订单支付的状态,同样也要配置开放白名单,否则微信无法请求到我们的服务器。

微信回调:

controller:

/**
     * 微信支付-回调
     * @param request request请求
     * @param response response返回
     */
    @PostMapping("/notify")
    public String wxPayNotify(HttpServletRequest request, HttpServletResponse response) {
        return wxPayService.wxPayNotify(request,response);
    }

service:

 /**
     * 微信支付回调
     *
     * @param request 请求
     * @param response 返回
     * @return 结果
     */
    @Override
    public String wxPayNotify(HttpServletRequest request, HttpServletResponse response) {
        //读取参数
        InputStream inputStream ;
        StringBuffer sb = null;
        try {
            sb = new StringBuffer();
            inputStream = request.getInputStream();
            String s ;
            BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            while ((s = in.readLine()) != null){
                sb.append(s);
            }
            in.close();
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //解析xml成map
        Map<String, String> map = new HashMap<String, String>();
        try {
            map = XMLUtil4jdom.doXMLParse(sb.toString());
        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //过滤空 设置 TreeMap
        SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
        Iterator it = map.keySet().iterator();
        while (it.hasNext()) {
            String parameter = (String) it.next();
            String parameterValue = map.get(parameter);
            String v = "";
            if(null != parameterValue) {
                v = parameterValue.trim();
            }
            packageParams.put(parameter, v);
        }

        //判断签名是否正确
        if(PayToolUtil.isTenpaySign("UTF-8", packageParams, wxPayConfig.getKey())) {
            //------------------------------
            //处理业务开始
            //------------------------------
            String resXml = "";
            if("SUCCESS".equals((String)packageParams.get("result_code"))){
                // 这里是支付成功
                //执行自己的业务逻辑
                String mch_id = (String)packageParams.get("mch_id");
                String openid = (String)packageParams.get("openid");
                String is_subscribe = (String)packageParams.get("is_subscribe");
                String out_trade_no = (String)packageParams.get("out_trade_no");

                String total_fee = (String)packageParams.get("total_fee");

                //执行自己的业务逻辑
                productOrderService.updatePayStatus(out_trade_no,PayStatus.WAIT_SEND.getValue());

                log.info("支付成功");

                //通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                        + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";

            } else {
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                        + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
                return ("fail");
            }
            //------------------------------
            //处理业务完毕
            //------------------------------
            try {
                BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
                out.write(resXml.getBytes());
                out.flush();
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        } else{
            log.info("通知签名验证失败");
        }
        return ("success");
    }

4.模拟微信扫码支付,逻辑其实是一样的

1.单一商品下单,微信扫一扫:

 轮询查询订单状态:

 当订单状态为完成支付时,倒计时5秒跳转:

getByOrderNumFun(){
        getByOrderNum(this.orderNum).then(res => {
          if(res.code === 200){
            if(res.data.payStatus === 1){//待支付
              if(this.url === ''){
                this.url = '/api/wxPay/getQrcode?orderNum=' + this.orderNum;
              }
            }else if(res.data.payStatus === 2){//已支付
              clearInterval(this.inter);
              this.tipColor = '#67C23A';
              this.tip = '支付成功,正在为您跳转订单页面~';
              this.countDownFun(5,'/order/index');
            }else if(res.data.payStatus === 4){//已取消订单
              clearInterval(this.inter);
              this.tip = '订单已取消,正在为您跳转首页~';
              this.countDownFun(5,'/home/index');
            }else{
              clearInterval(this.inter);
            }

          }else{
            clearInterval(this.inter);
          }
        },error => {
          clearInterval(this.inter);
        })
      },

多个商品下单:

 看到订单列表超过两个商品时显示样式有问题,修正优化~:

 待支付,可以点击进行支付:

已付款,待发货:

可取消订单:

 微信模拟扫码截图(忽略提示乱码):

 

 5.至此订单告一段落

感谢看到这里,需要源码或有什么问题和想说的请私聊!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/54064.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【使用 BERT 的问答系统】第 5 章 :BERT模型应用:问答系统

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

arthars在线诊断

官网地址&#xff1a;https://arthas.aliyun.com/doc/quick-start.html#_6- 1.安装启动 curl -O https://arthas.aliyun.com/arthas-boot.jar java -jar arthas-boot.jar2.选择对应进程编号回车3.watch命令 官网命令文档&#xff1a;https://arthas.aliyun.com/doc/arthas-t…

[python][学习]循环与嵌套---打印乘法口诀

打印乘法口诀表 打印乘法口诀表 按照规律可以直接用一个for循环进行实现 加就是显示字符串&#xff0c;没有加就是直接计算或者显示 for i in range(1,10): print(i,*,1,,i*1,end ) print(i,*,2,,i*2,end ) print(i,*,3,,i*3,end ) print(i,*,4,,i*…

STC 51单片机50——中断问题演示

// 功能要求&#xff1a;按下按钮后&#xff0c;Led的状态改变。 #include "reg51.h" sbit LedP1^0; sbit ButtonP3^2; //外中断0对应的引脚 void delay(unsigned char m) { unsigned char i,j,k; for(i0;i<m;i) for(j0;j<250;j) for(k…

JWT生成tonken验证+AOP拦截验证

JSON Web Token&#xff08;JWT&#xff09;是目前都在用的前后分离跨域验证规则。 JWT由3部分组成 Header——头部一般Base64URL编码&#xff0c;作用&#xff1a;声明token类型&#xff0c;声明token使用的加密算法。一般都是使用HMAC-SHA256或者RSA支持很多种算法&#xf…

岩藻多糖-聚乙二醇-刀豆球蛋白A,ConcanavalinA-PEG-Fucoidan,刀豆球蛋白A-PEG-岩藻多糖

岩藻多糖-聚乙二醇-刀豆球蛋白A&#xff0c;ConcanavalinA-PEG-Fucoidan&#xff0c;刀豆球蛋白A-PEG-岩藻多糖 中文名称&#xff1a;岩藻多糖-刀豆球蛋白A 英文名称&#xff1a;Fucoidan-ConcanavalinA 别称&#xff1a;刀豆球蛋白A修饰岩藻多糖&#xff0c;ConA-岩藻多糖 …

分享从零开始学习网络设备配置--任务2.6 避免网络环路

任务描述 某公司最近由于业务迅速发展和对网络可靠性的要求&#xff0c;使用了两台高性能交换机作为核心交换机&#xff0c;接入层交换机与核心层交换机互联&#xff0c;形成冗余结构&#xff0c;来满足网络的可靠性&#xff0c;达到最佳的工作效率。 生成树技术&#xff08;S…

在一次又一次的失败中, 我总结了这份万字的《MySQL 性能调优笔记》

MySQL 性能调优学习导图》概要 说明&#xff1a;本文篇幅有限&#xff0c;故只展示部分内容&#xff0c;《MySQL 性能调优学习导图》资料已整理成文档&#xff0c;需要获取的小伙伴可以直接转发关注后私信&#xff08;学习&#xff09;即可获取哦 一、性能监控 1. 使用 show p…

2022/12/1 结构体

结构体 声明结构体&#xff1a; struct 结构体名 例如&#xff1a;struct student 其中&#xff0c;student又称为结构体标记 结构体可以拥有成员&#xff0c;例如&#xff1a; struct student { int num; int score; char name[20] }; 注意&#xff0c;分号不可以少…

万应低代码11月重点更新内容速递

速览版 详情版 低代码开发效率升级 1.页面变量支持快速构建 在「数据提交」、「调用逻辑」等存在数据返回的节点中&#xff0c;支持根据其输出的内容去自动创建数据类型相同的变量&#xff0c;并自动建立好映射关系。 ● 【功能上线前】&#xff1a; 需要为每一个输出字…

Elasticsearch_第一章_ elasticsearch基础

Elasticsearch_第一章_ elasticsearch基础 – elasticsearch基础 文章目录Elasticsearch_第一章_ elasticsearch基础0.学习目标1.初识elasticsearch1.1.了解ES1.1.1.elasticsearch的作用1.1.2.ELK技术栈1.1.3.elasticsearch和lucene1.1.4.为什么不是其他搜索技术&#xff1f;1…

企业该如何选择自己合适的云财务软件?

据相关数据统计&#xff0c;2021年&#xff0c;我国云服务市场规模达274亿美元&#xff08;超1700亿元人民币&#xff09;&#xff0c;预计到2026年将增长至850亿美元&#xff08;约5400亿元人民币&#xff09;。可见&#xff0c;云服务市场的发展之迅。对于企业而言&#xff0…

python入门项目03:完成黑心资本家发工资的程序

本题来源于黑马程序员b站视频&#xff0c;如有侵权&#xff0c;请联系删除。 import random #总金额10000 all_money10000 n0#记录发放工资的人 #发工资 for i in range(1,21):#20个员工jixiaorandom.randint(1,10)#生成一个1&#xff0c;10的随机数if jixiao<5:print(f员工…

12.1排序

目录 0.修改栈堆内存 一.堆排序 1 原理 2.代码实现 3.分析 二.冒泡排序 1 原理 2.实现 3.分析 三.快速排序(重要) 1 原理-总览 2.方法:挖坑法 步骤一 步骤二 步骤三 步骤四 步骤五 步骤六 3.代码实现挖坑法 4.分析 四.字符串转整数 1.字符串方法 2.字符…

【C++重点语法下】可变参数模板,STL里面的push_back和emplace_back区别 ,包装器function,bind

目录 1.可变参数模板 1.1取出参数包内的参数方法一&#xff1a; 1.2取出参数包内的参数方法二&#xff1a; 1.3STL里面的push_back和emplace_back区别 2.包装器function 2.1function&#xff08;头文件functional&#xff09; 2.1.1可调用类型和包装器 2.1.2类的成员函数…

实验十 符号计算基础与符号微积分(matlab)

目录 实验十 符号计算基础与符号微积分 1.1实验目的 1.3流程图 1.4程序清单 1.5运行结果及分析 1.6实验的收获与体会 1.1实验目的 1.2实验内容 符号计算基础与符号微积分 课本第372页 1.3流程图 1.4程序清单 实验十 1 clear xsym(6); ysym(5); z(1x)/(sqrt(…

第9章 登录页面的跳转实现

1 “swg-login.html”登录按钮不能触发异常 由于.Net框架默认支持“HTTPS”协议从而导致“swg-login.html”登录按钮不能触发&#xff0c;其异常信息如下&#xff1a;“Mixed Content: The page at https://localhost:7037/swg-login.html was loaded over HTTPS, but requeste…

FinalShell软件连接成功后,root文件夹显示一直加载中....

出现这样的问题就是因为我们一开始进入的用户是普通用户&#xff0c;然后你就会想着使用su命令转为超级用户&#xff0c;但是这样式不可行的&#xff0c;因为虚拟机会默认你第一次进入的用户是当前用户&#xff0c;还是解决不了问题。 解决办法&#xff1a; 再开一个连接进入…

java基于springboot_vue的校园闲置物品交易系统-计算机毕业设计

开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven 本文从管理员、用户的功能要求…

Java(八)----多线程(二)

1. 生产者与消费者 1.1 安全问题产生 线程本身就是一个新创建的方法栈内存 (CPU进来读取数据) 线程的notify(),唤醒第一个等待的线程 解决办法 : 全部唤醒 notifyAll() 被唤醒线程,已经进行过if判断,一旦醒来继续执行 线程被唤醒后,不能立刻就执行,再次判断标志位,利用循环 …