Springboot实现支付宝沙箱支付功能(上)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: Springboot实现支付宝沙箱支付功能(上)

我们在做开发时很多时候会涉及到支付功能,常见会对接支付宝和微信支付,本文将对JAVA对接支付宝进行详细的讲解。


前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站点击跳转浏览。

在进行开发编码之前我们首选需要去申请开发所需要的支付宝一些配置,即支付宝沙盒环境的申请、


  1. 支付宝沙盒环境申请
    1.1 注册
    登录支付宝开发者网站 https://developers.alipay.com/ ,使用个人的支付宝扫码登录后,填写个人信息并提交
    1.2 沙盒环境设置
    按如下图所示进入沙盒环境设置页面





打开页面 https://opendocs.alipay.com/open/291/105971 下载公私钥生成工具,安装后生成应用的公钥和私钥



生成公私钥如下图



将公私钥保存好,后面程序中要用到,同时将公钥复制到沙盒环境的设置中,如下图

保存会生成一个支付宝的公钥,这个公钥用于后续的支付宝支付后的回调验签使用

至此。沙盒环境设置完毕。

  1. 开发部分

我们模拟一个简单的订单支付功能,只有一个简单的金额字段

2.1 数据库表结构


CREATE TABLE `user_order_t` (
  `order_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单Id',
  `user_id` varchar(64) DEFAULT NULL COMMENT '用户Id',
  `order_no` varchar(64) NOT NULL COMMENT '订单号',
  `order_amount` decimal(16,4) NOT NULL COMMENT '订单金额',
  `order_status` int(11) NOT NULL COMMENT '0 待付款 1 已付款 -1 已取消',
  `create_time` varchar(32) NOT NULL COMMENT '创建时间',
  `last_update_time` varchar(32) NOT NULL COMMENT '最后修改时间',
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4;

2.2 项目结构

新建一个springboot项目,结构如图


2.3 pom依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.devin</groupId>
    <artifactId>alipay_demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>alipay_demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.0.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <version>2.0.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.17</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.22</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
            <version>2.1.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.57</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.5</version>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.10</version>
        </dependency>
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>3.4.27.ALL</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>maven-ali</id>
            <url>http://maven.aliyun.com/nexus/content/groups/public//</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
                <updatePolicy>always</updatePolicy>
                <checksumPolicy>fail</checksumPolicy>
            </snapshots>
        </repository>
    </repositories>
</project>

2.4 模型的定义


2.4.1 订单model  
package com.devin.alipay_demo.entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.math.BigDecimal;
/**
 * @author Devin Zhang
 * @className UserOrder
 * @description TODO
 * @date 2020/5/12 10:49
 */
@Table(name = "user_order_t")
public class UserOrder {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Integer orderId;
    private String orderNo;
    private String userId;
    private BigDecimal orderAmount;
    private Integer orderStatus;
    private String createTime;
    private String lastUpdateTime;
    public Integer getOrderId() {
        return orderId;
    }
    public void setOrderId(Integer orderId) {
        this.orderId = orderId;
    }
    public String getOrderNo() {
        return orderNo;
    }
    public void setOrderNo(String orderNo) {
        this.orderNo = orderNo;
    }
    public String getUserId() {
        return userId;
    }
    public void setUserId(String userId) {
        this.userId = userId;
    }
    public BigDecimal getOrderAmount() {
        return orderAmount;
    }
    public void setOrderAmount(BigDecimal orderAmount) {
        this.orderAmount = orderAmount;
    }
    public Integer getOrderStatus() {
        return orderStatus;
    }
    public void setOrderStatus(Integer orderStatus) {
        this.orderStatus = orderStatus;
    }
    public String getCreateTime() {
        return createTime;
    }
    public void setCreateTime(String createTime) {
        this.createTime = createTime;
    }
    public String getLastUpdateTime() {
        return lastUpdateTime;
    }
    public void setLastUpdateTime(String lastUpdateTime) {
        this.lastUpdateTime = lastUpdateTime;
    }
}

2.4.2 支付宝调用参数封装model


package com.devin.alipay_demo.dto;
/**
 * @author Devin Zhang
 * @className AlipayBean
 * @description TODO
 * @date 2020/5/12 11:33
 */
public class AlipayBean {
    /**
     * 商户订单号,必填
     */
    private String out_trade_no;
    /**
     * 订单名称,必填
     */
    private String subject;
    /**
     * 付款金额,必填
     * 根据支付宝接口协议,必须使用下划线
     */
    private String total_amount;
    /**
     * 商品描述,可空
     */
    private String body;
    /**
     * 超时时间参数
     */
    private String timeout_express = "10m";
    /**
     * 产品编号
     */
    private String product_code = "FAST_INSTANT_TRADE_PAY";
    public String getOut_trade_no() {
        return out_trade_no;
    }
    public void setOut_trade_no(String out_trade_no) {
        this.out_trade_no = out_trade_no;
    }
    public String getSubject() {
        return subject;
    }
    public void setSubject(String subject) {
        this.subject = subject;
    }
    public String getTotal_amount() {
        return total_amount;
    }
    public void setTotal_amount(String total_amount) {
        this.total_amount = total_amount;
    }
    public String getBody() {
        return body;
    }
    public void setBody(String body) {
        this.body = body;
    }
    public String getTimeout_express() {
        return timeout_express;
    }
    public void setTimeout_express(String timeout_express) {
        this.timeout_express = timeout_express;
    }
    public String getProduct_code() {
        return product_code;
    }
    public void setProduct_code(String product_code) {
        this.product_code = product_code;
    }
}

2.5 工具类

2.5.1 支付宝参数读取

package com.devin.alipay_demo.util;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
 * @author Devin Zhang
 * @className AlipayProperties
 * @description 读取支付宝配置
 * @date 2020/5/12 11:16
 */
@Component
public class AlipayProperties {
    private static final String APP_ID = "appId";
    private static final String PRIVATE_KEY = "privateKey";
    private static final String PUBLIC_KEY = "publicKey";
    private static final String NOTIFY_URL = "notifyUrl";
    private static final String RETURN_URL = "returnUrl";
    private static final String SIGN_TYPE = "signType";
    private static final String CHARSET = "charset";
    private static final String GATEWAY_URL = "gatewayUrl";
    private static final String LOG_PATH = "logPath";
    /**
     * 保存加载配置参数
     */
    private static Map<String, String> propertiesMap = new HashMap<>();
    /**
     * 加载属性
     */
    @PostConstruct
    public void loadProperties() throws Exception {
        // 获得PathMatchingResourcePatternResolver对象
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        try {
            // 加载resource文件(也可以加载resources)
            Resource resources = resolver.getResource("classpath:config/alipay.properties");
            PropertiesFactoryBean config = new PropertiesFactoryBean();
            config.setLocation(resources);
            config.afterPropertiesSet();
            Properties prop = config.getObject();
            // 循环遍历所有得键值对并且存入集合
            for (String key : prop.stringPropertyNames()) {
                propertiesMap.put(key, (String) prop.get(key));
            }
        } catch (Exception e) {
            throw new Exception("配置文件加载失败");
        }
    }
    public String getAppId() {
        return propertiesMap.get(APP_ID);
    }
    public String getPrivateKey() {
        return propertiesMap.get(PRIVATE_KEY);
    }
    public String getPublicKey() {
        return propertiesMap.get(PUBLIC_KEY);
    }
    public String getNotifyUrl() {
        return propertiesMap.get(NOTIFY_URL);
    }
    public String getReturnUrl() {
        return propertiesMap.get(RETURN_URL);
    }
    public String getSignType() {
        return propertiesMap.get(SIGN_TYPE);
    }
    public String getCharset() {
        return propertiesMap.get(CHARSET);
    }
    public String getGatewayUrl() {
        return propertiesMap.get(GATEWAY_URL);
    }
    public String getLogPath() {
        return propertiesMap.get(LOG_PATH);
    }
}

2.5.2 支付宝工具类

package com.devin.alipay_demo.util;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.devin.alipay_demo.dto.AlipayBean;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
 * @author Devin Zhang
 * @className AliPayUtil
 * @description TODO
 * @date 2020/5/12 10:47
 */
@Component
public class AliPayUtil {
    @Resource
    private AlipayProperties alipayProperties;
    /**
     * 支付接口
     *
     * @param alipayBean 封装的支付宝入参
     * @return 返回支付结果
     * @throws AlipayApiException 抛出异常
     */
    public String pay(AlipayBean alipayBean) throws AlipayApiException {
        // 1、获得初始化的AlipayClient
        String serverUrl = alipayProperties.getGatewayUrl();
        String appId = alipayProperties.getAppId();
        String privateKey = alipayProperties.getPrivateKey();
        String format = "json";
        String charset = alipayProperties.getCharset();
        String alipayPublicKey = alipayProperties.getPublicKey();
        String signType = alipayProperties.getSignType();
        String returnUrl = alipayProperties.getReturnUrl();
        String notifyUrl = alipayProperties.getNotifyUrl();
        AlipayClient alipayClient = new DefaultAlipayClient(serverUrl, appId, privateKey, format, charset, alipayPublicKey, signType);
        // 2、设置请求参数
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        // 页面跳转同步通知页面路径
        alipayRequest.setReturnUrl(returnUrl);
        // 服务器异步通知页面路径
        alipayRequest.setNotifyUrl(notifyUrl);
        // 封装参数
        alipayRequest.setBizContent(JSON.toJSONString(alipayBean));
        // 3、请求支付宝进行付款,并获取支付结果
        return alipayClient.pageExecute(alipayRequest).getBody();
    }
}

2.5.3 订单状态枚举

package com.devin.alipay_demo.util;
/**
 * @author Devin Zhang
 * @className OrderEnum
 * @description TODO
 * @date 2020/5/12 11:43
 */
public enum OrderEnum {
    ORDER_STATUS_PAID(1, "已支付"),
    ORDER_STATUS_NOT_PAY(0, "待支付"),
    ORDER_STATUS_CANCEL(2, "已取消");
    private int status;
    private String statusName;
    OrderEnum(int status, String statusName) {
        this.status = status;
        this.statusName = statusName;
    }
    public int getStatus(){
        return this.status;
    }
}

2.6 dao

2.6.1 订单操作dao


因为使用了通用mapper tkmybatis,所以dao只是实现了相应的父类


package com.devin.alipay_demo.dao;
import com.devin.alipay_demo.entity.UserOrder;
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.MySqlMapper;
/**
 * @author Devin Zhang
 * @className UserOrderMapper
 * @description TODO
 * @date 2020/5/12 10:54
 */
public interface UserOrderMapper extends Mapper<UserOrder>, MySqlMapper<UserOrder> {
}

2.6.2 对应的mapper xml文件

同样,xml文件中内容也为空


1 <?xml version="1.0" encoding="UTF-8" ?>
2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
3 <mapper namespace="com.devin.alipay_demo.dao.UserOrderMapper" >
4 
5 </mapper>

2.7 service

service中定义的下单等方法,其中下单时回去调用支付宝
package com.devin.alipay_demo.service;
import com.alipay.api.AlipayApiException;
import com.devin.alipay_demo.dao.UserOrderMapper;
import com.devin.alipay_demo.dto.AlipayBean;
import com.devin.alipay_demo.entity.UserOrder;
import com.devin.alipay_demo.util.AliPayUtil;
import com.devin.alipay_demo.util.OrderEnum;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.UUID;
/**
 * @author Devin Zhang
 * @className OrderService
 * @description TODO
 * @date 2020/5/12 10:53
 */
@Service
public class OrderService {
    @Resource
    private UserOrderMapper userOrderMapper;
    @Resource
    private AliPayUtil aliPayUtil;
    /**
     * 下单
     *
     * @param orderAmount 订单金额
     * @return 返回支付结果页面内容
     * @throws AlipayApiException
     */
    public String orderPay(BigDecimal orderAmount) throws AlipayApiException {
        //1. 产生订单
        UserOrder order = new UserOrder();
        order.setOrderNo(System.currentTimeMillis() + "");
        order.setUserId(UUID.randomUUID().toString());
        order.setOrderAmount(orderAmount);
        order.setOrderStatus(OrderEnum.ORDER_STATUS_NOT_PAY.getStatus());
        String format = "yyyy-MM-dd HH:mm:ss";
        order.setCreateTime(DateFormatUtils.format(new Date(), format));
        order.setLastUpdateTime(DateFormatUtils.format(new Date(), format));
        userOrderMapper.insert(order);
        //2. 调用支付宝
        AlipayBean alipayBean = new AlipayBean();
        alipayBean.setOut_trade_no(order.getOrderNo());
        alipayBean.setSubject("充值:" + order.getOrderAmount());
        alipayBean.setTotal_amount(orderAmount.toString());
        String pay = aliPayUtil.pay(alipayBean);
        System.out.println("pay:" + pay);
        return pay;
    }
    /**
     * 根据订单号查询订单
     *
     * @param orderNo
     * @return 返回订单信息
     */
    public UserOrder getOrderByOrderNo(String orderNo) {
        Example example = new Example(UserOrder.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("orderNo", orderNo);
        return userOrderMapper.selectOneByExample(example);
    }
    /**
     * 更新订单
     *
     * @param userOrder 订单对象
     * @return 返回更新结果
     */
    public int updateOrder(UserOrder userOrder) {
        return userOrderMapper.updateByPrimaryKeySelective(userOrder);
    }
}
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
Java 关系型数据库 MySQL
创建一个SpringBoot项目,实现简单的CRUD功能和分页查询
【9月更文挑战第6天】该内容介绍如何使用 Spring Boot 实现具备 CRUD 功能及分页查询的项目。首先通过 Spring Initializr 创建项目并选择所需依赖;其次配置数据库连接,并创建实体类与数据访问层;接着构建服务层处理业务逻辑;最后创建控制器处理 HTTP 请求。分页查询可通过添加 URL 参数实现。
|
8天前
|
存储 前端开发 Java
Spring Boot 集成 MinIO 与 KKFile 实现文件预览功能
本文详细介绍如何在Spring Boot项目中集成MinIO对象存储系统与KKFileView文件预览工具,实现文件上传及在线预览功能。首先搭建MinIO服务器,并在Spring Boot中配置MinIO SDK进行文件管理;接着通过KKFileView提供文件预览服务,最终实现文档管理系统的高效文件处理能力。
|
4天前
|
缓存 NoSQL Java
Springboot自定义注解+aop实现redis自动清除缓存功能
通过上述步骤,我们不仅实现了一个高度灵活的缓存管理机制,还保证了代码的整洁与可维护性。自定义注解与AOP的结合,让缓存清除逻辑与业务逻辑分离,便于未来的扩展和修改。这种设计模式非常适合需要频繁更新缓存的应用场景,大大提高了开发效率和系统的响应速度。
18 2
|
9天前
|
前端开发 Java easyexcel
SpringBoot操作Excel实现单文件上传、多文件上传、下载、读取内容等功能
SpringBoot操作Excel实现单文件上传、多文件上传、下载、读取内容等功能
15 6
|
1月前
|
NoSQL 前端开发 Java
使用 Spring Boot + Neo4j 实现知识图谱功能开发
在数据驱动的时代,知识图谱作为一种强大的信息组织方式,正逐渐在各个领域展现出其独特的价值。本文将围绕使用Spring Boot结合Neo4j图数据库来实现知识图谱功能开发的技术细节进行分享,帮助读者理解并掌握这一技术栈在实际项目中的应用。
108 4
|
2月前
|
机器学习/深度学习 文字识别 前端开发
基于 Spring Boot 3.3 + OCR 实现图片转文字功能
【8月更文挑战第30天】在当今数字化信息时代,图像中的文字信息越来越重要。无论是文档扫描、名片识别,还是车辆牌照识别,OCR(Optical Character Recognition,光学字符识别)技术都发挥着关键作用。本文将围绕如何使用Spring Boot 3.3结合OCR技术,实现图片转文字的功能,分享工作学习中的技术干货。
80 2
|
2月前
|
安全 Java 应用服务中间件
如何在 Spring Boot 3.3 中实现请求 IP 白名单拦截功能
【8月更文挑战第30天】在构建Web应用时,确保应用的安全性是至关重要的。其中,对访问者的IP地址进行限制是一种常见的安全措施,特别是通过实施IP白名单策略,可以只允许特定的IP地址或IP段访问应用,从而有效防止未授权的访问。在Spring Boot 3.3中,我们可以通过多种方式实现这一功能,下面将详细介绍几种实用的方法。
95 1
|
14天前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的服装商城管理系统
基于Java+Springboot+Vue开发的服装商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的服装商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
45 2
基于Java+Springboot+Vue开发的服装商城管理系统
|
14天前
|
前端开发 JavaScript Java
SpringBoot项目部署打包好的React、Vue项目刷新报错404
本文讨论了在SpringBoot项目中部署React或Vue打包好的前端项目时,刷新页面导致404错误的问题,并提供了两种解决方案:一是在SpringBoot启动类中配置错误页面重定向到index.html,二是将前端路由改为hash模式以避免刷新问题。
66 1
|
12天前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的大学竞赛报名管理系统
基于Java+Springboot+Vue开发的大学竞赛报名管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的大学竞赛报名管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
29 3
基于Java+Springboot+Vue开发的大学竞赛报名管理系统