第三方支付

发布时间:2025-12-10 11:32:06 浏览次数:3

1、请求学习中心服务创建选课记录

2、请求订单服务创建商品订单、生成支付二维码。

3、用户扫码请求订单支付服务,订单支付服务请求第三方支付平台生成支付订单。

4、前端唤起支付客户端,用户输入密码完成支付。

5、第三方支付平台支付完成发起支付通知。

6、订单支付服务接收第三方支付通知结果。

7、用户在前端查询支付结果,请求订单支付服务查询支付结果。

8、订单支付服务向学习中心服务通知支付结果。

9、学习中心服务收到支付结果,如果支付成功则更新选课记录,并添加到我的课程表。

 

 

微信JSAPI支付接口 

支付宝手机网站支付

1)用户在商户的H5网站下单支付后,商户系统按照手机网站支付接口alipay.trade.wap.payAPI的参数规范生成订单数据

2)前端页面通过Form表单的形式请求到支付宝。此时支付宝会自动将页面跳转至支付宝H5收银台页面,如果用户手机上安装了支付宝APP,则自动唤起支付宝APP。

3)输入支付密码完成支付。

4)用户在支付宝APP或H5收银台完成支付后,会根据商户在手机网站支付API中传入的前台回跳地址return_url自动跳转回商户页面,同时在URL请求中以Query String的形式附带上支付结果参数,详细回跳参数见“手机网站支付接口alipay.trade.wap.pay”前台回跳参数。

5)支付宝还会根据原始支付API中传入的异步通知地址notify_url,通过POST请求的形式将支付结果作为参数通知到商户系统,详情见支付结果异步通知。

 

 生成二维码

执行流程:

1、前端调用学习中心服务的添加选课接口。

2、添加选课成功请求订单服务生成支付二维码接口。

3、生成二维码接口:创建商品订单、生成支付交易记录、生成二维码。

4、将二维码返回到前端,用户扫码。

 订单表:记录订单信息

订单明细表记录订单的详细信息 

支付交易记录表记录每次支付的交易明细

 

订单号注意唯一性、安全性、尽量短等特点,生成方案常用的如下:

1、时间戳+随机数

年月日时分秒毫秒+随机数

2、高并发场景

年月日时分秒毫秒+随机数+redis自增序列

3、订单号中加上业务标识

订单号加上业务标识方便客服,比如:第10位是业务类型,第11位是用户类型等。

4、雪花算法

雪花算法是推特内部使用的分布式环境下的唯一ID生成算法,它基于时间戳生成,保证有序递增,加以入计算机硬件等元素,可以满足高并发环境下ID不重复。

本项目订单号生成采用雪花算法。

支付通知

学习中心服务:对收费课程选课需要支付,与订单服务对接完成支付。

学习资源服务:对收费的学习资料需要购买后下载,与订单服务对接完成支付。

订单服务完成支付后将支付结果发给每一个与订单服务对接的微服务,订单服务将消息发给交换机,由交换机广播消息,每个订阅消息的微服务都可以接收到支付结果.

微服务收到支付结果根据订单的类型去更新自己的业务数据。

使用消息队列进行异步通知需要保证消息的可靠性,即生产端将消息成功通知到消费端。

消息从生产端发送到消费端经历了如下过程:

1、消息发送到交换机

2、消息由交换机发送到队列

3、消息者收到消息进行处理

保证消息的可靠性需要保证以上过程的可靠性,本项目使用RabbitMQ可以通过如下方面保证消息的可靠性。

1、生产者确认机制

发送消息前使用数据库事务将消息保证到数据库表中

成功发送到交换机将消息从数据库中删除

2、mq持久化

mq收到消息进行持久化,当mq重启即使消息没有消费完也不会丢失。

需要配置交换机持久化、队列持久化、发送消息时设置持久化。

3、消费者确认机制

消费者消费成功自动发送ack,否则重试消费。

RabbitMQ搭建环境

1、首先在订单服务(消息生产方和消费方(学习服务))添加消息队列依赖

XML<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency>

2、在nacos配置rabbitmq-dev.yaml为通用配置文件

YAMLspring:rabbitmq:host: 192.168.101.65port: 5672username: guestpassword: guestvirtual-host: /publisher-confirm-type: correlated #correlated 异步回调,定义ConfirmCallback,MQ返回结果时会回调这个ConfirmCallbackpublisher-returns: false #开启publish-return功能,同样是基于callback机制,需要定义ReturnCallbacktemplate:mandatory: false #定义消息路由失败时的策略。true,则调用ReturnCallback;false:则直接丢弃消息listener:simple:acknowledge-mode: none #出现异常时返回unack,消息回滚到mq;没有异常,返回ack ,manual:手动控制,none:丢弃消息,不回滚到mqretry:enabled: true #开启消费者失败重试initial-interval: 1000ms #初识的失败等待时长为1秒multiplier: 1 #失败的等待时长倍数,下次等待时长 = multiplier * last-intervalmax-attempts: 3 #最大重试次数stateless: true #true无状态;false有状态。如果业务中包含事务,这里改为false

3、在订单服务接口工程引入rabbitmq-dev.yaml配置文件

YAMLshared-configs:- data-id: rabbitmq-${spring.profiles.active}.yamlgroup: xuecheng-plus-commonrefresh: true

4、在订单服务service工程编写MQ配置类,配置交换机

 

