App支付成功后如何配置支付宝异步回调地址?
支付阿杰
明确结论
支付宝 App 支付的异步回调地址由 notify_url 指定。商家后端调用 alipay.trade.app.pay 创建支付订单时,需要把这个地址传给支付宝。它不需要在 App 中配置,也不需要单独提供支付完成后的网页。
使用支付宝 Java SDK 时,可以通过 AlipayTradeAppPayRequest#setNotifyUrl() 设置:
request.setNotifyUrl("https://api.example.com/alipay/notify");
支付完成后,支付宝服务器会向这个地址发送异步通知。
配置步骤
1. 准备公网回调接口
后端需要提供一个支付宝服务器可以访问的 HTTP 或 HTTPS 地址,例如:
https://api.example.com/alipay/notify
该接口不能使用 localhost、127.0.0.1 或局域网 IP。测试环境同样需要支持公网访问。
2. 创建 App 支付订单时设置 notify_url
下面以支付宝 Java SDK 为例:
AlipayClient alipayClient = new DefaultAlipayClient(
"https://openapi.alipay.com/gateway.do",
appId,
merchantPrivateKey,
"json",
"UTF-8",
alipayPublicKey,
"RSA2"
);
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setOutTradeNo("ORDER_202609090001");
model.setSubject("商品订单");
model.setTotalAmount("99.00");
model.setProductCode("QUICK_MSECURITY_PAY");
request.setBizModel(model);
request.setNotifyUrl("https://api.example.com/alipay/notify");
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
if (response.isSuccess()) {
// 将 orderString 返回给 App,由 App 调起支付宝客户端
String orderString = response.getBody();
} else {
// 根据实际日志和错误码处理下单失败
throw new IllegalStateException(
response.getCode() + ": " + response.getSubMsg()
);
}
notify_url 是请求级参数,不应放入 biz_content。后端生成的 orderString 已包含调用支付宝所需的签名信息,App 只需使用该字符串调起支付。
3. 接收并验证异步通知
支付宝会以表单参数的形式向 notify_url 发起请求。后端收到通知后,必须先验签,再处理订单:
@PostMapping("/alipay/notify")
public String handleAlipayNotify(HttpServletRequest request) throws Exception {
Map<String, String> params = new HashMap<>();
request.getParameterMap().forEach((key, values) -> {
if (values != null && values.length > 0) {
params.put(key, String.join(",", values));
}
});
boolean verified = AlipaySignature.rsaCheckV1(
params,
alipayPublicKey,
"UTF-8",
"RSA2"
);
if (!verified) {
return "failure";
}
String outTradeNo = params.get("out_trade_no");
String tradeNo = params.get("trade_no");
String tradeStatus = params.get("trade_status");
String totalAmount = params.get("total_amount");
String appIdFromNotify = params.get("app_id");
String sellerId = params.get("seller_id");
// 1. 根据 outTradeNo 查询本地订单
// 2. 核对 app_id、seller_id、订单金额等信息
// 3. 判断 trade_status
// 4. 以幂等方式更新订单状态
// 5. 保存支付宝交易号 tradeNo
if ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus)) {
// 只有业务处理成功后才返回 success
return "success";
}
return "failure";
}
不同版本的支付宝 SDK 可能使用不同的类名和异常类型,但处理流程相同:获取原始通知参数,使用支付宝公钥验签,核对订单信息,再以幂等方式更新业务状态。
为什么不需要回调页面
App 支付有两类结果:
- App 端支付结果:用于提示用户支付操作是否结束,不能作为后端确认到账的唯一依据。
- 支付宝异步通知:支付宝服务器会直接请求商家的
notify_url,后端据此确认支付结果并更新订单状态。
所以,“不需要回调页面”不代表”不需要回调地址”。App 支付通常不依赖浏览器跳转使用的 return_url,但仍应配置服务器异步通知地址 notify_url。
注意事项
notify_url必须能从公网访问,不能要求用户登录,也不能依赖浏览器 Cookie 或 Session。- 回调接口应尽快处理请求并返回纯文本
success。只有验签、订单校验和业务更新全部成功后,才能返回success。 - 支付宝可能重复发送通知,因此订单更新必须具备幂等性,避免重复发货或重复充值。
- 不能只根据
trade_status更新订单,还要核对out_trade_no、app_id、seller_id和total_amount等字段。 - 验签需要使用支付宝公钥,不能使用应用公钥或商家私钥。
- 不要信任 App 客户端上传的”支付成功”状态。客户端结果可以用于界面展示,最终状态应以验签通过的异步通知或服务端主动查询结果为准。
- 如果没有收到通知,可以调用支付宝交易查询接口主动查询订单状态,作为补偿机制。
- 沙箱环境和生产环境应分别使用各自对应的网关、应用配置、密钥及回调地址。
备注:内容仅供参考。
