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
- 订单号
- 订单完成时间
- 订单金额
- 交易状态
- t_Order
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; } }

浙公网安备 33010602011771号