JDK17+SpringCloud2023.0.3搭建企业级支付系统-预付卡支付交易微服务
JDK17+SpringCloud2023.0.3搭建企业级支付系统-预付卡支付交易微服务

在上一篇文章JDK17+SpringBoot3.4.0+Netty4.1.115搭建企业级支付系统POS网关中,我们基于JDK17 SpringBoot3.4.0、Netty4.1.115.Final搭建了企业级支付系统-POS网关,在开始今天系统搭建之前。我们先纠正下上一篇文章的一个小bug。目前Spring Cloud最新稳定版本为2023.0.3,最新稳定版本只支持SpringBoot3.4以下版本,为了使用最新稳定版本的SpringCloud我们将SpringBoot进行降级,最终版本为3.3.6,其它框架版本不变。具体的版本和使用SpringBoot3.4集成SpringCloud2023.0.3错误信息见下图
完整代码在文章最后,如果觉得本篇文章对你有用,记得点赞、关注、收藏哦。你的支持是我持续更新的动力!



项目整体结构

1 搭建预付卡支付交易微服务
1.1 创建pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.itbeien</groupId>
<artifactId>pay-labs-master</artifactId>
<version>1.0</version>
</parent>
<artifactId>pay-spring-cloud-alibaba</artifactId>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
</dependencies>
</project>
1.2 application.properties
创建应用配置属性文件,该文件主要信息为连接Nacos注册配置中心的信息。下图中的信息为Nacos的地址、用户名和密码、命名空间和group信息、应用的注册名称和应用端口
#spring.config.import=nacos://192.168.0.250:8848
spring.application.name=ecard
spring.cloud.nacos.discovery.server-addr=192.168.0.250:8848
spring.cloud.nacos.username=itbeien
spring.cloud.nacos.password=itbeien
spring.cloud.nacos.discovery.namespace=819c1589-ca79-4fdd-b585-9a8086876df4
spring.cloud.nacos.discovery.group=dtpay
#server.port=9080
server.port=9081
1.3 创建启动类
该类目前添加两个注解即可@SpringBootApplication和@EnableDiscoveryClient,声明为Springboot应用和启用服务注册与发现组件功能
package cn.itbeien.pay;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author itbeien
* 项目网站:https://www.itbeien.cn
* 公众号:贝恩聊架构
* 全网同名,欢迎小伙伴们关注
*/
@SpringBootApplication
@EnableDiscoveryClient
public class PayApplication {
public static void main(String[] args) {
SpringApplication.run(PayApplication.class,args);
}
}
1.4 启动应用
我们在这一步启动应用,看是否能正常注册到注册中心Nacos,下图为启动和注册成功日志信息

接下来我们在Nacos管理后台,查看服务是否按照我们在属性文件中的配置信息要求进行注册

1.4.1 注册两个支付交易微服务
为了在下面的流程中测试支付前置系统到支付交易微服务的负载均衡功能,我们在idea中设置允许启动多个实例



按照上图进行设置后,我们在application.properties配置文件中修改端口后,点击debug或者run进行实例的启动,这时候Nacos注册中心已经注册成功两个支付微服务


