03、SpringBoot + 微信支付 ---- 创建订单、保存二维码url、显示订单列表

news2024/11/26 23:23:55

目录

  • Native 下单
    • 1、创建课程订单保存到数据库
      • 1-1:需求:
      • 1-2:代码:
      • 1-3:测试结果:
    • 2、保存支付二维码的url
      • 2-1:需求:
      • 2-2:代码:
      • 2-3:测试:
      • 2-4:完整代码:
        • 后端:
          • WxPayController
          • WxPayService
          • WxPayServiceImpl
          • OrderInfoService
          • OrderInfoServiceImpl
    • 3、显示订单列表
      • 3-1:需求:
      • 3-2:代码:
        • 前端:
        • 后端:
      • 3-3:测试:
        • 查看swagger
        • 查看订单列表
      • 3-4:完整代码
        • 后端:
          • OrderInfoController
          • OrderInfoService
          • OrderInfoServiceImpl

Native 下单

1、创建课程订单保存到数据库

1-1:需求:

之前的下单,只是获取支付二维码,但是并没有将订单存到数据库

需求1:点击确认支付后,创建商品的订单存到数据库

需求2:每次确认支付之前,要判断这个人是否存在已下单未支付的订单,有的话就不用再创建订单了,把他的订单查询出来给他就行。

在这里插入图片描述

1-2:代码:

在这里插入图片描述

需求1:点击确认支付后,创建商品的订单存到数据库

在这里插入图片描述

需求2:每次确认支付之前,要判断这个人是否存在已下单未支付的订单,有的话就不用再创建订单了,把他的订单查询出来给他就行。

在这里插入图片描述

1-3:测试结果:

成功在数据库添加订单,并且多次点击确认下单,并不会重复添加订单到数据库

在这里插入图片描述

2、保存支付二维码的url

2-1:需求:

上面创建订单的时候,是没有存二维码的url到数据库的,这里需要在创建订单的时候,把url存进去。
Native调起支付
在这里插入图片描述

2-2:代码:

解释:

因为获取支付二维码url的代码在创建订单之后,所以第一次创建订单是没有支付二维码的url的。

所以在往下的代码中,添加了保存二维码的代码。
在这里插入图片描述

在这里插入图片描述

2-3:测试:

保存二维码成功,并且在重复访问的时候,因为存在二维码,所以不会再去调用微信的下单接口。

因为二维码有效期为2小时,所以后面还需要优化,如果二维码过期,需要再次更新数据库中的二维码。

在这里插入图片描述

2-4:完整代码:

包含创建订单和保存支付二维码的代码

后端:
WxPayController
@CrossOrigin //跨域
@RestController
@RequestMapping("/api/wx-pay")
@Api(tags = "网站微信支付API") //swagger 注解
@Slf4j
public class WxPayController
{
    @Resource
    private WxPayService wxPayService;

    //调用统一下单API,生成支付二维码的链接和订单号
    //swagger注解
    @ApiOperation("调用统一下单API,生成支付二维码")
    @PostMapping("/native/{productId}")
    public R nativePay(@PathVariable Long productId) throws Exception
    {
        log.info("发起支付请求");
        //返回支付二维码的链接和订单号
        Map<String,Object> map = wxPayService.nativePay(productId);
        return R.ok().setData(map);
    }
}
WxPayService
public interface WxPayService
{
    //调用统一下单API,生成支付二维码的链接和订单号
    Map<String, Object> nativePay(Long productId) throws  Exception;
}
WxPayServiceImpl

