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

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

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

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

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

支付模块接口实现
1.导入依赖和配置文件并且明确sql表大概思路
- <dependencies>
- <dependency>
- <groupId>com.github.wxpaygroupId>
- <artifactId>wxpay-sdkartifactId>
- <version>0.0.3version>
- dependency>
-
- <dependency>
- <groupId>com.alibabagroupId>
- <artifactId>fastjsonartifactId>
- dependency>
- dependencies>
1.2两个表一个订单表,当支付成功,订单状态发送改变,并且向支付日志表插入新的数据

1.3配置文件
- # 服务端口
- server.port=8007
- # 服务名
- spring.application.name=service-order
-
- # mysql数据库连接
- spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
- spring.datasource.url=jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8
- spring.datasource.username=root
- spring.datasource.password=root
-
- #返回json的全局时间格式
- spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
- spring.jackson.time-zone=GMT+8
-
- #配置mapper xml文件的路径
- mybatis-plus.mapper-locations=classpath:com/atguigu/orderservice/mapper/xml/*.xml
-
- #mybatis日志
- mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
-
- # nacos服务地址
- spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
-
- #开启熔断机制
- feign.hystrix.enabled=true
- # 设置hystrix超时时间,默认1000ms
- hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000
创建订单
思路:很简单,像订单这种肯定是关于用户和课程的,所以下单的话需要根据课程id和用户id创建订单返回订单id(利用了feign的远程调用)
- /**
- * 1.生成订单的方法
- */
- @GetMapping("createOrder/{courseId}")
- public R saveOrder(@PathVariable String courseId, HttpServletRequest request){
- //1.生成订单号
- String orderNo=orderService.createOrders(courseId,JwtUtils.getMemberIdByJwtToken(request));
- return R.ok().data("orderId",orderNo);
- }
思路:1.首先是通过用户id远程调用用户模块得到用户信息,然后课程模块类似——>2.创建Order对象,向里面添加数据,然后进行插入,返回订单号(这个定义了一个时间类OrderNoUtil根据时间生成)
- /**
- * 1.生成订单的方法,通过远程调用两个feign方法(课程信息和人物信息)结合到Order中为订单
- * @param courseId
- * @param memberId
- * @return
- */
- @Override
- public String createOrders(String courseId, String memberId) {
- //1.通过远程调用根据用户id获取用户信息
- UcenterMemberOrder userInfoOrder = ucenterClient.getUserInfoOrder(memberId);
-
- //2.通过远程调用根据课程id获取课程信息
- CourseWebVoOrder courseInfoOrder = eduClient.getCourseInfoOrder(courseId);
-
- //3.创建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();
- }
- package com.atguigu.eduorder.utils;
-
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.Random;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
-
- /**
- * 订单号工具类
- *
- * @author qy
- * @since 1.0
- */
- public class OrderNoUtil {
-
- /**
- * 获取订单号
- * @return
- */
- public static String getOrderNo() {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
- String newDate = sdf.format(new Date());
- String result = "";
- Random random = new Random();
- for (int i = 0; i < 3; i++) {
- result += random.nextInt(10);
- }
- return newDate + result;
- }
-
- }
用户:通过request对里面token进行解析得到用户id然后根据id获取用户信息
- /**
- * 3.根据token获取用户信息
- */
- @GetMapping("getMemberInfo")
- public R getMemberInfo(HttpServletRequest request){
- //getMemberIdByJwtToken根据请求头中的token得到用户信息(token得到userid->取出信息)
- String memberId = JwtUtils.getMemberIdByJwtToken(request);
- //根据用户id查询用户信息
- UcenterMember member = memberService.getById(memberId);
- return R.ok().data("userInfo",member);
- }
- /**
- * 2.根据课程id查询课程基本信息
- */
- @GetMapping("getCourseInfo/{courseId}")
- public R getCourseInfo(@PathVariable String courseId){
- CourseInfoVo courseInfoVo=courseService.getCourseInfo(courseId);
- return R.ok().data("courseInfo",courseInfoVo);
- }
根据订单id查询订单信息
- /**
- * 2.根据订单id查询订单信息
- */
- @GetMapping("getOrderInfo/{orderId}")
- public R getOrderInfo(@PathVariable String orderId){//订单id查询
- QueryWrapper
wrapper = new QueryWrapper<>(); - wrapper.eq("order_no",orderId);
-
- Order order = orderService.getOne(wrapper);
- return R.ok().data("item",order);
- }
判断是否购买课程
思路:根据课程id和人物id还有课程状态进行判断,然后根据这些条件进行查询如果>0代表已经支付
- @GetMapping("isBuyCourse/{courseId}/{memberId}")
- public boolean isBuyCourse(@PathVariable String courseId,@PathVariable String memberId){
- QueryWrapper
wrapper = new QueryWrapper<>(); - wrapper.eq("course_id",courseId);
- wrapper.eq("member_id",memberId);
- wrapper.eq("status",1);
-
- int count = orderService.count(wrapper);
- if(count>0){
- //代表已经支付
- return true;
- }else{
- return false;
- }
-
- }
支付生成微信支付的二维码接口
- /**
- * 1.生成微信支付的二维码接口
- */
- @GetMapping("createNative/{orderNo}")
- public R createNative(@PathVariable String orderNo) {
- //1.返回信息含有二维码的地址还有其他信息
- Map map = payLogService.createNative(orderNo);
- System.out.println("*****返回二维码map集合****:"+map);
- return R.ok().data(map);
- }
2.1首先根据订单id查询订单信息
2.2然后我们在map设置二维码参数
2.3根据微信提供的固定地址生成HttpClient
2.4因为我们httpClient发送的请求需要时xml格式,所以需要将map转一下
2.5发送请求得到请求返回的结果,然后将结果转为map格式
2.6前面只是将map数据给到http生成对应二维码,我们还有一些提示信息,所以定义一个最终结果map,将之前的map二维码地址,状态码啥的还有订单的价格及相关数据封装到结果集中,然后返回
- /**
- * 1.返回二维码地址
- * @param orderNo
- * @return
- */
- @Override
- public Map createNative(String orderNo) {
- try {
- //1.根据订单id获取订单信息
- QueryWrapper
wrapper = new QueryWrapper<>(); - wrapper.eq("order_no", orderNo);
- Order order = orderService.getOne(wrapper);
-
- //2.使用map设置二维码需要的参数
- HashMap map = new HashMap();
- map.put("appid","wx74862e0dfcf69954");
- map.put("mch_id", "1558950191");
- map.put("nonce_str", WXPayUtil.generateNonceStr());//随机生成一个二维码
- map.put("body", order.getCourseTitle());
- map.put("out_trade_no", orderNo);//二维码标识订单号
- map.put("total_fee", order.getTotalFee().multiply(new BigDecimal("100")).longValue()+"");//价格
- map.put("spbill_create_ip", "127.0.0.1");
- map.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify\n");
- map.put("trade_type", "NATIVE");
-
- //3.发送httpclient请求,传递参数按照xml格式,微信支付提供固定地址
- HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
-
- //设置参数,我们的商户key会对这些map参数进行加密->将map根据key进行加密并且传送到请求中去
- client.setXmlParam(WXPayUtil.generateSignedXml(map,"T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));
- client.setHttps(true);
-
- //执行请求发送
- client.post();
-
- //4.得到发送请求返回的结果,返回的内容是xml格式返回的(之前传参数也是xml格式)
- String content = client.getContent();
- //5.再把xml转为map
- Map
resultMap=WXPayUtil.xmlToMap(content); -
- //6.前面的map只是为了给到http生成二维码,需要转为xml格式,现在我们这个结果resultMap就是二维码了
- //还需要一些关于订单的提示信息
- Map res = new HashMap<>();
- res.put("out_trade_no", orderNo);
- res.put("course_id", order.getCourseId());
- res.put("total_fee", order.getTotalFee());
- res.put("result_code", resultMap.get("result_code"));//状态码
- res.put("code_url", resultMap.get("code_url"));//二维码地址
-
- return res;
-
- } catch (Exception e) {
- throw new GuliException(20001,"生成二维码失败");
- }
- }
支付之后的后续操作
思路:1.支付之后需要查询支付状态(二维码中的参数),如果状态SUCCESS,就将我们的支付记录添加到支付表中——>2.并且支付成功后还要修改订单表的状态,表示已经支付
- /**
- * 2.查询订单状态
- * @param orderNo
- * @return
- */
- @GetMapping("queryPayStatus/{orderNo}")
- public R queryPayStatus(@PathVariable String orderNo){
- Map
map= payLogService.queryPayStatus(orderNo); - System.out.println("返回二维码状态:"+map);
- //1.根据查询出来的订单状态进行判断
- if(map==null){
- return R.error().message("支付出错了...");
- }
-
- //2.如果返回的map不为空,从这里面获取订单状态
- if(map.get("trade_state").equals("SUCCESS")){//支付成功
- //3.添加记录到支付表中,并且更新订单表的状态
- payLogService.updateOrdersStatus(map);
- return R.ok();
- }
- return R.ok().code(25000).message("支付中");
- }
1.还是一样的思路根据orderNo查询订单支付状态queryPayStatus()
,封装订单号微信id密钥,然后设置httpClient发送请求,并且利用商户key进行加密——>2.然后请求之后返回的内容再转为map返回——>3.根据返回map中get的状态决定是否添加支付记录和更新订单状态updateOrdersStatus()方法
- /**
- * 1.查询订单支付状态
- * @param orderNo
- * @return
- */
- @Override
- public Map
queryPayStatus(String orderNo) { - //1.封装参数
- try {
- HashMap map = new HashMap();
- map.put("appid", "wx74862e0dfcf69954");
- map.put("mch_id", "1558950191");
- map.put("out_trade_no", orderNo);
- map.put("nonce_str", WXPayUtil.generateNonceStr());
-
- //2.设置请求,利用xml进行请求
- HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
- client.setXmlParam(WXPayUtil.generateSignedXml(map,"T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));
- client.setHttps(true);
- client.post();
-
- //3.返回第三方的数据
- String xml = client.getContent();
- Map
resultMap = WXPayUtil.xmlToMap(xml);//将xml转为map数据 -
- return resultMap;
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- return null;
- }
支付成功后添加支付记录和更新订单状态
1.从map中获取订单号,然后根据订单号得到订单数据,修改订单状态(记得先判断状态提高效率)——>2.然后向支付记录表中设置支付记录
- /**
- * 2.添加支付记录和更新订单状态
- * @param map
- */
- @Override
- public void updateOrdersStatus(Map
map) { - //1.从map中获取订单号
- String orderNo = map.get("out_trade_no");
- //2.根据订单号查询订单信息
- QueryWrapper
wrapper = new QueryWrapper<>(); - wrapper.eq("order_no",orderNo);
- Order order = orderService.getOne(wrapper);
-
- //3.更新订单表的订单状态
- if(order.getStatus().intValue()==1){
- return;//说明支付过了
- }
- order.setStatus(1);
- orderService.updateById(order);
-
- //4.向支付表中添加支付记录
- PayLog payLog = new PayLog();
- payLog.setOrderNo(orderNo);
- payLog.setPayTime(new Date());
- payLog.setPayType(1);//支付类型 1wx
- payLog.setTotalFee(order.getTotalFee());//总金额(分)
- payLog.setTradeState(map.get("trade_state"));//支付状态
- payLog.setTransactionId(map.get("transaction_id"));//账单流水号
- payLog.setAttr(JSONObject.toJSONString(map));
-
- baseMapper.insert(payLog);//插入到支付日志表
- }