电商+支付双系统项目------怎么用代码实现支付系统?

前言

上篇文章讲了我们应该怎么设计支付系统,构建了一个支付系统的蓝图,这篇文章我来讲一下怎么用代码来设计支付系统。当然,我肯定不是手把手的教你哈哈哈,我只是把支付系统的核心的部分展示给大家看,让大家了解一下原来用代码是这样子做的!

项目目录简述

整个支付系统都放在一个叫pay的大文件夹里,其中pay大文件夹中有很多小文件夹。

config文件夹是用来对微信和支付宝账户进行配置的;

Dao层是用来与数据库打交道的;

service层是用来实现支付的业务逻辑的;

controller层是用来对数据进行组装;

pojo文件夹里面的PayInfo 是一个用于表示支付信息的类,它包含了一系列的属性;

enums文件夹中的定义了一个枚举类PayPlatformEnum,用于表示支付平台的类型。其中包含两个枚举值 ALIPAY 和 WX,分别代表支付宝和微信支付平台;

mappers文件夹是将一个数据模型或对象转换为另一个数据模型或对象。它们通常用于处理不同层或组件之间的数据映射和转换;

templates文件夹里面的两个文件的作用是将支付链接转成对应的二维码页面。

关键文件夹

Dao层

我们在代码中建立一个Dao文件夹,用于存放Dao层的所有内容,这个Dao文件夹就一个文件:PayInfoMapper,文件的内容如下所示:

package com.imooc.pay.dao;import com.imooc.pay.pojo.PayInfo;
/***这行代码是用于导入 com.imooc.pay.pojo.PayInfo 类。*通过这个导入语句,您可以在当前的代码文件中使用 PayInfo 类。* PayInfo 类 其实就是一些参数数据而已*/public interface PayInfoMapper {//以下这些方法提供了对支付信息的常见数据库操作,包括插入、查询、更新和删除。您可以根据具体的业务需求选择适合的方法来操作支付信息数据int deleteByPrimaryKey(Integer id);// 根据主键删除支付信息。该方法根据传入的主键值,在数据库中找到对应的支付信息,并将其删除int insert(PayInfo record);// 插入支付信息。该方法将传入的支付信息对象插入到数据库中int insertSelective(PayInfo record);// 有选择地插入支付信息。该方法只插入支付信息对象中非空的属性到数据库中,对于空属性的字段不进行插入PayInfo selectByPrimaryKey(Integer id);// 根据主键查询支付信息。该方法根据传入的主键值,在数据库中找到对应的支付信息,并返回查询到的支付信息对象int updateByPrimaryKeySelective(PayInfo record);// 有选择地更新支付信息。该方法根据传入的支付信息对象中非空的属性值,对数据库中对应的支付信息进行更新int updateByPrimaryKey(PayInfo record);// 更新支付信息。该方法将传入的支付信息对象的所有属性值更新到数据库中对应的支付信息PayInfo selectByOrderNo(Long orderNo);// 根据订单号查询支付信息。该方法根据传入的订单号,在数据库中找到对应的支付信息,并返回查询到的支付信息对象
}

通过定义这些方法,我们就可以使用PayInfoMapper接口来执行与支付信息相关的数据库操作。

关于接口的具体实现,通常是由持久化框架(比如Mybatis)来完成。

service层

有一个接口,这个接口定义了支付服务的核心功能(业务逻辑):创建支付,处理异步通知和查询支付记录

//在这段代码中,IPayService 是一个接口,用于定义支付服务相关的方法package com.imooc.pay.service;import com.imooc.pay.pojo.PayInfo;
import com.lly835.bestpay.enums.BestPayTypeEnum;
import com.lly835.bestpay.model.PayResponse;import java.math.BigDecimal;public interface IPayService {//以下的这些方法定义了支付服务的核心功能,包括创建支付、处理异步通知和查询支付记录。通过实现这些方法,您可以具体实现支付服务的业务逻辑/**创建/发起支付:该方法用于根据订单号、支付金额和支付类型发起支付请求,并返回支付的响应结果*/PayResponse create(String orderId, BigDecimal amount, BestPayTypeEnum bestPayTypeEnum);/*** 异步通知处理:* 该方法用于处理支付异步通知的数据,通常在支付平台支付成功后,支付平台会发送异步通知,该方法负责处理这个异步通知的数据* @param notifyData*/String asyncNotify(String notifyData);/*** 查询支付记录(通过订单号):该方法根据订单号查询支付记录,返回支付信息对象* @param orderId* @return*/PayInfo queryByOrderId(String orderId);
}

接下来是实现接口中的功能:

