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

目录

  • 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
{@Resourceprivate 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
{@Resourceprivate WxPayConfig wxPayConfig;/* 原本应该注 入WxPayConfig 这个类,然后调用 getWxPayClient() 方法获取 HttpClient请求对象* 但是因为 getWxPayClient() 方法加了@Bean注解,交给了spring容器管理,所以项目启动的时候就会执行这个方法,* 就会存在返回值为 CloseableHttpClient 类型的 HttpClient请求对象* 所以这里可以直接注入这个 CloseableHttpClient 对象*/@Resourceprivate CloseableHttpClient wxPayClient;@Resourceprivate OrderInfoService orderInfoService;/*** 创建订单,调用 Native 支付接口** @param productId 商品id* @return code_url 和 订单号* @throws Exception*///调用统一下单API,生成支付二维码的链接和订单号@Overridepublic 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 的urlHttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(WxApiType.NATIVE_PAY.getType()));// 请求body参数---------调用接口需要的参数Gson gson = new Gson();//数据类型不固定,所以就不写泛型了Map paramsMap = new HashMap();//设置参数 --- 根据官网要求设置对应的参数paramsMap.put("appid", wxPayConfig.getAppid()); //公众号IDparamsMap.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){ //处理成功,无返回BodySystem.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 是 二维码的keycodeUrl = 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
{@Resourceprivate ProductMapper productMapper;@Resourceprivate OrderInfoMapper orderInfoMapper;//创建商品订单@Overridepublic 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); //商品idorderInfo.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*/@Overridepublic 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);//执行sqlorderInfoMapper.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
{//依赖注入@Resourceprivate 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 商品订单列表,倒序显示*/@Overridepublic 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.hqwc.cn/news/161770.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch:RAG vs Fine-tunning (大语言模型微调)

如果你对 RAG 还不是很熟悉的话&#xff0c;请阅读之前的文章 “Elasticsearch&#xff1a;什么是检索增强生成 - RAG&#xff1f;”。你可以阅读文章 “Elasticsearch&#xff1a;在你的数据上训练大型语言模型 (LLM)” 来了解更多关于如何训练你的模型。在今天的文章中&#…

算法模板之单调栈解密 | 图文详解

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;算法模板、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. ⛳️单调栈讲解1.1 &#x1f514;单调栈的定义1.2 &#x1f514;如何维护一个单…

Docker容器技术实战4

11、docker安全 proc未被隔离&#xff0c;所以在容器内和宿主机上看到的东西是一样的 容器资源控制 cpu资源限制 top命令&#xff0c;查看cpu使用率 ctrlpq防止退出回收&#xff0c;容器会直接调用cgroup&#xff0c;自动创建容器id的目录 cpu优先级设定 测试时只保留一个cpu…

答题测评考试小程序的效果如何

在线答题系统是一种在线练习、考试、测评的智能答题系统&#xff0c;适用于企业培训、测评考试、知识竞赛、模拟考试等场景&#xff0c;管理员可任意组题、随机出题&#xff0c;答题者成功提交后&#xff0c;系统自动判分。 多种题目类型&#xff0c;两种答题模式 练习模式&a…

c语言实现http下载功能,显示进度条和下载速率

#include <stdio.h>//printf #include <string.h>//字符串处理 #include <sys/socket.h>//套接字 #include <arpa/inet.h>//ip地址处理 #include <fcntl.h>//open系统调用 #include <unistd.h>//write系统调用 #include <netdb.h>//…

若依笔记(四):代码生成器

已知使用MyBatisPlus代码生成器可以自动生成Entity、Mapper、Service、Controller代码&#xff0c;前提是数据库中有数据表&#xff0c;生成pojo类以及对于该数据表的增删改查命令的代码&#xff0c;若依更进一步能选择表后生成代码、预览、下载&#xff0c;同时可以生产前端代…

chrome 扩展 popup 弹窗的使用

popup的基本使用方法 popup介绍 popup 是点击 browser_action 或者 page_action图标时打开的一个小窗口网页&#xff0c;焦点离开网页就立即关闭&#xff0c;一般用来做一些临时性的交互。 popup配置 V3版本中&#xff08;V2版本是在 browser_action 中 &#xff09;&#x…

C语言——选择排序

完整代码&#xff1a; //选择排序 // 选择排序是一种简单直观的排序算法。它的工作原理如下:首先在未排序序列中找到最小&#xff08;大&#xff09;元素&#xff0c;存放到排序序列的起始位置&#xff0c;然后&#xff0c;再从剩余未排序元素中继续寻找最小&#xff08;大&am…

ke9案例三:页面提交文件,我服务器端接收

案例三:页面提交文件,我服务器端接收 ProcessFile.java 1value "/process-file" 2获取邮件消息的所有部分part--Collection<Part> partsrequest.getParts(); 3遍历每一个part 4之后可以打印头文件等String headerpart.getHeader("content-disposition&q…

每天五分钟计算机视觉:搭建手写字体识别的卷积神经网络

本文重点 我们学习了卷积神经网络中的卷积层和池化层,这二者都是卷积神经网络中不可缺少的元素,本例中我们将搭建一个卷积神经网络完成手写字体识别。 卷积和池化的直观体现 手写字体识别 手写字体的图片大小是32*32*3的,它是一张 RGB 模式的图片,现在我们想识别它是从 …

Ubuntu 20.04源码安装git 2.35.1

《如何在 Ubuntu 20.04 上从源代码安装 Git [快速入门]》和《如何在 Ubuntu 20.04 上安装 Git》是我参考的博客。 https://git-scm.com/是git官网。 lsb_release -r看到操作系统版本是20.04。 uname -r看到内核版本是5.4.0-156-generic。 sudo apt update更新一下源。 完…

IDEA中如何移除未使用的import

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的编码爱好者 大家好&#xff0c;我是全栈工…