Alipay Easy SDK 如何接收支付回调结果?
支付小周
结论
Alipay Easy SDK 提供了用于验证支付异步通知签名的方法:
Factory.Payment.Common().verifyNotify(parameters)
该方法用于确认支付宝回调数据的签名是否可信,返回值为 boolean。它不会接收 HTTP 请求,也不会将回调内容转换成统一的支付结果对象。应用需要自行读取回调请求中的参数,并在验签通过后,从参数集合中获取 trade_status、out_trade_no、trade_no 等业务字段。
处理步骤
- 在服务端配置一个支付宝可以访问的
notify_url。 - 读取支付宝 POST 请求中的全部表单参数。
- 将参数整理成
Map<String, String>。 - 调用
verifyNotify验证签名。 - 验签通过后,核对订单号、金额、商户身份和交易状态。
- 业务处理完成后,向支付宝返回纯文本
success。
Java 示例
下面的示例使用 Servlet API。Spring Boot 项目可以采用不同的参数获取方式,但调用验签方法时,仍需传入支付宝回调中的完整参数集合。
import com.alipay.easysdk.factory.Factory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public void alipayNotify(
HttpServletRequest request,
HttpServletResponse response) throws IOException {
Map<String, String> parameters = new HashMap<>();
request.getParameterMap().forEach((name, values) -> {
if (values != null && values.length > 0) {
parameters.put(name, String.join(",", values));
}
});
try {
boolean verified = Factory.Payment.Common()
.verifyNotify(parameters);
if (!verified) {
response.getWriter().write("failure");
return;
}
String tradeStatus = parameters.get("trade_status");
String outTradeNo = parameters.get("out_trade_no");
String tradeNo = parameters.get("trade_no");
String totalAmount = parameters.get("total_amount");
String appId = parameters.get("app_id");
String sellerId = parameters.get("seller_id");
// 必须结合本地订单进行业务校验:
// 1. outTradeNo 是否存在
// 2. totalAmount 是否与订单金额一致
// 3. appId、sellerId 是否属于当前商户
// 4. 订单是否已经处理,避免重复入账
// 5. tradeStatus 是否为业务允许的状态
if ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus)) {
// 使用事务或幂等机制更新订单状态
// updateOrderIfNotPaid(outTradeNo, tradeNo, totalAmount);
}
response.getWriter().write("success");
} catch (Exception e) {
// 验签或业务处理异常时,不应返回 success
response.getWriter().write("failure");
}
}
在 Spring MVC 中,也可以直接接收参数:
@PostMapping("/alipay/notify")
@ResponseBody
public String alipayNotify(@RequestParam Map<String, String> parameters) {
try {
boolean verified = Factory.Payment.Common()
.verifyNotify(parameters);
if (!verified) {
return "failure";
}
String tradeStatus = parameters.get("trade_status");
String outTradeNo = parameters.get("out_trade_no");
String tradeNo = parameters.get("trade_no");
String totalAmount = parameters.get("total_amount");
// 校验订单信息并进行幂等处理
return "success";
} catch (Exception e) {
return "failure";
}
}
如何获取回调结果
verifyNotify 只验证签名。回调结果需要从传入的 parameters 中读取,例如:
String tradeStatus = parameters.get("trade_status");
String outTradeNo = parameters.get("out_trade_no");
String tradeNo = parameters.get("trade_no");
String totalAmount = parameters.get("total_amount");
常见字段有:
trade_status:支付宝交易状态。out_trade_no:商户订单号。trade_no:支付宝交易号。total_amount:订单金额。app_id:支付宝应用 ID。seller_id:收款方支付宝用户 ID。
不同支付产品的字段可能有所区别,具体应以当前产品的异步通知协议为准。
注意事项
验签通过后仍需校验订单
验签通过只能说明通知带有可信签名。修改订单前,还要核对:
out_trade_no是否对应本地订单;total_amount是否与本地订单金额一致;app_id和seller_id是否符合预期;trade_status是否属于需要处理的成功状态。
回调处理必须幂等
支付宝可能多次发送同一笔交易的通知。重复回调不能导致重复发货、充值或记账。通常可以根据订单状态、支付宝交易号或数据库唯一约束来保证幂等。
验签前不要修改参数
应将支付宝回调中的完整原始参数传给 verifyNotify。验签前不要删除 sign、sign_type,也不要修改参数值或重新编码,否则可能造成验签失败。
业务处理完成后再返回 success
接口应返回以下纯文本:
success
如果验签未通过、订单校验失败或业务事务尚未完成,不应返回 success。否则,支付宝可能将该通知视为已经正确处理。
同步跳转不能代替异步通知
用户关闭页面或网络中断,都可能导致浏览器的同步跳转无法完成。因此,订单最终状态应主要以服务端异步通知为准,必要时还可以调用交易查询接口进行补偿确认。
备注:内容仅供参考。