package com.imooc.pay.service.impl;//import导入包这些就省掉了,太长不写进来@Slf4j    
@Service  // @Service 注解表示该类是一个服务类public class PayServiceImpl implements IPayService {private final static String QUEUE_PAY_NOTIFY = "payNotify";//@Autowired 注解用于依赖注入@Autowiredprivate BestPayService bestPayService;@Autowiredprivate PayInfoMapper payInfoMapper;@Autowiredprivate AmqpTemplate amqpTemplate;/*** 创建/发起支付:* 在该方法中,首先将支付信息写入数据库,* 然后使用 BestPayService 发起支付请求,* 并返回支付的响应结果** @param orderId* @param amount*/@Overridepublic PayResponse create(String orderId, BigDecimal amount, BestPayTypeEnum bestPayTypeEnum) {//写入数据库PayInfo payInfo = new PayInfo(Long.parseLong(orderId),PayPlatformEnum.getByBestPayTypeEnum(bestPayTypeEnum).getCode(),OrderStatusEnum.NOTPAY.name(),amount);payInfoMapper.insertSelective(payInfo);PayRequest request = new PayRequest();request.setOrderName("4559066-最好的支付sdk");request.setOrderId(orderId);request.setOrderAmount(amount.doubleValue());request.setPayTypeEnum(bestPayTypeEnum);PayResponse response = bestPayService.pay(request);log.info("发起支付 response={}", response);return response;}/*** 异步通知处理:在方法内部,首先进行签名检验,* 然后查询数据库中的订单信息,并校验订单金额。* 如果校验通过,将修改订单支付状态,并通过消息队列发送支付信息。* 最后,根据支付平台的不同,返回相应的响应结果** @param notifyData*/@Overridepublic String asyncNotify(String notifyData) {//1. 签名检验PayResponse payResponse = bestPayService.asyncNotify(notifyData);log.info("异步通知 response={}", payResponse);//2. 金额校验(从数据库查订单)//比较严重(正常情况下是不会发生的)发出告警:钉钉、短信PayInfo payInfo = payInfoMapper.selectByOrderNo(Long.parseLong(payResponse.getOrderId()));if (payInfo == null) {//告警throw new RuntimeException("通过orderNo查询到的结果是null");}//如果订单支付状态不是"已支付"if (!payInfo.getPlatformStatus().equals(OrderStatusEnum.SUCCESS.name())) {//Double类型比较大小,精度。1.00  1.0if (payInfo.getPayAmount().compareTo(BigDecimal.valueOf(payResponse.getOrderAmount())) != 0) {//告警throw new RuntimeException("异步通知中的金额和数据库里的不一致,orderNo=" + payResponse.getOrderId());}//3. 修改订单支付状态payInfo.setPlatformStatus(OrderStatusEnum.SUCCESS.name());payInfo.setPlatformNumber(payResponse.getOutTradeNo());payInfoMapper.updateByPrimaryKeySelective(payInfo);}//TODO pay发送MQ消息,mall接受MQ消息amqpTemplate.convertAndSend(QUEUE_PAY_NOTIFY, new Gson().toJson(payInfo));if (payResponse.getPayPlatformEnum() == BestPayPlatformEnum.WX) {//4. 告诉微信不要再通知了return "<xml>\n" +"  <return_code><![CDATA[SUCCESS]]></return_code>\n" +"  <return_msg><![CDATA[OK]]></return_msg>\n" +"</xml>";}else if (payResponse.getPayPlatformEnum() == BestPayPlatformEnum.ALIPAY) {return "success";}throw new RuntimeException("异步通知中错误的支付平台");}@Overridepublic PayInfo queryByOrderId(String orderId) {return payInfoMapper.selectByOrderNo(Long.parseLong(orderId));}
}

我会详细的讲讲创建/发起支付,处理异步通知和查询支付记录的意思。

创建/发起支付

首先支付平台会接受到用户的订单信息,然后将订单信息集成为一个对象,之后后端服务器会调用支付接口(调用接口其实就是让支付平台给用户一个二维码,让用户扫码支付,二维码中的内容就是一个链接,那要生成二维码需要哪个接口呢?其实这个接口名字叫:统一下单接口。在调用这个API时,要传递一些参数给这个API,所以说之前要将订单信息 集成为一个对象,原因就是这样!)

异步通知处理

首先,签名检验。对异步通知数据进行签名验证,确保通知的合法性;

其次,金额校验。从数据库中查询订单信息,并与异步通知中的订单金额进行比较校验。如果订单不存在,会触发告警并抛出异常。如果订单支付状态不是"已支付",还会比较订单金额与异步通知中的金额是否一致。如果金额不一致,同样会触发告警并抛出异常;

