springboot+mybatis-plus+vue完成微信支付(前后端分离)

2023-05-16

微信支付的学习链接:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
一、数据库准备

t_order表 主要完成订单查询

/*
 Navicat Premium Data Transfer

 Source Server         : Michinaish
 Source Server Type    : MySQL
 Source Server Version : 80011
 Source Host           : localhost:3306
 Source Schema         : wechat

 Target Server Type    : MySQL
 Target Server Version : 80011
 File Encoding         : 65001

 Date: 16/08/2022 19:52:38
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

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

-- ----------------------------
-- Records of t_order
-- ----------------------------
INSERT INTO `t_order` VALUES ('0195f142a5824e0b88f', 'c60801fbdd2d45f9bc6', '1408424998648799234', 'Java初中级系统架构师组合套餐课(含12门课程)', 'https://kk-books.oss-cn-hangzhou.aliyuncs.com/44df362b-8d51-43a9-b823-090b02a3f2e8.jpg', '张飞', '1402269617551667201', '仲梦君', '15092182775', 32.00, 1, 0, 0, '2021-06-25 23:03:35', '2021-06-25 23:03:35');
INSERT INTO `t_order` VALUES ('21e04d165a324e59b96', '0322b37415384df0bbf', '1408421906918268930', '从零学习netty网络IO通讯开发视频教程', 'https://kk-books.oss-cn-hangzhou.aliyuncs.com/7b4a1bc6-b016-4afa-84c2-9ecfa0fae14d.jpg', '张飞', '1306495996639842305', '邱成相', '15092182541', 120.00, 1, 1, 0, '2020-06-25 23:02:27', '2020-06-25 23:02:27');
INSERT INTO `t_order` VALUES ('383714ba15e9474eb1a', 'e334ce2a6b1d4bc6936', '1408411851292532738', '姜维自传', 'https://kk-books.oss-cn-hangzhou.aliyuncs.com/8f731951-d606-4b86-8d4d-1cea38ee39f5.jpg', '姜维', '1408590478277685250', '李白', '15972331424', 0.01, 0, 0, 0, '2021-06-26 09:08:56', '2021-06-26 09:08:56');
INSERT INTO `t_order` VALUES ('39a3553e85bb4d69894', '9de332298489407fa81', '1408413427792994305', '成功的秘诀', 'https://kk-books.oss-cn-hangzhou.aliyuncs.com/03fa648e-a619-4500-a832-4750b3fa44ec.jpg', '张飞', '1402269617551667201', '仲梦君', '15092182775', 20.00, 0, 1, 0, '2021-06-25 23:09:34', '2021-06-25 23:09:50');
INSERT INTO `t_order` VALUES ('3fbd422bd60a4614a3b', '3d2e34a108174a67aba', '1408410177668767745', '水淹七军', 'https://kk-books.oss-cn-hangzhou.aliyuncs.com/74b7e90d-8463-4801-bdf8-52efd9cb6e41.jpg', '关羽', '1448200763102928897', NULL, '15660773278', 0.01, 0, 0, 0, '2021-10-13 16:17:05', '2021-10-13 16:17:05');
INSERT INTO `t_order` VALUES ('699aba8a2753439eb6a', '858c6dc73bb84c898a5', '1408409971229319170', '大白话领域驱动设计', 'https://kk-books.oss-cn-hangzhou.aliyuncs.com/5ae6a380-dbd6-4e74-8c99-a8c2ede79455.jpg', '张飞', '1408590478277685250', '李白', '15972331424', 0.01, 0, 1, 0, '2021-06-26 08:59:31', '2021-06-26 08:59:47');
INSERT INTO `t_order` VALUES ('7951387ce957484c80c', '2e4a952c118b4c47a79', '1408410177668767745', '水淹七军', 'https://kk-books.oss-cn-hangzhou.aliyuncs.com/74b7e90d-8463-4801-bdf8-52efd9cb6e41.jpg', '关羽', '1402268479037206530', '舒隆振', '15092182328', 0.01, 0, 1, 0, '2021-06-25 23:22:56', '2021-06-25 23:24:02');
INSERT INTO `t_order` VALUES ('9de56a01d7fb4a9895a', '7ff948d22cf04e61a06', '1408411851292532738', '姜维自传', 'https://kk-books.oss-cn-hangzhou.aliyuncs.com/8f731951-d606-4b86-8d4d-1cea38ee39f5.jpg', '姜维', '1448200763102928897', NULL, '15660773278', 0.01, 0, 0, 0, '2021-10-13 16:37:28', '2021-10-13 16:38:20');
INSERT INTO `t_order` VALUES ('b25d3987e7e94092b53', 'da27dfff1ae440c2aed', '1408411851292532738', '姜维自传', 'https://kk-books.oss-cn-hangzhou.aliyuncs.com/8f731951-d606-4b86-8d4d-1cea38ee39f5.jpg', '姜维', '1306495996639842305', '邱成相', '15092182541', 100.00, 0, 0, 0, '2019-06-25 21:54:13', '2019-06-25 21:54:13');
INSERT INTO `t_order` VALUES ('b9d4382ae2d84c568f9', '67164fb9916a4209876', '1408410177668767745', '水淹七军', 'https://kk-books.oss-cn-hangzhou.aliyuncs.com/74b7e90d-8463-4801-bdf8-52efd9cb6e41.jpg', '关羽', '1406792661091491841', '景晨曦', '15007124873', 10.00, 0, 0, 0, '2021-06-25 23:09:51', '2021-06-25 23:10:13');
INSERT INTO `t_order` VALUES ('c1fddde4bdae4ad68a6', 'def097eeafe44d7b8dd', '1408425438857781250', '亿级电商微服务优惠劵系统全实现', 'https://kk-books.oss-cn-hangzhou.aliyuncs.com/08a1a97e-a059-4ed7-a026-131890a4700f.JPG', '张飞', '1402269617551667201', '仲梦君', '15092182775', 0.01, 0, 0, 0, '2021-06-25 23:11:04', '2021-06-25 23:11:04');
INSERT INTO `t_order` VALUES ('dbad9d7063ab47a9b8b', '821d7109fa6d4520982', '1408409971229319170', '大白话领域驱动设计', 'https://kk-books.oss-cn-hangzhou.aliyuncs.com/5ae6a380-dbd6-4e74-8c99-a8c2ede79455.jpg', '张飞', '1448200763102928897', NULL, '15660773278', 211.00, 1, 0, 0, '2021-10-13 16:16:42', '2021-10-13 16:16:42');
INSERT INTO `t_order` VALUES ('e5f80e6712894b8793c', '28d54a53bdf14c3c853', '1408409971229319170', '大白话领域驱动设计', 'https://kk-books.oss-cn-hangzhou.aliyuncs.com/5ae6a380-dbd6-4e74-8c99-a8c2ede79455.jpg', '张飞', '1402269617551667201', '仲梦君', '15092182775', 210.00, 0, 0, 0, '2018-06-25 21:54:13', '2018-06-25 21:54:13');
INSERT INTO `t_order` VALUES ('ea744617fe8d4112796', 'e334ce2a6b1c4bc6018', '1408411851292532738', '姜维自传', 'https://kk-books.oss-cn-hangzhou.aliyuncs.com/8f731951-d606-4b86-8d4d-1cea38ee39f5.jpg', '姜维', '1306147989314703362', '木白', '15700085997', 0.01, 0, 0, 0, '2021-10-27 17:54:32', '2022-08-14 17:27:26');

SET FOREIGN_KEY_CHECKS = 1;

t_pay_log 主要是用于支付成功后 记录显示

/*
 Navicat Premium Data Transfer

 Source Server         : Michinaish
 Source Server Type    : MySQL
 Source Server Version : 80011
 Source Host           : localhost:3306
 Source Schema         : wechat

 Target Server Type    : MySQL
 Target Server Version : 80011
 File Encoding         : 65001

 Date: 16/08/2022 19:52:45
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

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

SET FOREIGN_KEY_CHECKS = 1;

二、后端准备
(1)配置application.properities

#连接数据库
server.port=8999
spring.datasource.druid.url=jdbc:mysql://localhost:3306/wechat?serverTimezone=Asia/Shanghai
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.username=root
spring.datasource.druid.password=root
#初始化的个数
spring.datasource.druid.initial-size=5
# 最大活跃数
spring.datasource.druid.max-active=10
# 最大等待时间
spring.datasource.druid.max-wait=3000
# 最小的闲置个数
spring.datasource.druid.min-idle=5

# 微信的appid 商家id 密钥---填上自己的就行(无法申请因为需要营业执照)
weixin.appid=
weixin.mch_id=
weixin.api_key=

(2)在pom.xml引入依赖
这里可以使自己的springboot版本降低会比较稳定

<dependencies>

        <!--微信依赖-->
        <dependency>
            <groupId>repMaven.com.github.wxpay</groupId>
            <artifactId>wxpay-sdk</artifactId>
            <version>0.0.3</version>
        </dependency>

        <!--Java端发送请求-->
        <dependency>
            <groupId>repMaven.org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.3</version>
        </dependency>


        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.8</version>
        </dependency>

        <dependency>
            <groupId>com.spring4all</groupId>
            <artifactId>swagger-spring-boot-starter</artifactId>
            <version>1.9.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>1.7.8</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.1</version>
        </dependency>

        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.28</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

(3)使用代码生成器 对俩表自动生成
代码生成器配置类为:

public class GeneCode {
    public static void main(String[] args) {
        //数据库配置
        String url="jdbc:mysql://localhost:3306/wechat?serverTimezone=Asia/Shanghai";
        //要自动生成的表名 多个表之间使用逗号隔开
        String[] tables = {"t_order","t_pay_log"};
        //获取本项目的路径
        String projectPath = System.getProperty("user.dir");
        //包项目路径
        String outputDir =projectPath+"/src/main/java";
        //xml文件路径
        String outputDirMapper = projectPath+"/src/main/resources/mapper";
        //快速创建
        FastAutoGenerator.create(url, "root", "root")
                .globalConfig(builder -> {
                    builder.author("wx") // 设置作者
                            .enableSwagger() // 开启 swagger 模式
                            .fileOverride() // 覆盖已生成文件
                            .outputDir(outputDir); // 指定输出目录
                })
                .packageConfig(builder -> {
                    builder.parent("com.wx") // 设置父包名
                            .pathInfo(Collections.singletonMap(OutputFile.mapperXml, outputDirMapper)); // 设置mapperXml生成路径
                })
                .strategyConfig(builder -> {
                    builder.addInclude(tables) // 设置需要生成的表名
                            .addTablePrefix("t_"); // 设置过滤表前缀,多个前缀可用逗号隔开
                })
                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .execute();
    }
}

(4)配置类
用于Java端模拟浏览器

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;
   }
}

(5)添加和修改实体类

Result类用于返回给前端

@AllArgsConstructor
@NoArgsConstructor
@Data
@ApiModel(value = "统一json数据")
public class Result {
    @ApiModelProperty("状态码2000表示成功 5000表示失败")
    private Integer code;
    @ApiModelProperty("响应的消息内容")
    private String msg;
    @ApiModelProperty("响应的数据")
    private Object data;
}

修改实体类 主要用于mp 可以自动注入时间

package com.wx.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * <p>
 * 订单
 * </p>
 *
 * @author wx
 * @since 2022-08-13
 */
