SpringBoot整合支付宝沙箱支付的完整案例

 更新时间:2026年07月07日 09:08:03   作者:张老师技术栈  
支付是电商系统的核心功能,接入支付宝需要企业资质,但支付宝提供了沙箱环境——模拟真实支付流程,不需要真实商户号,适合开发测试和学习,本文给大家介绍了SpringBoot整合支付宝沙箱支付的完整案例,需要的朋友可以参考下

支付是电商系统的核心功能。接入支付宝需要企业资质,但支付宝提供了沙箱环境——模拟真实支付流程,不需要真实商户号,适合开发测试和学习。

一、沙箱环境准备

1. 登录开放平台

打开 open.alipay.com → 控制台 → 沙箱应用

2. 获取配置参数

APP_ID: 9021000122691234(沙箱专用)
应用私钥: 生成的 RSA2 私钥
支付宝公钥: 生成的 RSA2 公钥
服务器异步通知地址: http://你的地址/pay/notify

3. 沙箱账号

沙箱环境提供测试买家账号:

买家账号: rixwby5818@sandbox.com
登录密码: 111111
支付密码: 111111

二、SpringBoot 集成

1. 引入依赖

<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>4.38.200.ALL</version>
</dependency>

2. 配置

alipay:
  app-id: 9021000122691234
  app-private-key: |
    -----BEGIN RSA PRIVATE KEY-----
    MIIEowIBAAKCAQ...
    (你的应用私钥,不要写在配置里!建议用Jasypt加密)
    -----END RSA PRIVATE KEY-----
  alipay-public-key: |
    -----BEGIN PUBLIC KEY-----
    MIIBIjANBgk...
    (支付宝公钥)
    -----END PUBLIC KEY-----
  notify-url: http://你的公网IP/pay/notify
  return-url: http://localhost:9090/pay/return
  gateway-url: https://openapi-sandbox.dl.alipaydev.com/gateway.do

3. 配置类

@Configuration
@ConfigurationProperties(prefix = "alipay")
@Data
public class AlipayConfig {
    private String appId;
    private String appPrivateKey;
    private String alipayPublicKey;
    private String notifyUrl;
    private String returnUrl;
    private String gatewayUrl;

    @Bean
    public AlipayClient alipayClient() {
        return new DefaultAlipayClient(
            gatewayUrl, appId, appPrivateKey,
            "json", "utf-8", alipayPublicKey, "RSA2"
        );
    }
}

三、统一下单接口

@Service
public class AlipayService {

    @Autowired
    private AlipayClient alipayClient;

    @Autowired
    private AlipayConfig alipayConfig;

    /**
     * 创建支付订单,返回支付页面表单
     */
    public String createPay(String orderNo, BigDecimal amount, String subject) {
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();

        // 同步回调地址(用户支付完成后跳转)
        request.setReturnUrl(alipayConfig.getReturnUrl());
        // 异步回调地址(支付宝服务器通知)
        request.setNotifyUrl(alipayConfig.getNotifyUrl());

        JSONObject biz = new JSONObject();
        biz.put("out_trade_no", orderNo);       // 商户订单号
        biz.put("total_amount", amount.toString()); // 支付金额
        biz.put("subject", subject);            // 订单标题
        biz.put("product_code", "FAST_INSTANT_TRADE_PAY"); // 电脑网站支付

        request.setBizContent(biz.toJSONString());

        try {
            AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
            if (response.isSuccess()) {
                return response.getBody();  // 返回 HTML 表单
            }
        } catch (AlipayApiException e) {
            throw new RuntimeException("支付宝下单失败", e);
        }
        return null;
    }
}

四、Controller

@RestController
@RequestMapping("/pay")
public class PayController {

    @Autowired
    private AlipayService alipayService;

    /**
     * 发起支付
     */
    @PostMapping("/create")
    public ResultVO<String> createPay(@RequestBody PayDTO dto) {
        // 生成订单号
        String orderNo = "ORD" + IdUtil.simpleUUID().toUpperCase();
        String form = alipayService.createPay(orderNo, dto.getAmount(), dto.getSubject());
        return ResultVO.success(form);
    }

    /**
     * 同步回调(用户支付完成后跳转到此页面)
     */
    @GetMapping("/return")
    public String returnUrl(HttpServletRequest request) {
        // 获取支付宝返回的参数
        Map<String, String> params = getParamsFromRequest(request);
        try {
            // 验签
            AlipaySignature.rsaCheckV1(
                params, alipayConfig.getAlipayPublicKey(), "utf-8", "RSA2");
            String orderNo = params.get("out_trade_no");
            String tradeNo = params.get("trade_no");
            return "支付成功!订单号: " + orderNo;
        } catch (AlipayApiException e) {
            return "验签失败";
        }
    }

