Spring Boot + MyBatis 集成支付宝支付流程

news2025/6/10 17:24:35

Spring Boot + MyBatis 集成支付宝支付流程

核心流程
  1. 商户系统生成订单
  2. 调用支付宝创建预支付订单
  3. 用户跳转支付宝完成支付
  4. 支付宝异步通知支付结果
  5. 商户处理支付结果更新订单状态
  6. 支付宝同步跳转回商户页面

代码实现示例(电脑网站支付)

1. 添加依赖
<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- MyBatis & MySQL -->
    <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>
    </dependency>
    
    <!-- 支付宝SDK -->
    <dependency>
        <groupId>com.alipay.sdk</groupId>
        <artifactId>alipay-sdk-java</artifactId>
        <version>4.35.0.ALL</version>
    </dependency>
    
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>
2. 支付宝配置类
@Configuration
public class AlipayConfig {

    @Value("${alipay.app_id}")
    private String appId;
    
    @Value("${alipay.merchant_private_key}")
    private String merchantPrivateKey;
    
    @Value("${alipay.alipay_public_key}")
    private String alipayPublicKey;
    
    @Value("${alipay.notify_url}")
    private String notifyUrl;
    
    @Value("${alipay.return_url}")
    private String returnUrl;
    
    @Value("${alipay.gateway_url}")
    private String gatewayUrl;
    
    @Value("${alipay.sign_type}")
    private String signType;
    
    @Value("${alipay.charset}")
    private String charset;

    @Bean
    public AlipayClient alipayClient() {
        return new DefaultAlipayClient(
            gatewayUrl,
            appId,
            merchantPrivateKey,
            "json",
            charset,
            alipayPublicKey,
            signType
        );
    }
}
3. 实体类和Mapper
// 订单实体
@Data
public class Order {
    private Long id;
    private String orderNo;   // 商户订单号
    private BigDecimal amount;// 支付金额
    private Integer status;   // 0-待支付, 1-已支付
    private LocalDateTime createTime;
}

// MyBatis Mapper
@Mapper
public interface OrderMapper {
    @Insert("INSERT INTO orders(order_no, amount, status, create_time) " +
            "VALUES(#{orderNo}, #{amount}, 0, NOW())")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    void insert(Order order);
    
    @Update("UPDATE orders SET status = #{status} WHERE order_no = #{orderNo}")
    void updateStatus(@Param("orderNo") String orderNo, @Param("status") int status);
}
4. 支付服务类
@Service
public class PayService {

    @Autowired private AlipayClient alipayClient;
    @Autowired private OrderMapper orderMapper;
    @Value("${alipay.return_url}") private String returnUrl;
    @Value("${alipay.notify_url}") private String notifyUrl;

    // 创建支付订单
    public String createPayOrder(Order order) throws AlipayApiException {
        orderMapper.insert(order); // 保存订单到数据库
        
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        request.setReturnUrl(returnUrl);
        request.setNotifyUrl(notifyUrl);
        
        // 构造业务参数
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", order.getOrderNo());
        bizContent.put("total_amount", order.getAmount());
        bizContent.put("subject", "商品支付");
        bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY"); // 电脑网站支付
        
        request.setBizContent(bizContent.toString());
        
        return alipayClient.pageExecute(request).getBody();
    }

    // 处理异步通知
    public boolean handleNotify(Map<String, String> params) throws AlipayApiException {
        // 1. 验证签名
        boolean signVerified = AlipaySignature.rsaCheckV1(
                params,
                alipayPublicKey,  // 注入支付宝公钥
                charset,
                signType
        );
        
        if (!signVerified) return false;

        // 2. 验证交易状态
        String tradeStatus = params.get("trade_status");
        if (!"TRADE_SUCCESS".equals(tradeStatus)) return false;

        // 3. 更新订单状态
        String orderNo = params.get("out_trade_no");
        orderMapper.updateStatus(orderNo, 1); // 更新为已支付
        
        return true;
    }
}
5. 控制器
@RestController
@RequestMapping("/pay")
public class PayController {

