SpringBoot 整合支付宝沙箱支付(完整踩坑教程)
标签#Java #SpringBoot #支付宝沙箱 #支付开发 #后端实战前言在做电商、订单类业务系统时支付功能是必不可少的模块。正式支付宝支付需要企业资质个人开发者可以使用支付宝沙箱环境完成支付功能开发与调试无需真实资金。本文基于 SpringBoot Alipay Easy SDK 实现网页支付包含环境准备、密钥配置、代码实现、异步回调、常见踩坑总结完整可运行我自己的项目中也使用这套方案完成对账支付模块开发。重要概念区分return_url同步跳转地址支付完成后浏览器页面跳转不可信仅做页面展示不能作为修改订单状态的依据。notify_url异步通知地址支付宝服务端 POST 请求推送支付结果这是修改订单状态的唯一可靠来源必须公网可访问localhost本地无法接收需要内网穿透工具测试。一、沙箱环境准备1.1 进入支付宝沙箱控制台支付宝开放平台地址支付宝开放平台登录支付宝账号进入控制台找到左下角【沙箱】进入沙箱应用页面。获取核心参数APPID沙箱应用 ID沙箱网关https://openapi.alipaydev.com/gateway.do⚠️正式环境要去掉 dev接口加签方式选择系统默认密钥快速测试复制应用私钥、支付宝公钥。沙箱测试账号页面可以获取买家账号密码下载【支付宝沙箱版 APP】只能用沙箱账号登录做支付测试普通支付宝 APP 无法测试沙箱支付。⚠️注意不要泄露应用私钥私钥用于我们程序签名支付宝公钥用于程序验签。二、项目依赖引入Maven pom.xml 引入支付宝 Easy SDK简化签名、验签逻辑不需要手写 RSA 加密。!--支付宝Easy SDK-- dependency groupIdcom.alipay.sdk/groupId artifactIdalipay-easysdk/artifactId version2.2.0/version /dependency !--lombok-- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency三、配置文件编写 application.ymlserver: port: 8080 alipay: app-id: 你的沙箱APPID app-private-key: 你的应用私钥 alipay-public-key: 你的支付宝公钥 同步跳转地址支付完成浏览器跳转页面可以是前端页面 return-url: http://127.0.0.1:8080/pay/success 异步通知地址必须公网可访问本地测试使用natapp内网穿透地址 notify-url: http://xxx.natappfree.cc/pay/notify四、Java 代码实现4.1 读取配置实体类 AliPayPropertiesimport lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; Data Component ConfigurationProperties(prefix alipay) public class AliPayProperties { private String appId; private String appPrivateKey; private String alipayPublicKey; private String returnUrl; private String notifyUrl; }4.2 支付宝 SDK 初始化配置类项目启动时初始化一次 SDK 全局配置沙箱网关 host 固定openapi.alipaydev.comimport com.alipay.easysdk.factory.Factory; import com.alipay.easysdk.kernel.Config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; Component public class AliPayInitConfig { Autowired private AliPayProperties aliPayProperties; PostConstruct public void initAlipay() { Config config new Config(); //沙箱环境域名 config.gatewayHost openapi.alipaydev.com; config.signType RSA2; config.appId aliPayProperties.getAppId(); config.merchantPrivateKey aliPayProperties.getAppPrivateKey(); config.alipayPublicKey aliPayProperties.getAlipayPublicKey(); config.notifyUrl aliPayProperties.getNotifyUrl(); // 设置全局配置只需要初始化一次 Factory.setOptions(config); } }4.3 Controller 支付接口import com.alipay.easysdk.factory.Factory; import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; 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 javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Map; Slf4j RestController RequestMapping(/pay) public class AliPayController { Autowired private AliPayProperties aliPayProperties; /** 网页支付接口 param subject 订单标题 param outTradeNo 商户订单号自己业务系统订单号唯一 param totalAmount 订单金额 return 返回form表单html前端直接渲染自动跳转支付宝收银台 */ GetMapping(/goPay) public String goPay(String subject, String outTradeNo, String totalAmount, HttpServletResponse response) throws Exception { AlipayTradePagePayResponse resp Factory.Payment.Page() .pay(subject, outTradeNo, totalAmount, aliPayProperties.getReturnUrl()); return resp.getBody(); } /** 同步跳转 return_url仅页面展示不能修改订单状态 */ GetMapping(/success) public String payReturn(HttpServletRequest request){ log.info(支付同步跳转参数{},request.getParameterMap()); // 这里只做页面展示不要在这里更新数据库订单 return lt;h1gt;支付页面跳转成功请等待异步通知确认订单结果lt;/h1gt;; } /** 支付宝异步通知接口 POST 支付宝服务器主动调用修改订单状态的核心接口 */ PostMapping(/notify) public String payNotify(HttpServletRequest request) throws Exception{ log.info(收到支付宝异步通知); //1. 获取所有回调参数 Maplt;String,Stringgt; paramMap new HashMaplt;gt;(); Maplt;String,String[]gt; requestMap request.getParameterMap(); for(String key : requestMap.keySet()){ paramMap.put(key,requestMap.get(key)[0]); } //2. SDK做签名校验校验请求是否来自支付宝防止伪造请求 boolean signVerified Factory.Payment.Common().verifyNotify(paramMap); if(!signVerified){ log.error(异步通知验签失败请求非法); return fail; } //3. 判断交易状态TRADE_SUCCESS代表支付成功 String tradeStatus paramMap.get(trade_status); if(TRADE_SUCCESS.equals(tradeStatus)){ //商户订单号我们自己系统的订单号 String outTradeNo paramMap.get(out_trade_no); //支付宝交易号 String tradeNo paramMap.get(trade_no); String totalAmount paramMap.get(total_amount); log.info(订单{}支付宝交易号{}支付金额{}支付成功,outTradeNo,tradeNo,totalAmount); // 业务逻辑修改数据库订单状态为已支付执行对账逻辑 // orderService.updateOrderSuccess(outTradeNo,tradeNo,totalAmount); // ⭐必须返回字符串 success支付宝收到success才停止重试回调 return success; } return fail; } }4.4 支付交互时序图下图展示了用户浏览器、SpringBoot 后端与支付宝沙箱服务器之间的完整支付交互流程涵盖发起支付、同步跳转和异步通知三个关键环节sequenceDiagram participant U as 用户浏览器 participant B as SpringBoot 后端 participant A as 支付宝沙箱服务器 U-gt;gt;B: 1. 访问 /pay/goPay携带订单参数 B-gt;gt;A: 2. 调用支付宝 SDK 发起支付请求 A--gt;gt;B: 3. 返回支付表单 HTML B--gt;gt;U: 4. 返回 form 表单页面 U-gt;gt;A: 5. 自动跳转沙箱收银台并完成付款 A--gt;gt;U: 6. 同步跳转 return_url仅页面展示 A-gt;gt;B: 7. 异步通知 notify_urlPOST携带交易结果 B-gt;gt;B: 8. 验签并更新订单状态 B--gt;gt;A: 9. 返回 success停止重试通知五、测试流程启动 SpringBoot 项目本地notify_url需要使用内网穿透工具natapp把本地 8080 端口映射为公网地址替换 yml 中 notify-url 配置支付宝服务器需要公网访问这个接口才能推送回调通知。浏览器访问接口示例http://127.0.0.1:8080/pay/goPay?subject外协工单支付outTradeNoORD202610020001totalAmount88.50页面会自动渲染支付宝的 form 表单跳转到沙箱收银台使用支付宝沙箱版 APP登录沙箱买家账号扫码完成付款支付完成浏览器跳转到/pay/success同步页面支付宝服务器 POST 请求访问内网穿透的/pay/notify打印日志更新订单业务。注意普通支付宝 APP 扫码沙箱二维码会报错必须下载沙箱版本客户端六、高频踩坑总结避坑异步通知收不到notify_url 必须公网可访问localhost、127.0.0.1 支付宝外网无法访问必须内网穿透或者部署服务器接口请求方式是 POST不要写 GetMapping。处理完成必须返回success否则支付宝会持续重试通知最多 8 次。验签失败确认私钥、支付宝公钥复制完整不要带多余换行空格沙箱网关不要写成正式网关沙箱网关带dev复制密钥的时候不要复制-----BEGIN PRIVATE KEY-----标记。同步 return_url 收到参数但是订单状态没更新return_url 只是浏览器跳转用户可以篡改参数绝对不能用同步跳转去修改订单状态必须以异步 notify_url 为准。金额格式报错金额字符串保留两位小数例如0.01不要传数字类型不要传整数。沙箱切换正式环境上线修改网关地址为正式网关https://openapi.alipay.com/gateway.do删除 dev替换正式环境 appId、密钥notify_url 改为线上公网域名地址。七、拓展项目业务结合在我的外协加工跟催管理系统中就是使用这套沙箱支付方案工单对账完成之后调用支付接口异步通知收到支付成功后更新工单对账状态、保存支付宝交易流水完成业务闭环。实际开发中还要考虑订单幂等防止异步通知多次调用重复更新订单、订单超时关闭、退款接口等业务逻辑。八、参考官方文档支付宝开放平台文档小程序文档 - 支付宝文档中心

相关新闻

多物理参数融合与AI算法:锂电池热失控监测传感器实战解析

多物理参数融合与AI算法:锂电池热失控监测传感器实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/24 12:27:00 阅读更多 →
远程 IO 模块怎么选?2026 选购指南

远程 IO 模块怎么选?2026 选购指南

远程IO模块的选型,从来不是选“最好”的品牌,而是为控制系统找到“最合适”的分布式信号入口。当EtherCAT、PROFINET、EtherNet/IP等工业总线在产线加速普及,远程IO早已不只是PLC的附属扩展,而是独立选型中的关键一环——它直接关…

2026/11/1 15:09:28 阅读更多 →
人际关系的边界与分寸

人际关系的边界与分寸

人际关系的边界与分寸 引言 《增广贤文》有言:“久住令人贱,频来亲也疏。” 人与人之间,走得太近,往往是一场灾难。你掏心掏肺,对方可能觉得理所应当;你毫无保留,关系反而变得廉价。这并非人心不…

2026/10/31 19:22:32 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/10 23:32:01 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/10 23:32:03 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/11 2:31:33 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/10 16:14:15 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/10 12:51:50 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/10 23:32:10 阅读更多 →

日新闻

周新闻

月新闻