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.rhkb.cn/news/182246.html

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

相关文章

【WinForm详细教程七】WinForm中的DataGridView控件

文章目录 1.主要属性DataSource行&#xff08;Row 相关属性&#xff09;列&#xff08;Column 相关属性&#xff09;单元格&#xff08;Cell 相关属性&#xff09;逻辑删除AllowUserToAddRowsAllowUserToDeleteRowsAllowUserToOrderColumns其他布局和行为属性 2.控件中的行、列…

【Linux进程】进程地址空间

目录 程序地址空间回顾 进程地址空间 宏观理解 谈细节 1、进程地址空间究竟是什么&#xff1f; 2、管理地址空间 3、页表 总结几个问题&#xff1a; 1、为什么要有进程地址空间&#xff1f; 2、进程切换 3、进程具有独立性&#xff0c;怎么做到的&#xff1f; 程序地…

axios 全局错误处理和请求取消

这两个功能都是用拦截器实现。 前景提要&#xff1a; ts 简易封装 axios&#xff0c;统一 API 实现在 config 中配置开关拦截器 全局错误处理 在构造函数中&#xff0c;添加一个响应拦截器即可。在构造函数中注册拦截器的好处是&#xff0c;无论怎么实例化封装类&#xff0c…

改进的yolov5

The networkstructure of these models is constant, but the modules and con-volution kernels are scaled, which alters the complexity and sizeof each model.&#xff08;这些模型的网络结构是恒定的&#xff0c;但模块和卷积核被缩放&#xff0c;这改变了每个模型的复杂…

node版本管理神器|nvm安装使用教程

文章目录 导文安装前提下载安装包进行安装第一步&#xff0c;下载安装包&#xff0c;[nvm下载地址](https://github.com/coreybutler/nvm-windows/releases)第二步&#xff0c;进行安装&#xff0c;点击nvm-setup.exe文件&#xff0c;接受用户协议第三步&#xff0c;选择安装目…

​Z时代时尚SUV新宠:起亚赛图斯值不值得年轻人买?

在当今汽车行业中&#xff0c;随着消费者偏好的多样化和年轻化&#xff0c;汽车制造商们正面临着前所未有的挑战与机遇。在2023年上海车展上&#xff0c;起亚汽车公司正式发布了全新紧凑级SUV——赛图斯。这款车型不仅标志着起亚对年轻消费市场的深入洞察&#xff0c;也展现了公…

Chatgpt批量改写文章网页版可多开软件-自动登录换号生成word或者TXT

Chatgpt批量改写文章网页版可多开软件介绍&#xff1a; 1、改写后生成docx格式文档和生成txt文档二选一。 2、支持原来docx文档里带图片的改写&#xff0c;改写伪原创后的docx里也带图片。 3、软件可以设置是否开启标题改写&#xff0c;可以自定义标题改写指令。 4、可以设置…

CVE-2023-34040 Kafka 反序列化RCE

漏洞描述 Spring Kafka 是 Spring Framework 生态系统中的一个模块&#xff0c;用于简化在 Spring 应用程序中集成 Apache Kafka 的过程&#xff0c;记录 (record) 指 Kafka 消息中的一条记录。 受影响版本中默认未对记录配置 ErrorHandlingDeserializer&#xff0c;当用户将容…

2023 现阶段H5的机型适配

个人愚见 现在的主流体验&#xff0c;是大屏手机展示更多的内容&#xff0c;并不着重于放大展示&#xff0c; 所以&#xff0c;外层布局使用vw,百分比&#xff0c;flex&#xff0c;内层直接px就行 比如微信服务页面&#xff0c;大屏下展示更多数据

海外服务器和国内的几大差别!如何选择一款优质的海外服务器?

随着互联网的普及&#xff0c;越来越多的企业或者个人都在搭建自己的网站&#xff0c;而对于搭建网站来说&#xff0c;选择一款合适的服务器显得至关重要。那么许多小伙伴可能都在纠结的一个问题是&#xff0c;海外服务器和国内服务器有什么区别&#xff1f;我们是否应该选择一…