@TableName("t_order")
@ApiModel(value = "Order对象", description = "订单")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order implements Serializable {

    private static final long serialVersionUID = 1L;

    private String id;

    @ApiModelProperty("订单号")
    private String orderNo;

    @ApiModelProperty("课程id")
    private String courseId;

    @ApiModelProperty("课程名称")
    private String courseTitle;

    @ApiModelProperty("课程封面")
    private String courseCover;

    @ApiModelProperty("讲师名称")
    private String teacherName;

    @ApiModelProperty("会员id")
    private String memberId;

    @ApiModelProperty("会员昵称")
    private String nickname;

    @ApiModelProperty("会员手机")
    private String mobile;

    @ApiModelProperty("订单金额(分)")
    private BigDecimal totalFee;

    @ApiModelProperty("支付类型(0:微信 1:支付宝)")
    private Integer payType;

    @ApiModelProperty("订单状态(0:未支付 1:已支付)")
    private Integer status;

    @ApiModelProperty("逻辑删除 1(true)已删除, 0(false)未删除")
    private Boolean isDeleted;

    @ApiModelProperty("创建时间")
    private LocalDateTime gmtCreate;

    @ApiModelProperty("更新时间")
    private LocalDateTime gmtModified;
}

package com.wx.entity;

import com.baomidou.mybatisplus.annotation.*;