    @Autowired private PayService payService;

    // 创建支付订单
    @PostMapping("/create")
    public String createOrder(@RequestParam BigDecimal amount) 
        throws AlipayApiException {
        
        Order order = new Order();
        order.setOrderNo(UUID.randomUUID().toString().replace("-", ""));
        order.setAmount(amount);
        
        return payService.createPayOrder(order); // 返回支付页面表单
    }

    // 支付宝异步通知(需公网可访问)
    @PostMapping("/notify")
    public String alipayNotify(HttpServletRequest request) 
        throws UnsupportedEncodingException, AlipayApiException {
        
        Map<String, String> params = parseRequestParams(request);
        boolean success = payService.handleNotify(params);
        return success ? "success" : "failure"; // 通知支付宝处理结果
    }

    // 支付宝同步跳转
    @GetMapping("/return")
    public String alipayReturn(HttpServletRequest request) {
        // 简单展示支付结果(实际需要验签和状态检查)
        return "支付完成!订单号:" + request.getParameter("out_trade_no");
    }

    // 解析请求参数
    private Map<String, String> parseRequestParams(HttpServletRequest request) {
        // 转换参数Map(参考支付宝示例代码)
    }
}
6. 配置文件
# application.properties
# 支付宝配置
alipay.app_id=2021000123456789
alipay.merchant_private_key=MIIEvQIBADANB...
alipay.alipay_public_key=MIIBIjANBgkq...
alipay.notify_url=http://your-domain.com/pay/notify
alipay.return_url=http://your-domain.com/pay/return
alipay.gateway_url=https://openapi.alipay.com/gateway.do
alipay.sign_type=RSA2
alipay.charset=UTF-8

# MySQL配置
spring.datasource.url=jdbc:mysql://localhost:3306/alipay_demo
spring.datasource.username=root
spring.datasource.password=123456

关键流程说明

  1. 生成商户订单

    • 生成唯一订单号(推荐雪花算法)
    • 保存订单到数据库(状态=待支付)
  2. 调用支付宝接口

    • 使用 AlipayTradePagePayRequest 构建请求
    • 关键参数:订单号、金额、支付标题、回调地址
  3. 接收异步通知

    • 必须验证签名(防止伪造请求)
    • 检查 trade_status 是否为 TRADE_SUCCESS
    • 更新订单状态(注意处理幂等性)
  4. 安全注意事项

    • 支付金额需与订单金额比对(防止金额篡改)
    • 敏感操作记录日志
    • 异步通知处理需要保证幂等性
  5. 扩展功能

    • 订单超时关闭:定时任务扫描未支付订单
    • 支付结果查询:通过 trade_no 调用支付宝查询接口
    • 退款功能:使用 AlipayTradeRefundRequest

提示:实际开发中需要:

  1. 替换为正式支付宝账户
  2. 配置公网可访问的域名
  3. 添加完整的错误处理
  4. 实现参数解析工具方法
  5. 添加数据库事务管理

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

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

相关文章

云安全与网络安全:核心区别与协同作用解析

在数字化转型的浪潮中&#xff0c;云安全与网络安全作为信息安全的两大支柱&#xff0c;常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异&#xff0c;并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全&#xff1a;聚焦于保…

ui框架-文件列表展示

ui框架-文件列表展示 介绍 UI框架的文件列表展示组件&#xff0c;可以展示文件夹&#xff0c;支持列表展示和图标展示模式。组件提供了丰富的功能和可配置选项&#xff0c;适用于文件管理、文件上传等场景。 功能特性 支持列表模式和网格模式的切换展示支持文件和文件夹的层…

QT开发技术【ffmpeg + QAudioOutput】音乐播放器

一、 介绍 使用ffmpeg 4.2.2 在数字化浪潮席卷全球的当下&#xff0c;音视频内容犹如璀璨繁星&#xff0c;点亮了人们的生活与工作。从短视频平台上令人捧腹的搞笑视频&#xff0c;到在线课堂中知识渊博的专家授课&#xff0c;再到影视平台上扣人心弦的高清大片&#xff0c;音…