//创建订单,调用 Native 支付接口
@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService
{
    @Resource
    private WxPayConfig wxPayConfig;
    /* 原本应该注 入WxPayConfig 这个类,然后调用 getWxPayClient() 方法获取 HttpClient请求对象
     * 但是因为 getWxPayClient() 方法加了@Bean注解,交给了spring容器管理,所以项目启动的时候就会执行这个方法,
     * 就会存在返回值为 CloseableHttpClient 类型的 HttpClient请求对象
     * 所以这里可以直接注入这个 CloseableHttpClient 对象
     */
    @Resource
    private CloseableHttpClient wxPayClient;
    @Resource
    private OrderInfoService orderInfoService;

    /**
     * 创建订单,调用 Native 支付接口
     *
     * @param productId 商品id
     * @return code_url 和 订单号
     * @throws Exception
     */
    //调用统一下单API,生成支付二维码的链接和订单号
    @Override
    public Map<String, Object> nativePay(Long productId) throws Exception
    {
        //生成订单
        OrderInfo orderInfo = orderInfoService.createOrderInfoByProduct(productId);

        //获取二维码url-----如果是第一次生成订单,那么这个订单是没有二维码url的
        String codeUrl = orderInfo.getCodeUrl();
        //判断--如果订单存在,并且二维码的url也存在,那么就不需要再去调用微信的下单接口来获取支付二维码了
        if (orderInfo != null && !StringUtils.isEmpty(codeUrl))
        {
            //创建一个包含url和订单号的返回值
            Map<String, Object> map = new HashMap<>();
            map.put("codeUrl", codeUrl);
            map.put("orderNo", orderInfo.getOrderNo());
            return map;
        }



        /*
         * 官方提供的 Native下单 接口
         * 支持商户:【普通商户】
         * 请求方式:【POST】/v3/pay/transactions/native
         * 请求域名:【主域名】https://api.mch.weixin.qq.com
         * "https://api.mch.weixin.qq.com/v3/pay/transactions/native" 改成
         * wxPayConfig.getDomain().concat(WxApiType.NATIVE_PAY.getType())
         */

        log.info("调用统一下单API.....");

        //调用统一下单API---拷贝官网的实例代码进行修改---统一下单的接口地址
        //封装统一下单API 的url
        HttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(WxApiType.NATIVE_PAY.getType()));

        // 请求body参数---------调用接口需要的参数
        Gson gson = new Gson();
        //数据类型不固定,所以就不写泛型了
        Map paramsMap = new HashMap();
        //设置参数 --- 根据官网要求设置对应的参数
        paramsMap.put("appid", wxPayConfig.getAppid()); //公众号ID
        paramsMap.put("mchid", wxPayConfig.getMchId()); //直连商户号
        paramsMap.put("description", orderInfo.getTitle()); // 商品描述
        paramsMap.put("out_trade_no", orderInfo.getOrderNo()); //商户订单号
        paramsMap.put("notify_url", wxPayConfig.getNotifyDomain().concat(WxNotifyType.NATIVE_NOTIFY.getType())); //通知地址
        //订单金额有两个参数--嵌套数据
        Map amountMap = new HashMap();
        amountMap.put("total", orderInfo.getTotalFee()); //总金额
        amountMap.put("currency", "CNY"); //货币类型
        paramsMap.put("amount", amountMap); // 订单金额

        //将参数转成字符串
        String jsonParams = gson.toJson(paramsMap);

        log.info("支付的请求参数:" + jsonParams);

        //把参数设置到请求体当中
        StringEntity entity = new StringEntity(jsonParams, "utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        //希望得到的响应类型
        httpPost.setHeader("Accept", "application/json");

        //完成签名并执行请求
        CloseableHttpResponse response = wxPayClient.execute(httpPost);

        //这些就是对调用下单方法的响应结果的处理了
        try
        {
            //字符串形式的响应体
            String bodyAsString = EntityUtils.toString(response.getEntity());
            //响应状态码
            int statusCode = response.getStatusLine().getStatusCode();

            if (statusCode == 200)
            { //处理成功
                System.out.println("成功, 返回结果  = " + bodyAsString);
            } else if (statusCode == 204)
            { //处理成功,无返回Body
                System.out.println("成功");
            } else
            {
                System.out.println("下单失败, 响应码 = " + statusCode + ", 返回结果 = " + bodyAsString);
                throw new IOException("请求失败 request failed");
            }
            //响应结果---如果下单成功,获取响应结果,   gson.fromJson()用于将 JSON 字符串转换为 Java 对象
            Map<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
            //二维码---从返回的结果中获取二维码的url, 从官网看出 code_url 是 二维码的key
            codeUrl = resultMap.get("code_url");

            //保存二维码
            String orderNo = orderInfo.getOrderNo();
            orderInfoService.saveCodeUrl(orderNo,codeUrl);

            //创建一个包含url和订单号的返回值
            Map<String, Object> map = new HashMap<>();
            map.put("codeUrl", codeUrl);
            map.put("orderNo", orderInfo.getOrderNo());
            return map;
        } finally
        {
            response.close();
        }
    }
}
OrderInfoService

