2026/10/3 12:56:39

SpringBoot 整合支付宝沙箱支付(完整踩坑教程)

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 改为线上公网域名地址。七、拓展项目业务结合在我的外协加工跟催管理系统中就是使用这套沙箱支付方案工单对账完成之后调用支付接口异步通知收到支付成功后更新工单对账状态、保存支付宝交易流水完成业务闭环。实际开发中还要考虑订单幂等防止异步通知多次调用重复更新订单、订单超时关闭、退款接口等业务逻辑。八、参考官方文档支付宝开放平台文档小程序文档 - 支付宝文档中心