根据uni-pay组件提示配置好支付流程并且在、common/uni-config-center/uni-pay/config.js中设置沙箱模式为true,第一次调用后不触发@success事件,第二次再次调用verifyRes显示值为{},排查云端函数调用,verifyReceiptFromAppleiap方法,结果为:[uni-pay-co/ac1cc31c1762765764968164560/1821ms/ERROR] undefined
undefined请求响应状态: fail。
- 发布:2025-11-10 17:30
- 更新:2025-11-10 17:30
- 阅读:11
产品分类: uniapp/App
PC开发环境操作系统: Windows
PC开发环境操作系统版本号: win11
HBuilderX类型: 正式
HBuilderX版本号: 4.84
手机系统: iOS
手机系统版本号: iOS 26
手机厂商: 苹果
手机机型: iphone13
页面类型: vue
vue版本: vue3
打包方式: 云端
项目创建方式: HBuilderX
操作步骤:
预期结果:
支付成功
支付成功
实际结果:
无法触发支付成功事件
无法触发支付成功事件
bug描述:
根据uni-pay组件提示配置好支付流程并且在、common/uni-config-center/uni-pay/config.js中设置沙箱模式为true,第一次调用后不触发@success事件,第二次再次调用verifyRes显示值为{},排查云端函数调用,verifyReceiptFromAppleiap方法,结果为:[uni-pay-co/ac1cc31c1762765764968164560/1821ms/ERROR] undefined
undefined请求响应状态: fail。
可能是什么问题,该怎么解决,
我在。error.js文件里加了54002错误码进行调试时,就会显示54002订单未支付
引用支付组件代码如下
<template>
<view class="rechargePage" @click="onRootClick">
<!-- 底部按钮 -->
<view class="bottom-bar">
<view class="agree-text">充值即代表同意<text style="color: #9e60ff;">《泡泡充值协议》</text></view>
<view class="btn-primary" @click="openPaySheet">立即充值</view>
</view>
<!-- 支付失败弹窗 -->
<view class="dialog-mask" v-if="showPayFail" @click.self="closeFailDialog">
<view class="dialog-panel box_16">
<view class="text-group_7">
<view class="dialog-title text_21">支付失败</view>
<view class="dialog-subtitle text_22">支付遇到问题,请尝试重新支付</view>
</view>
<view class="dialog-actions box_17">
<view class="btn-cancel button_1" @click="closeFailDialog"><text class="text_23">取消</text></view>
<view class="btn-primary button_2" @click="retryPay"><text class="text_24">继续支付</text></view>
</view>
</view>
</view>
<!-- 选择支付方式弹窗(底部弹出) -->
<uni-popup ref="paySheetRef" type="bottom" :mask-click="true" @maskClick="closePaySheet">
<view class="pay-sheet">
<view class="sheet-header">
<view class="sheet-title font-medium">选择支付方式</view>
<uni-icons class="sheet-close" type="closeempty" size="30rpx" color="#363A44"
@click="closePaySheet"></uni-icons>
</view>
<view class="sheet-amount font-medium">¥ {{ currentPriceDisplay }}</view>
<view class="method-list">
<view class="method-item" v-for="m in payMethods" :key="m.id" @click="selectMethod(m.id)">
<view class="method-left">
<image class="method-icon" :src="m.icon" mode="aspectFit"></image>
<text class="method-name">{{ m.name }}</text>
</view>
<view class="radio" v-if="selectedMethod!=m.id"></view>
<image class="radio" src="/static/pay/check.png" mode="" v-else></image>
</view>
</view>
<view class="sheet-confirm btn-primary" @click="confirmPay">确认</view>
</view>
</uni-popup>
<!-- uni-pay 融合支付组件 -->
<uni-pay ref="payRef" @success="onPaySuccess" @fail="onPayFail" @cancel="onPayCancel"></uni-pay>
<!-- 苹果内购支付组件 -->
<uni-pay v-if="applePayEnabled" ref="applePayRef" :debug="true" :adpid="adpid"
return-url="/pages/pay_result/pay_result" @mounted="handleApplePayMounted" @success="onAppleSuccess"
@fail="onAppleFail" @cancel="onAppleCancel"></uni-pay>
</view>
</template>
<script setup>
import {
ref,
computed,
onMounted
} from 'vue'
import {
useStore
} from 'vuex'
import {
onShow
} from '@dcloudio/uni-app'
import {
getTotalAmount,
getPayComboList,
getHasPassword,
prePay,
wxQuery
} from '@/api/pay.js'
const store = useStore()
const scrollTop = ref(0)
const scrollViewHeight = ref(0)
const balance = ref(0)
const balanceDisplay = computed(() => balance.value.toLocaleString())
const totalAmount = ref(0)
const remainingAmount = ref(0)
const spentAmount = ref(0)
const payPassword = ref('')
const isOpen = ref('n')
const amountList = ref([])
const currentId = ref(undefined)
const currentPriceDisplay = computed(() => {
const cur = amountList.value.find(i => i.id === currentId.value)
return cur ? cur.price.toFixed(2) : '0.00'
})
const showPayFail = ref(false)
const showPaySheet = ref(false)
const paySheetRef = ref(null)
const selectedMethod = ref('wechat')
const payMethods = ref([{
id: 'wechat',
name: '微信支付',
icon: '/static/pay/weixin.png'
},
// {
// id: 'alipay',
// name: '支付宝支付',
// icon: '/static/pay/alipay.png'
// },
// {
// id: 'unionpay',
// name: '云闪付',
// icon: '/static/pay/unionpay.png'
// }
])
const applePayEnabled = ref(false)
const appleLoading = ref(false)
const applePayRef = ref(null)
const adpid = ref('1000000001')
const appleOrderNo = ref('')
const appleOutTradeNo = ref('')
const hasPassword = ref(false)
const showMenu = ref(false)
const menuTop = computed(() => {
// 自定义导航:状态栏高度 + 导航栏内容高度(约 88rpx)
return (store.state.barHeight || 0) + 88
})
// uni-pay 组件引用
const payRef = ref(null)
function onRootClick() {
if (showMenu.value) {
showMenu.value = false
}
}
function createAppleOrder() {
if (!applePayEnabled.value) {
return
}
if (!currentId.value) {
uni.showToast({
title: '请选择充值套餐',
icon: 'none'
})
return
}
const currentCombo = amountList.value.find(item => item.id === currentId.value)
if (!currentCombo) {
uni.showToast({
title: '请选择充值套餐',
icon: 'none'
})
return
}
if (!applePayRef.value || typeof applePayRef.value.createOrder !== 'function') {
uni.showToast({
title: '苹果支付未初始化',
icon: 'none'
})
return
}
const orderNo = `ios${Date.now()}`
appleOrderNo.value = orderNo
appleOutTradeNo.value = orderNo
appleLoading.value = true
uni.showLoading({
title: '发起支付...'
})
try {
applePayRef.value.createOrder({
provider: 'appleiap',
order_no: appleOrderNo.value,
out_trade_no: appleOutTradeNo.value,
type: 'appleiap',
productid: 'paopao_coin_120',
custom: {
comboId: currentId.value,
coin: currentCombo.coin,
price: currentCombo.price
}
})
} catch (err) {
appleLoading.value = false
uni.hideLoading()
console.error('苹果内购下单失败', err)
uni.showToast({
title: '下单失败,请重试',
icon: 'none'
})
}
}
function handleApplePayMounted() {
if (applePayRef.value && typeof applePayRef.value.appleiapRestore === 'function') {
applePayRef.value.appleiapRestore()
}
}
function onAppleSuccess(res) {
appleLoading.value = false
uni.hideLoading()
console.log('苹果内购支付成功', res)
if (res && res.user_order_success) {
uni.showToast({
title: '支付成功',
icon: 'success'
})
setTimeout(() => {
fetchTotalAmount()
uni.navigateTo({
url: '/pages/pay_result/pay_result?status=success'
})
}, 1500)
} else {
uni.showToast({
title: '支付结果待确认',
icon: 'none'
})
}
}
function onAppleFail(err) {
appleLoading.value = false
uni.hideLoading()
console.error('苹果内购支付失败', err)
uni.showToast({
title: '支付失败,请重试',
icon: 'none'
})
}
function onAppleCancel() {
appleLoading.value = false
uni.hideLoading()
uni.showToast({
title: '支付已取消',
icon: 'none'
})
}
onMounted(() => {
const sysInfo = uni.getSystemInfoSync();
const pxToRpx = 750 / sysInfo.windowWidth;
const topBarHeight = store.state.barHeight + 88;
scrollViewHeight.value = sysInfo.windowHeight * pxToRpx - topBarHeight - 160;
applePayEnabled.value = sysInfo.platform === 'ios'
})
// 拉取钱包总额信息
async function fetchTotalAmount() {
try {
const res = await getTotalAmount()
const data = (res && (res.root || res.data)) || res || {}
totalAmount.value = Number(data.totalAmount || 0)
remainingAmount.value = Number(data.remainingAmount || 0)
spentAmount.value = Number(data.spentAmount || 0)
payPassword.value = data.payPassword || ''
isOpen.value = data.isOpen || 'n'
balance.value = remainingAmount.value
} catch (e) {
console.error('获取总额失败', e)
}
}
onShow(() => {
const platform = uni.getSystemInfoSync().platform
applePayEnabled.value = platform === 'ios'
fetchTotalAmount()
fetchHasPwd()
fetchPayCombos()
if (typeof plus !== 'undefined' && plus.payment && typeof plus.payment.getChannels === 'function') {
plus.payment.getChannels(function(channels) {
for (let i = 0; i < channels.length; i++) {
const channel = channels[i]
if (channel.id === 'appleiap') {
console.log('已获取苹果内购通道')
break
}
}
}, function(e) {
console.log('获取iap支付通道失败:' + e.message)
})
} else {
console.log('当前环境不支持获取苹果内购通道')
}
if (applePayEnabled.value && applePayRef.value && typeof applePayRef.value.appleiapRestore ===
'function') {
applePayRef.value.appleiapRestore()
}
})
function toBack() {
uni.navigateBack({
delta: 1
})
}
function toSetPayPwd() {
uni.navigateTo({
url: '/pages/pay_password/pay_password'
})
}
function toModifyPayPwd() {
showMenu.value = false
uni.navigateTo({
url: '/pages/pay_forget_password/pay_forget_password'
})
}
function toDisablePayPwd() {
showMenu.value = false
uni.navigateTo({
url: '/pages/pay_disable_password/pay_disable_password'
})
}
function toggleMenu() {
showMenu.value = !showMenu.value
}
function toDetail() {
uni.navigateTo({
url: '/pages/pay_details/pay_details'
})
}
function selectAmount(item) {
currentId.value = item.id
}
function openPaySheet() {
if (!currentId.value) {
uni.showToast({
title: '请选择充值套餐',
icon: 'none'
})
return
}
if (applePayEnabled.value) {
if (appleLoading.value) {
return
}
createAppleOrder()
return
}
if (paySheetRef.value && typeof paySheetRef.value.open === 'function') {
paySheetRef.value.open('bottom')
} else {
showPaySheet.value = true
}
}
function closePaySheet() {
if (paySheetRef.value && typeof paySheetRef.value.close === 'function') {
paySheetRef.value.close()
}
showPaySheet.value = false
}
function selectMethod(id) {
selectedMethod.value = id
}
function confirmPay() {
closePaySheet()
openUniPay(selectedMethod.value)
}
/**
* 使用 uni-pay 融合支付
*/
function openUniPay(payType = 'wechat') {
if (!currentId.value) {
uni.showToast({
title: '请选择充值套餐',
icon: 'none'
})
return
}
// 获取当前选中的套餐信息
const currentCombo = amountList.value.find(i => i.id === currentId.value)
if (!currentCombo) {
uni.showToast({
title: '请选择充值套餐',
icon: 'none'
})
return
}
// 先调用后端预下单生成订单号
uni.showLoading({
title: '生成订单...'
})
prePay({
comboId: currentId.value,
payType: payType
})
.then((res) => {
uni.hideLoading()
const order_no = res.root
const out_trade_no = order_no
// 金额从套餐列表取,若后端返回金额则优先使用
const amountYuan = currentCombo.price
const total_fee = amountYuan * 100
const description = (typeof root === 'object' && root.description) || `充值${currentCombo.coin}泡泡币`
if (payRef.value) {
payRef.value.open({
total_fee: total_fee,
order_no: order_no,
out_trade_no: out_trade_no,
description: description,
type: 'recharge',
custom: {
comboId: currentId.value,
coin: currentCombo.coin,
price: amountYuan,
payType: payType
}
})
} else {
uni.showToast({
title: '支付组件未初始化',
icon: 'none'
})
}
})
.catch((err) => {
uni.hideLoading()
console.error('预下单失败', err)
uni.showToast({
title: '下单失败,请重试',
icon: 'none'
})
})
}
/**
* 支付成功回调
*/
function onPaySuccess(res) {
console.log('支付成功', res)
uni.showToast({
title: '支付成功',
icon: 'success'
})
// 支付成功后查询微信订单号
try {
const outTradeNo = res && res.pay_order && res.pay_order.out_trade_no
console.log(outTradeNo)
if (outTradeNo) {
wxQuery({
outTradeNo
}).then(r => {
console.log('wxQuery 查询结果', r)
}).catch(e => {
console.warn('wxQuery 查询失败', e)
})
}
} catch (e) {
console.warn('调用 wxQuery 异常', e)
}
// 刷新余额
setTimeout(() => {
fetchTotalAmount()
// 跳转到支付结果页
uni.navigateTo({
url: '/pages/pay_result/pay_result?status=success'
})
}, 1500)
}
/**
* 支付失败回调
*/
function onPayFail(err) {
console.error('支付失败', err)
showPayFail.value = true
}
/**
* 支付取消回调
*/
function onPayCancel() {
console.log('支付取消')
uni.showToast({
title: '支付已取消',
icon: 'none'
})
}
function closeFailDialog() {
showPayFail.value = false
}
function retryPay() {
showPayFail.value = false
// 重新发起支付
openUniPay()
}
// 拉取可购买套餐列表,amountList为请求结果
async function fetchPayCombos() {
try {
// 根据当前平台传入 comboPlatformType(ios/android)
const platformType = uni.getSystemInfoSync().platform // 'ios' | 'android'
const res = await getPayComboList({
comboPlatformType: platformType
})
const list = (res && (res.root || res.data)) || []
amountList.value = (Array.isArray(list) ? list : []).map(it => ({
id: it.id,
coin: Number(it.comboCoinNumber || 0),
price: Number(it.comboPrice || 0)
}))
if (amountList.value.length > 0) {
// 默认选中第一项
currentId.value = amountList.value[0].id
} else {
currentId.value = undefined
}
} catch (e) {
console.error('获取套餐列表失败', e)
}
}
// 查询是否存在支付密码
async function fetchHasPwd() {
try {
const res = await getHasPassword()
// 后端返回:root 为 boolean
const value = typeof res === 'boolean' ? res : (res && (res.root ?? res.data))
hasPassword.value = Boolean(value)
} catch (e) {
console.error('查询是否存在支付密码失败', e)
hasPassword.value = false
}
}
</script>