• 支付模块实现


    目录

     2.付费课程流程

     支付模块接口实现

    创建订单 

    1.编写控制层(创建订单)

    2.生成订单的业务实现

    3.订单号生成类

    4.远程调用的用户和课程信息

    根据订单id查询订单信息

    支付生成微信支付的二维码接口

    1.控制层,返回二维码集合

     2.业务实现层

    支付之后的后续操作

    1.控制层

    2.业务层


    场景:

    课程分为免费课程和付费课程,如果是免费课程可以直接观看,如果是付费观看的课程,用户需下单支付后才可以观看

     2.付费课程流程

    2.1如果是付费课程,在用户选择课程,进入到课程详情页面时候,会显示 “立即购买”

    2.2 点击“立即购买”,会生成课程的订单,跳转到订单页面

     2.3点击“去支付”,会跳转到支付页面,生成微信扫描的二维码

     使用微信扫描支付后,会跳转回到课程详情页面,同时显示“立即观看”

     支付模块接口实现

    1.导入依赖和配置文件并且明确sql表大概思路

    1. <dependencies>
    2. <dependency>
    3. <groupId>com.github.wxpaygroupId>
    4. <artifactId>wxpay-sdkartifactId>
    5. <version>0.0.3version>
    6. dependency>
    7. <dependency>
    8. <groupId>com.alibabagroupId>
    9. <artifactId>fastjsonartifactId>
    10. dependency>
    11. dependencies>

    1.2两个表一个订单表,当支付成功,订单状态发送改变,并且向支付日志表插入新的数据

    1.3配置文件

    1. # 服务端口
    2. server.port=8007
    3. # 服务名
    4. spring.application.name=service-order
    5. # mysql数据库连接
    6. spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    7. spring.datasource.url=jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8
    8. spring.datasource.username=root
    9. spring.datasource.password=root
    10. #返回json的全局时间格式
    11. spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
    12. spring.jackson.time-zone=GMT+8
    13. #配置mapper xml文件的路径
    14. mybatis-plus.mapper-locations=classpath:com/atguigu/orderservice/mapper/xml/*.xml
    15. #mybatis日志
    16. mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    17. # nacos服务地址
    18. spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
    19. #开启熔断机制
    20. feign.hystrix.enabled=true
    21. # 设置hystrix超时时间,默认1000ms
    22. hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000

    创建订单 

    1.编写控制层(创建订单)

    思路:很简单,像订单这种肯定是关于用户和课程的,所以下单的话需要根据课程id和用户id创建订单返回订单id(利用了feign的远程调用)

    1. /**
    2. * 1.生成订单的方法
    3. */
    4. @GetMapping("createOrder/{courseId}")
    5. public R saveOrder(@PathVariable String courseId, HttpServletRequest request){
    6. //1.生成订单号
    7. String orderNo=orderService.createOrders(courseId,JwtUtils.getMemberIdByJwtToken(request));
    8. return R.ok().data("orderId",orderNo);
    9. }

    2.生成订单的业务实现

    思路:1.首先是通过用户id远程调用用户模块得到用户信息,然后课程模块类似——>2.创建Order对象,向里面添加数据,然后进行插入,返回订单号(这个定义了一个时间类OrderNoUtil根据时间生成)

    1. /**
    2. * 1.生成订单的方法,通过远程调用两个feign方法(课程信息和人物信息)结合到Order中为订单
    3. * @param courseId
    4. * @param memberId
    5. * @return
    6. */
    7. @Override
    8. public String createOrders(String courseId, String memberId) {
    9. //1.通过远程调用根据用户id获取用户信息
    10. UcenterMemberOrder userInfoOrder = ucenterClient.getUserInfoOrder(memberId);
    11. //2.通过远程调用根据课程id获取课程信息
    12. CourseWebVoOrder courseInfoOrder = eduClient.getCourseInfoOrder(courseId);
    13. //3.创建order对象,向order对象里面设置需要的数据
    14. Order order = new Order();
    15. order.setOrderNo(OrderNoUtil.getOrderNo());
    16. order.setCourseId(courseId); //课程id
    17. order.setCourseTitle(courseInfoOrder.getTitle());
    18. order.setCourseCover(courseInfoOrder.getCover());
    19. order.setTeacherName(courseInfoOrder.getTeacherName());
    20. order.setTotalFee(courseInfoOrder.getPrice());
    21. order.setMemberId(memberId);
    22. order.setMobile(userInfoOrder.getMobile());
    23. order.setNickname(userInfoOrder.getNickname());
    24. order.setStatus(0);//订单状态(0:未支付 1:已支付)
    25. order.setPayType(1);//支付类型 微信1
    26. baseMapper.insert(order);//插入
    27. return order.getOrderNo();
    28. }

    3.订单号生成类

    1. package com.atguigu.eduorder.utils;
    2. import java.text.SimpleDateFormat;
    3. import java.util.Date;
    4. import java.util.Random;
    5. import java.util.regex.Matcher;
    6. import java.util.regex.Pattern;
    7. /**
    8. * 订单号工具类
    9. *
    10. * @author qy
    11. * @since 1.0
    12. */
    13. public class OrderNoUtil {
    14. /**
    15. * 获取订单号
    16. * @return
    17. */
    18. public static String getOrderNo() {
    19. SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
    20. String newDate = sdf.format(new Date());
    21. String result = "";
    22. Random random = new Random();
    23. for (int i = 0; i < 3; i++) {
    24. result += random.nextInt(10);
    25. }
    26. return newDate + result;
    27. }
    28. }

    4.远程调用的用户和课程信息

    用户:通过request对里面token进行解析得到用户id然后根据id获取用户信息

    1. /**
    2. * 3.根据token获取用户信息
    3. */
    4. @GetMapping("getMemberInfo")
    5. public R getMemberInfo(HttpServletRequest request){
    6. //getMemberIdByJwtToken根据请求头中的token得到用户信息(token得到userid->取出信息)
    7. String memberId = JwtUtils.getMemberIdByJwtToken(request);
    8. //根据用户id查询用户信息
    9. UcenterMember member = memberService.getById(memberId);
    10. return R.ok().data("userInfo",member);
    11. }
    1. /**
    2. * 2.根据课程id查询课程基本信息
    3. */
    4. @GetMapping("getCourseInfo/{courseId}")
    5. public R getCourseInfo(@PathVariable String courseId){
    6. CourseInfoVo courseInfoVo=courseService.getCourseInfo(courseId);
    7. return R.ok().data("courseInfo",courseInfoVo);
    8. }

    根据订单id查询订单信息

    1. /**
    2. * 2.根据订单id查询订单信息
    3. */
    4. @GetMapping("getOrderInfo/{orderId}")
    5. public R getOrderInfo(@PathVariable String orderId){//订单id查询
    6. QueryWrapper wrapper = new QueryWrapper<>();
    7. wrapper.eq("order_no",orderId);
    8. Order order = orderService.getOne(wrapper);
    9. return R.ok().data("item",order);
    10. }

    判断是否购买课程

    思路:根据课程id和人物id还有课程状态进行判断,然后根据这些条件进行查询如果>0代表已经支付

    1. @GetMapping("isBuyCourse/{courseId}/{memberId}")
    2. public boolean isBuyCourse(@PathVariable String courseId,@PathVariable String memberId){
    3. QueryWrapper wrapper = new QueryWrapper<>();
    4. wrapper.eq("course_id",courseId);
    5. wrapper.eq("member_id",memberId);
    6. wrapper.eq("status",1);
    7. int count = orderService.count(wrapper);
    8. if(count>0){
    9. //代表已经支付
    10. return true;
    11. }else{
    12. return false;
    13. }
    14. }

    支付生成微信支付的二维码接口

    1.控制层,返回二维码集合

    1. /**
    2. * 1.生成微信支付的二维码接口
    3. */
    4. @GetMapping("createNative/{orderNo}")
    5. public R createNative(@PathVariable String orderNo) {
    6. //1.返回信息含有二维码的地址还有其他信息
    7. Map map = payLogService.createNative(orderNo);
    8. System.out.println("*****返回二维码map集合****:"+map);
    9. return R.ok().data(map);
    10. }

     2.业务实现层

     2.1首先根据订单id查询订单信息

     2.2然后我们在map设置二维码参数

     2.3根据微信提供的固定地址生成HttpClient

     2.4因为我们httpClient发送的请求需要时xml格式,所以需要将map转一下

     2.5发送请求得到请求返回的结果,然后将结果转为map格式

     2.6前面只是将map数据给到http生成对应二维码,我们还有一些提示信息,所以定义一个最终结果map,将之前的map二维码地址,状态码啥的还有订单的价格及相关数据封装到结果集中,然后返回

    1. /**
    2. * 1.返回二维码地址
    3. * @param orderNo
    4. * @return
    5. */
    6. @Override
    7. public Map createNative(String orderNo) {
    8. try {
    9. //1.根据订单id获取订单信息
    10. QueryWrapper wrapper = new QueryWrapper<>();
    11. wrapper.eq("order_no", orderNo);
    12. Order order = orderService.getOne(wrapper);
    13. //2.使用map设置二维码需要的参数
    14. HashMap map = new HashMap();
    15. map.put("appid","wx74862e0dfcf69954");
    16. map.put("mch_id", "1558950191");
    17. map.put("nonce_str", WXPayUtil.generateNonceStr());//随机生成一个二维码
    18. map.put("body", order.getCourseTitle());
    19. map.put("out_trade_no", orderNo);//二维码标识订单号
    20. map.put("total_fee", order.getTotalFee().multiply(new BigDecimal("100")).longValue()+"");//价格
    21. map.put("spbill_create_ip", "127.0.0.1");
    22. map.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify\n");
    23. map.put("trade_type", "NATIVE");
    24. //3.发送httpclient请求,传递参数按照xml格式,微信支付提供固定地址
    25. HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
    26. //设置参数,我们的商户key会对这些map参数进行加密->将map根据key进行加密并且传送到请求中去
    27. client.setXmlParam(WXPayUtil.generateSignedXml(map,"T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));
    28. client.setHttps(true);
    29. //执行请求发送
    30. client.post();
    31. //4.得到发送请求返回的结果,返回的内容是xml格式返回的(之前传参数也是xml格式)
    32. String content = client.getContent();
    33. //5.再把xml转为map
    34. MapresultMap=WXPayUtil.xmlToMap(content);
    35. //6.前面的map只是为了给到http生成二维码,需要转为xml格式,现在我们这个结果resultMap就是二维码了
    36. //还需要一些关于订单的提示信息
    37. Map res = new HashMap<>();
    38. res.put("out_trade_no", orderNo);
    39. res.put("course_id", order.getCourseId());
    40. res.put("total_fee", order.getTotalFee());
    41. res.put("result_code", resultMap.get("result_code"));//状态码
    42. res.put("code_url", resultMap.get("code_url"));//二维码地址
    43. return res;
    44. } catch (Exception e) {
    45. throw new GuliException(20001,"生成二维码失败");
    46. }
    47. }

    支付之后的后续操作

    1.控制层

    思路:1.支付之后需要查询支付状态(二维码中的参数),如果状态SUCCESS,就将我们的支付记录添加到支付表中——>2.并且支付成功后还要修改订单表的状态,表示已经支付

    1. /**
    2. * 2.查询订单状态
    3. * @param orderNo
    4. * @return
    5. */
    6. @GetMapping("queryPayStatus/{orderNo}")
    7. public R queryPayStatus(@PathVariable String orderNo){
    8. Mapmap= payLogService.queryPayStatus(orderNo);
    9. System.out.println("返回二维码状态:"+map);
    10. //1.根据查询出来的订单状态进行判断
    11. if(map==null){
    12. return R.error().message("支付出错了...");
    13. }
    14. //2.如果返回的map不为空,从这里面获取订单状态
    15. if(map.get("trade_state").equals("SUCCESS")){//支付成功
    16. //3.添加记录到支付表中,并且更新订单表的状态
    17. payLogService.updateOrdersStatus(map);
    18. return R.ok();
    19. }
    20. return R.ok().code(25000).message("支付中");
    21. }

    2.业务层

    1.还是一样的思路根据orderNo查询订单支付状态queryPayStatus()

    ,封装订单号微信id密钥,然后设置httpClient发送请求,并且利用商户key进行加密——>2.然后请求之后返回的内容再转为map返回——>3.根据返回map中get的状态决定是否添加支付记录和更新订单状态updateOrdersStatus()方法

    1. /**
    2. * 1.查询订单支付状态
    3. * @param orderNo
    4. * @return
    5. */
    6. @Override
    7. public Map queryPayStatus(String orderNo) {
    8. //1.封装参数
    9. try {
    10. HashMap map = new HashMap();
    11. map.put("appid", "wx74862e0dfcf69954");
    12. map.put("mch_id", "1558950191");
    13. map.put("out_trade_no", orderNo);
    14. map.put("nonce_str", WXPayUtil.generateNonceStr());
    15. //2.设置请求,利用xml进行请求
    16. HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
    17. client.setXmlParam(WXPayUtil.generateSignedXml(map,"T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));
    18. client.setHttps(true);
    19. client.post();
    20. //3.返回第三方的数据
    21. String xml = client.getContent();
    22. Map resultMap = WXPayUtil.xmlToMap(xml);//将xml转为map数据
    23. return resultMap;
    24. } catch (Exception e) {
    25. e.printStackTrace();
    26. }
    27. return null;
    28. }

    支付成功后添加支付记录和更新订单状态

     1.从map中获取订单号,然后根据订单号得到订单数据,修改订单状态(记得先判断状态提高效率)——>2.然后向支付记录表中设置支付记录

    1. /**
    2. * 2.添加支付记录和更新订单状态
    3. * @param map
    4. */
    5. @Override
    6. public void updateOrdersStatus(Map map) {
    7. //1.从map中获取订单号
    8. String orderNo = map.get("out_trade_no");
    9. //2.根据订单号查询订单信息
    10. QueryWrapper wrapper = new QueryWrapper<>();
    11. wrapper.eq("order_no",orderNo);
    12. Order order = orderService.getOne(wrapper);
    13. //3.更新订单表的订单状态
    14. if(order.getStatus().intValue()==1){
    15. return;//说明支付过了
    16. }
    17. order.setStatus(1);
    18. orderService.updateById(order);
    19. //4.向支付表中添加支付记录
    20. PayLog payLog = new PayLog();
    21. payLog.setOrderNo(orderNo);
    22. payLog.setPayTime(new Date());
    23. payLog.setPayType(1);//支付类型 1wx
    24. payLog.setTotalFee(order.getTotalFee());//总金额(分)
    25. payLog.setTradeState(map.get("trade_state"));//支付状态
    26. payLog.setTransactionId(map.get("transaction_id"));//账单流水号
    27. payLog.setAttr(JSONObject.toJSONString(map));
    28. baseMapper.insert(payLog);//插入到支付日志表
    29. }

  • 相关阅读:
    【 与百度搜索相同的bootstrap4与5自动补全功能(autocomplete)】
    看完这篇 教你玩转渗透测试靶机Vulnhub——Hacksudo: Aliens
    【JAVA程序设计】基于Springboot+Thymeleaf新闻管理系统
    STM32CUBEMX开发GD32F303(14)----IIC之配置OLED
    Ambari-yarn-timeline 内置 HBase数据表清理
    软件测试2年,想去培训性能测试自动化测试,28岁了,要不要培训?
    Spring Cloud Alibaba微服务第11章之MyBatis-plus
    IGS文件格式说明与下载方式- Renix atx ANTEX: The Antenna Exchange Format
    Ubuntu 14.04:安装 PaddleOCR 2.3
    小程序websocket接入
  • 原文地址:https://blog.csdn.net/weixin_57128596/article/details/126080321