alipay.trade.page.pay 支付成功后如何设置返回页面?
支付阿杰
明确结论
调用 alipay.trade.page.pay 时,需要在支付请求中设置同步返回地址 return_url。用户完成支付后,支付宝会尝试将浏览器跳转到这个地址。
还需要设置异步通知地址 notify_url。业务系统不能根据返回页面直接判定支付成功,订单的最终状态应以验签通过的异步通知或主动查询结果为准。
设置方法
以支付宝 Java SDK 为例,return_url 和 notify_url 应设置在 AlipayTradePagePayRequest 上,不要放入业务参数 biz_content:
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
// 支付完成后的浏览器返回页面
request.setReturnUrl("https://www.example.com/alipay/return");
// 支付结果异步通知地址
request.setNotifyUrl("https://www.example.com/alipay/notify");
AlipayTradePagePayModel model = new AlipayTradePagePayModel();
model.setOutTradeNo("ORDER_202609090001");
model.setProductCode("FAST_INSTANT_TRADE_PAY");
model.setTotalAmount("99.00");
model.setSubject("订单支付");
request.setBizModel(model);
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
if (response.isSuccess()) {
// response.getBody() 通常是自动提交到支付宝收银台的 HTML 表单
String paymentForm = response.getBody();
httpServletResponse.setContentType("text/html;charset=UTF-8");
httpServletResponse.getWriter().write(paymentForm);
httpServletResponse.getWriter().flush();
} else {
throw new RuntimeException(
"创建支付请求失败:" + response.getSubCode() + " - " + response.getSubMsg()
);
}
如果没有使用 SDK,而是自行组装网关请求,需要在支付宝网关的公共请求参数中传入:
return_url=https://www.example.com/alipay/return
notify_url=https://www.example.com/alipay/notify
这两个地址通常都必须是支付宝服务器或用户浏览器能够访问的完整 URL。生产环境中不要使用 localhost、内网地址或无法公开访问的测试域名。
同步返回页面如何处理
支付完成后,支付宝会将用户浏览器带回 return_url,同时携带交易参数和签名。返回接口应先验签,再向用户展示结果。
Java SDK 的处理方式如下:
@GetMapping("/alipay/return")
public String alipayReturn(
HttpServletRequest request,
Model model
) throws AlipayApiException {
Map<String, String> params = new HashMap<>();
request.getParameterMap().forEach((name, values) -> {
if (values != null && values.length > 0) {
params.put(name, values[0]);
}
});
boolean valid = AlipaySignature.rsaCheckV1(
params,
alipayPublicKey,
"UTF-8",
"RSA2"
);
if (!valid) {
model.addAttribute("message", "返回数据验签失败");
return "payment-result";
}
String outTradeNo = params.get("out_trade_no");
// 建议根据 outTradeNo 查询本地订单,
// 必要时再调用 alipay.trade.query 核实支付宝交易状态。
model.addAttribute("outTradeNo", outTradeNo);
model.addAttribute("message", "支付结果正在确认");
return "payment-result";
}
这段代码采用普通公钥方式。如果应用使用支付宝证书模式,需要使用与证书模式对应的 SDK 初始化和验签方法,不能直接套用普通公钥配置。
为什么还要设置 notify_url
return_url 只是浏览器的同步跳转地址,不能作为可靠的支付结果通知。用户可能因为以下情况没有回到返回页面:
- 支付后直接关闭浏览器或页面。
- 浏览器跳转被网络、插件或安全策略阻断。
- 用户重复刷新返回页面。
- 同步跳转已经完成,但异步通知稍后才到达。
- 返回参数被伪造或篡改。
处理流程建议如下:
- 创建支付请求时,同时设置
return_url和notify_url。 return_url用于展示“支付处理中”或订单的当前状态。notify_url用于接收支付宝服务端发送的异步通知。- 收到异步通知后进行验签,并校验
app_id、out_trade_no、金额和收款方等数据。 - 确认交易状态为
TRADE_SUCCESS,或符合业务要求的TRADE_FINISHED后,再更新本地订单。 - 异步通知处理成功后,按照支付宝接口要求返回纯文本
success。 - 如果通知中的状态不明确,可以调用
alipay.trade.query主动查询。
异步通知处理示意:
@PostMapping("/alipay/notify")
@ResponseBody
public String alipayNotify(HttpServletRequest request)
throws AlipayApiException {
Map<String, String> params = new HashMap<>();
request.getParameterMap().forEach((name, values) -> {
if (values != null && values.length > 0) {
params.put(name, values[0]);
}
});
boolean valid = AlipaySignature.rsaCheckV1(
params,
alipayPublicKey,
"UTF-8",
"RSA2"
);
if (!valid) {
return "failure";
}
String outTradeNo = params.get("out_trade_no");
String tradeStatus = params.get("trade_status");
String totalAmount = params.get("total_amount");
// 还应根据本地数据校验 app_id、订单金额、收款方等信息。
if ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus)) {
// 必须使用幂等更新,避免重复通知造成重复入账。
updateOrderPaidIdempotently(outTradeNo, totalAmount);
return "success";
}
return "success";
}
注意事项
return_url是用户支付后的返回页面,notify_url是接收服务端支付结果通知的入口,两者用途不同。- 不要根据同步返回参数直接将订单改为“已支付”。至少要先验签并核对订单,更稳妥的做法是以异步通知或
alipay.trade.query的查询结果为准。 - 回调地址应使用 HTTPS,并保证外网可以访问。
- 支付宝可能重复发送异步通知,因此订单更新必须具备幂等性。
- 页面跳转地址应尽量保持固定。如果需要返回订单详情页,可以通过验签后的
out_trade_no查询本地订单,再由服务端完成跳转,不要直接信任未经验证的 URL 参数。 - 验签时使用支付宝公钥还是支付宝证书,取决于应用采用的密钥配置方式。
- 沙箱环境和生产环境使用不同的网关、应用配置和密钥。切换环境时,应同时检查回调地址与验签配置。
备注:内容仅供参考。
