Spring Boot 对接支付宝支付的详细流程和步骤

以下是 Spring Boot 对接支付宝支付的详细流程和步骤,以​​电脑网站支付(即时到账)​​场景为例,涵盖从环境准备到生产环境部署的全流程。

​一、前期准备​

1. 注册支付宝开放平台账号

  • 访问 支付宝开放平台,使用支付宝企业/个人账号登录(企业账号需完成实名认证)。
  • 进入「开发者中心」→「应用管理」→「创建应用」,选择「电脑网站支付」场景,填写应用信息(如应用名称、官网链接等),提交审核(一般1-3个工作日)。

2. 配置应用密钥

支付宝使用 RSA/RSA2 加密验证请求合法性,需为应用生成密钥对:

  • ​生成密钥工具​ ​:使用支付宝提供的 密钥生成工具 或 OpenSSL 命令。

    csharp 复制代码
    # 生成 RSA2 私钥(2048位)
    openssl genrsa -out app_private_key.pem 2048
    # 生成公钥(用于上传到支付宝)
    openssl rsa -in app_private_key.pem -pubout -out app_public_key.pem
  • ​上传公钥到支付宝​ ​:在开放平台应用的「开发设置」→「应用公钥」中,粘贴生成的 app_public_key.pem 内容并保存。

  • ​获取支付宝公钥​​:在应用「开发设置」→「支付宝公钥」中查看(支付宝会为你生成对应的公钥,用于验证其回调)。

3. 配置应用网关(回调地址)

