支付宝小程序支付成功后点击完成出现异常跳转或关闭
结论
支付宝收银台的“完成”按钮通常没有单独的点击事件可供监听。小程序通过 my.tradePay 发起支付后,收银台返回小程序时会触发 success、fail 或 complete 回调。支付是否最终成功,应以服务端异步通知和主动查单结果为准。
支付后有时跳到淘宝页面,有时直接关闭小程序,通常不是同一个支付回调随机产生了不同结果,而可能与以下情况有关:
- 扫码入口、外部页面或收银台的返回链路不同。
- 支付完成后,小程序执行了
navigateBack、reLaunch或关闭页面等跳转逻辑。 - 小程序通过临时扫码会话进入,返回时页面栈中没有可以恢复的上一页。
- 实际链路不只是小程序支付,还经过了 H5、中间页、短链接或其他应用页面。
- 二维码内容、订单返回地址,或服务端创建交易时使用的应用身份不一致。
- 淘宝页面可能是收银台结果页中的运营入口,用户点击的并不是单纯的“完成”按钮。仅凭页面现象无法确认,需要结合录屏和日志判断。
“完成”后如何回调
典型的小程序支付流程如下:
- 服务端创建支付宝交易并取得
trade_no。 - 小程序调用
my.tradePay。 - 支付宝收银台显示支付页面。
- 用户完成支付,然后关闭收银台或点击完成。
- 收银台将控制权交还给调用方,
my.tradePay返回结果。 - 支付宝另行向商户服务端发送异步支付通知。
示例:
my.tradePay({
tradeNO: tradeNo,
success: (res) => {
const resultCode = String(res.resultCode || '');
if (resultCode === '9000') {
// 这里只能表示客户端收到“支付成功”结果,
// 页面可进入“确认支付结果”状态,然后向服务端查单。
this.confirmPayment(orderId);
return;
}
if (resultCode === '8000') {
// 结果处理中,应向服务端查询订单状态。
this.confirmPayment(orderId);
return;
}
if (resultCode === '6001') {
my.showToast({
content: '支付已取消'
});
return;
}
my.showToast({
content: `支付未完成:${resultCode}`
});
},
fail: (error) => {
console.error('my.tradePay fail', error);
// fail 也不能直接认定订单未支付,
// 网络中断时用户仍可能已经完成付款。
this.confirmPayment(orderId);
},
complete: (res) => {
// 无论调用成功或失败都可能进入 complete。
// 它不是“用户点击完成”的专用事件。
console.log('my.tradePay complete', res);
}
});
小程序应通过自己的服务端确认订单状态:
confirmPayment(orderId) {
my.request({
url: 'https://example.com/api/orders/payment-status',
method: 'GET',
data: {
orderId
},
success: ({ data }) => {
if (data.status === 'PAID') {
my.redirectTo({
url: `/pages/pay-result/pay-result?orderId=${orderId}`
});
} else {
my.showToast({
content: '支付结果确认中'
});
}
},
fail: (error) => {
console.error('query payment status failed', error);
}
});
}
不要在 complete 中直接返回或关闭页面,例如:
complete: () => {
// 不建议:支付失败、取消或异常时同样可能执行
my.navigateBack();
}
为什么第一次跳到淘宝页面
先检查从扫码到支付之间是否经过了外部页面。
如果二维码打开的是 H5、短链接或其他应用页面,再由该页面唤起小程序,收银台结束后就可能返回调用链中的外部页面。如果外部页面配置了淘宝链接、广告落地页或兜底跳转,用户看到的现象就是“点击完成后进入淘宝”。
还需要确认用户看到的“淘宝精品”是否为支付结果页中的推荐入口。建议录制完整操作过程,确认用户点击的具体位置,并对比跳转前后的支付宝标题栏、页面样式和右上角菜单信息。
同时检查以下内容:
- 解码后的二维码原始内容是否确实为支付宝小程序码。
- 两次扫码得到的二维码内容是否完全相同。
- 链路中是否存在 302 重定向、短链接、H5 中间页或 Universal Link。
- 创建订单所用的
app_id是否与当前小程序及支付产品配置匹配。 - 代码是否根据
scene、query或订单状态执行了不同跳转。 - 支付结果页是否包含淘宝商品、广告或其他外部链接。
如果同一套代码通过不同二维码进入时表现不同,应优先检查二维码入口和服务端订单参数,而不是只排查 my.tradePay。
为什么第二次直接关闭小程序
通过扫码打开的小程序可能处于一次性启动链路中。支付完成后,如果代码尝试返回上一页,而当前页面已经位于页面栈底部,用户看到的结果就可能是小程序退出或关闭。
常见代码包括:
my.navigateBack();
还要检查支付成功、页面 onShow 或订单状态变化后执行的条件跳转:
if (paid) {
my.navigateBack();
}
重复支付时也可能进入不同的代码分支,例如:
- 第一次订单状态为待支付,因此进入支付结果页。
- 第二次发现订单已支付,代码直接返回。
- 扫码参数被缓存,第二次启动仍在读取旧订单。
onShow和支付回调同时执行了跳转。success、complete和查单回调分别触发跳转,导致连续返回。- 同一个按钮被重复绑定,或者
my.tradePay被重复调用。
可以为跳转增加一次性保护:
Page({
data: {
handlingPaymentResult: false
},
handlePaid(orderId) {
if (this.data.handlingPaymentResult) {
return;
}
this.setData({
handlingPaymentResult: true
});
my.redirectTo({
url: `/pages/pay-result/pay-result?orderId=${orderId}`
});
}
});
支付结果页通常更适合使用 redirectTo,这样可以避免用户再次回到支付发起页。具体使用 redirectTo、navigateTo 还是 reLaunch,需要根据实际页面层级决定。页面栈状态不明确时,不要直接调用 navigateBack。
建议的排查步骤
1. 记录完整支付结果
至少记录以下信息:
- 订单号和支付宝交易号。
my.tradePay的返回值。- 发起支付的页面路径。
- 扫码带入的参数。
- 支付前后的页面栈。
- 小程序
onShow、页面onShow和支付回调的执行顺序。 - 每次跳转调用的目标地址。
调试时可以输出页面栈:
function logPageStack(stage) {
const pages = getCurrentPages();
console.log(stage, pages.map((page) => ({
route: page.route
})));
}
分别在发起支付、收到支付回调和页面重新显示时调用:
logPageStack('before tradePay');
logPageStack('tradePay success');
logPageStack('page onShow');
2. 全局检查跳转代码
搜索这些 API,并逐一检查调用位置:
my.navigateBack
my.navigateTo
my.redirectTo
my.reLaunch
my.switchTab
重点查看 success、complete、页面 onShow、定时器和订单轮询中是否存在重复跳转。
3. 对比两次扫码的启动参数
确认两次扫码是否进入同一页面,是否携带相同的 scene 和订单参数,同时检查第二次扫码是否错误复用了第一次支付的订单号。
启动参数的具体字段可能因入口类型和基础库能力而不同,应以实际日志和当前支付宝小程序文档为准,不能只根据页面表现判断入口。
4. 核对服务端交易信息
服务端应保存并核对:
- 商户订单号。
- 支付宝交易号。
- 创建交易时使用的应用身份。
- 订单金额。
- 异步通知处理结果。
- 主动查单结果。
同一个商户订单号通常不应作为全新订单重复支付。用户再次扫码时,应先查询订单状态。订单已经支付,就直接展示支付结果,不要再次调起收银台。
注意事项
客户端返回 resultCode === '9000',不能作为发货或记账的唯一依据。客户端可能断网或退出,回调也可能丢失,还可能出现结果处理中等状态。服务端需要验签支付宝的异步通知,必要时再调用支付宝交易查询接口确认结果。
“点击完成后跳到哪里”还会受到支付宝客户端版本、扫码入口、页面栈和支付产品接入方式的影响。如果缺少二维码原始内容、my.tradePay 返回日志、页面跳转日志和服务端交易参数,就无法判断淘宝页面由哪一方产生。分别记录两次操作的完整链路,才能确认跳转发生在支付宝收银台内部、小程序代码中,还是扫码前经过的外部页面中。
备注:内容仅供参考。