2 网关系统调用支付微服务
2.1 网关系统核心配置和代码
2.1.1 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.itbeien</groupId>
<artifactId>pay-labs-master</artifactId>
<version>1.0</version>
</parent>
<artifactId>posp-gateway</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
<!-- <dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
</dependencies>
</project>
2.1.2 项目配置信息
下面的配置信息中有几个重要的地方,一个是连接Nacos的配置信息(用于从注册中心发现支付交易微服务),第二个信息为tcp/ip端口8600,该端口用于和POS智能硬件终端通信。
#spring.config.import=nacos://192.168.0.250:8848
spring.application.name=posp-gateway
spring.cloud.nacos.discovery.server-addr=192.168.0.250:8848
spring.cloud.nacos.username=itbeien
spring.cloud.nacos.password=itbeien
spring.cloud.nacos.discovery.namespace=819c1589-ca79-4fdd-b585-9a8086876df4
spring.cloud.nacos.discovery.group=dtpay
server.port=8080
spring.pay.application.name=ecard
netty.port=8600
netty.url=127.0.0.1
2.1.3 POS前置和智能终端通信
package cn.itbeien;
import cn.itbeien.socket.NettyServer;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* SpringBoot+Netty启动类
* @author itbeien
* 项目网站:https://www.itbeien.cn
* 公众号:贝恩聊架构
* 全网同名,欢迎小伙伴们关注
* Copyright© 2024 itbeien
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients //用于创建Feign代理实现类->动态代理
public class GatewayApplication implements CommandLineRunner {
@Autowired
private NettyServer nettyServer;
private static Logger logger = LoggerFactory.getLogger(GatewayApplication.class);
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
logger.info("NettyServer启动服务开始 port:8600");
Future<Channel> future = Executors.newFixedThreadPool(2).submit(nettyServer);
Channel channel = future.get();
if (null == channel) throw new RuntimeException("netty server start error channel is null");
while (!channel.isActive()) {
logger.info("NettyServer启动服务 ...");
Thread.sleep(500);
}
logger.info("NettyServer启动服务完成 {}", channel.localAddress());
}
}
2.1.4 POS前置调用支付微服务
2.1.4.1 使用RestTemplate调用
package cn.itbeien.service.impl;
import cn.itbeien.enums.ServiceInstanceEnum;
import cn.itbeien.service.IPayConsumerService;
import cn.itbeien.service.feign.PayFeign;
import cn.itbeien.vo.PaymentVO;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import jakarta.annotation.Resource;
/**
* @author itbeien
* 项目网站:https://www.itbeien.cn
* 公众号:贝恩聊架构
* 全网同名,欢迎小伙伴们关注
* 从服务注册中心发现pay服务并进行消费调用
* Copyright© 2024 itbeien
*/
@Service
@Slf4j
public class PayConsumerService implements IPayConsumerService {
@Resource
private LoadBalancerClient loadBalancerClient;
//@Resource
private RestTemplate restTemplate;
@Value("${spring.pay.application.name}")
private String appName;
@Autowired
private PayFeign payFeign;
@Override
public String callPayService(JSONObject jsonObject) {
String result = null;
//使用 LoadBalanceClient 和 RestTemplate 结合的方式来访问
//ServiceInstance serviceInstance = loadBalancerClient.choose(appName);
//String url = String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort());
String url = String.format("http://%s",appName);
url += ServiceInstanceEnum.PayServiceURL.getCode();
log.info("request url:{}",url);
//PaymentVO paymentVO = new PaymentVO();
//对象转成JSONObject
//JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(paymentVO));
result = restTemplate.postForObject(url,jsonObject,String.class);
return result;
}
@Override
public String callFeignPayService(JSONObject jsonObject) {
return payFeign.orderFromClient(jsonObject);
}
//实例化 RestTemplate 实例
@Bean
@LoadBalanced //负载均衡
public RestTemplate restTemplate(){
this.restTemplate = new RestTemplate();
return restTemplate;
}
}
2.1.4.2 使用OpenFeign调用
package cn.itbeien.service.feign;
import com.alibaba.fastjson2.JSONObject;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @author itbeien
* 项目网站:https://www.itbeien.cn
* 公众号:贝恩聊架构
* 全网同名,欢迎小伙伴们关注
*/
@FeignClient(value = "ecard")
public interface PayFeign {
@RequestMapping(value = "/api/pay",method = RequestMethod.POST)
String orderFromClient(JSONObject jsonObject);
}
2.2 启动POS前置系统