public interface OrderInfoService extends IService<OrderInfo> {


    /**
     * 根据商品id创建商品订单
     * @param productId 商品id
     * @return 订单对象
     */
    OrderInfo createOrderInfoByProduct(Long productId);


    /**
     * 将支付二维码的url存到订单中
     * @param orderNo 订单编号
     * @param codeUrl 支付二维码的地址url
     */
    void saveCodeUrl(String orderNo, String codeUrl);


}
OrderInfoServiceImpl

@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService
{
    @Resource
    private ProductMapper productMapper;
    @Resource
    private OrderInfoMapper orderInfoMapper;

    //创建商品订单
    @Override
    public OrderInfo createOrderInfoByProduct(Long productId)
    {
        //用户点击确认支付,要先查找该用户是否存在已下单未支付的订单
        OrderInfo orderInfo = this.getNoPayOrderByProductId(productId);
        if (orderInfo != null)
        {
            //表示该用户已经下单了,还没有支付,那就直接把他的订单返回回去就行了,否则就创建新订单
            return orderInfo;
        }
        //根据商品的id获取到该商品对象数据
        Product product = productMapper.selectById(productId);

        orderInfo = new OrderInfo();
        orderInfo.setTitle(product.getTitle()); //订单标题
        orderInfo.setOrderNo(OrderNoUtils.getOrderNo()); //生成订单号
        orderInfo.setProductId(productId); //商品id
        orderInfo.setTotalFee(1); //单位是:分
        orderInfo.setOrderStatus(OrderStatus.NOTPAY.getType()); //支付状态
        //把商品订单存到数据库
        orderInfoMapper.insert(orderInfo);
        return orderInfo;
    }

    /**
     * 根据商品id查询已下单未支付的订单,防止重复创建订单
     * @param productId 商品id
     * @return 订单对象
     */
    private OrderInfo getNoPayOrderByProductId(Long productId)
    {
        //QueryWrapper 是 MyBatis-Plus 提供的一个用于构建查询条件的工具类
        QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();

        //相当于封装查询对象,这是查询条件
        //就是查询该表中,是否有 列名 product_id 对应的值等于这个 productId ,有就查询出来
        queryWrapper.eq("product_id", productId);
        queryWrapper.eq("order_status", OrderStatus.NOTPAY.getType());

        //把 queryWrapper 作为查询条件对象
        //selectOne 就是查询出一条,如果查询出多条,则会报错
        OrderInfo orderInfo = orderInfoMapper.selectOne(queryWrapper);

        return orderInfo;
    }


    /**
     * 将支付二维码的url存到订单中
     * @param orderNo 订单编号
     * @param codeUrl 支付二维码的地址url
     */
    @Override
    public void saveCodeUrl(String orderNo, String codeUrl)
    {
        //QueryWrapper 是 MyBatis-Plus 提供的一个用于构建查询条件的工具类
        QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
        //组装查询条件
        queryWrapper.eq("order_no",orderNo);

        //要修改的字段,存到这个 orderInfo 对象里面
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setCodeUrl(codeUrl);

        //执行sql
        orderInfoMapper.update(orderInfo,queryWrapper);

    }

}

3、显示订单列表

3-1:需求:

在我的订单页面按时间倒序显示订单列表
目前是有订单,但是没展示出来。
在这里插入图片描述

3-2:代码:

前端:

调用后端接口的是api模块

<script> 脚本模块
<template> 模板,是用来定义组件的模板部分,用于描述组件的结构和布局

在这里插入图片描述

将后端返回的list商品订单列表赋值给 orders.vue 这个类后,就需要对这个数据进行渲染。
在这里插入图片描述

后端:

创建一个订单的controller
在这里插入图片描述

3-3:测试:

查看swagger

在这里插入图片描述

查看订单列表

成功显示
在这里插入图片描述

3-4:完整代码

后端:
OrderInfoController
@CrossOrigin //开放前端的跨域访问
@RestController
@RequestMapping(value = "/api/order-info")
@Api(tags = "商品订单管理")
public class OrderInfoController
{
    //依赖注入
    @Resource
    private OrderInfoService orderInfoService;

