uniapp调用支付宝支付提示62001用户取消的原因
结论
在 uni-app 中,62001 表示支付流程被取消,但不一定是用户主动点击了“取消”,也不能据此判断 order_str 的签名或参数有问题。
支付宝参数自查通过后,可以重点检查以下情况:
orderInfo传给uni.requestPayment前经过了解析、编码或重新拼接,最终字符串与服务端生成的内容不一致。- 支付按钮被重复触发,多个支付请求互相覆盖。
- App 的支付宝支付模块、包名、Bundle ID、应用签名与支付宝开放平台中的应用配置不一致。
- 支付宝客户端未正常拉起,或者支付完成后无法正确回跳 App。
- 用户关闭了收银台、切换了应用,或支付流程被系统中断。
- 前端收到了“取消”回调,但订单实际上已经支付成功。
排查时不能只看 62001。还需要确认支付宝收银台是否拉起、失败发生在哪个阶段,并以服务端查询到的订单状态为准。
先确认 orderInfo 的传递方式
支付宝 App 支付使用的 orderInfo 应是服务端生成的完整签名字符串。前端收到后直接传入即可,不要将其转换为对象,也不要再次调用 encodeURIComponent、decodeURIComponent 或自行拼接。
uni.requestPayment({
provider: 'alipay',
orderInfo: orderStr,
success(res) {
console.log('支付宝支付回调成功:', res)
},
fail(err) {
console.error('支付宝支付回调失败:', JSON.stringify(err))
}
})
以下处理方式都可能破坏签名:
// 不要把 order_str 解析成对象后重新拼接
const params = parseOrderString(orderStr)
// 不要对完整 order_str 再次编码
const encoded = encodeURIComponent(orderStr)
// 不要把字符串包装成额外对象传入
uni.requestPayment({
provider: 'alipay',
orderInfo: {
orderStr
}
})
尤其要检查字符串中的 +、%、&、=、引号和空格。通过某些表单或 URL 参数传递时,或者解码方式不正确时,+ 可能会变成空格,导致传入 SDK 的内容与服务端生成的内容不同。
建议分别记录:
- 服务端完成签名后的
order_str - App 从接口响应中收到的原始字符串
- 调用
uni.requestPayment前的字符串
比较三者的长度和内容,确认字符串没有被修改。如果日志包含签名等敏感数据,只能用于受控调试,不要上传到公开平台。
判断错误发生在哪个阶段
62001 出现在不同阶段,排查方向也不同。
支付宝收银台完全没有拉起
先检查 App 的集成配置:
manifest.json是否勾选了支付宝支付模块。- 修改配置后,当前安装包是否重新云打包,或者重新制作了自定义基座。
- Android 包名和应用签名是否与支付宝开放平台登记的信息一致。
- iOS 的 Bundle ID、URL Scheme 等回跳配置是否正确。
- 当前支付宝应用是否已经开通 App 支付能力,应用环境与订单环境是否一致。
- 真机是否安装了能够正常运行的支付宝客户端。
- 是否混用了正式环境和沙箱环境。
如果只在 HBuilderX 中修改模块配置,却继续使用修改前的基座或安装包,新的配置不会自动进入旧包,需要重新打包后再验证。
收银台拉起后立即关闭或返回
需要检查:
order_str是否是本次请求新生成的订单。- 商户订单号是否被重复使用。
- 订单是否已经关闭、超时或完成支付。
- 服务端创建订单时使用的
app_id、私钥和支付宝公钥是否属于同一个开放平台应用。 product_code等 App 支付业务参数是否正确。- 拉起支付后,代码是否立即跳转页面、关闭页面或退出 WebView。
参数自查工具通常只能检查订单参数和签名结构,不能证明运行时的 App 配置、包签名、支付权限及回跳链路都没有问题。
收银台正常显示,关闭后提示 62001
这通常表示用户关闭了收银台,或者支付过程被系统中断。前端可以按“支付未完成”处理,但不要立即将服务端订单标记为失败。此时订单可能仍在处理中,也可能已经支付成功。
用户已经付款,但 App 仍提示取消
遇到这种情况,应重点检查回跳和订单状态确认:
- App 是否能从支付宝正常返回。
- 是否重复调用了
uni.requestPayment。 - 页面恢复时是否再次发起了支付。
- 是否将前一次请求的失败回调当成了当前订单的结果。
- 服务端是否收到支付宝异步通知。
- 调用支付宝订单查询接口后,订单是否已经成功。
客户端回调不能作为最终支付凭证。只要客户端结果与实际情况不一致,就应由服务端查询订单状态。
防止重复拉起支付
按钮连续点击、生命周期重复执行或接口重复返回,都可能同时发起多个支付请求。可以增加支付锁:
let paying = false
async function payWithAlipay() {
if (paying) {
return
}
paying = true
try {
const orderStr = await createOrder()
if (typeof orderStr !== 'string' || !orderStr.trim()) {
throw new Error('服务端未返回有效的 orderInfo')
}
await new Promise((resolve, reject) => {
uni.requestPayment({
provider: 'alipay',
orderInfo: orderStr,
success: resolve,
fail: reject
})
})
// 不直接以前端回调作为入账依据
await confirmOrderStatus()
} catch (err) {
console.error('支付宝支付未完成:', err)
// 取消或未知结果时仍查询服务端订单状态
await confirmOrderStatus()
} finally {
paying = false
}
}
还要检查 onShow、按钮点击事件和请求重试逻辑,确认其中没有重复发起支付的代码。
正确处理支付结果
支付流程可以按以下方式处理:
- App 请求业务服务端创建订单。
- 服务端调用支付宝接口并完成签名。
- 服务端将原始
order_str返回给 App。 - App 通过
uni.requestPayment拉起支付宝。 - 无论前端返回成功、失败还是取消,都向业务服务端查询订单状态。
- 服务端根据支付宝异步通知或
alipay.trade.query的查询结果更新订单。 - App 只展示服务端确认后的结果。
前端可以这样处理:
uni.requestPayment({
provider: 'alipay',
orderInfo: orderStr,
async success() {
const order = await queryBusinessOrder(orderNo)
showOrderResult(order)
},
async fail(err) {
console.warn('客户端支付回调:', err)
const order = await queryBusinessOrder(orderNo)
if (order.status === 'PAID') {
uni.showToast({
title: '支付成功',
icon: 'success'
})
} else {
uni.showToast({
title: '支付未完成',
icon: 'none'
})
}
}
})
queryBusinessOrder 应查询业务服务端。App 不能持有支付宝私钥,也不能直接使用私钥调用支付宝接口。
建议的排查顺序
- 建立一个最简单的测试页面,只保留一次
uni.requestPayment调用。 - 确认
orderInfo是字符串,并逐字符比对,确保它与服务端生成的原始内容完全一致。 - 记录完整的
fail回调对象,不要只截取errMsg。 - 观察支付宝收银台是否成功拉起,并判断错误发生在拉起前、收银台内还是回跳后。
- 使用全新的商户订单号测试,避免重复订单造成干扰。
- 核对支付宝开放平台中的
app_id、App 支付能力、Android 包名与签名,以及 iOS Bundle ID 和回跳配置。 - 修改
manifest.json或原生支付配置后,重新打包并安装。 - 换一台真机、另一个支付宝账号和稳定的网络进行交叉验证。
- 调用服务端订单查询接口,确认真实的交易状态。
- 如果仍无法定位,整理 App 平台、uni-app/HBuilderX 版本、打包方式、完整错误对象及支付宝 SDK 返回信息,再提交给 DCloud 或支付宝技术支持。
注意事项
- 不要因为收到
62001就直接关闭业务订单。 - 不要只根据
success回调发货或开通权益。 - 支付宝私钥必须保存在服务端,不能写入 uni-app 前端代码。
- 不要公开完整的
order_str,其中可能含有商户信息、订单信息和签名。 - Android 和 iOS 需要分别测试,因为两端的包标识、签名及回跳机制不同。
- 如果自定义基座可以支付,而正式包不能,重点比较两者的包名、签名和模块配置。
- 如果正式包可以支付,而自定义基座不能,通常也与基座配置或签名不符合支付宝平台登记信息有关。
参数自查通过后,应优先确认传入 SDK 的字符串是否保持原样、支付是否被重复拉起、安装包的身份配置是否匹配,以及服务端查询到的真实订单状态。
备注:内容仅供参考。