3 POS硬件终端调用POSP前置系统
我们今天用代码来模拟硬件调用POSP前置系统
3.1 核心调用代码
package cn.itbeien.client;
import cn.itbeien.client.socket.NettyClient;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import jakarta.annotation.Resource;
import java.util.concurrent.*;
/**
* @author itbeien
* 项目网站:https://www.itbeien.cn
* 公众号:贝恩聊架构
* 全网同名,欢迎小伙伴们关注
* Copyright© 2024 itbeien
*/
@SpringBootApplication
public class ClientApplication implements CommandLineRunner {
@Resource
private NettyClient nettyClient;
private static Logger logger = LoggerFactory.getLogger(ClientApplication.class);
private static ExecutorService executorService = Executors.newFixedThreadPool(2);
private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
// 1. 启动socket连接
logger.info("NettyClient连接服务开始 host:{} port:{}", "127.0.0.1", 8600);
Future<Channel> future = executorService.submit(nettyClient);
Channel channel = future.get();
if (null == channel) throw new RuntimeException("netty client start error channel is null");
while (!nettyClient.isActive()) {
logger.info("NettyClient启动服务 ...");
Thread.sleep(500);
}
logger.info("NettyClient连接服务完成 {}", channel.localAddress());
logger.info("通信管道巡检:通信管道状态 " + nettyClient.isActive());
String sendMsg = "{\"amount\":\"100.00\",\"interfaceCode\":\"pay\",\"merchantCode\":\"1000001\",\"payType\":\"js\"}";
//sendMsg(channel, "pos发起支付交易");
sendMsg(channel, sendMsg);//发送支付交易指令
// Channel状态定时巡检;3秒后每5秒执行一次 模拟硬件和软件重连
scheduledExecutorService.scheduleAtFixedRate(() -> {
while (!nettyClient.isActive()) {//判断是否和服务器连接上
logger.info("通信管道巡检:通信管道状态 " + nettyClient.isActive());
try {
logger.info("通信管道巡检:断线重连[Begin]");
Channel freshChannel = executorService.submit(nettyClient).get();
freshChannel.writeAndFlush("abc");
} catch (InterruptedException | ExecutionException e) {
logger.error("通信管道巡检:断线重连[Error]");
}
}
}, 3, 5, TimeUnit.SECONDS);
}
public void sendMsg(Channel channel, String msg) {
try {
logger.info("通信管道发送消息:[Begin]");
//Channel channel = executorService.submit(nettyClient).get();
channel.writeAndFlush(msg);
logger.info("通信管道发送消息:[End]");
// Channel状态定时巡检;60秒后每60秒执行一次
scheduledExecutorService.scheduleAtFixedRate(() -> {
logger.info("通信管道模拟游客支付:通信管道状态 " + nettyClient.isActive());
try {
logger.info("通信管道模拟游客支付:[Begin]");
channel.writeAndFlush(msg);
logger.info("通信管道模拟游客支付:[End]");
} catch (Exception e) {
logger.error("通信管道模拟游客支付:异常[Error]");
}
}, 3, 30, TimeUnit.SECONDS);
} catch (Exception e) {
logger.error("通信管道模拟游客支付:异常");
}
}
}
3.2 启动POS客户端
下图为POS智能终端向POSP前置系统发起支付交易指令

下图为POSP前置系统接收到支付指令,并从注册中心发现支付交易微服务

最后支付交易微服务接收到POSP前置系统进行支付业务处理


欢迎大家关注我的项目实战内容itbeien.cn,一起学习一起进步,在项目和业务中理解各种技术。

欢迎沟通交流技术和支付业务,一起探讨聚合支付/预付卡系统业务、技术、系统架构、微服务、容器化。并结合聚合支付系统深入技术框架/微服务原理及分布式事务原理。加入我的知识星球吧

跟着我学微服务系列
01跟着我学微服务,什么是微服务?微服务有哪些主流解决方案?
05SpringCloudAlibaba之图文搞懂微服务核心组件在企业级支付系统中的应用
06JDK17+SpringBoot3.4.0+Netty4.1.115搭建企业级支付系统POS网关
贝恩聊架构-项目实战地址
4 源码地址
跟着我学微服务-基于企业级支付项目系列文章、资料和源代码会同步到以下地址,代码和资料每周都会同步更新
该仓库地址主要用于基于企业级支付系统,学习微服务整体技术栈