    @ApiOperation("显示商品订单列表")
    @GetMapping("/list")
    public R getOrderInfoList()
    {
        List<OrderInfo> list =
                orderInfoService.getOrderInfoListByCreateTimeDesc();
        return R.ok().data("list",list);
    }
}
OrderInfoService
    /**
     * 获取商品订单列表,并按时间倒序显示
     * @return 商品订单列表,倒序显示
     */
    List<OrderInfo> getOrderInfoListByCreateTimeDesc();
OrderInfoServiceImpl
    /**
     * 获取商品订单列表,并按时间倒序显示
     * @return 商品订单列表,倒序显示
     */
    @Override
    public List<OrderInfo> getOrderInfoListByCreateTimeDesc()
    {
        //QueryWrapper 是 MyBatis-Plus 提供的一个用于构建查询条件的工具类
        QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
        //组装查询条件
        queryWrapper.orderByDesc("create_time");

        //查询
        List<OrderInfo> list = orderInfoMapper.selectList(queryWrapper);

        return list;
    }

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

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

相关文章

python 之 sorted 函数

文章目录 sorted() 函数的语法返回值使用示例&#xff1a;示例 1&#xff1a;基本使用示例 2&#xff1a;指定降序排序示例 3&#xff1a;使用 key 参数进行自定义排序 注意事项&#xff1a; sorted() 是 Python 中的一个内置函数&#xff0c;用于对可迭代对象进行排序&#xf…

jquery之checkbox全选反选提交参数