import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * <p>
 * 支付日志表
 * </p>
 *
 * @author wx
 * @since 2022-08-13
 */
@TableName("t_pay_log")
@ApiModel(value = "PayLog对象", description = "支付日志表")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PayLog implements Serializable {

    private static final long serialVersionUID = 1L;


    @TableId(value = "id",type = IdType.ASSIGN_ID)
    private Long id;

    @ApiModelProperty("订单号")
    private String orderNo;

    @ApiModelProperty("支付完成时间")
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime payTime;

    @ApiModelProperty("支付金额(分)")
    private BigDecimal totalFee;

    @ApiModelProperty("交易流水号")
    private String transactionId;

    @ApiModelProperty("交易状态")
    private String tradeState;

    @ApiModelProperty("支付类型(0:微信 1:支付宝)")
    private Integer payType;

    @ApiModelProperty("其他属性")
    private String attr;

    @ApiModelProperty("逻辑删除 1(true)已删除, 0(false)未删除")
    @TableLogic
    private Boolean isDeleted;

    @ApiModelProperty("创建时间")
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime gmtCreate;

    @ApiModelProperty("更新时间")
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime gmtModified;
}

(6)配置自动修改和创建时间的配置文件

package com.wx.config;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        this.strictInsertFill(metaObject, "gmtCreate", LocalDateTime.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "gmtModified", LocalDateTime.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "payTime", LocalDateTime.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "isDeleted", Boolean.class, false);
        System.out.println("我执行了");
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ....");
        // 默认提供的strictUpdateFill为有值不覆盖, gmtModified需要覆盖,利用通用塞值的方法填充
        this.setFieldValByName("gmtModified", LocalDateTime.now(), metaObject);
    }
}

