微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”十、微信扫码支付功能,

一、环境搭建

1、引入依赖

<!--微信支付-->
        <dependency>
            <groupId>com.github.wxpay</groupId>
            <artifactId>wxpay-sdk</artifactId>
            <version>0.0.3</version>
        </dependency>
        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.70</version>
        </dependency>

在这里插入图片描述

2、数据库

在这里插入图片描述

# Host: localhost  (Version 5.7.19)
# Date: 2019-11-18 15:49:50
# Generator: MySQL-Front 6.1  (Build 1.26)


#
# Structure for table "t_order"
#

CREATE TABLE `t_order` (
  `id` char(19) NOT NULL DEFAULT '',
  `order_no` varchar(20) NOT NULL DEFAULT '' COMMENT '订单号',
  `course_id` varchar(19) NOT NULL DEFAULT '' COMMENT '课程id',
  `course_title` varchar(100) DEFAULT NULL COMMENT '课程名称',
  `course_cover` varchar(255) DEFAULT NULL COMMENT '课程封面',
  `teacher_name` varchar(20) DEFAULT NULL COMMENT '讲师名称',
  `member_id` varchar(19) NOT NULL DEFAULT '' COMMENT '会员id',
  `nickname` varchar(50) DEFAULT NULL COMMENT '会员昵称',
  `mobile` varchar(11) DEFAULT NULL COMMENT '会员手机',
  `total_fee` decimal(10,2) DEFAULT '0.01' COMMENT '订单金额(分)',
  `pay_type` tinyint(3) DEFAULT NULL COMMENT '支付类型(1:微信 2:支付宝)',
  `status` tinyint(3) DEFAULT NULL COMMENT '订单状态(0:未支付 1:已支付)',
  `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
  `gmt_create` datetime NOT NULL COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_order_no` (`order_no`),
  KEY `idx_course_id` (`course_id`),
  KEY `idx_member_id` (`member_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单';

#
# Data for table "t_order"
#

INSERT INTO `t_order` VALUES ('1195693605513891841','1195693605555834880','18','Java精品课程','http://guli-file.oss-cn-beijing.aliyuncs.com/cover/2019/03/06/866e9aca-b530-4f71-a690-72d4a4bfd1e7.jpg','晴天','1','小三1231','13700000001',1,NULL,0,0,'2019-11-16 21:22:25','2019-11-16 21:22:25'),('1195694200178118657','1195694200186507264','18','Java精品课程','http://guli-file.oss-cn-beijing.aliyuncs.com/cover/2019/03/06/866e9aca-b530-4f71-a690-72d4a4bfd1e7.jpg','晴天','1','小三1231','13700000001',1,NULL,0,0,'2019-11-16 21:24:47','2019-11-16 21:24:47'),('1196264007411744769','1196264005255872512','1192252213659774977','java基础课程:test','https://guli-file-190513.oss-cn-beijing.aliyuncs.com/cover/default.gif','晴天','1','小三1231','13700000001',1,NULL,1,0,'2019-11-18 11:09:00','2019-11-18 11:10:14'),('1196265495278174209','1196265495273979904','18','Java精品课程','http://guli-file.oss-cn-beijing.aliyuncs.com/cover/2019/03/06/866e9aca-b530-4f71-a690-72d4a4bfd1e7.jpg','晴天','1','小三1231','13700000001',1,NULL,0,0,'2019-11-18 11:14:54','2019-11-18 11:14:54');

#
# Structure for table "t_pay_log"
#

CREATE TABLE `t_pay_log` (
  `id` char(19) NOT NULL DEFAULT '',
  `order_no` varchar(20) NOT NULL DEFAULT '' COMMENT '订单号',
  `pay_time` datetime DEFAULT NULL COMMENT '支付完成时间',
  `total_fee` decimal(10,2) DEFAULT '0.01' COMMENT '支付金额(分)',
  `transaction_id` varchar(30) DEFAULT NULL COMMENT '交易流水号',
  `trade_state` char(20) DEFAULT NULL COMMENT '交易状态',
  `pay_type` tinyint(3) NOT NULL DEFAULT '0' COMMENT '支付类型(1:微信 2:支付宝)',
  `attr` text COMMENT '其他属性',
  `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
  `gmt_create` datetime NOT NULL COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_order_no` (`order_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付日志表';

#
# Data for table "t_pay_log"
#

INSERT INTO `t_pay_log` VALUES ('1194498446013001730','1194498300579704832','2019-11-13 14:13:17',1,'4200000469201911130676624386','SUCCESS',1,'{\"transaction_id\":\"4200000469201911130676624386\",\"nonce_str\":\"2Lc23ILl231It53M\",\"trade_state\":\"SUCCESS\",\"bank_type\":\"CFT\",\"openid\":\"oNpSGwR-QGG5DaZtDkh2UZlsFDQE\",\"sign\":\"5404850AA3ED0E844DE104651477F07A\",\"return_msg\":\"OK\",\"fee_type\":\"CNY\",\"mch_id\":\"1473426802\",\"cash_fee\":\"1\",\"out_trade_no\":\"1194498300579704832\",\"cash_fee_type\":\"CNY\",\"appid\":\"wx8397f8696b538317\",\"total_fee\":\"1\",\"trade_state_desc\":\"支付成功\",\"trade_type\":\"NATIVE\",\"result_code\":\"SUCCESS\",\"attach\":\"\",\"time_end\":\"20191113141314\",\"is_subscribe\":\"N\",\"return_code\":\"SUCCESS\"}',0,'2019-11-13 14:13:17','2019-11-13 14:13:17'),('1195253787449430017','1195253049260314624','2019-11-15 16:14:44',1,'4200000454201911150981874895','SUCCESS',1,'{\"transaction_id\":\"4200000454201911150981874895\",\"nonce_str\":\"MAM5UM4Xhv1lItvO\",\"trade_state\":\"SUCCESS\",\"bank_type\":\"CFT\",\"openid\":\"oNpSGwR-QGG5DaZtDkh2UZlsFDQE\",\"sign\":\"7DBDCAF4A078B30BB3EF073E6A238C20\",\"return_msg\":\"OK\",\"fee_type\":\"CNY\",\"mch_id\":\"1473426802\",\"cash_fee\":\"1\",\"out_trade_no\":\"1195253049260314624\",\"cash_fee_type\":\"CNY\",\"appid\":\"wx8397f8696b538317\",\"total_fee\":\"1\",\"trade_state_desc\":\"支付成功\",\"trade_type\":\"NATIVE\",\"result_code\":\"SUCCESS\",\"attach\":\"\",\"time_end\":\"20191115161440\",\"is_subscribe\":\"N\",\"return_code\":\"SUCCESS\"}',0,'2019-11-15 16:14:44','2019-11-15 16:14:44'),('1196264321397342210','1196264005255872512','2019-11-18 11:10:14',1,'4200000453201911184025781554','SUCCESS',1,'{\"transaction_id\":\"4200000453201911184025781554\",\"nonce_str\":\"D1dHexCLIFIxAAg2\",\"trade_state\":\"SUCCESS\",\"bank_type\":\"CFT\",\"openid\":\"oNpSGwR-QGG5DaZtDkh2UZlsFDQE\",\"sign\":\"C9F5CA1EE49EA7891736D73BEB423962\",\"return_msg\":\"OK\",\"fee_type\":\"CNY\",\"mch_id\":\"1473426802\",\"cash_fee\":\"1\",\"out_trade_no\":\"1196264005255872512\",\"cash_fee_type\":\"CNY\",\"appid\":\"wx8397f8696b538317\",\"total_fee\":\"1\",\"trade_state_desc\":\"支付成功\",\"trade_type\":\"NATIVE\",\"result_code\":\"SUCCESS\",\"attach\":\"\",\"time_end\":\"20191118111011\",\"is_subscribe\":\"N\",\"return_code\":\"SUCCESS\"}',0,'2019-11-18 11:10:14','2019-11-18 11:10:14');

在这里插入图片描述

3、代码生成器生成代码

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4、application.yml

server:
  port: 8006 #微服务端口号为8001
spring:
  application:
    name: service-order #服务名
  profiles:
    active: dev #环境设置 dev表示构建阶段,test表示测试阶段,prod表示发布阶段
  datasource: #数据源
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/gulischool?serverTimeZone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true
    username: root
    password: 123456
  jackson: #我们的时区是东八区,应该加8个小时,时区显示格式也需要改成我们想要的
    date-format: yyyy-MM-DD HH:mm:ss
    time-zone: GMT+8
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848 #nacos
feign:
  client:
    config:
      default:
        connect-timeout: 10000 #设置超时限制,必须超过10000ms才报错
        read-timeout: 10000 #设置Feign服务熔断机制的最大超时限制
  hystrix:
    enabled: true #开启熔断机制
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000 #设置hystri超时时间 默认1000ms(10s)


mybatis-plus:
  mapper-locations: classpath:com/yzpnb/order_service/mapper/xml/*.xml #配置mapper xml文件的路径
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #mybatis日志

5、启动类

package com.yzpnb.order_service;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages={"com.yzpnb"})//这样它会将所有com.yzpnb包下内容扫描,而不只是扫描子文件或同级文件
@EnableDiscoveryClient //nacos注册
@MapperScan("com.yzpnb.order_service.mapper")
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class,args);
    }
}


二、后端

所需接口
生成订单(用户点击购买按钮)
根据订单id查询订单信息
生成微信支付二维码
查询订单支付状态接口

1、生成订单

分析
我们需要根据课程id获取课程信息
根据用户id获取用户信息
需要使用Feign

在这里插入图片描述

1、编写根据课程id获取课程详细信息的接口

@ApiOperation("根据课程id获取课程信息")
    @GetMapping("selectCourseAllInfoVoApiById")
    public CourseAllInfoVo selectCourseAllInfoVoApiById(@ApiParam(name = "id",value = "课程id")
                                                        @RequestParam(value = "id") String id){
        CourseAllInfoVo courseAllInfoVo = eduCourseService.selectCourseAllInfoVo(id);
        return courseAllInfoVo;
    }

在这里插入图片描述

2、复制对应实体类

在这里插入图片描述

3、编写feign接口

在这里插入图片描述

4、编写生成订单和根据订单id查询订单的接口

package com.yzpnb.order_service.controller;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yzpnb.common_utils.JwtUtils;
import com.yzpnb.common_utils.Result;
import com.yzpnb.order_service.entity.TOrder;
import com.yzpnb.order_service.entity.educourse.CourseAllInfoVo;
import com.yzpnb.order_service.entity.ucenter.UcenterMember;
import com.yzpnb.order_service.feign.FeignToEduServiceClient;
import com.yzpnb.order_service.feign.FeignToUcenterClient;
import com.yzpnb.order_service.service.TOrderService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;

/**
 * <p>
 * 订单 前端控制器
 * </p>
 *
 * @author testjava
 * @since 2020-06-06
 */
@RestController
@RequestMapping("/order_service/t-order")
@CrossOrigin
public class TOrderController {

    @Autowired
    private TOrderService tOrderService;

    @Autowired
    private FeignToEduServiceClient feignToEduServiceClient;

    @Autowired
    private FeignToUcenterClient feignToUcenterClient;

    @ApiOperation("根据课程id和用户id创建订单,返回订单id")
    @PostMapping("createOrder/{courseId}")
    public Result createOrder(@ApiParam(name = "courseId",value = "课程id")
                              @PathVariable(value = "courseId") String courseId,
                              HttpServletRequest request){
        //1、获取用户id
        String memberId = JwtUtils.getMemberIdByJwtToken(request);

        //2、根据课程id获取课程信息
        CourseAllInfoVo courseAllInfoVo = feignToEduServiceClient.selectCourseAllInfoVoApiById(courseId);

        //3、根据用户id获取用户信息
        UcenterMember ucenterMember = feignToUcenterClient.selectById(memberId);

        //4、随机生成订单号
        SimpleDateFormat sd=new SimpleDateFormat("yyyyMMddHHmmss");

        Random random=new Random();
        String orderNo="";
        for(int i=0;i<=3;i++){
            orderNo += random.nextInt(10);
        }
        orderNo=sd.format(new Date())+orderNo;

        //4、创建订单
        TOrder order=new TOrder();
        order.setOrderNo(orderNo);//订单号
        order.setCourseId(courseId);//课程id
        order.setCourseTitle(courseAllInfoVo.getTitle());//课程标题
        order.setCourseCover(courseAllInfoVo.getCover());//课程封面
        order.setTeacherName(courseAllInfoVo.getTeacherName());//讲师姓名
        order.setTotalFee(courseAllInfoVo.getPrice());//课程价格
        order.setMemberId(memberId);//用户id
        order.setMobile(ucenterMember.getMobile());//用户电话
        order.setNickname(ucenterMember.getNickname());//用户昵称
        order.setStatus(0);//订单状态,默认未支付
        order.setPayType(1);//支付类型默认为1,微信支付

        tOrderService.save(order);
        return Result.ok().data("orderId",orderNo);
    }

    @ApiOperation("根据订单id获取订单信息")
    @GetMapping("selectOrderById/{orderId}")
    public Result selectOrderById(@PathVariable String orderId) {
        QueryWrapper<TOrder> wrapper = new QueryWrapper<>();
        wrapper.eq("order_no",orderId);
        TOrder order = tOrderService.getOne(wrapper);
        return Result.ok().data("item", order);
    }
}


在这里插入图片描述

5、测试

涉及到HttpClient,就不要想着用swagger测试了,请直接跳到前端的1、订单页面整合中测试

2、生成微信支付二维码

1、前戏

和上次的微信二维码登陆一样,这功能也需要企业认证,我们还是蹭尚硅谷的进行学习
weixin:
	pay:
		# 关联的公众号appid
		appid: wx74862e0dfcf69954
		# 商户号
		partner: 1558950191
		# 商户key
		partnerkey: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb
		# 回调地址
		notifyurl: http://guli.shop/api/order/weixinPay/weixinNotify

2、controller

package com.yzpnb.order_service.controller;


import com.yzpnb.common_utils.Result;
import com.yzpnb.order_service.service.TPayLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

/**
 * <p>
 * 支付日志表 前端控制器
 * </p>
 *
 * @author testjava
 * @since 2020-06-06
 */
@RestController
@RequestMapping("/order_service/t-pay-log")
@CrossOrigin
public class TPayLogController {

    @Autowired
    private TPayLogService tPayLogServicee;
    /**
     * 生成二维码
     *
     * @return
     */
    @ApiOperation("根据订单号生成支付二维码")
    @GetMapping("createNative/{orderNo}")
    public Result createNative( @ApiParam(name = "orderNo",value = "订单号")
                                @PathVariable String orderNo) {
        Map map = tPayLogServicee.createNative(orderNo);
        return Result.ok().data(map);
    }
}


在这里插入图片描述

3、工具类

引入依赖
<!--apache HttpClient-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.12</version>
        </dependency>

在这里插入图片描述

工具类,用来将字符串转换为Xml文件
package com.yzpnb.order_service.utils;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * http请求客户端
 * 
 * @author qy
 * 
 */
public class HttpClient {
	private String url;
	private Map<String, String> param;
	private int statusCode;
	private String content;
	private String xmlParam;
	private boolean isHttps;

	public boolean isHttps() {
		return isHttps;
	}

	public void setHttps(boolean isHttps) {
		this.isHttps = isHttps;
	}

	public String getXmlParam() {
		return xmlParam;
	}

	public void setXmlParam(String xmlParam) {
		this.xmlParam = xmlParam;
	}

	public HttpClient(String url, Map<String, String> param) {
		this.url = url;
		this.param = param;
	}

	public HttpClient(String url) {
		this.url = url;
	}

	public void setParameter(Map<String, String> map) {
		param = map;
	}

	public void addParameter(String key, String value) {
		if (param == null)
			param = new HashMap<String, String>();
		param.put(key, value);
	}

	public void post() throws ClientProtocolException, IOException {
		HttpPost http = new HttpPost(url);
		setEntity(http);
		execute(http);
	}

	public void put() throws ClientProtocolException, IOException {
		HttpPut http = new HttpPut(url);
		setEntity(http);
		execute(http);
	}

	public void get() throws ClientProtocolException, IOException {
		if (param != null) {
			StringBuilder url = new StringBuilder(this.url);
			boolean isFirst = true;
			for (String key : param.keySet()) {
				if (isFirst)
					url.append("?");
				else
					url.append("&");
				url.append(key).append("=").append(param.get(key));
			}
			this.url = url.toString();
		}
		HttpGet http = new HttpGet(url);
		execute(http);
	}

	/**
	 * set http post,put param
	 */
	private void setEntity(HttpEntityEnclosingRequestBase http) {
		if (param != null) {
			List<NameValuePair> nvps = new LinkedList<NameValuePair>();
			for (String key : param.keySet())
				nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数
			http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数
		}
		if (xmlParam != null) {
			http.setEntity(new StringEntity(xmlParam, Consts.UTF_8));
		}
	}

	private void execute(HttpUriRequest http) throws ClientProtocolException,
			IOException {
		CloseableHttpClient httpClient = null;
		try {
			if (isHttps) {
				SSLContext sslContext = new SSLContextBuilder()
						.loadTrustMaterial(null, new TrustStrategy() {
							// 信任所有
							public boolean isTrusted(X509Certificate[] chain,
									String authType)
									throws CertificateException {
								return true;
							}
						}).build();
				SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
						sslContext);
				httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)
						.build();
			} else {
				httpClient = HttpClients.createDefault();
			}
			CloseableHttpResponse response = httpClient.execute(http);
			try {
				if (response != null) {
					if (response.getStatusLine() != null)
						statusCode = response.getStatusLine().getStatusCode();
					HttpEntity entity = response.getEntity();
					// 响应内容
					content = EntityUtils.toString(entity, Consts.UTF_8);
				}
			} finally {
				response.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			httpClient.close();
		}
	}

	public int getStatusCode() {
		return statusCode;
	}

	public String getContent() throws ParseException, IOException {
		return content;
	}

}

在这里插入图片描述

4、service

package com.yzpnb.order_service.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.wxpay.sdk.WXPayUtil;
import com.yzpnb.order_service.entity.TOrder;
import com.yzpnb.order_service.entity.TPayLog;
import com.yzpnb.order_service.mapper.TPayLogMapper;
import com.yzpnb.order_service.service.TOrderService;
import com.yzpnb.order_service.service.TPayLogService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yzpnb.order_service.utils.HttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 支付日志表 服务实现类
 * </p>
 *
 * @author testjava
 * @since 2020-06-06
 */
@Service
public class TPayLogServiceImpl extends ServiceImpl<TPayLogMapper, TPayLog> implements TPayLogService {

    @Autowired
    private TOrderService tOrderService; //引入订单接口

    /**
     * 根据订单号获取支付二维码
     * @param orderNo
     * @return
     */
    @Override
    public Map createNative(String orderNo) {
        try {
            //根据订单id获取订单信息
            QueryWrapper<TOrder> wrapper = new QueryWrapper<>();
            wrapper.eq("order_no",orderNo);
            TOrder order = tOrderService.getOne(wrapper);

            Map m = new HashMap();
            //1、设置支付参数
            m.put("appid", "wx74862e0dfcf69954");               //关联公众号
            m.put("mch_id", "1558950191");                      //商户号
            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()+"");//订单价格,改成圆角分的Long类型,并转为字符串
            m.put("spbill_create_ip", "127.0.0.1");             //支付ip地址,一般写你的项目域名yzpnb.XXXX,这里做本地测试,所以写127.0.0.1
            m.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify\n");//回调地址
            m.put("trade_type", "NATIVE");//生成支付类型,NATIAE表示根据价格生成二维码

            //2、HTTPClient来根据URL访问第三方接口并且传递参数,发送httpclient请求,传递参数xml格式,传入微信支付提供的固定地址
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");

            //client设置参数
            client.setXmlParam(WXPayUtil.generateSignedXml(m, "T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));//使用了微信工具类,通过商户key对参数进行加密
            client.setHttps(true);//我们访问https请求,默认不支持,将其设置为支持
            client.post();//使用post发送http请求
            
            //3、返回第三方的数据
            String xml = client.getContent();//返回信息为xml格式信息
            Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);//通过微信工具类,将xml格式解析为map集合
            
            //4、封装返回结果集
            Map map = new HashMap<>();
            map.put("out_trade_no", orderNo);                       //订单编号
            map.put("course_id", order.getCourseId());              //课程id
            map.put("total_fee", order.getTotalFee());              //总价格
            map.put("result_code", resultMap.get("result_code"));   //发送请求后状态码(微信返回的)
            map.put("code_url", resultMap.get("code_url"));         //二维码地址(微信返回的)

            //微信支付二维码2小时过期,可采取2小时未支付取消订单
            //redisTemplate.opsForValue().set(orderNo, map, 120, TimeUnit.MINUTES);
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            return new HashMap<>();//请求二维码失败,返回空的map集合
        }
    }
}

在这里插入图片描述

3、查询支付状态,订单是否支付成功

如果支付成功,将订单表中支付状态改为1,日志表中增加记录(用于申诉等使用)

1、controller

@ApiOperation("根据订单号查询支付状态,支付成功,添加订单日志")
    @GetMapping("/queryPayStatus/{orderNo}")
    public Result queryPayStatus(@PathVariable String orderNo) {

        //调用查询接口,查询订单状态(不是我们的数据库状态,而是微信中支付状态,为了方便,还是返回map)
        Map<String, String> map = tPayLogServicee.queryPayStatus(orderNo);

        if (map == null) {//没有值,表示没有支付,出错
            return Result.error().message("支付出错");
        }
        if (map.get("trade_state").equals("SUCCESS")) {//获取微信返回数据中支付状态,如果成功
            //更改订单状态,改订单状态为1,并添加日志
            tPayLogServicee.updateOrderStatus(map);
            return Result.ok().message("支付成功");
        }

        return Result.ok().code(25000).message("支付中");
    }

在这里插入图片描述### 2、service

   /**
     * 根据订单号查询订单支付状态(微信)
     * @param orderNo
     * @return
     */
    @Override
    public Map<String, String> queryPayStatus(String orderNo) {
        try {
            //1、封装参数
            Map m = new HashMap<>();
            m.put("appid", "wx74862e0dfcf69954");               //公众号
            m.put("mch_id", "1558950191");                      //商户号
            m.put("out_trade_no", orderNo);                     //二维码唯一标识,这里我直接填订单号
            m.put("nonce_str", WXPayUtil.generateNonceStr());   //微信官方提供工具类,帮我们生成随机字符串

            //2、设置请求
            //HTTPClient来根据URL访问第三方接口并且传递参数,发送httpclient请求,传递参数xml格式,传入微信支付提供的固定地址
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
            client.setXmlParam(WXPayUtil.generateSignedXml(m, "T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));//使用了微信工具类,通过商户key对参数进行加密
            client.setHttps(true);//我们访问https请求,默认不支持,将其设置为支持
            client.post();//使用post发送http请求
            //3、返回第三方的数据
            String xml = client.getContent();//返回信息为xml格式信息
            //4、转成Map
            Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);//通过微信工具类,将xml格式解析为map集合
            //5、返回
            return resultMap;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 更改订单状态,改订单状态为1,并添加日志
     * @param map
     */
    @Override
    public void updateOrderStatus(Map<String, String> map) {
        //获取订单id
        String orderNo = map.get("out_trade_no");

        //根据订单id查询订单信息
        QueryWrapper<TOrder> wrapper = new QueryWrapper<>();
        wrapper.eq("order_no",orderNo);
        TOrder order = tOrderService.getOne(wrapper);

        if(order.getStatus().intValue() == 1) return;//为1表示已经支付,不执行下面的操作
        order.setStatus(1);//否则就设置为1
        tOrderService.updateById(order);//修改订单状态

        //记录支付日志
        TPayLog payLog=new TPayLog();
        payLog.setOrderNo(order.getOrderNo());//支付订单号
        payLog.setPayTime(new Date());
        payLog.setPayType(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);//插入到支付日志表
    }

在这里插入图片描述

4、课程详情功能完善

查看课程详情的时候,判断用户是否已经购买课程

1、编写根据用户id和课程id查询订单信息接口

@ApiOperation("根据用户id和课程id查询订单信息,已经支付返回true,没有返回false")
    @GetMapping("isBuyCourse")
    public boolean isBuyCourse(@RequestParam(value = "memberid") String memberid,
                               @RequestParam(value = "courseid") String courseid) {
        //订单状态是1表示支付成功
        int count = tOrderService.count(new QueryWrapper<TOrder>().eq("member_id", memberid).eq("course_id", courseid).eq("status", 1));
        if(count>0) {
            return true;
        } else {
            return false;
        }
    }

在这里插入图片描述

2、service-edu中远程调用接口

在这里插入图片描述

3、编写查询是否被购买接口

 //根据id查询课程详情信息
    @Autowired
    private FeignToOrderClient feignToOrderClient;
    @ApiOperation("根据id查询课程详情,并判断课程十分被购买")
    @GetMapping("getCourseInfo/{id}")
    public Result getCourseInfo(@PathVariable(value = "id") String id, HttpServletRequest request) {
        //课程查询课程基本信息
        CourseApiInfoVo courseApiInfoVo = eduCourseService.selectCourserApiInfoVoById(id);
        //查询课程里面大纲数据
        List<ChapterVo> chapterVideoList = eduChapterService.selectChapterVideoByCourseId(id);

        //远程调用,判断课程是否被购买
        boolean buyCourse = feignToOrderClient.isBuyCourse(JwtUtils.getMemberIdByJwtToken(request), id);
		
		Map<String , Object> map=new HashMap<>();
        map.put("courseFrontInfo",courseApiInfoVo);
        map.put("chapterVideoList",chapterVideoList);
        map.put("isbuy",buyCourse);
        return Result.ok().data(map);
    }

在这里插入图片描述

4、测试

在这里插入图片描述

三、前端

1、订单页面整合

1、修改课程详情页面代码,实现点击购买,跳转页面

在这里插入图片描述

2、编写订单动态路由页面

在这里插入图片描述
在这里插入图片描述

2、二维码页面整合(qriously组件)

需要使用qriously组件,它会根据地址实时下载图片显示

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

1、修改订单页面,点击去支付,跳转到支付页面

在这里插入图片描述

2、编写api接口

在这里插入图片描述

3、编写程序

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4、测试

在这里插入图片描述
在这里插入图片描述

3、修改课程页面,让其判断,购买状态,如果已购,不显示立即购买,显示立即观看

1、api接口

在这里插入图片描述

2、代码

在这里插入图片描述
在这里插入图片描述

3、测试

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

殷丿grd_志鹏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值