Javapackage com.xuecheng.orders.config;import com.alibaba.fastjson.JSON;import com.xuecheng.messagesdk.model.po.MqMessage;import com.xuecheng.messagesdk.service.MqMessageService;import lombok.extern.slf4j.Slf4j;import org.springframework.amqp.core.*;import org.springframework.amqp.rabbit.core.RabbitTemplate;import org.springframework.beans.BeansException;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/*** @author Mr.M* @version 1.0* @description TODO* @date 2023/2/23 16:59*/@Slf4j@Configurationpublic class PayNotifyConfig implements ApplicationContextAware {//交换机public static final String PAYNOTIFY_EXCHANGE_FANOUT = "paynotify_exchange_fanout";//支付结果通知消息类型public static final String MESSAGE_TYPE = "payresult_notify";//支付通知队列public static final String PAYNOTIFY_QUEUE = "paynotify_queue";//声明交换机,且持久化@Bean(PAYNOTIFY_EXCHANGE_FANOUT)public FanoutExchange paynotify_exchange_fanout() {// 三个参数:交换机名称、是否持久化、当没有queue与其绑定时是否自动删除return new FanoutExchange(PAYNOTIFY_EXCHANGE_FANOUT, true, false);}//支付通知队列,且持久化@Bean(PAYNOTIFY_QUEUE)public Queue course_publish_queue() {return QueueBuilder.durable(PAYNOTIFY_QUEUE).build();}//交换机和支付通知队列绑定@Beanpublic Binding binding_course_publish_queue(@Qualifier(PAYNOTIFY_QUEUE) Queue queue, @Qualifier(PAYNOTIFY_EXCHANGE_FANOUT) FanoutExchange exchange) {return BindingBuilder.bind(queue).to(exchange);}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {// 获取RabbitTemplateRabbitTemplate rabbitTemplate = applicationContext.getBean(RabbitTemplate.class);//消息处理serviceMqMessageService mqMessageService = applicationContext.getBean(MqMessageService.class);// 设置ReturnCallbackrabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {// 投递失败,记录日志log.info("消息发送失败,应答码{},原因{},交换机{},路由键{},消息{}",replyCode, replyText, exchange, routingKey, message.toString());MqMessage mqMessage = JSON.parseObject(message.toString(), MqMessage.class);//将消息再添加到消息表mqMessageService.addMessage(mqMessage.getMessageType(),mqMessage.getBusinessKey1(),mqMessage.getBusinessKey2(),mqMessage.getBusinessKey3());});}}

RabbitMQ发送消息

Java@Overridepublic void notifyPayResult(MqMessage message) {//1、消息体,转jsonString msg = JSON.toJSONString(message);//设置消息持久化Message msgObj = MessageBuilder.withBody(msg.getBytes(StandardCharsets.UTF_8)).setDeliveryMode(MessageDeliveryMode.PERSISTENT).build();// 2.全局唯一的消息ID,需要封装到CorrelationData中CorrelationData correlationData = new CorrelationData(message.getId().toString());// 3.添加callbackcorrelationData.getFuture().addCallback(result -> {if(result.isAck()){// 3.1.ack,消息成功log.debug("通知支付结果消息发送成功, ID:{}", correlationData.getId());//删除消息表中的记录mqMessageService.completed(message.getId());}else{// 3.2.nack,消息失败log.error("通知支付结果消息发送失败, ID:{}, 原因{}",correlationData.getId(), result.getReason());}},ex -> log.error("消息发送异常, ID:{}, 原因{}",correlationData.getId(),ex.getMessage()));// 发送消息rabbitTemplate.convertAndSend(PayNotifyConfig.PAYNOTIFY_EXCHANGE_FANOUT, "", msgObj,correlationData);} Java@Transactional@Overridepublic void saveAliPayStatus(PayStatusDto payStatusDto) {.......//保存消息记录,参数1:支付结果通知类型,2: 业务id,3:业务类型MqMessage mqMessage = mqMessageService.addMessage("payresult_notify", orders.getOutBusinessId(), orders.getOrderType(), null);//通知消息notifyPayResult(mqMessage);}}

RabbitMQ接收消息

Javapackage com.xuecheng.learning.service.impl;import com.alibaba.fastjson.JSON;import com.rabbitmq.client.Channel;import com.xuecheng.base.exception.XueChengPlusException;import com.xuecheng.learning.config.PayNotifyConfig;import com.xuecheng.learning.service.MyCourseTablesService;import com.xuecheng.messagesdk.model.po.MqMessage;import com.xuecheng.messagesdk.service.MqMessageService;import lombok.extern.slf4j.Slf4j;import org.springframework.amqp.core.Message;import org.springframework.amqp.rabbit.annotation.RabbitListener;import org.springframework.amqp.rabbit.core.RabbitTemplate;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.io.IOException;/*** @author Mr.M* @version 1.0* @description 接收支付结果* @date 2023/2/23 19:04*/@Slf4j@Servicepublic class ReceivePayNotifyService {@Autowiredprivate RabbitTemplate rabbitTemplate;@AutowiredMqMessageService mqMessageService;@AutowiredMyCourseTablesService myCourseTablesService;//监听消息队列接收支付结果通知@RabbitListener(queues = PayNotifyConfig.PAYNOTIFY_QUEUE)public void receive(Message message, Channel channel) {try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}//获取消息MqMessage mqMessage = JSON.parseObject(message.getBody(), MqMessage.class);log.debug("学习中心服务接收支付结果:{}", mqMessage);//消息类型String messageType = mqMessage.getMessageType();//订单类型,60201表示购买课程String businessKey2 = mqMessage.getBusinessKey2();//这里只处理支付结果通知if (PayNotifyConfig.MESSAGE_TYPE.equals(messageType) && "60201".equals(businessKey2)) {//选课记录idString choosecourseId = mqMessage.getBusinessKey1();//添加选课boolean b = myCourseTablesService.saveChooseCourseStauts(choosecourseId);if(!b){//添加选课失败,抛出异常,消息重回队列XueChengPlusException.cast("收到支付结果,添加选课失败");}}}}
需要做网站?需要网络推广?欢迎咨询客户经理 13272073477