支付宝小程序 my.tradePay 支付成功后未触发回调
结论
my.tradePay 正常结束并返回小程序后,success、fail 至少会执行一个,complete 也会执行。如果支付已经成功,三个回调却都没有触发,并且页面直接进入“感谢您的消费”,通常不是普通的支付失败,而是支付流程没有回到发起 my.tradePay 的小程序页面。
建议先排查以下情况:
- 后端创建支付订单时选错了产品,例如使用手机网站支付或电脑网站支付,而不是适用于小程序的交易创建接口。
- 支付订单设置了
return_url,付款后收银台跳转到了其他页面。 - 付款过程中,调用支付的页面被关闭、重定向或重新创建,原页面中的回调无法继续执行。
- 回调名称、代码位置或日志查看方式有误,例如将
complete写成complate。 - 支付结果页并未真正退出。用户看到的“完成”只是页面内部跳转,没有让
my.tradePay调用返回。
支付是否成功,最终要通过服务端异步通知或主动查询交易状态来确认,不能只看前端的 success 回调。
先检查回调写法
complete 必须拼写正确,并与 success、fail 保持同级。不要写成 complate,也不要将它放进 success 内部。
Page({
async handlePay() {
console.log('准备调用 my.tradePay');
my.tradePay({
tradeNO: this.data.tradeNo,
success: (res) => {
console.log('tradePay success:', JSON.stringify(res));
this.confirmPaymentResult();
},
fail: (err) => {
console.error('tradePay fail:', JSON.stringify(err));
},
complete: (res) => {
console.log('tradePay complete:', JSON.stringify(res));
}
});
console.log('my.tradePay 已发起');
},
confirmPaymentResult() {
// 向业务服务器查询订单状态
},
onShow() {
console.log('支付发起页面 onShow');
},
onUnload() {
console.log('支付发起页面 onUnload');
}
});
如果“准备调用”和“已发起”都能输出,但支付完成后没有出现任何回调,需要继续确认页面是否被卸载,以及后端是否使用了错误的支付产品。
检查后端创建订单的方式
如果 my.tradePay 使用的 tradeNO 来自服务端,服务端应通过与支付宝小程序支付场景匹配的交易创建能力生成订单。不要将手机网站支付或电脑网站支付生成的表单、跳转地址交给小程序使用。
检查服务端实际调用的接口或 SDK 方法,特别注意是否误用了:
alipay.trade.wap.pay
alipay.trade.page.pay
这类支付产品通常包含浏览器跳转流程,支付完成后还可能根据 return_url 打开结果页面。如果用户看到“感谢您的消费”后没有回到小程序,也没有触发回调,这与误用网页支付产品的表现比较吻合。
小程序支付的常见流程如下:
小程序请求业务服务器
↓
业务服务器创建交易并返回 tradeNO
↓
小程序调用 my.tradePay
↓
支付宝收银台完成支付
↓
返回小程序
↓
小程序向业务服务器查询最终订单状态
服务端究竟该使用哪个接口、传入哪些参数,还要根据当前接入的支付宝支付产品和官方文档确定,不能只凭前端现象判断。
排查 return_url 和自定义完成页
检查创建支付订单时是否传入了 return_url,以及支付链路中是否配置了付款完成后的跳转地址。
如果小程序误用了 H5 支付产品,return_url 可能会在支付完成后直接打开网页。此时用户点击“完成”,触发的是网页导航,并没有关闭收银台并恢复原来的小程序调用,因此 JavaScript 回调不会执行。
可以暂时移除非必要的支付完成跳转配置,再用测试订单验证。如果移除后可以正常返回小程序,基本可以确定问题与支付产品或回跳配置有关。
确认发起支付的页面没有被销毁
下面的写法可能会关闭支付发起页:
my.tradePay({
tradeNO,
success: handleSuccess,
fail: handleFail,
complete: handleComplete
});
my.redirectTo({
url: '/pages/order/result'
});
调用支付后立即执行 redirectTo、reLaunch 或 navigateBack,或者由其他状态监听逻辑自动跳转,都可能让原页面无法继续接收回调。
可以通过生命周期日志判断页面状态:
Page({
onShow() {
console.log('payment page onShow');
},
onHide() {
console.log('payment page onHide');
},
onUnload() {
console.log('payment page onUnload');
}
});
如果付款过程中出现 onUnload,需要检查所有页面跳转、登录态刷新、路由守卫和订单状态监听逻辑。
在真机环境查看正确的日志
支付能力通常要在真机上验证。不要只看开发者工具里的普通控制台,因为真机支付后产生的日志不一定会自动显示在其中。
排查时可以这样做:
- 使用支付宝小程序开发工具的真机调试功能。
- 同时记录页面生命周期和三个支付回调。
- 调用前检查
tradeNO是否存在,但不要输出完整订单号或敏感签名。 - 留意真机调试过程中是否发生断连。
- 分别测试取消支付、支付失败和支付成功。
如果主动取消支付可以触发 fail 和 complete,只有支付成功时会跳到网页,问题大概率出在支付产品或成功回跳配置,而不是回调函数本身。
不要把前端回调作为支付依据
即使 success 正常执行,也不能直接将业务订单改为“已支付”。用户可能会关闭小程序、断网,或者停留在支付宝结果页。此时前端收不到回调,但服务端交易可能已经成功。
可以按下面的方式处理:
success: () => {
queryOrderStatus();
},
fail: () => {
queryOrderStatus();
},
complete: () => {
// 只处理 loading、按钮状态等界面逻辑
}
服务端负责确认最终支付结果:
- 验证支付宝异步通知的签名。
- 根据商户订单号或支付宝交易号主动查询交易状态。
- 对重复发送的通知做幂等处理。
- 只有服务端确认支付成功后,才更新业务订单。
页面重新显示时,也可以再查询一次:
Page({
onShow() {
if (this.data.waitingForPaymentResult) {
this.queryOrderStatus();
}
}
});
建议的排查顺序
- 确认回调名称是
complete,并补上fail回调。 - 在调用前后、三个回调以及
onShow、onHide、onUnload中加入日志。 - 确认传入了有效的
tradeNO,并且只使用当前支付方式要求的参数。 - 检查服务端实际调用的支付宝支付接口,排除
wap.pay、page.pay等网页支付产品。 - 检查是否设置了
return_url或其他支付成功后的跳转地址。 - 排除付款过程中执行
redirectTo、reLaunch、navigateBack等操作的情况。 - 使用真机调试,分别测试取消支付和支付成功。
- 根据服务端异步通知和交易查询结果确认订单,不要依赖前端回调。
如果完成这些检查后问题仍能稳定复现,提交问题时应提供脱敏后的 my.tradePay 调用代码、服务端创建交易时使用的接口名称、是否设置 return_url、页面生命周期日志、支付宝客户端版本和真机录屏。不要公开完整订单号、签名、用户标识或应用私钥。
备注:内容仅供参考。