再而,修改订单支付状态。如果经过校验后,订单支付状态为非"已支付",则将订单的支付状态修改为"已支付",并更新支付平台的交易号;

接着,发送MQ消息。向消息队列(MQ)发送支付信息,以便其他模块(如mall接受MQ消息)可以进行相应的处理;

最后,返回不同支付平台的响应。如果是微信支付(BestPayPlatformEnum.WX),返回一个XML格式的成功响应。如果是支付宝支付(BestPayPlatformEnum.ALIPAY),返回字符串"success"。如果支付平台枚举不匹配任何已知支付平台,则抛出异常。

查询支付记录(通过订单号)

根据订单号查询数据库中的支付信息,并将查询结果(支付信息对象,比如支付状态、支付金额、订单号、支付时间等)返回

后记

这篇文章讲了支付系统的内部代码的实现,重点讲了关于service层业务逻辑。我们这个项目叫:电商+支付双系统项目,那么支付系统讲完了,接下来下一篇文章讲一下电商系统需要怎么设计!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/690024.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C 语言 devc++ 使用 winsock 实现 windows UDP 利用 IP 进行局域网发送消息

UDP 通信流程_udp通信过程-CSDN博客参考来源 UDP 通信流程_udp通信过程-CSDN博客 这里移植到windows 上 &#xff0c;使用 devc 开发。 服务端代码 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <…

【python】深入探索使用Matplotlib中的plt.legend()添加图例

当我们绘制复杂的图表&#xff0c;尤其是包含多个数据系列的图表时&#xff0c;一个清晰、易读的图例是至关重要的。plt.legend()函数是Matplotlib库中用于添加和定制图例的关键工具。在本篇博文中&#xff0c;我们将深入探讨plt.legend()的功能、用法以及如何通过它提升图表的…

ChatGLM3:打造更智能、更安全的代码解释器和工具使用体验

ChatGLM3 是由智谱AI训练的第三代大型语言模型&#xff0c;它不仅能理解和生成人类语言&#xff0c;还能执行代码、调用工具&#xff0c;并以 markdown 格式进行响应。为了提高用户体验&#xff0c;同时避免用户输入的注入攻击&#xff0c;ChatGLM3 采用了全新的对话格式。下载…

Qt的基本操作

文章目录 1. Qt Hello World 程序1.1 通过图形化界面的方式1.2 通过代码的方式实现 2. Qt 的编码问题3. 使用输入框实现hello world4. 使用按钮实现hello world5. Qt 编程注意事项6. 查询文档的方式7. 认识Qt坐标系 1. Qt Hello World 程序 1.1 通过图形化界面的方式 我们先讲…

Vue3中 状态管理器 ( Pinia ) 详解及使用

传送门&#xff1a; Vue中 状态管理器&#xff08;vuex&#xff09;详解及应用场景 传送门&#xff1a;Pinia 中文文档 注意&#xff1a;本文项目使用脚手架为 Vite&#xff1b; 1. 前言 Pinia 对比 Vuex Pinia 同时支持 Vue2 以及 Vue3 &#xff0c;这让同时使用两个版本的…

现货白银投资热度推升因子

白银作为一种贵金属&#xff0c;其投资价值会受到多种因素的影响。在一些特殊的情况下&#xff0c;现货白银作为具有高杠杆的白银替代投资方式&#xff0c;会成为热门的投资工具&#xff0c;大受市场的追捧&#xff0c;本文将为大家介绍一下相关情况。 通常当全球的经济面临不确…

【Linux】进程间通信——共享内存

文章目录 共享内存的概要创建共享内存shmget()参数keyshmget()参数sizeshmget()参数shmflg 删除共享内存挂载共享内存去关联 共享内存的概要 共享内存允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间传递数据的一种非常有效的方式。不同进程之间…

【ArcGIS Pro二次开发】(81):玩个花活_控规指标块生成

一、要实现的效果 废话不多说&#xff0c;这次要实现的是类似控规指标块的标注&#xff1a; 这里只是示例&#xff0c;用了5个格子&#xff0c;做成9个格子也是可以的。 实现这个效果最关键的是要用到Pro中的复合标注。 关于复合标注的用法可以搜一下帮助里的【使用复合注释…

【区块链技术开发语言】在ubuntu18 系统环境下命令操作安装GO语言开发环境