算术操作符与类型转换:从基础到精通

目录 前言&#xff1a;从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符&#xff1a;、-、*、/、% 赋值操作符&#xff1a;和复合赋值 单⽬操作符&#xff1a;、--、、- 前言&#xff1a;从基础到实践——探索运算符与类型转换的奥秘 在先前的文…

jdbc查询mysql数据库时,出现id顺序错误的情况

我在repository中的查询语句如下所示&#xff0c;即传入一个List<intager>的数据&#xff0c;返回这些id的问题列表。但是由于数据库查询时ID列表的顺序与预期不一致&#xff0c;会导致返回的id是从小到大排列的&#xff0c;但我不希望这样。 Query("SELECT NEW com…

sshd代码修改banner

sshd服务连接之后会收到字符串&#xff1a; SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢&#xff1f; 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头&#xff0c…

前端开发者常用网站

Can I use网站&#xff1a;一个查询网页技术兼容性的网站 一个查询网页技术兼容性的网站Can I use&#xff1a;Can I use... Support tables for HTML5, CSS3, etc (查询浏览器对HTML5的支持情况) 权威网站&#xff1a;MDN JavaScript权威网站&#xff1a;JavaScript | MDN

如何在Windows本机安装Python并确保与Python.NET兼容

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

快速排序算法改进:随机快排-荷兰国旗划分详解

随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…

篇章二 论坛系统——系统设计

目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…

32单片机——基本定时器

STM32F103有众多的定时器&#xff0c;其中包括2个基本定时器&#xff08;TIM6和TIM7&#xff09;、4个通用定时器&#xff08;TIM2~TIM5&#xff09;、2个高级控制定时器&#xff08;TIM1和TIM8&#xff09;&#xff0c;这些定时器彼此完全独立&#xff0c;不共享任何资源 1、定…

热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁

赛门铁克威胁猎手团队最新报告披露&#xff0c;数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据&#xff0c;严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能&#xff0c;但SEMR…

Matlab实现任意伪彩色图像可视化显示

Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中&#xff0c;如何展示好看的实验结果图像非常重要&#xff01;&#xff01;&#xff01; 1、灰度原始图像 灰度图像每个像素点只有一个数值&#xff0c;代表该点的​​亮度&#xff08;或…

图解JavaScript原型:原型链及其分析 | JavaScript图解

​​ 忽略该图的细节&#xff08;如内存地址值没有用二进制&#xff09; 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么&#xff1a;保存在堆中一块区域&#xff0c;同时在栈中有一块区域保存其在堆中的地址&#xff08;也就是我们通常说的该变量指向谁&…

《信号与系统》第 6 章 信号与系统的时域和频域特性

目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …

一些实用的chrome扩展0x01

简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序&#xff0c;无论是测试应用程序、搜寻漏洞还是收集情报&#xff0c;它们都能提升工作流程。 FoxyProxy 代理管理工具&#xff0c;此扩展简化了使用代理&#xff08;如 Burp…

AxureRP-Pro-Beta-Setup_114413.exe (6.0.0.2887)

Name&#xff1a;3ddown Serial&#xff1a;FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq7w1RH97k5MWctqVHA 注册用户名&#xff1a;Axure 序列号&#xff1a;8t3Yk/zu4cX601/seX6wBZgYRVj/lkC2PICCdO4sFKCCLx8mcCnccoylVb40lP

02.运算符

目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&&#xff1a;逻辑与 ||&#xff1a;逻辑或 &#xff01;&#xff1a;逻辑非 短路求值 位运算符 按位与&&#xff1a; 按位或 | 按位取反~ …

uni-app学习笔记三十五--扩展组件的安装和使用

由于内置组件不能满足日常开发需要&#xff0c;uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件&#xff0c;需要安装才能使用。 一、安装扩展插件 安装方法&#xff1a; 1.访问uniapp官方文档组件部分&#xff1a;组件使用的入门教程 | uni-app官网 点击左侧…

6.9-QT模拟计算器

源码: 头文件: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMouseEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);…