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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 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);
    }
}
相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
3月前
|
Java 开发者 微服务
手写模拟Spring Boot自动配置功能
【11月更文挑战第19天】随着微服务架构的兴起,Spring Boot作为一种快速开发框架,因其简化了Spring应用的初始搭建和开发过程,受到了广大开发者的青睐。自动配置作为Spring Boot的核心特性之一,大大减少了手动配置的工作量,提高了开发效率。
76 0
|
3月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
155 62
|
2月前
|
XML 安全 Java
Spring Boot 怎么接入 Stripe 支付?
本文介绍了如何在 Spring Boot 应用中接入 Stripe 支付,提供了一个基本框架,并展示了处理 Webhook 的代码示例。通过捕获异常返回错误信息,成功处理事件则返回确认消息。作者 JustinNeil 在文中还提到可根据需求扩展更多功能,如订阅管理和优惠券应用等。
|
3月前
|
消息中间件 缓存 Java
手写模拟Spring Boot启动过程功能
【11月更文挑战第19天】Spring Boot自推出以来,因其简化了Spring应用的初始搭建和开发过程,迅速成为Java企业级应用开发的首选框架之一。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,帮助读者深入理解其工作机制。
60 3
|
3月前
|
前端开发 Java easyexcel
SpringBoot操作Excel实现单文件上传、多文件上传、下载、读取内容等功能
SpringBoot操作Excel实现单文件上传、多文件上传、下载、读取内容等功能
180 8
|
3月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
196 2
|
3月前
|
JSON Java API
springboot集成ElasticSearch使用completion实现补全功能
springboot集成ElasticSearch使用completion实现补全功能
56 1
|
前端开发 JavaScript Java
SpringBoot实现国际化i18n功能
SpringBoot实现国际化i18n功能
1123 0
SpringBoot实现国际化i18n功能
|
9天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue实现的留守儿童爱心网站设计与实现(计算机毕设项目实战+源码+文档)
博主是一位全网粉丝超过100万的CSDN特邀作者、博客专家,专注于Java、Python、PHP等技术领域。提供SpringBoot、Vue、HTML、Uniapp、PHP、Python、NodeJS、爬虫、数据可视化等技术服务,涵盖免费选题、功能设计、开题报告、论文辅导、答辩PPT等。系统采用SpringBoot后端框架和Vue前端框架,确保高效开发与良好用户体验。所有代码由博主亲自开发,并提供全程录音录屏讲解服务,保障学习效果。欢迎点赞、收藏、关注、评论,获取更多精品案例源码。
39 10
|
9天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue实现的家政服务管理平台设计与实现(计算机毕设项目实战+源码+文档)
面向大学生毕业选题、开题、任务书、程序设计开发、论文辅导提供一站式服务。主要服务:程序设计开发、代码修改、成品部署、支持定制、论文辅导,助力毕设!
29 8