7网页前台微信支付及订单模块

七 网页前台微信支付及订单模块

1具体功能

​ 1.1支付全流程

  • 前端进入具体一门课程的页面,点击立即购买

  • 调用后端第一个接口:生成订单接口

    • 传入courseID和HttpServletRequest request
    • 之前前端有一个request拦截器,将用户信息的cookie放到请求头中
    • 后端得到request,使用jwt工具从中获得cookie中保存的用户id
    • 根据用户id和课程id远程调用其他模块,获得用户详细信息和课程详细信息
    • 创建Order对象,将以上对象中需要的内容放到Order对象中(orderno(订单号)是随机生成的)
    • 将orderno返回给前端
  • 前端跳转到订单页面,调用后端第二个接口

    • 根据订单号/orderno查询订单信息
    • 在前端显示,用户确认没问题后,点击去支付
  • 前端跳转到支付页面,根据上面返回的orderno调用后端第三个接口:生成微信支付二维码

    • 先调用上面后端的第二个接口:根据订单号查询订单信息
    • 使用map设置生成二维码需要参数
      • 里面包括了微信支付的appid,商户号
      • 也包括了订单对象的具体信息(课程信息,用户信息)
    • 发送httpclient请求,传递参数xml格式,微信支付提供的固定的地址
      • 设置xml格式的参数(商户key)
      • 执行post请求
    • 得到发送请求返回结果
      • 返回内容,是使用xml格式返回
      • 将xml转成map,然后返回
  • 前端根据map生成二维码,等待客户扫码支付

  • 前端每隔3秒执行一次后端的第四个接口:查询订单支付状态接口,数据库插入订单信息接口

    • 参数还是订单号

    • 调用微信方法,得到相关map,来获取订单状态

    • 如果失败就返回错误信息

    • 如果成功调用数据库插入更新方法

      • 通过订单号得到订单信息
      • 更新订单状态为已支付
      • 然后向支付表添加支付记录(订单完成时间,金额)
    • 如果没有失败也没成功则返回25000

  • 前端添加response拦截器

    • 如果是25000(支付中)就一直支付
  • 支付成功则返回主页

2具体知识点

2.1订单流程简介

  • 第一个接口:生成订单的接口
  • 第二个接口:根据订单id查询订单信息
  • 第三个接口:生成微信支付二维码
  • 第四个接口:查询订单支付状态接口,数据库插入订单信息接口
  • 两张表
    • t_Order
      • 订单号
      • 课程id,课程名称,讲师名称
      • 用户id,用户名字,用户手机号
    • t_Pay_Log
      • 订单号
      • 订单完成时间
      • 订单金额
      • 交易状态

2.2生成订单接口

  • 简介

    • 在service-order中生成订单,它需要课程信息(service-edu模块),它还需要用户信息(service-ucenter模块)
    • 回忆之前 用户信息是放到cookie中 前端是写了个request拦截器 把token(包含了用户相关信息)放到header里面了
    • 所以后端调用时候需要传HttpServletRequest request,然后使用jwt工具,从request里面的token中获得用户id
  • 其他模块接口准备

    • edu模块中根据课程id查询课程基本信息
    • ucenter模块中更具用户id获取用户信息
  • order模块调用上面两个模块

    • controller

    •     //1 生成订单的方法
          //因为要远程调用
          @PostMapping("createOrder/{courseId}")
          public R saveOrder(@PathVariable String courseId, HttpServletRequest request) {
              //创建订单,返回订单号
              String orderNo =
                      orderService.createOrders(courseId, JwtUtils.getMemberIdByJwtToken(request));
              return R.ok().data("orderId",orderNo);
          }
      
    • service

    •     //1生成订单的方法
          @Override
          public String createOrders(String courseId, String memberId) {
              //通过远程调用根据用户Id获取用户信息
              UcenterMemberOrder userInfoOrder = ucenterClient.getUserInfoOrder(memberId);
      
              //通过远程调用根据课程id获取课程信息
              CourseWebVoOrder courseInfoOrder = eduClient.getCourseInfoOrder(courseId);
      
      
              //创建Order对象,向order对象里面设置需要数据
              Order order = new Order();
              order.setOrderNo(OrderNoUtil.getOrderNo());//订单号
              order.setCourseId(courseId); //课程id
              order.setCourseTitle(courseInfoOrder.getTitle());
              order.setCourseCover(courseInfoOrder.getCover());
              order.setTeacherName(courseInfoOrder.getTeacherName());
              order.setTotalFee(courseInfoOrder.getPrice());
              order.setMemberId(memberId);
              order.setMobile(userInfoOrder.getMobile());
              order.setNickname(userInfoOrder.getNickname());
              order.setStatus(0);  //订单状态(0:未支付 1:已支付)
              order.setPayType(1);  //支付类型 ,微信1
              baseMapper.insert(order);
              //返回订单号
              return order.getOrderNo();
          }
      
    • 订单号是随机唯一的值 这边用一个工具类

  • order第二个接口,根据订单id查询订单信息

