微信支付的学习链接
:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
一、数据库准备
t_order表 主要完成订单查询
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
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;
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 主要是用于支付成功后 记录显示
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
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>
<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(使用前将#替换为@)