要在Ubuntu 18系统上安装GO语言开发环境,您可以按照以下步骤进行: 打开终端(Ctrl + Alt + T)。 使用以下命令下载GO语言安装包: 或者手动打开链接下载: wget https://golang.org/dl/go1.17.5.linux-amd64.tar.gz确保替换链接中的版本号为最新版本。 解压下载的安装包…

yolov8源码解读Detect层

yolov8源码解读Detect层 Detect层解读网络各层解读及detect层后的处理 关于网络的backbone,head&#xff0c;以及detect层后处理&#xff0c;可以参考文章结尾博主的文章。 Detect层解读 先贴一下全部代码,下面一一解读。 class Detect(nn.Module):"""YOLOv8 …

【EndNote20】Endnote20和word的一些操作

文章目录 前言一、如何导入参考文献到EndNote201.1.在谷歌学术或知网上下载文献1.2.将下载好的文件导入EndNote20(可批量导入)1.3.书籍如何导入 二、Word中加入参考文献 前言 做毕设时学习了EndNote20的一些使用方法&#xff0c;并在此慢慢做汇总。 一、如何导入参考文献到End…

【python】python入门之输入与进入交互模式的方法

目录 一次性输入&#xff08;进入交互模式&#xff09;&#xff1a; 输入函数&#xff1a; 一次性输入&#xff08;进入交互模式&#xff09;&#xff1a; 交互模式介绍&#xff1a;写一行读一行&#xff0c;不用print也可以显示出来 &#xff08;当进行某些一次性计算或者纠错…

在石家庄有哪家券商证券公司可以免费开量化软件Ptrade、QMT

在石家庄有少数证券公司可以免费开量化软件QMT、Ptrade&#xff0c;如国金证券、广发证券等&#xff0c;之前要100万才可开通&#xff0c;现在只需满足资金50万的条件即可免费办理使用&#xff0c;详情找客户经理孙经理咨询。 证券佣金低价标准是“成本价”&#xff0c;默认佣金…

多线程——

目录 一、为什么要有多线程&#xff1f; 1、线程与进程 2、多线程的应用场景 3、小结​编辑 二、多线程中的两个概念&#xff08;并发和并行&#xff09; 1、并发 2、并行 3、小结 三、多线程的三种实现方式 1、继承Thread类的方式进行实现 2、实现Runnable接口的方…

为什么有的代理IP速度比较慢?

“为什么有的IP代理速度比较慢&#xff1f;”随着数字化时代的不断发展&#xff0c;代理服务成为了许多网络操作的关键环节。然而&#xff0c;有时我们可能会遇到IP代理速度慢的问题&#xff0c;这可能会对我们的网络操作产生影响。让我们一起揭开这个谜团&#xff0c;探寻其中…

EMQX Enterprise 5.4 发布:OpenTelemetry 分布式追踪、OCPP 网关、Confluent 集成支持

EMQX Enterprise 5.4.0 版本已正式发布&#xff01; 新版本提供 OpenTelemetry 分布式追踪与日志集成功能&#xff0c;新增了开放充电协议 OCPP 协议接入能力&#xff0c;并为数据集成添加了 Confluent 支持。此外&#xff0c;新版本还进行了多项改进以及 BUG 修复&#xff0c…

AI提示工程实战:从零开始利用提示工程学习应用大语言模型【文末送书-19】

文章目录 背景什么是提示工程&#xff1f;从零开始&#xff1a;准备工作设计提示调用大语言模型 实际应用示例文字创作助手代码生成持续优化与迭代数据隐私与安全性可解释性与透明度总结 AI提示工程实战&#xff1a;从零开始利用提示工程学习应用大语言模型【文末送书-19】⛳粉…

SpringMVC 的参数绑定之list集合、Map

标签中name属性的值就是pojo类的属性名 参数绑定4 list [对象] <form action"teaupd.do" method"post"> <c:forEach items"${list}" var"tea" varStatus "status"> 教师编号&#xff1a;<input…

希尔排序算法

目录 ShellSort希尔排序 整体思路 图解分析 【1】预排序 单组排序 多组并排 【2】直接插入排序 关于gap取值 总代码实现 时间复杂度 ShellSort希尔排序 希尔排序法又称缩小增量法。 希尔排序法的基本思想是&#xff1a;先选定一个整数&#xff0c;把待排序文件中所有…

Vue3快速上手(七) ref和reactive对比

一、ref和reactive对比 表格形式更加直观吧&#xff1a; 项目refreactive是否支持基本类型支持不支持是否支持对象类型支持支持对象类型是否支持属性直接赋值不支持&#xff0c;需要.value支持是否支持直接重新分配对象支持&#xff0c;因为操作的.value不支持&#xff0c;需…