    /**
     * 异步回调(支付宝服务器通知,必须返回 success)
     */
    @PostMapping("/notify")
    public String notifyUrl(HttpServletRequest request) {
        Map<String, String> params = getParamsFromRequest(request);
        try {
            boolean signVerified = AlipaySignature.rsaCheckV1(
                params, alipayConfig.getAlipayPublicKey(), "utf-8", "RSA2");

            if (signVerified) {
                String orderNo = params.get("out_trade_no");
                String tradeNo = params.get("trade_no");
                String tradeStatus = params.get("trade_status");

                // 支付成功
                if ("TRADE_SUCCESS".equals(tradeStatus)) {
                    // 更新订单状态、归还库存等业务逻辑
                    updateOrderStatus(orderNo, tradeNo);
                }
                return "success";  // 必须返回 success,否则支付宝会重复通知
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return "fail";
    }

    private Map<String, String> getParamsFromRequest(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));
        }
        return params;
    }
}

五、秒杀系统集成支付

支付完成后更新订单状态并归还库存:

@Service
public class PayService {

    @Autowired
    private OrderService orderService;

    @Autowired
    private ProductService productService;

    /**
     * 支付成功回调处理
     */
    @Transactional
    public void handlePaySuccess(String orderNo, String tradeNo) {
        // 1. 查询订单
        SeckillOrder order = orderService.lambdaQuery()
            .eq(SeckillOrder::getOrderNo, orderNo)
            .one();

        if (order == null || order.getStatus() != 0) {
            return;  // 订单不存在或已处理
        }

        // 2. 更新订单状态为已支付
        order.setStatus(1);
        order.setPayTime(LocalDateTime.now());
        order.setTradeNo(tradeNo);
        orderService.updateById(order);

        // 3. 记录支付日志
        log.info("支付成功: 订单={}, 支付宝交易号={}", orderNo, tradeNo);
    }
}

六、前端调用

<button onclick="pay()">支付宝支付</button>
<script>
async function pay() {
    const resp = await fetch('/pay/create', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({
            amount: 99.99,
            subject: '秒杀商品 - iPhone 15'
        })
    });
    const result = await resp.json();
    if (result.code === 200) {
        // 把返回的 HTML 表单写到页面并自动提交
        document.body.innerHTML = result.data;
        document.forms[0].submit();  // 自动跳转到支付宝
    }
}
</script>

七、注意事项

  1. 异步通知必须返回 “success”(全小写),否则支付宝会重复通知 8 次
  2. 验签必不可少,防止伪造回调
  3. 幂等处理——支付宝通知可能重复,判断订单状态防止重复更新
  4. 沙箱金额随便填,不会真实扣款
  5. 上生产环境只需把 gateway-url 换成生产地址,其他代码不变

到此这篇关于SpringBoot整合支付宝沙箱支付的完整案例的文章就介绍到这了,更多相关SpringBoot整合支付宝沙箱支付内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java 如何读取properties文件

    java 如何读取properties文件

    这篇文章主要介绍了java 如何读取properties文件,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2020-11-11
  • SpringMVC @ControllerAdvice使用场景

    SpringMVC @ControllerAdvice使用场景

    这篇文章主要介绍了SpringMVC @ControllerAdvice使用场景,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • JavaWeb中的文件的上传和下载

    JavaWeb中的文件的上传和下载

    JavaWeb 文件的上传和下载是指在Web应用中实现用户上传文件到服务器和从服务器下载文件的功能,通过JavaWeb技术,可以方便地实现文件的上传和下载操作,提供更好的用户体验和数据交互,需要的朋友可以参考下
    2023-10-10
  • Java实现Huffman编码的示例代码

    Java实现Huffman编码的示例代码

    Huffman编码是一种编码方式,本文主要介绍了Java实现Huffman编码的示例代码,具有一定的参考价值,感兴趣的可以了解一下
    2023-08-08
  • 浅谈virtual、abstract方法和静态方法、静态变量理解

    浅谈virtual、abstract方法和静态方法、静态变量理解

    下面小编就为大家带来一篇浅谈virtual、abstract方法和静态方法、静态变量理解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • Java实现PDF转为线性PDF详解

    Java实现PDF转为线性PDF详解

    线性化PDF文件是PDF文件的一种特殊格式,可以通过Internet更快地进行查看。本文将通过后端Java程序实现将PDF文件转为线性化PDF。感兴趣的可以了解一下
    2021-12-12
  • response对象的使用(实例讲解)

    response对象的使用(实例讲解)

    下面小编就为大家带来一篇response对象的使用(实例讲解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • 浅谈IDEA实用的Servlet模板

    浅谈IDEA实用的Servlet模板

    今天给大家分享一下IDEA实用的Servlet模板,文中有非常详细的图文介绍及代码示例,对小伙伴们很有帮助哦,需要的朋友可以参考下
    2021-05-05
  • Spring解决依赖版本不一致报错问题

    Spring解决依赖版本不一致报错问题

    许多同学经常会遇到依赖版本不一致导致代码报错,所以这篇文章就给大家详细介绍一下Spring解决依赖版本不一致报错问题,需要的朋友跟着小编一起来看看吧
    2023-07-07
  • 使用synchronized实现一个Lock代码详解

    使用synchronized实现一个Lock代码详解

    这篇文章主要介绍了使用synchronized实现一个Lock代码详解,具有一定借鉴价值,需要的朋友可以参考下。
    2017-12-12

最新评论