实现效果 <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>Checkbox操作示例</title><script src"https://code.jquery.com/jquery-3.5.1.min.js"></script><script>$(document).ready(…

FPGA高端项目:图像缩放+GTP+UDP架构,高速接口以太网视频传输,提供2套工程源码加QT上位机源码和技术支持

目录 1、前言免责声明本项目特点 2、相关方案推荐我这里已有的 GT 高速接口解决方案我这里已有的以太网方案我这里已有的图像处理方案 3、设计思路框架设计框图视频源选择ADV7611 解码芯片配置及采集动态彩条跨时钟FIFO图像缩放模块详解设计框图代码框图2种插值算法的整合与选择…

【数据结构与算法】JavaScript实现哈希表

文章目录 一、哈希表简介1.1.认识哈希表1.2.哈希化的方式1.3.解决冲突的方法1.4.寻找空白单元格的方式线性探测二次探测再哈希化 1.5.不同探测方式性能的比较1.6.优秀的哈希函数快速计算均匀分布 二、初步封装哈希表2.1.哈希函数的简单实现2.2.创建哈希表2.3.put(key,value)2.4…

时间序列预测模型实战案例(七)(TPA-LSTM)结合TPA注意力机制的LSTM实现多元预测

论文地址->TPA-LSTM论文地址 项目地址-> TPA-LSTM时间序列预测实战案例 本文介绍 本文通过实战案例讲解TPA-LSTM实现多元时间序列预测&#xff0c;在本文中所提到的TPA和LSTM分别是注意力机制和深度学习模型,通过将其结合到一起实现时间序列的预测&#xff0c;本文利用…

Google发布移动终端对象检测模型——mediapipe,无GPU依然飞快

对象检测模型最出名的当选YOLO系列,其YOLO系列已经更新到V8系列,但是现有的YOLO模型面临限制,如量化支持不足和准确性延迟权衡不足。 YOLO-NAS模型在包括COCO、Objects365和Roboflow 100在内的知名数据集上进行了预训练,使其非常适合生产环境中的下游对象检测任务。YOLO-NA…

unity【动画】脚本_角色动画控制器 c#

首先创建一个代码文件夹Scripts 从人物角色Player的基类开始 创建IPlayer类 首先我们考虑到如果不挂载MonoBehaviour需要将角色设置成预制体实例化到场景上十分麻烦&#xff0c; 所以我们采用继承MonoBehaviour类的角色基类方法写代码 也就是说这个脚本直接绑定在角色物体…

Quartz之JDBC-JobStoreTX配置

一、前言 上篇 《Quartz介绍》中使用的是RAMJobStored存储调度信息&#xff0c;当进程终止调度信息会丢失&#xff0c;本篇我们介绍使用JDBCJobStore来存储调度信息&#xff08;jobs、Triggers和日历&#xff09;。 二、Quartz 表结构 可以从官网&#xff08;http://www.qua…

康耐视深度学习ViDi-ViDi四大工具介绍与主要用途

Cognex ViDi 工具是一系列机器视觉工具&#xff0c;通过深度学习解决各种难以解决的挑战。虽然这些工具共享一个引擎&#xff0c;但它们在图像中寻找的内容不同。更具体地说&#xff0c;在分析单个点、单个区域或完整图像时&#xff0c;每个工具都有不同的侧重点。 Locate&…

JUC并发编程系列(一):Java线程

前言 JUC并发编程是Java程序猿必备的知识技能&#xff0c;只有深入理解并发过程中的一些原则、概念以及相应源码原理才能更好的理解软件开发的流程。在这篇文章中荔枝会梳理并发编程的基础&#xff0c;整理有关Java线程以及线程死锁的知识&#xff0c;希望能够帮助到有需要的小…

30、JAVA进阶——Socket编程

✅作者简介:热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:乐趣国学的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏:JAVA开发者成长之路 ✨特色专栏:国学周更-心性养成之路 🥭本文内容:JAVA进阶——Socket编程 更多内容点击👇 …

idea2023 PoJie以后无法修改内存无效

1. 打开电脑环境变量 2. 找到对应pojie文件 vmoptions目录 3. 修改这个文件 添加或者修改配置 -Xms128m -Xmx8192m4. 重启idea 修改成功

flutter开发报错The instance member ‘widget‘ can‘t be accessed in an initializer

文章目录 问题描述问题原因解决方法 问题描述 The instance member ‘widget’ can’t be accessed in an initializer. 问题原因 “The instance member ‘widget’ can’t be accessed in an initializer” 错误是因为在初始化器列表中&#xff08;constructor initializer…

JavaScript使用正则表达式

正则表达式(RegExp)也称规则表达式(regular expression)&#xff0c;是非常强大的字符串操作工具&#xff0c;语法格式为一组特殊字符构成的匹配模式&#xff0c;用来匹配字符串。ECMAScript 3以Perl为基础规范JavaScript正则表达式&#xff0c;实现Perl 5正则表达式的子集。Ja…

Linux常用命令——cdrecord命令

在线Linux命令查询工具 cdrecord Linux系统下光盘刻录功能命令 补充说明 cdrecord命令用于Linux系统下光盘刻录&#xff0c;它支持cd和DVD格式。linux下一般都带有cdrecord软件。 语法 cdrecord(选项)(参数)选项 -v&#xff1a;显示刻录光盘的详细过程&#xff1b; -eje…

基于社交网络算法的无人机航迹规划-附代码

基于社交网络算法的无人机航迹规划 文章目录 基于社交网络算法的无人机航迹规划1.社交网络搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用社交网络算法来优化无人机航迹规划。 …

UseGalaxy.cn生信云|新增热图绘制工具:heatmap2

2023-11-05&#xff0c;Galaxy生信云平台 UseGalaxy.cn 新增绘制热图工具。 Graph/Display Data heatmap2 (Galaxy Version 3.1.3galaxy0) 使用方法 进入网址&#xff1a; https://usegalaxy.cn/root?tool_idtoolshed.g2.bx.psu.edu/repos/iuc/ggplot2_heatmap2/ggplot2_heatm…

8.接口与抽象类 深入多态

8.1 不该初始化的class 这个结构有什么不对&#xff1f; 这个class结构不算太差。如此设计已经能够维持最少的重复程序代码&#xff0c;且有需要特地实现的方法也已经被覆盖过。从多态的角度来看&#xff0c;我们也做到了适应性&#xff0c;所以任何Animal的子型&#xff0c;包…

什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码

&#x1f9f8;欢迎来到dream_ready的博客&#xff0c;&#x1f4dc;相信您对这几篇博客也感兴趣o (ˉ▽ˉ&#xff1b;) &#x1f4dc;什么是SpringMVC&#xff1f;简单好理解&#xff01;什么是应用分层&#xff1f;SpringMVC与应用分层的关系&#xff1f; 什么是三层架构&…

【Unity细节】为什么UI移动了锚点,中心点和位置,运行的时候还是不在设置的位置当中

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 &#x1f636;‍&#x1f32b;️收录于专栏&#xff1a;unity细节和bug &#x1f636;‍&#x1f32b;️优质专栏 ⭐【…