本项目主要通过以下链接进行学习
【尚硅谷】微信支付开发,基于SpringBoot+Vue架构的Java在线支付视频教程
这篇文章结合了自已的思考以及笔记
该项目主要是基于
后端技术有:springboot+springmvc+restful+vue+mybatis-plus+mysql
前端技术有:html+css+vue
本文的框架主要介绍
本文的框架涉及的知识点 可看我之前的文章
基础知识:
服务端:
数据库:
web服务器:Tomcat详细配置(全)
项目管理:
前端知识点:
按照密钥的使用方式,加密可以分为两大类:对称加密和非对称加密
对称加密
特点:只使用一个密钥,密钥必须保密,常用的有 AES算法
非对称加密
特点:使用两个密钥:公钥和私钥,公钥可以任意分发而私钥保密,常用的有 RSA
一般来说公钥加密之后,公钥都是公开的,通过私钥进行解密
而如果反过来,通过私钥加密,公钥解密,那就是身份认证,专属自已的唯一钥匙
主要为了保证数据没有被修改过,保证信息的完整性
摘要算法就是我们常说的散列函数、哈希函数(Hash Function),它能够把任意长度的数据“压缩”成
固定长度、而且独一无二的“摘要”字符串,就好像是给这段数据生成了一个数字“指纹”。
主要的特性有:
为了凸显数据的完整性,通过加入摘要算法,将其添加进数据中
而拿到数据的人为了验证数据完整性,通常也要通过摘要算法,也就是哈希,判断数据前后是否一致,依次判定是否有修改
但如果中间被截获,生成新的摘要算法,很难判断是否又被其他人修改
数字签名:
将数据通过摘要算法之后还要再用私钥加密变成数字签名
而获得数据的人,需要通过公钥进行解密以及通过摘要算法来验证数据的完整性
上面的公钥可以伪造
为此多了一个数字证书
数字证书:
数字证书解决“公钥的信任”问题,可以防止黑客伪造公钥。
不能直接分发公钥,公钥的分发必须使用数字证书,数字证书由CA颁发
具体数字证书生成流程如下:
通过哈希算法也就是摘要算法生成信息再通过CA的私钥加密变成数字证书
A用数字证书发送数据,B取到数据后,通过哈希算法也就是摘要算法进行确定完整性,再通过CA的公钥判定是否可以取出,取出之后就是A的公钥了。B再用取到的A公钥进行解密,以及摘要算法确定数据的完整性
创建springboot的项目
通过idea编译器 new 一个spring initializr的项目,java8的版本
以及服务器使用https://start.aliyun.com
,会比较快
具体关于springboot的相关知识可看我之前的文章
springboot从入门到精通(全)
此处修改以下pom文件下的依赖文件改成web,主要为了网页的启动
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
配置一个端口号(资源文件下)
server:
port: 8090 #服务端口
spring:
application:
name: payment-demo #应用的名字
配置一个界面,进行测试springboot的启动
@RestController //主要是为了传输json数据的一个注解
@RequestMapping("/api/product")
public class ProductController {
@GetMapping("/test")
public String test(){
return "hello";
}
}
在浏览器中输入url为
http://localhost:8090/api/product/test 进行测试
几个注意事项:
在搭建springboot的时候
使用maven管理jar包的时候不要混乱使用版本号
(注解不要混乱使用jar包各个版本)
自动生成接口文档和相关的测试界面
测试get post delete等界面的时候比较方便
一个是依赖文件一个是ui的展示界面依赖包
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
<version>2.7.0version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger-uiartifactId>
<version>2.7.0version>
dependency>
配置一个配置类
通过bean对象注入
如一个文本的文档对象
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(new ApiInfoBuilder().title("码农研究僧微信支付案例接口文档").build());
}
}
之后在网址中输入如下页面http://localhost:8090/swagger-ui.html#/
为了更好的通过url与swagger的更好融合
可以在业务层的代码模块中增加如下,@Api(tags = "商品管理")
,@ApiOperation("测试接口")
这个注解@ApiOperation(" )
只有一个value值 ,所以value值可以省略
@Api(tags = "商品管理")
@RestController //主要是为了传输json数据的一个注解
@RequestMapping("/api/product")
public class ProductController {
@ApiOperation("测试接口")
@GetMapping("/test")
public String test(){
return "hello";
}
}
之后在页面中就会这么显示
为了前后端更好的交互,定义一个统一的结果
主要显示响应码,响应消息
再依赖包中要放一个lombok
主要是可以简化开发,自动生成get、set等方法
(前提是有lombok的插件)
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
主要的代码模块为
@Data
public class R {
private Integer code; //响应码
private String message; //响应消息
private Map<String, Object> data = new HashMap<>();
public static R ok(){
R r = new R();
r.setCode(0);
r.setMessage("成功");
return r;
}
public static R error(){
R r = new R();
r.setCode(-1);
r.setMessage("失败");
return r;
}
public R data(String key, Object value){
this.data.put(key, value);
return this;
}
}
通过@Data
这个注解查看其structure结构,也可以看到自动生成了get和set等方法
关于@Data
这个注解可看我之前的文章
spring中@Data注解详细解析
给前端传输一些具体的信息
通过data这个哈希进行传输
public R data(String key, Object value){
this.data.put(key, value);
return this;
}
在显示界面中通过输入一定的值就可看到
链式操作,可以多个值传输
@ApiOperation("测试接口")
@GetMapping("/test")
public R test(){
return R.ok().data("message", "hello").data("now", new Date());
}
特别补充一下如果这样传输new Date()
的数据,不是一个标准的格式
如果要换成想要的格式
可以在资源文件中进行添加(本身时json的数据格式)
yyyy-MM-dd HH:mm:ss
年月日,分时秒,GMT+8
以及时区
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
通过navicat软件或者本机的mysql进行构建数据库
通过运行特定的数据库脚本或者自已书写数据库
具体sql的脚本如下
USE `payment_demo`;
/*Table structure for table `t_order_info` */
CREATE TABLE `t_order_info` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '订单id',
`title` varchar(256) DEFAULT NULL COMMENT '订单标题',
`order_no` varchar(50) DEFAULT NULL COMMENT '商户订单编号',
`user_id` bigint(20) DEFAULT NULL COMMENT '用户id',
`product_id` bigint(20) DEFAULT NULL COMMENT '支付产品id',
`total_fee` int(11) DEFAULT NULL COMMENT '订单金额(分)',
`code_url` varchar(50) DEFAULT NULL COMMENT '订单二维码连接',
`order_status` varchar(10) DEFAULT NULL COMMENT '订单状态',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
/*Table structure for table `t_payment_info` */
CREATE TABLE `t_payment_info` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '支付记录id',
`order_no` varchar(50) DEFAULT NULL COMMENT '商户订单编号',
`transaction_id` varchar(50) DEFAULT NULL COMMENT '支付系统交易编号',
`payment_type` varchar(20) DEFAULT NULL COMMENT '支付类型',
`trade_type` varchar(20) DEFAULT NULL COMMENT '交易类型',
`trade_state` varchar(50) DEFAULT NULL COMMENT '交易状态',
`payer_total` int(11) DEFAULT NULL COMMENT '支付金额(分)',
`content` text COMMENT '通知参数',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
/*Table structure for table `t_product` */
CREATE TABLE `t_product` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '商品id',
`title` varchar(20) DEFAULT NULL COMMENT '商品名称',
`price` int(11) DEFAULT NULL COMMENT '价格(分)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
/*Data for the table `t_product` */
insert into `t_product`(`title`,`price`) values ('Java课程',1);
insert into `t_product`(`title`,`price`) values ('大数据课程',1);
insert into `t_product`(`title`,`price`) values ('前端课程',1);
insert into `t_product`(`title`,`price`) values ('UI课程',1);
/*Table structure for table `t_refund_info` */
CREATE TABLE `t_refund_info` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '退款单id',
`order_no` varchar(50) DEFAULT NULL COMMENT '商户订单编号',
`refund_no` varchar(50) DEFAULT NULL COMMENT '商户退款单编号',
`refund_id` varchar(50) DEFAULT NULL COMMENT '支付系统退款单号',
`total_fee` int(11) DEFAULT NULL COMMENT '原订单金额(分)',
`refund` int(11) DEFAULT NULL COMMENT '退款金额(分)',
`reason` varchar(50) DEFAULT NULL COMMENT '退款原因',
`refund_status` varchar(10) DEFAULT NULL COMMENT '退款状态',
`content_return` text COMMENT '申请退款返回参数',
`content_notify` text COMMENT '退款结果通知参数',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
在java代码模块中
引入数据库的依赖包
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.3.1version>
dependency>
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/payment_demo?serverTimezone=GMT%2B8&characterEncoding=utf-8
username: root
password: 123456
数据库的版本号如果是5左右,版本名字叫做com.mysql.jdbc.Driver
如果是8的版本号,版本名字叫做com.mysql.cj.jdbc.Driver
,即使在pom文件中不给版本号,系统也会默认给8的版本
具体下载方式可以通过maven工程
也可以通过如下进行下载
代码模块中关于数据库
需要数据库表的实体类,映射文件,以及业务逻辑的代码模块
可以通过mybatis的逆向工程一键生成
具体可看我之前的文章
mybatis逆向工程详细配置讲解(全)
一共有四张表格,而也有公共的列名称,所以可以设置一个公有类,之后通过继承
在命名的时候如果表格与实体类不一样,可以通过@TableName("t_order_info")
这个注解进行映射
如果列名和数据库的列名不一样,也可以映射,但最好使用驼峰的命名规则,mybatisplus就会帮忙转换
(通过lombok的插件 可以自动生成get、set等方法)
如果数据库有主键还需要再主键中配备一个注解
定义主键策略:跟随数据库的主键自增@TableId(value = "id", type = IdType.AUTO)
这个类,是mybatis-plus中的统用maper
动态的生成一些特定的接口
xml文件也没有一些书写代码
补充:
再编译之后 target目录中,没有生成xml文件
默认情况下,java目录下非java代码生成不了
资源路径搭配的选项
让maven 中把非java文件也生成
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
让运行文件能够找到xml文件
应用程序的mapper如何找到
主要是这一行代码
mapper-locations: classpath:com/paymentdemo/mapper/xml/*.xml
顺便补充一下日志的配置文件
主要是开启事务
以及扫描映射文件
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@MapperScan("com.paymentdemo.mapper")
@EnableTransactionManagement //启用事务管理
public class MyBatisPlusConfig {
}
添加完数据库之后
就可以通过调用数据库的数据
@CrossOrigin //开放前端的跨域访问
@Api(tags = "商品管理")
@RestController
@RequestMapping("/api/product")
public class ProductController {
@Resource
private ProductService productService;
@GetMapping("/list")
public R list(){
List<Product> list = productService.list();
return R.ok().data("productList", list);
}
}
之后在页面中就可以访问到数据
http://localhost:8090/swagger-ui.html#!/21830216973164929702/listUsingGET
前端界面主要是vue+javascript+html
刚开始是安装node的安装包,可以通过官网进行安装
在前端搭建的项目中输入 npm run serve
默认是开启了前端的界面
但是搭建了后端界面,以及前端界面之后,是没有进行连接的,所以登陆进去会出错
前端的端口号中有为8080,但是后端的端口号为8090,所以是不一样的端口号,需要进行跨域
通信的方式为ajax方式
在后端的代码中需要添加一个注解,代表是进行跨域的请求访问
@CrossOrigin //开放前端的跨域访问
具体打开vue的代码模块通过vscode这个软件
具体怎么安装可通过官网进行下载
一般新手添加三个插件比较好(chinese的中文包、vuehelper的帮助文档等)
一般如果不安装镜像进行下载的话,会通过国内的链接进行下载,一般会比较卡
所以再cmd命令行下面输入这两行命令
npm config set registry https://registry.npm.taobao.org
,npm install的安装都会经过淘宝的镜像地址进行下载
npm install -g @vue/cli
进行全局的安装脚手架
正常安装情况下:(出现warn不要怕,出现error要害怕)
如果不能安装可查看我这篇文章
出现npm ERR code EPERM npm ERR syscall mkdir npm ERR path B:\nodejs\node_global_cacache 的解决方法
vscode的快捷键:
crtl + ~
打开终端
ctrl + l
滚动终端到上面
新建一个项目 vue create 项目名
运行这个项目 npm run serve -- --port 端口号
第一个-- 代表是占位符,可以不用填写,第二个–是端口号
vue的页面组件:
具体简单的vue页面架构如下:
App.vue的页面内容主要有:
<template>
<div>
div>
template>
<script>
export default {
}
script>
设计的知识点有:
数据绑定,双向绑定等
简单界面
<template>
<div>
<h1>Vue案例h1>
<p>{{course}}p>
<p>
<input type="text" v-model="course">
p>
<p>
<button @click="toPay()">去支付button>
p>
div>
template>
<script>
export default {
// 定义数据
data () {
return {
course: '微信支付'
}
},
// 定义方法
methods: {
toPay(){
console.log('去支付')
}
}
}
script>
引入支付参数:商户号、app的密钥等
加载商户的密钥:非对称密钥的使用,公钥私钥的使用
获取平台证书和验签器:微信平台和网站的两个证书辨别
获取HttpClient对象:远程请求访问,建立远程连接
微信支付API v3的Apache HttpClient扩展,实现了请求签名的生成和应答签名的验证。
如果你是使用Apache HttpClient的商户开发者,可以使用它构造HttpClient。得到的HttpClient在执行请求时将自动携带身份认证信息,并检查应答的微信支付签名。
其java语言的api文档可查看
java api文档
具体如何加载的流程图可看这里
APIv3证书与密钥使用说明
配置yml
主要是配置微信的支付参数
具体关于微信支付的官网如下
微信支付官网
具体微信支付的参数有:
证书的序列号,就如同CA数字签名
发送:商户的私钥文件加载到应用程序中主要是为了签名
签收:之后发送给微信的服务器端,微信根据证书的序列号对应的证书,再通过公钥进行验签
APIV3的密钥主要是对称加密
APPID就是公众号分配的一个id
微信的服务器地址,向微信服务器端发送支付的请求
接收结果的通知,也要向本地服务器(网站)返回一个结果地址(内网穿透,不一样地址)
# 微信支付相关参数
# 商户号
wxpay.mch-id=手机号
# 商户API证书序列号
wxpay.mch-serial-no=34345964330B66427E0D3D28826C4993C77E631F
# 商户私钥文件
wxpay.private-key-path=apiclient_key.pem
# APIv3密钥
wxpay.api-v3-key=UDuLFDcmy5Eb6o0nTNZdu6ek4DDh4K8B
# APPID
wxpay.appid=wx74862e0dfcf69954
# 微信服务器地址
wxpay.domain=https://api.mch.weixin.qq.com
# 接收结果通知地址
# 注意:每次重新启动ngrok,都需要根据实际情况修改这个配置
wxpay.notify-domain=https://500c-219-143-130-12.ngrok.io
# APIv2密钥
wxpay.partnerKey: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb
配置类
弄一个配置类主要是为了和微信的支付参数一一对应
@Configuration
@PropertySource("classpath:wxpay.properties") //读取配置文件
@ConfigurationProperties(prefix="wxpay") //读取wxpay节点
@Data //使用set方法将wxpay节点中的值填充到当前类的属性中
@Slf4j
public class WxPayConfig {
// 商户号
private String mchId;
// 商户API证书序列号
private String mchSerialNo;
// 商户私钥文件
private String privateKeyPath;
// APIv3密钥
private String apiV3Key;
// APPID
private String appid;
// 微信服务器地址
private String domain;
// 接收结果通知地址
private String notifyDomain;
// APIv2密钥
private String partnerKey;
}
配置完的配置文件点击进去之后进不了页面
可以将其配置文件配置为spring的配置文件
添加相对应的依赖包
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
重启启动或者clean之后
可以通过配置文件的信息crtl+右键
进入其配置类
需要将其私钥文件引入
主要是为了签名,进行加密
具体api文档可查看
微信支付开发者文档
需要加入依赖包
<dependency>
<groupId>com.github.wechatpay-apiv3groupId>
<artifactId>wechatpay-apache-httpclientartifactId>
<version>0.3.0version>
dependency>
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
new FileInputStream("/path/to/apiclient_key.pem"));
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
new ByteArrayInputStream(privateKey.getBytes("utf-8")));
在具体的配置类中可以加入这个方法,之后通过测试调用测试是否可以加载
/**
* 获取商户的私钥文件
* @param filename
* @return
*/
private PrivateKey getPrivateKey(String filename){
try {
return PemUtil.loadPrivateKey(new FileInputStream(filename));
} catch (FileNotFoundException e) {
throw new RuntimeException("私钥文件不存在", e);
}
}
具体的测试类调用通过如下
@SpringBootTest
class PaymentDemoApplicationTests {
@Resource
private WxPayConfig wxPayConfig;
/**
* 获取商户的私钥
*/
@Test
void testGetPrivateKey() {
//获取私钥路径
String privateKeyPath = wxPayConfig.getPrivateKeyPath();
//获取私钥
PrivateKey privateKey = wxPayConfig.getPrivateKey(privateKeyPath);
System.out.println(privateKey);
}
}
通过商户的公钥(微信平台的证书,防止被造假)来验证签名
new WechatPay2Credentials(merchantId, new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)),
代码参数分别为
merchantId
商户idmerchantSerialNumber
商户序列号merchantPrivateKey
商户私钥具体在配置类中添加一个方法
具体实现为
一启动就自动实现,而且不需要人为,所以通过@bean
进行注入
使用定时的更新签名验证器还需要结合一个对称加密apiV3Key.getBytes(StandardCharsets.UTF_8)
/**
* 获取签名验证器
* @return
*/
@Bean
public ScheduledUpdateCertificatesVerifier getVerifier(){
log.info("获取签名验证器");
//获取商户私钥
PrivateKey privateKey = getPrivateKey(privateKeyPath);
//私钥签名对象
PrivateKeySigner privateKeySigner = new PrivateKeySigner(mchSerialNo, privateKey);
//身份认证对象
WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);
// 使用定时更新的签名验证器,不需要传入证书
ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(
wechatPay2Credentials,
apiV3Key.getBytes(StandardCharsets.UTF_8));
return verifier;
}
/**
* 获取http请求对象
* @param verifier
* @return
*/
@Bean(name = "wxPayClient")
public CloseableHttpClient getWxPayClient(ScheduledUpdateCertificatesVerifier verifier){
log.info("获取httpClient");
//获取商户私钥
PrivateKey privateKey = getPrivateKey(privateKeyPath);
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, privateKey)
.withValidator(new WechatPay2Validator(verifier));
// ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient
// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
CloseableHttpClient httpClient = builder.build();
return httpClient;
}
添加一个json的依赖包
因为微信支付的api v3 要使用json作为消息体的数据交互格式
<dependency>
<groupId>com.google.code.gsongroupId>
<artifactId>gsonartifactId>
dependency>
具体各个功能都需要调用API接口
具体的接口地址可以通过这个网址查询
Native下单API
所以弄了一个枚举的类
@Getter
,而没有setter
的注解@AllArgsConstructor
@Getter
public enum PayType {
/**
* 微信
*/
WXPAY("微信"),
/**
* 支付宝
*/
ALIPAY("支付宝");
/**
* 类型
*/
private final String type;
}
@AllArgsConstructor
@Getter
public enum OrderStatus {
/**
* 未支付
*/
NOTPAY("未支付"),
/**
* 支付成功
*/
SUCCESS("支付成功"),
/**
* 已关闭
*/
CLOSED("超时已关闭"),
/**
* 已取消
*/
CANCEL("用户已取消"),
/**
* 退款中
*/
REFUND_PROCESSING("退款中"),
/**
* 已退款
*/
REFUND_SUCCESS("已退款"),
/**
* 退款异常
*/
REFUND_ABNORMAL("退款异常");
/**
* 类型
*/
private final String type;
}
以上两部分都是用户和商户平台的枚举类
下面商户和微信的支付平台的枚举类
工具类
订单工具类
主要用于获取订单编号、获取退款单编号、以及获取编号
public class OrderNoUtils {
/**
* 获取订单编号
* @return
*/
public static String getOrderNo() {
return "ORDER_" + getNo();
}
/**
* 获取退款单编号
* @return
*/
public static String getRefundNo() {
return "REFUND_" + getNo();
}
/**
* 获取编号
* @return
*/
public static String getNo() {
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;
}
}
接收微信的http服务器之后响应服务器,这种叫回调通知
具体的通知都放到HttpServletRequest
public class HttpUtils {
/**
* 将通知参数转化为字符串
* @param request
* @return
*/
public static String readData(HttpServletRequest request) {
BufferedReader br = null;
try {
StringBuilder result = new StringBuilder();
br = request.getReader();
for (String line; (line = br.readLine()) != null; ) {
if (result.length() > 0) {
result.append("\n");
}
result.append(line);
}
return result.toString();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
具体的流程图如图所示
工作的流程为:
通过书写一个接口类
public interface WxPayService {
}
接口实现类
一定要写上@Service
注解
@Service
public class WxPayServiceImpl implements WxPayService {
}
业务逻辑类如下
@CrossOrigin //跨域
@RestController //json的数据传输
@RequestMapping("/api/wx-pay")
@Api(tags = "网站微信支付APIv3") //swagger网址可以看到
@Slf4j //日志的输出结果
public class WxPayController {
@Resource
private WxPayService wxPayService;
/**
* Native下单
* @param productId
* @return
* @throws Exception
*/
@ApiOperation("调用统一下单API,生成支付二维码")
@PostMapping("/native/{productId}")
public R nativePay(@PathVariable Long productId) throws Exception {
log.info("发起支付请求 v3");
//返回支付二维码连接和订单号
Map<String, Object> map = wxPayService.nativePay(productId);
return R.ok().setData(map);
}
}
业务逻辑类需要书写的方法需要通过接口类和实现类进行实时补充
顺便补充一下关于返回的值可以使用链式,R.ok().setData(map);
则需要在R这个类上使用链式 的注解类@Accessors(chain = true)
,之后所有的返回值都可以进行链式操作