2.3生成微信支付二维码

  • 前期准备(使用老师课件,因为个人申请需要营业证)

    • 准备关联公众号的appid

    • 商户号

    • 商户key

    • 回调地址

  • 引入相关依赖

  • 生成二维码后端接口

    • controller

    • //生成微信支付二维码接口
         //参数是订单号
         @GetMapping("createNative/{orderNo}")
         public R createNative(@PathVariable String orderNo) {
             //返回信息,包含二维码地址,还有其他需要的信息
             Map map = payLogService.createNatvie(orderNo);
             //System.out.println("****返回二维码map集合:"+map);
             return R.ok().data(map);
         }
      
    • service

    • //生成微信支付二维码接口
        @Override
        public Map createNatvie(String orderNo) {
            try {
                //1 根据订单号查询订单信息
                QueryWrapper<Order> wrapper = new QueryWrapper<>();
                wrapper.eq("order_no",orderNo);
                Order order = orderService.getOne(wrapper);
        
                //2 使用map设置生成二维码需要参数
                Map m = new HashMap();
                m.put("appid","xx");
                m.put("mch_id", "xx");
                m.put("nonce_str", WXPayUtil.generateNonceStr());
                m.put("body", order.getCourseTitle()); //课程标题
                m.put("out_trade_no", orderNo); //订单号
                m.put("total_fee", order.getTotalFee().multiply(new BigDecimal("100")).longValue()+"");
                m.put("spbill_create_ip", "127.0.0.1");
                m.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify\n");
                m.put("trade_type", "NATIVE");
        
                //3 发送httpclient请求,传递参数xml格式,微信支付提供的固定的地址
                HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
                //设置xml格式的参数
                client.setXmlParam(WXPayUtil.generateSignedXml(m,"xx"));
                client.setHttps(true);
                //执行post请求发送
                client.post();
        
                //4 得到发送请求返回结果
                //返回内容,是使用xml格式返回
                String xml = client.getContent();
        
                //把xml格式转换map集合,把map集合返回
                Map<String,String> resultMap = WXPayUtil.xmlToMap(xml);
        
                //最终返回数据 的封装
                Map map = new HashMap();
                map.put("out_trade_no", orderNo);
                map.put("course_id", order.getCourseId());
                map.put("total_fee", order.getTotalFee());
                map.put("result_code", resultMap.get("result_code"));  //返回二维码操作状态码
                map.put("code_url", resultMap.get("code_url"));        //二维码地址
        
                return map;
            }catch(Exception e) {
                throw new GuliException(20001,"生成二维码失败");
            }
        
        }
      

