当前位置: 首页 > news >正文

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.lryc.cn/news/219512.html

相关文章:

  • 【echarts基础】在柱形图上设置文本
  • 小户型工业风,陌生上开花知书香。福州中宅装饰,福州装修
  • Gorm 中的迁移指南
  • 基于.NET、Uni-App开发支持多平台的小程序商城系统 - CoreShop
  • [python] 在多线程中将`logging.info`输出到不同的文件中 (生产者消费者)
  • MySQL进阶_5.逻辑架构和SQL执行流程
  • 【油猴脚本】学习笔记
  • 宝塔面板使用Supervisor进程守护插件,配置守护Mysql的操作教程。
  • Electron[2] Electron使用准备
  • npm create vue@latest 原理
  • 【Unity基础】7.动画状态参数
  • C语言映射表在串口数据解析中的应用
  • 叁[3],感兴趣区域ROI
  • 文件数据交换格式说明
  • 2023NOIP A层联测24 总结
  • vue3 项目如何配置测试环境打包
  • 【CSS】样式的计算过程
  • 【ArcGIS微课1000例】0076:KMZ转换KML的方法
  • Python基础入门例程46-NP46 菜品的价格(条件语句)
  • Docker数据管理、网络与Cgroup资源限制
  • ubuntu strings | grep使用说明
  • <Vue>使用依赖注入的方式共享数据
  • 从0到1:腾讯云服务器使用教程
  • VScode + opencv + c++ + win配置教程
  • 机器学习---SVM目标函数求解,SMO算法
  • 044_第三代软件开发-保存PDF
  • 2023红帽论坛:构建开放AI生态,助力企业数字革新之路
  • 阿里云国际站和华为云国际站之间该如何选择?
  • JavaScript设计模式之责任链模式
  • 云安全—kubelet攻击面