支付宝 JSSDK ap.tradePay 文档及支付结果查询
结论
ap.tradePay 的作用是在支付宝客户端内唤起收银台,它不会提供最终且可信的支付状态。前端回调可以用于更新页面交互,但不能作为发货、记账或开通服务的依据。
商户服务端应通过以下方式确认支付结果:
- 接收支付宝异步通知并完成验签。
- 调用
alipay.trade.query主动查询订单。 - 查询结果为
TRADE_SUCCESS或TRADE_FINISHED时,才能按支付成功处理。
使用 ap.tradePay 通常不代表必须申请生活号。接入支付真正需要的是支付宝开放平台应用、对应的支付产品权限,以及符合产品要求的签约和授权配置。只有当业务页面必须从生活号进入,或者所选支付产品明确要求生活号时,才需要申请生活号。具体准入条件以支付宝开放平台控制台当前显示的产品要求为准。
官方文档应该看哪些部分
参考页面中的 ap.tradePay 文档主要说明客户端如何调用接口,所以内容相对简单。完整的支付流程通常分布在以下几类文档中:
- 支付宝 JSAPI 的
ap.tradePay接口说明; - 所用支付产品的接入文档;
- 交易创建接口文档,例如适用场景下的
alipay.trade.create; alipay.trade.query统一收单线下交易查询接口文档;- 支付结果异步通知及 RSA2 验签文档。
接入前应先确认商户签约了哪一种支付产品。不同产品使用的订单创建接口、必填参数和准入条件可能不同,不能仅凭 ap.tradePay 页面推断整套支付流程。
推荐的支付流程
可以按下面的流程接入:
- 浏览器请求商户服务端创建订单。
- 商户服务端生成唯一的
out_trade_no,再根据已签约的产品调用支付宝交易创建接口。 - 服务端将支付宝返回的
trade_no传给前端。 - 前端调用
ap.tradePay唤起收银台。 - 收银台关闭后,前端请求商户服务端查询订单状态。
- 服务端调用
alipay.trade.query,或者读取已经验签并写入数据库的异步通知结果。 - 服务端统一处理业务状态,再将结果返回前端。
不要在浏览器中直接调用支付宝 OpenAPI。应用私钥、签名过程和订单金额都必须保留在服务端。
前端调用示例
async function pay(orderId) {
// tradeNO 应由商户服务端创建交易后返回
const createResponse = await fetch('/api/payment/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ orderId })
});
const order = await createResponse.json();
ap.tradePay(
{
tradeNO: order.tradeNo
},
function (result) {
// 该回调只能表示收银台本次交互结果,不能直接触发发货
console.log('tradePay result:', result);
pollPaymentStatus(order.orderNo);
}
);
}
async function pollPaymentStatus(orderNo) {
const maximumAttempts = 10;
for (let attempt = 0; attempt < maximumAttempts; attempt += 1) {
const response = await fetch(
`/api/payment/status?orderNo=${encodeURIComponent(orderNo)}`
);
const result = await response.json();
if (result.status === 'PAID') {
showPaymentSuccess();
return;
}
if (result.status === 'CLOSED' || result.status === 'FAILED') {
showPaymentFailure();
return;
}
await new Promise(resolve => setTimeout(resolve, 2000));
}
showPaymentProcessing();
}
ap.tradePay 回调中的 resultCode 常用于区分成功返回、处理中、用户取消和调用失败等结果。但网络中断、页面关闭等情况都可能影响客户端返回值,具体码值应以当前版本的官方 JSAPI 文档为准。即使客户端返回成功,仍需由服务端再次确认。
服务端查询示例
下面使用支付宝 Java SDK 的常见写法说明查询逻辑。实际类名和初始化参数应与项目使用的 SDK 版本一致。
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
request.setBizContent(
"{"
+ "\"out_trade_no\":\"" + outTradeNo + "\""
+ "}"
);
AlipayTradeQueryResponse response = alipayClient.execute(request);
if (!response.isSuccess()) {
// 这里表示查询接口调用失败,不应直接判定用户未付款
return PaymentStatus.UNKNOWN;
}
String tradeStatus = response.getTradeStatus();
if ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus)) {
return PaymentStatus.PAID;
}
if ("TRADE_CLOSED".equals(tradeStatus)) {
return PaymentStatus.CLOSED;
}
if ("WAIT_BUYER_PAY".equals(tradeStatus)) {
return PaymentStatus.PENDING;
}
return PaymentStatus.UNKNOWN;
各交易状态通常可以这样理解:
WAIT_BUYER_PAY:交易已创建,正在等待付款;TRADE_SUCCESS:支付成功;TRADE_FINISHED:交易已完成,通常不能再退款;TRADE_CLOSED:交易已关闭。
如果查询接口暂时失败或没有查到订单,不要立即将业务订单改为支付失败。网络延迟、异步通知延迟,以及交易刚创建但尚未完成同步,都可能造成短时间内无法取得最终结果。
异步通知也必须校验
收到支付宝异步通知后,应先验签,再更新订单。除签名外,还要核对以下内容:
app_id是否属于当前应用;seller_id是否属于当前收款商户;out_trade_no是否存在,并且对应当前业务订单;total_amount是否与订单应付金额一致;- 币种、交易状态等字段是否符合预期;
- 订单是否已经处理,防止重复发货。
通知处理必须具备幂等性。所有校验均通过且结果成功写入数据库后,通知接口才能返回支付宝要求的成功响应。请求中即使包含 trade_status=TRADE_SUCCESS,也不能直接认定支付成功。
是否必须申请生活号
生活号和开放平台应用不是同一个概念:
- 生活号用于触达用户和运营内容;
- 开放平台应用负责承载接口权限、密钥、网关调用和产品签约;
ap.tradePay是支付宝客户端内提供的 JSAPI 调用能力。
因此,仅仅使用 ap.tradePay,通常无法得出必须申请生活号的结论。页面也可以通过其他合规入口在支付宝客户端内打开。如果业务依赖生活号菜单、生活号用户体系,或者当前签约产品将生活号列为准入条件,则仍需申请并配置生活号。
正式接入前,应在支付宝开放平台控制台核实:
- 当前应用类型是否支持目标支付产品;
- 商户是否已经签约并开通相关能力;
- 应用是否已经配置公钥、回调地址和授权信息;
- 页面是否运行在支持
ap.tradePay的支付宝客户端环境中; - 当前产品是否要求使用生活号、小程序或其他特定载体。
最终应以所签约支付产品的最新准入规则和控制台审核结果为准,不能只根据 ap.tradePay 的 JSAPI 页面作出判断。
备注:内容仅供参考。
