支付宝沙箱支付是接入支付宝前必须走的一环,它能帮你在不动真金白银的前提下把下单、支付、回调整个链路验证清楚。不过沙箱环境的网关地址、账号体系、AppId都与正式环境不同,很多人第一次配置时会在验签、异步通知这些环节卡很久。本文基于SpringBoot完整演示沙箱支付的接入过程,代码可直接复用到正式环境,只需要换几个配置项即可。

一、沙箱环境准备与密钥配置
首先登录支付宝开放平台,进入控制台后找到「沙箱环境」入口。沙箱会自动分配一个沙箱专用的AppId、商家账号和买家账号,买家账号里有足够的测试余额,支付时直接用它扫码或登录即可。这里要注意,沙箱网关地址是https://openapi-sandbox.dl.alipaydev.com/gateway.do,和正式环境完全不同,配置错了会直接报系统繁忙或者AppId无效。
接下来是密钥部分,这是最容易出错的地方。支付宝目前推荐RSA2密钥,位数2048。你需要下载支付宝官方提供的「支付宝开放平台密钥工具」,生成应用公钥和私钥,然后把应用公钥填到沙箱环境的接口加签方式配置里,保存后平台会给你一份支付宝公钥。很多人把应用公钥当成支付宝公钥用,结果验签一直失败,这是最高频的坑。实际验签时用的是支付宝公钥,而不是你自己生成的那把公钥。
在SpringBoot项目的配置文件中,建议把沙箱相关的配置统一管理:
alipay: appId: 沙箱AppId # 你自己生成的应用私钥 privateKey: 应用私钥内容 # 平台返回给你的支付宝公钥 alipayPublicKey: 支付宝公钥内容 # 沙箱网关,注意与正式环境区分 gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do notifyUrl: http://内网穿透地址/alipay/notify returnUrl: http://localhost:8080/alipay/return
私钥建议放在独立的配置文件或者环境变量里,避免硬编码提交到仓库。notifyUrl必须是公网可访问的地址,本地开发需要借助内网穿透工具,比如把本地8080端口映射出去。
二、引入SDK并完成支付下单代码
支付宝官方提供了完善的Java SDK,引入依赖即可,不需要自己拼参数签名:
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.38.200.ALL</version>
</dependency>
然后写一个配置类,把AlipayClient注册成Bean。AlipayClient是线程安全的,全局一个实例就够,不要每次请求都new一个,频繁创建会拖慢响应速度:
@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;
@Bean
public AlipayClient alipayClient() throws AlipayApiException {
AlipayConfig config = new AlipayConfig();
config.setAppId(appId);
config.setGatewayUrl(gatewayUrl);
config.setPrivateKey(privateKey);
config.setAlipayPublicKey(alipayPublicKey);
config.setSignType("RSA2");
config.setFormat("json");
config.setCharset("UTF-8");
return new DefaultAlipayClient(config);
}
}
下单这里以电脑网站支付为例,构建AlipayTradePagePayRequest,设置好回调地址和业务参数,返回一段HTML表单给前端,浏览器渲染后会自动跳转到沙箱收银台:
@Service
public class AlipayService {
@Autowired
private AlipayClient alipayClient;
@Value("${alipay.notifyUrl}")
private String notifyUrl;
@Value("${alipay.returnUrl}")
private String returnUrl;
public String pagePay(String outTradeNo, String subject, String amount) throws AlipayApiException {
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setNotifyUrl(notifyUrl);
request.setReturnUrl(returnUrl);
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", outTradeNo);
bizContent.put("total_amount", amount);
bizContent.put("subject", subject);
bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
request.setBizContent(bizContent.toString());
// 返回一段HTML,前端直接输出即可跳转收银台
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
return response.getBody();
}
}
Controller层把这段HTML直接写回响应即可,注意设置Content-Type为text/html。沙箱环境下支付页面会显示明显的沙箱标识,用沙箱买家账号登录付款,不会产生真实扣款。
三、异步通知处理与验签
支付成功后,支付宝会向你的notifyUrl发送POST请求。这个通知是不保证只发一次的,支付宝会在收不到success应答时按 4m、10m、10m... 的频率重试,所以回调接口必须做幂等处理。验签使用AlipaySignature.rsaCheckV1,注意参数要从HttpServletRequest里原始地取出来,不要经过任何编码转换:
@PostMapping("/alipay/notify")
public String notify(HttpServletRequest request) {
try {
Map<String, String> params = new HashMap<>();
Map<String, String[]> requestParams = request.getParameterMap();
for (String name : requestParams.keySet()) {
String[] values = requestParams.get(name);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < values.length; i++) {
sb.append(values[i]).append(i == values.length - 1 ? "" : ",");
}
params.put(name, sb.toString());
}
// 验签,verifyFlag为true才可信
boolean verifyFlag = AlipaySignature.rsaCheckV1(params,
alipayPublicKey, "UTF-8", "RSA2");
if (!verifyFlag) {
return "failure";
}
String tradeStatus = params.get("trade_status");
if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {
String outTradeNo = params.get("out_trade_no");
String tradeNo = params.get("trade_no");
// 这里执行业务:幂等校验、更新订单状态、记录流水
}
// 必须返回success,否则支付宝会不停重试
return "success";
} catch (Exception e) {
return "failure";
}
}
有两个细节务必注意:第一,业务处理要判断订单金额和通知金额是否一致,防止极端情况下被篡改的金额通过验签;第二,返回值必须是纯文本的success,不要包成JSON返回,否则支付宝识别不到会一直重试。
四、常见问题与排查思路
验签失败是最常见的问题,优先排查三点:是否把应用公钥填到了支付宝公钥的位置;密钥格式是否选择了PKCS8还是PKCS1,Java端默认用PKCS8,工具生成时别选错;沙箱和正式环境的支付宝公钥不同,切换环境时记得同步更换。
异步通知收不到,先确认notifyUrl是否公网可达,用内网穿透工具时免费版可能不稳定,建议换付费或自建。再看服务器防火墙和安全组有没有放行端口。还有一个容易忽略的点:如果notifyUrl用了https但证书不合法,支付宝会拒绝发送。
沙箱AppId无效或无权限,多半是网关地址配错了,把正式环境的网关配到了沙箱AppId上。沙箱必须用沙箱网关,两者不能混搭。
另外提醒几点:沙箱支付二维码要用沙箱版支付宝钱包(沙箱专用的买家账号)扫码,正式App扫沙箱码会提示异常;金额单位是元,字符串格式传两位小数即可,不要传分;out_trade_no在商户侧必须唯一,重复的话下单会直接报错。切换到正式环境时只需要替换AppId、网关、支付宝公钥三项,代码完全不用动,这也是前期把配置抽离出来的好处。
springboot支付宝沙箱支付支付宝沙箱环境支付宝当面付修改时间:2026-09-15 04:48:33