2.3查询订单支付状态、数据库插入订单信息后端接口

  • controller

  •     //查询订单支付状态
        //参数:订单号,根据订单号查询 支付状态
        @GetMapping("queryPayStatus/{orderNo}")
        public R queryPayStatus(@PathVariable String orderNo) {
            Map<String,String> map = payLogService.queryPayStatus(orderNo);
            //System.out.println("*****查询订单状态map集合:"+map);
            if(map == null) {
                return R.error().message("支付出错了");
            }
            //如果返回map里面不为空,通过map获取订单状态
            if(map.get("trade_state").equals("SUCCESS")) {//支付成功
                //添加记录到数据库支付表,更新订单表订单状态
                payLogService.updateOrdersStatus(map);
                return R.ok().message("支付成功");
            }
            return R.ok().code(25000).message("支付中");
        }
    
  • service

  • //查询订单支付状态
       @Override
       public Map<String, String> queryPayStatus(String orderNo) {
           try {
               //1、封装参数
               Map m = new HashMap<>();
               m.put("appid", "xx");
               m.put("mch_id", "x");
               m.put("out_trade_no", orderNo);
               m.put("nonce_str", WXPayUtil.generateNonceStr());
      
               //2 发送httpclient
               HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
               client.setXmlParam(WXPayUtil.generateSignedXml(m,"xx"));
               client.setHttps(true);
               client.post();
      
               //3 得到请求返回内容
               String xml = client.getContent();
               Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
               //6、转成Map再返回
               return resultMap;
           }catch(Exception e) {
               return null;
           }
       }
      
       //数据库添加支付记录和更新订单状态
       @Override
       public void updateOrdersStatus(Map<String, String> map) {
           //从map获取订单号
           String orderNo = map.get("out_trade_no");
           //根据订单号查询订单信息
           QueryWrapper<Order> wrapper = new QueryWrapper<>();
           wrapper.eq("order_no",orderNo);
           Order order = orderService.getOne(wrapper);
      
           //更新订单表订单状态
           if(order.getStatus().intValue() == 1) { return; }
           order.setStatus(1);//1代表已经支付
           orderService.updateById(order);
      
           //向支付表添加支付记录
           PayLog payLog = new PayLog();
           payLog.setOrderNo(orderNo);  //订单号
           payLog.setPayTime(new Date()); //订单完成时间
           payLog.setPayType(1);//支付类型 1微信
           payLog.setTotalFee(order.getTotalFee());//总金额(分)
      
           payLog.setTradeState(map.get("trade_state"));//支付状态
           payLog.setTransactionId(map.get("transaction_id")); //流水号
           payLog.setAttr(JSONObject.toJSONString(map));
      
           baseMapper.insert(payLog);
       }
    

2.4前端

  • 首先在渲染之前生成二维码

  • asyncData({ params, error }) {//nuxt的一种方式
            return ordersApi.createNatvie(params.pid)
               .then(response => {
                   return {
                         payObj: response.data.data
                       }
               })
        }
    
  • 然后每隔3秒调用一次查询订单状态的方法

  • //每隔3秒调用一次查询订单状态的方法
         mounted() {//页面渲染之后执行
            this.timer1 = setInterval(() => {
                this.queryOrderStatus(this.payObj.out_trade_no)
            },3000);
         },
         methods:{
             queryOrderStatus(orderNo) {
                 ordersApi.queryPayStatus(orderNo)
                    .then(response => {
                         if (response.data.success) {
                            //支付成功,清除定时器
                            clearInterval(this.timer1)
                            //提示
                            this.$message({
                                type: 'success',
                                message: '支付成功!'
                            })
                            //跳转回到课程详情页面
                            this.$router.push({path: '/course/' + this.payObj.course_id})
                         }
                    })
             }
         }
    
  • 添加response拦截器,如果是25000(支付中)就一直支付

  • // http response 拦截器
     service.interceptors.response.use(
       response => {
         //debugger
         if (response.data.code == 28004) {
             console.log("response.data.resultCode是28004")
             // 返回 错误代码-1 清除ticket信息并跳转到登录页面
             //debugger
             window.location.href="/login"
             return
         }else{
           if (response.data.code !== 20000) {
             //25000:订单支付中,不做任何提示
             if(response.data.code != 25000) {
               Message({
                 message: response.data.message || 'error',
                 type: 'error',
                 duration: 5 * 1000
               })
             }
           } else {
             return response;
           }
         }
       
    
posted @ 2022-07-14 15:55  fao99  阅读(756)  评论(0)    收藏  举报