在应用「开发设置」→「网页&移动应用」→「电脑网站支付」中,配置「返回 URL」(支付成功后用户跳转的页面)和「异步通知地址」(支付宝服务器主动推送支付结果的 URL,需为公网可访问的地址,如 https://your-domain.com/api/alipay/notify)。

​二、Spring Boot 项目搭建​

1. 创建 Spring Boot 项目

使用 IDEA 或 Spring Initializr 创建项目,添加以下依赖(pom.xml):

xml 复制代码
<dependencies>
    <!-- Spring Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- 支付宝 SDK -->
    <dependency>
        <groupId>com.alipay.sdk</groupId>
        <artifactId>alipay-sdk-java</artifactId>
        <version>4.24.1.ALL</version> <!-- 最新版本 -->
    </dependency>
    <!-- 工具类(可选,如 JSON 解析) -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>2.0.31</version>
    </dependency>
</dependencies>

2. 配置支付宝参数

application.yml 中配置支付宝相关参数(替换为你的实际值):

yaml 复制代码
alipay:
  appId: 202100xxxxxxxxxx  # 开放平台审核通过的应用 APPID
  gatewayUrl: https://openapi.alipay.com/gateway.do  # 生产环境网关;沙箱环境为 https://openapi.alipaydev.com/gateway.do
  privateKey: |  # 应用的私钥(格式需为 PKCS8,且去掉头尾的 -----BEGIN PRIVATE KEY----- 等注释)
    MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
  alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...  # 支付宝公钥(从开放平台获取)
  charset: UTF-8
  signType: RSA2  # 推荐 RSA2
  notifyUrl: https://your-domain.com/api/alipay/notify  # 异步通知地址(需与开放平台配置一致)
  returnUrl: https://your-domain.com/pay/success  # 同步返回地址(用户支付后跳转)

​三、核心代码实现​

1. 支付宝配置类

创建 AlipayConfig 类,加载配置并初始化支付宝客户端:

kotlin 复制代码
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AlipayConfig {

    @Value("${alipay.appId}")
    private String appId;

    @Value("${alipay.gatewayUrl}")
    private String gatewayUrl;

    @Value("${alipay.privateKey}")
    private String privateKey;

    @Value("${alipay.alipayPublicKey}")
    private String alipayPublicKey;

    @Value("${alipay.charset}")
    private String charset;

    @Value("${alipay.signType}")
    private String signType;

    @Bean
    public AlipayClient alipayClient() {
        return new DefaultAlipayClient(
                gatewayUrl,
                appId,
                privateKey,
                "json",
                charset,
                alipayPublicKey,
                signType
        );
    }
}

2. 支付服务类(生成支付链接)

创建 AlipayService 类,封装支付请求逻辑:

typescript 复制代码
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.response.AlipayTradePagePayResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class AlipayService {

    @Autowired
    private AlipayClient alipayClient;

    @Value("${alipay.returnUrl}")
    private String returnUrl;

    @Value("${alipay.notifyUrl}")
    private String notifyUrl;

    /**
     * 生成支付宝支付链接(电脑网站支付)
     */
    public String createPayLink(String orderId, String subject, String totalAmount) throws AlipayApiException {
        // 构造请求模型
        AlipayTradePagePayModel model = new AlipayTradePagePayModel();
        model.setOutTradeNo(orderId);  // 商户订单号(需唯一)
        model.setSubject(subject);    // 商品标题
        model.setTotalAmount(totalAmount);  // 支付金额(单位:元,保留两位小数)
        model.setProductCode("FAST_INSTANT_TRADE_PAY");  // 销售产品码(固定值)

        // 构造请求对象
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        request.setReturnUrl(returnUrl);       // 同步返回地址
        request.setNotifyUrl(notifyUrl);       // 异步通知地址
        request.setBizModel(model);

        // 发送请求并获取响应
        AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
        if (response.isSuccess()) {
            // 成功则返回支付宝支付页面的 URL(前端需要跳转到此 URL 完成支付)
            return response.getBody();
        } else {
            throw new RuntimeException("支付宝支付请求失败:" + response.getMsg());
        }
    }
}

3. 支付控制器(接收前端请求)

创建 AlipayController 类,提供支付入口接口:

less 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AlipayController {

    @Autowired
    private AlipayService alipayService;

    /**
     * 前端调用此接口生成支付链接,跳转到支付宝页面
     */
    @GetMapping("/api/pay/create")
    public String createPay(
            @RequestParam("orderId") String orderId,
            @RequestParam("subject") String subject,
            @RequestParam("amount") String amount
    ) {
        try {
            String payUrl = alipayService.createPayLink(orderId, subject, amount);
            // 重定向到支付宝支付页面(或返回 URL 给前端跳转)
            return "redirect:" + payUrl;
        } catch (Exception e) {
            e.printStackTrace();
            return "支付异常:" + e.getMessage();
        }
    }
}

4. 处理异步通知(关键)

支付宝支付完成后,会向 notifyUrl 发送 POST 请求通知支付结果(需验证签名以确保真实性)。创建 AlipayNotifyController 处理:

kotlin 复制代码
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api/alipay")
public class AlipayNotifyController {

    @Value("${alipay.alipayPublicKey}")
    private String alipayPublicKey;

    @Value("${alipay.charset}")
    private String charset;

    @Value("${alipay.signType}")
    private String signType;

    /**
     * 处理支付宝异步通知
     */
    @PostMapping("/notify")
    public String handleNotify(HttpServletRequest request) {
        // 1. 获取支付宝回调的参数(包括签名)
        Map<String, String> params = new HashMap<>();
        Map<String, String[]> requestParams = request.getParameterMap();
        for (String name : requestParams.keySet()) {
            String[] values = requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            params.put(name, valueStr);
        }

        // 2. 验证签名(防止伪造请求)
        boolean signVerified = false;
        try {
            signVerified = AlipaySignature.rsaCheckV1(
                    params,
                    alipayPublicKey,
                    charset,
                    signType
            );
        } catch (AlipayApiException e) {
            e.printStackTrace();
            return "failure";  // 签名验证失败,返回 failure 给支付宝
        }

        // 3. 验证业务参数(如交易状态、金额等)
        if (signVerified) {
            String tradeStatus = params.get("trade_status");
            String outTradeNo = params.get("out_trade_no");  // 商户订单号
            String tradeNo = params.get("trade_no");          // 支付宝交易号
            String totalAmount = params.get("total_amount");  // 实际支付金额

            // 业务逻辑:更新订单状态(需保证幂等性,避免重复处理)
            if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {
                // 调用你的订单服务,标记订单为已支付
                boolean updateSuccess = yourOrderService.updateOrderStatus(outTradeNo, tradeNo, totalAmount);
                if (updateSuccess) {
                    return "success";  // 通知支付宝处理成功,不再重复通知
                } else {
                    return "failure";  // 业务处理失败,支付宝会重试(最多8次)
                }
            }
        }

        return "failure";
    }
}

5. 处理同步返回(可选)

用户支付完成后,支付宝会将用户重定向到 returnUrl,可在该页面显示支付结果(需再次验证签名,避免伪造):

typescript 复制代码
// 在 Controller 中添加同步返回处理接口
@GetMapping("/pay/success")
public String paySuccess(HttpServletRequest request) {
    // 验证同步返回的签名(逻辑类似异步通知)
    Map<String, String> params = new HashMap<>();
    Map<String, String[]> requestParams = request.getParameterMap();
    for (String name : requestParams.keySet()) {
        params.put(name, request.getParameter(name));
    }

    boolean signVerified = false;
    try {
        signVerified = AlipaySignature.rsaCheckV1(
                params,
                alipayPublicKey,
                charset,
                signType
        );
    } catch (AlipayApiException e) {
        e.printStackTrace();
        return "支付结果验证失败";
    }

    if (signVerified) {
        String outTradeNo = params.get("out_trade_no");
        String tradeNo = params.get("trade_no");
        // 查询订单状态(可选,确保同步返回的准确性)
        String orderStatus = yourOrderService.getOrderStatus(outTradeNo);
        if ("PAID".equals(orderStatus)) {
            return "支付成功!订单号:" + outTradeNo + ",支付宝交易号:" + tradeNo;
        } else {
            return "支付结果异常,请联系客服";
        }
    } else {
        return "非法请求,签名验证失败";
    }
}

​四、测试与上线​

1. 沙箱环境测试

  • 支付宝提供 沙箱环境 用于测试,需在沙箱开放平台重新创建应用并获取沙箱 APPID 和网关(https://openapi.alipaydev.com/gateway.do)。
  • 使用沙箱买家账号(可在沙箱「账户中心」→「测试账号」中查看,如 13800138000,密码 111111)完成支付流程测试。
  • 重点测试:支付成功、支付失败、异步通知延迟、重复通知等场景。

2. 生产环境部署

  • 替换 application.yml 中的沙箱参数为生产环境参数(APPID、网关、密钥等)。
  • 确保 notifyUrlreturnUrl 为生产环境的公网地址,且服务器防火墙开放对应端口(如80/443)。
  • 监控异步通知日志,处理可能的异常(如网络延迟导致的重复通知)。

​五、常见问题​

  1. ​签名错误​ :检查私钥是否为 PKCS8 格式(支付宝 SDK 要求),公钥是否正确上传到支付宝,signType 是否与配置一致(RSA2)。
  2. ​异步通知未收到​ :检查 notifyUrl 是否可公网访问(可通过 curl 测试),支付宝是否将你的 IP 加入白名单(开放平台「IP 白名单」设置)。
  3. ​订单状态未更新​:确保异步通知处理逻辑的幂等性(如通过数据库唯一索引或状态字段判断是否已处理)。
  4. ​交易金额单位错误​ :支付宝金额单位为元(如 100.00 表示100元),注意前端传递的金额是否已转换为元。

通过以上步骤,即可完成 Spring Boot 与支付宝支付的对接。实际开发中需根据业务需求调整参数验证、订单状态管理等逻辑,并做好异常处理和日志记录。

相关推荐
Kookoos8 分钟前
ABP VNext + GraphQL Federation:跨微服务联合 Schema 分层
后端·微服务·.net·graphql·abp vnext·schema 分层
不太厉害的程序员9 分钟前
eclipse更改jdk环境和生成webservice客户端代码
java·ide·后端·eclipse·webservice
悟能不能悟10 分钟前
ode with me是idea中用来干嘛的插件
java·ide·intellij-idea
Murphy_lx11 分钟前
C++多态的原理
java·开发语言·c++
神仙别闹13 分钟前
基于JSP+MySQL 实现(Web)毕业设计题目收集系统
java·前端·mysql
小韩学长yyds29 分钟前
大疆无人机开发:MQTT 赋能机场系统集成的Java实战之旅
java·无人机
前端伪大叔30 分钟前
第 6 篇:《量化使用看图说话!plot-dataframe 图表可视化教程》
后端·github
考虑考虑32 分钟前
JDK21中的虚拟线程
java·后端·java ee
转转技术团队1 小时前
要想线上事故少,系统稳定性建设得搞好
后端