(7)修改控制层代码–添加业务

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private IOrderService orderService;

    @PostMapping("createQRCode/{orderNo}")
    public Result createQRCode(@PathVariable String orderNo){
        return orderService.createQRCode(orderNo);
    }

    @PostMapping("queryPayStatus/{orderNo}")
    public Result queryPayStatus(@PathVariable String orderNo){
        return orderService.queryPayStatus(orderNo);
    }
}

(8)修改service及其实现类

package com.wx.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.wxpay.sdk.WXPayUtil;
import com.wx.entity.Order;
import com.wx.entity.PayLog;
import com.wx.mapper.OrderMapper;
import com.wx.mapper.PayLogMapper;
import com.wx.service.IOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wx.utils.HttpClient;
import com.wx.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * <p>
 * 订单 服务实现类
 * </p>
 *
 * @author wx
 * @since 2022-08-13
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

    @Autowired
    private OrderMapper orderMapper;

    /*获取微信的appid*/
    @Value("${weixin.appid}")
    private String appId;

    /*获取配置文件中的商家id*/
    @Value("${weixin.mch_id}")
    private String mchId;

    /*获取密钥*/
    @Value("${weixin.api_key}")
    private String apiKey;

    @Autowired
    private PayLogMapper payLogMapper;

    /*创建订单*/
    @Override
    public Result createQRCode(String orderNo) {
        /*查询orderNo的订单信息*/
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("order_no", orderNo);
        wrapper.eq("status", 0);
        Order order = orderMapper.selectOne(wrapper);
        /*不为空 才能进行业务操作*/
        if (order != null) {
            try {
                //获取下单的接口url
                HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
                //设置支持https请求
                client.setHttps(true);
                /*以下的字段是必填字段
                * https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
                * 该链接中对必填字段有描述
                * */
                Map<String, String> parms = new HashMap<>();
                parms.put("appid", appId);
                parms.put("mch_id", mchId);
                parms.put("nonce_str", WXPayUtil.generateNonceStr());
                parms.put("body", order.getCourseTitle());
                parms.put("out_trade_no", orderNo);
                parms.put("total_fee", (order.getTotalFee().multiply(new BigDecimal(100))).longValue() + "");
                parms.put("spbill_create_ip", "127.0.0.1");
                parms.put("notify_url", "http://localhost:8999/pay/back");
                parms.put("trade_type","NATIVE");
                client.setXmlParam(WXPayUtil.generateSignedXml(parms, apiKey));
                /*post请求*/
                client.post();
                String content = client.getContent();
                /*得到的content其实是xml格式 所以要转为map格式*/
                Map<String,String> map = WXPayUtil.xmlToMap(content);
                /*若返回值成功 就会有一个codeurl 该值返回给前端会通过vue将连接转为二维码*/
                if(map.get("result_code").equals("SUCCESS")){
                    /*将需要的值返回给前端*/
                    Map<String,Object> result = new HashMap<>();
                    result.put("codeUrl",map.get("code_url"));
                    result.put("price",order.getTotalFee());
                    result.put("orderNo",order.getOrderNo());
                    return new Result(2000,"生成二维码成功",result);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return new Result(5000, "创建失败", null);
    }

    /*创建订单后 要查询订单的状态*/
    @Override
    @Transactional
    public Result queryPayStatus(String orderNo) {

        try {
            /*https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_2
            注意查询状态和获取订单的url不一样
            * 
            * */
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
            client.setHttps(true);
            Map<String, String> parms = new HashMap<>();
            parms.put("appid", appId);
            parms.put("mch_id", mchId);
            parms.put("out_trade_no", orderNo);
            parms.put("nonce_str", WXPayUtil.generateNonceStr());
            client.setXmlParam(WXPayUtil.generateSignedXml(parms, apiKey));
            client.post();
            String content = client.getContent();
            System.out.println(content);
            Map<String,String> map = WXPayUtil.xmlToMap(content);
            String transactionId = map.get("out_trade_no");
            /*支付成功就会得到交易状态 若成功继续执行*/
            if(map.get("trade_state").equals("SUCCESS")){
                Order order = new Order();
                /*成功后改变当前订单的状态*/
                order.setStatus(1);
                order.setGmtModified(LocalDateTime.now());
                QueryWrapper<Order> wrapper = new QueryWrapper<>();
                wrapper.eq("order_no",orderNo);
                wrapper.eq("status",0);
                Order one = orderMapper.selectOne(wrapper);
                orderMapper.update(order,wrapper);
                /*支付成功后修改t_pay_log*/
                String tradeState = map.get("trade_state");
                PayLog payLog = new PayLog();
                payLog.setOrderNo(orderNo);
                payLog.setPayType(one.getPayType());
                payLog.setTotalFee(one.getTotalFee());
                payLog.setTransactionId(transactionId);
                payLog.setTradeState(tradeState);
                payLog.setIsDeleted(false);
                payLogMapper.insert(payLog);
                return new Result(2000,"支付成功",null);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new Result(5000,"支付失败",null);
    }
}

前后端分离需要解决跨域问题

package com.wx.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class CorsConfig {

    // 当前跨域请求最大有效时长。这里默认1天
    private static final long MAX_AGE = 24 * 60 * 60;

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*"); // 1 设置访问源地址
        corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头
        corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法
        corsConfiguration.setMaxAge(MAX_AGE);
        source.registerCorsConfiguration("/**", corsConfiguration); // 4 对接口配置跨域设置
        return new CorsFilter(source);
    }
}

三、前端准备

前端需要引入 vue-qr依赖
在这里插入图片描述

(1)前端主页面

<template>
    <div>
        <el-button type="primary" @click="pay">支付</el-button>
        <el-dialog
                title="收银台"
                :visible.sync="dialogVisible"
                width="30%">
            <div style="text-align: center">
                <p>微信支付{{payResult.price}}元</p>
                <div style="border: 10px solid grey;width:160px;padding:10px;margin:0px auto">
                    <vue-qr
                        :text="payResult.codeUrl"
                         colorLight="#fff"
                        :size="160"
                         margin="0"
                        :logo-src="require('../assets/3.webp')"
                    >
                    </vue-qr>
                </div>
                <el-divider></el-divider>
                <div style="font-size: 13px">
                    提示:<br>
                    支付成功前请勿手动关闭页面<br>
                    二维码两个小时有效,请及时扫码支付<br>
                </div>
            </div>
        </el-dialog>
    </div>

</template>

<script>
    import vueQr from 'vue-qr'

    export default {
        components:{
            vueQr
        },
        name: "WeChatPay",
        data(){
            return{
                orderNo:"e334ce2a6b1c4bc6018",
                payResult:{
                    codeUrl:"",
                    price:0,
                    orderNo:'',
                },
                dialogVisible:false,
                timer:"",
                flag:0,

            }
        },

        methods:{
            queryPayStatus(orderNo){
              this.$http.post("/order/queryPayStatus/"+orderNo).then(result=>{
                  if(result.data.code===2000){
                      clearInterval(this.timer);
                      this.timer=null;
                      this.$message.success("支付成功");
                      this.dialogVisible = false;
                  }
                })
            },
            pay(){
                this.dialogVisible = true;
                /*创建订单*/
                this.$http.post("order/createQRCode/"+this.orderNo).then(result=>{
                    if(result.data.code===2000){
                        this.payResult = result.data.data;
                        /*每隔三秒查询订单的状态 是否支付*/
                        this.timer = setInterval(()=>{
                            this.queryPayStatus(this.payResult.orderNo)
                        },3000);
                    }
              })
            },
        }
    }
</script>

<style scoped>

</style>

(2)修改main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import './plugins/element.js'
import axios from "axios";
axios.defaults.baseURL="http://localhost:8999/"
Vue.config.productionTip = false
Vue.prototype.$http=axios
new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

springboot+mybatis-plus+vue完成微信支付(前后端分离) 的相关文章

  • Redhat Linux advance Server V2.1无法进入桌面(转)

    Redhat Linux advance Server V2 xff11 无法进入桌面 转 64 more 64 在本地调试安装了个Redhat Linux advance Server V2 1 xff0c 一共有5张光盘 xff0c 我
  • Mac上类似于xshell的远程工具:finalshell 和 royal tsx

    FinalShell 国产 国产 国产 自己研发的 是一体化的的服务器 网络管理软件 不仅是ssh客户端 还是功能强大的开发 运维工具 充分满足开发 运维需求 特色功能 免费海外服务器远程桌面加速 ssh加速 本地化命令输入框 支持自动补全
  • css查找元素注意事项

    一 CSS ID 选择器查找元素 1 注意 xff1a 如果元素的ID不唯一 xff0c 或者是动态的 或者name以及linktext属性值也不唯一 我们就需要考虑用Xpath来查找元素了 xff0c 然后再对元素执行操作 不管用什么方式
  • OS2.3.7:多生产者,多消费者问题

    文章目录 0 问题描述1 问题分析2 实现3 总结 0 问题描述 桌子上有一只盘子 xff0c 每次只能向其中放入一个水果 爸爸专向盘子中放苹果 xff0c 妈妈专向盘子中放橘子 xff0c 儿子专等着吃盘子中的橘子 xff0c 女儿专等着
  • java 方法名类名命名规范

    一 命名规范 1 项目名全部小写 2 包名全部小写 3 类名首字母大写 xff0c 如果类名由多个单词组成 xff0c 每个单词的首字母都要大写 大驼峰 xff0c 如 xff1a public class MyFirstClass 4 变
  • Qt arm环境安装

    一 相关工作准备 Qt opensource 和 Qt everywhere 下载 链接 版本为5 9 8 arm linux gcc下载 链接 版本为4 8 3 tslib 下载 链接 版本为1 21 ps 可以不安装Qt opensou
  • STM32驱动ST7789V2 tft屏幕

    一 简介 本次教程使用的是1 54寸240 240像素的tft屏幕 xff0c 其接口协议为SPI协议 在使用的过程中仅需要四根数据即可驱动点亮屏幕 然后硬件使用的是STM32F103C8T6核心板 xff0c 用的是SPI2 一般购买屏幕
  • linux设置复杂度策略、登录超时处理功能

    1 在字符终端下 xff0c 实现某一用户连续错误登陆N次后 xff0c 就锁定该用户X分钟 pam tally2 执行 vi etc pam d login 在 PAM 1 0 下新起一行 xff0c 加入 auth required p
  • 飞控陀螺仪,磁力计,加速计,四元数姿态结算

    MPU6050主要包含陀螺仪和加速度计 陀螺仪主要测量角速度 xff0c 即可以测出某一时间段物体转过的角度 加速度计测量的是物体的加速度 xff0c 重力加速度即物体受重力作用的情况下具有的加速度 xff0c 物体静止时 xff0c 加速
  • 智慧物业管理系统(Springboot)

    开发工具 xff1a IDEA xff0c jdk1 8 数据库 xff1a mysql5 7 前台框架 xff1a layui 后端技术 xff1a springboot 项目描述 xff1a 1 前台住户登录 2 智慧物业管理后台 2
  • 北京大学2020公开课 AVL-Python实现代码

    class TreeNode def init self key val left 61 None right 61 None parent 61 None self key 61 key self payload 61 val self
  • Docker-2020详细教程<配合千锋Java学习营>

    Docker 2020详细教程 lt 配合千锋Java学习营 gt 2020 Docker最新超详细版教程通俗易懂 一 Docker介绍 1 下载Dcoker依的赖环境 想安装Docker xff0c 需要先将依赖的环境全部下载下来 xff
  • 使用阿里云部署Flask网页

    使用阿里云部署Flask网页 前端网页部署 阿里云apache CentOS 配置好Apache后 xff0c 将一整个html css js文件全部copy进 var www html目录下 之后就可以通过访问IP地址访问到你的index
  • MapReduce的个人理解

    MapReduce的个人理解 文章目录 MapReduce模型简介Map和Reduce函数这里给出一个简单实例 MapReduce的工作流程工作流程概述MapReduce的各个执行阶段 Shuffle过程详解Shuffle过程简介Map端的
  • Hadoop配置

    Hadoop配置 文章目录 Linux shell配置环境变量使环境变量生效Hadoop 集群安装配置到两台阿里云linux主机上Hadoop集群模式安装实验环境实验内容1 安装jdk2 下面来修改环境变量3 安装hadoop4 下面来修改
  • HDFS 的使用和管理

    HDFS 的使用和管理 文章目录 HDFS 的使用和管理实验环境实验内容实验步骤1 启动hadoop的hdfs相关进程2 用jps查看HDFS是否启动3 验证HDFS运行状态4 ls 命令5 put 命令6 moveFromLocal 命令
  • HDFS API操作

    HDFS API操作 实验环境 Linux Ubuntu 16 04 前提条件 xff1a 1 xff09 Java 运行环境部署完成 2 xff09 Hadoop 的单点部署完成 上述前提条件 xff0c 我们已经为你准备就绪了 实验内容
  • HBase的安装部署和使用

    HBase的安装部署和使用 文章目录 HBase的安装部署和使用实验环境实验内容实验步骤1 点击 34 命令行终端 34 xff0c 打开新的命令行窗口2 解压安装包3 更改文件夹名和所属用户4 设置HBASE HOME环境变量5 修改hb
  • 熟悉常用的HBase操作

    熟悉常用的HBase操作 文章目录 实验环境实验内容1 编程实现以下指定功能 xff0c 并用Hadoop提供的HBase Shell命令完成相同的任务 xff08 1 xff09 列出HBase所有的表的相关信息 xff0c 如表名 创建
  • Hive的安装部署和管理

    Hive的安装部署和管理 文章目录 实验环境实验内容实验步骤1 点击 34 命令行终端 34 xff0c 打开新窗口2 解压安装包3 更改文件夹名和所属用户4 设置HIVE HOME环境变量5 导入MySql jdbc jar包到hive

随机推荐

  • Hive数仓:使用桶表

    Hive数仓 xff1a 使用桶表 文章目录 Hive数仓 xff1a 使用桶表实验环境实验步骤1 点击 34 命令行终端 34 xff0c 打开新窗口2 启动MySQL3 指定元数据数据库类型并初始化Schema4 启动Hadoop5 启
  • python 获取当前文件路径

    一 Python 获取当前文件路径方法 sys path 0 获取文件当前工作目录路径 绝对路径 sys argv 0 获得模块所在的路径 由系统决定是否是全名 若显示调用python指令 xff0c 如python demo py xff
  • PySpark中的RDD基本操作

    PySpark中的RDD基本操作 课程性质 xff1a PySpark数据处理 文章目录 1 实验目标2 本次实验主要使用的 P y t h
  • PySpark中的RDD创建

    PySpark中的RDD创建 课程性质 xff1a PySpark数据处理 文章目录 1 实验目标2 本次实验主要使用的 P y t h
  • el-table-column的formatter的使用

    当后端返回来的数据格式需要再去处理 xff1b 可以使用formatter属性 lt el table column label 61 34 性别 34 align 61 34 center 34 formatter 61 34 genda
  • 提示“无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系“的解决方案

    使用sudo apt get install lt packgename gt 时出现提示无法修正错误 xff0c 因为您要求某些软件包保持现状 xff0c 就是它们破坏了软件包间的依赖关系 可以换个命令 sudo aptitude ins
  • aosp下载、编译、刷机和单编framework(android 12)

    我的设备 xff1a 咸鱼上买的pixel 3a 一 aosp下载 1 安装repo mkdir bin PATH 61 bin PATH curl sSL 39 https gerrit googlesource proxy ustclu
  • LAMP架构之mysql的安装部署

    mysql的安装部署 一 mysql编译安装1 编译过程 二 LAMP架构的部署 一 mysql编译安装 官网地址如下 xff0c 进入选择版本 xff1a https downloads mysql com archives commun
  • hexo博客绑定自己的域名

    hexo博客绑定自己的域名 学习网址1 学习网址2 学习网址3 一 购买域名 登录阿里云账号 控制台 搜索框输入域名 域名注册 输入需要注册的域名 xff08 查看是否被占用 xff09 加入购物车 xff08 显示不能备案的不可买 xff
  • SimpleDateFormat类 格式化日期

    功能 xff1a 格式化和解析日期 将Date类型的日期格式化成我们需要的日期类型一般是 字符串类型将字符串类的日期再转回来 用到两个方法 format Date date xff1a 将date型转换成特定格式的字符串 parse Str
  • 队列(Java实现)

    1 1应用场景 银行排队 xff1a 1 2基本介绍 特点 队列是一个有序列表 xff0c 可以用数组或是链表来实现 遵循先入先出的原则 即 xff1a 先存入队列的数据 xff0c 要先取出 后存入的要后取出 示意图 解释 MaxSize
  • IO字节流读取文本中文乱码

    1 1问题说明 我们都知道字符流适用于读取文本 xff0c 而字节流能读取文本 照片 视频等 xff0c 但是用字节流读取文本到我们程序的控制台中会出现中文乱码的情况 xff0c 如下图 我的文本中的数据是 生活很简单 xff0c 过了今天
  • glibc所安装的工具程序

    catchsegv 当程序发生segmentation fault的时候 用来建立一个 堆栈跟踪 gencat 建立消息列表 getconf 针对文件系统的指定变量显示其系统设置值 getent 从 系统管理数据库获取一个条目 glibcb
  • 单链表(java实现)

    1 1 链表 Linked List 介绍 链表是有序的列表 xff0c 但是它在内存中是存储如下 链表是以节点的方式来存储 是链式存储每个节点包含 data 域 xff0c next 域 xff1a 指向下一个节点 如图 xff1a 发现
  • prepareStatement的使用

    1 1prepareStatement解决sql注入的问题 span class token comment 演示sql注入的安全问题 span span class token keyword public span static voi
  • 动态sql

    1 什么是动态sql sql的内容是变化的 可以根据条件获取到不同的sql语句 主要是where部分发生变化 动态sql的实现 使用的是mybatis提供的标签 2 为什么使用动态sql 使用动态sql可以解决某些功能的使用 例如使用条件查
  • 分页插件--PageHelper

    mybatis的分页查询可以通过PageHelper插件实现 在数据库中我们使用分页查询的sql语句为 xff1a select from 表名 where 条件 limit page 1 pageSize pageSize page 当前
  • springboot框架

    1 什么是springboot框架 Spring是一个开源框架 xff0c Spring是于2003 年兴起的一个轻量级的Java 开发框架 xff0c 由Rod Johnson 在其著作 Expert One On One J2EE De
  • Elasticsearch入门及整合springboot

    1 Elasticsearch概述 1 1 搜索是什么 概念 xff1a 用户输入想要的关键词 xff0c 返回含有该关键词的所有信息 场景 xff1a 1 互联网搜索 xff1a 谷歌 百度 各种新闻首页 2 站内搜索 xff08 垂直搜
  • springboot+mybatis-plus+vue完成微信支付(前后端分离)

    微信支付的学习链接 https pay weixin qq com wiki doc api native php chapter 61 9 1 一 数据库准备 t order表 主要完成订单查询 span class token comm