http中的Content-Type类型

浏览器的Content-Type 最近在做web端下载的时候需要给前端返回一个二进制的流&#xff0c;需要在请求头中设置一个 writer.Header().Set("Content-Type", "application/octet-stream")那么http中的Content-Type有具体有哪些呢&#xff1f;他们具体的使用场…

消息中间件-RabbitMQ介绍

一、基础知识 1. 什么是RabbitMQ RabbitMQ是2007年发布&#xff0c;是一个在AMQP(高级消息队列协议)基础上完成的&#xff0c;简称MQ全称为Message Queue, 消息队列&#xff08;MQ&#xff09;是一种应用程序对应用程序的通信方法&#xff0c;由Erlang&#xff08;专门针对于大…

HarmonyOS 数据管理与应用数据持久化(二)

通过键值型数据库实现数据持久化 场景介绍 键值型数据库存储键值对形式的数据&#xff0c;当需要存储的数据没有复杂的关系模型&#xff0c;比如存储商品名称及对应价格、员工工号及今日是否已出勤等&#xff0c;由于数据复杂度低&#xff0c;更容易兼容不同数据库版本和设备…

win中安装nvm进行Node版本控制

之前有安装node.js安装包的需要先给卸载掉先通过官网下载安装包nvm-setup.zip nvm官网地址命令行打开输入nvm -v&#xff0c;测试安装是否成功 此时如果进行node相关版本安装的话&#xff0c;可能下载速度过慢&#xff0c;此时需要更换镜像源。 斜体样式找到nvm的安装路径 &am…

Java智慧工地管理平台可视化大数据建造工地APP源码

建筑行业是国民经济的重要物质生产部门和支柱产业之一&#xff0c;同时&#xff0c;建筑业也是一个安全事故多发的高危行业。如何加强施工现场安全管理、降低事故发生频率、杜绝各种违规操作和不文明施工、提高建筑工程质量&#xff0c;是摆在各级政府部门、施工企业面前的一道…

CRM软件如何高效培育销售线索?

​ 通过线索培育可以挖掘出更多CRM软件销售管道中的有价值客户提高销售业绩。但机遇与挑战总是共存的&#xff0c;培育线索要从不同的渠道执行大量重复性的操作&#xff0c;人为操控不仅速度慢而且容易出错&#xff0c;那么企业如何高效培育销售线索? 发送个性化邮件 我们知…

时间序列预测模型实战案例(八)(Informer)个人数据集、详细参数、代码实战讲解

论文地址->Informer论文地址PDF点击即可阅读 代码地址-> 论文官方代码地址点击即可跳转下载GIthub链接 本文介绍 本篇博客带大家看的是Informer模型进行时间序列预测的实战案例&#xff0c;它是在2019年被提出并在ICLR 2020上被评为Best Paper&#xff0c;可以说Inform…

5.网络之IP

IP协议&#xff08;网络层&#xff09; 文章目录 IP协议&#xff08;网络层&#xff09;1. 报文格式2. IP地址2. 地址管理3. 特殊IP地址 IP协议&#xff08;Internet Protocol&#xff0c;互联网协议&#xff09;&#xff0c;是TCP/IP协议栈中最核心的协议之一&#xff0c;通过…

wordpress版本识别

wordpress版本识别 1.通过RSS Feed识别 RSS Feed参考 访问网站/feed或者?feedrss 例如 默认结构&#xff1a;https://www.example.com/?feedrss2 其他结构&#xff1a;https://www.example.com/feed/ 返回结果中搜索 generator 可以看到直接是5.9.7版本 2.wpscan等工具扫一…

STM32 IIC 实验

1. 可以选择I2C1&#xff0c;也可以选择I2C2&#xff0c;或者同时选择&#xff0c;同时运行 配置时钟信号 为节约空间&#xff0c;选择这两个&#xff0c;然后选择GENERATE CODE 二、HAL_I2C_Mem_Write I2C_HandleTypeDef *hi2c&#xff1a;I2C设备句柄 uint16_t DevAddress&am…