uni-app ios内购问题

uni-app ios内购问题

操作步骤

根据uni-pay组件提示配置好支付流程并且在、common/uni-config-center/uni-pay/config.js中设置沙箱模式为true,第一次调用后不触发@success事件,第二次再次调用verifyRes显示值为{},排查云端函数调用,verifyReceiptFromAppleiap方法,结果为:[uni-pay-co/ac1cc31c1762765764968164560/1821ms/ERROR] undefined
undefined请求响应状态: fail。

预期结果

支付成功

实际结果

无法触发支付成功事件

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](/user/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](/user/success)="onAppleSuccess"  
            @fail="onAppleFail" @cancel="onAppleCancel"></uni-pay>  
</template>  

<script setup>  
import {  
    ref,  
    computed,  
    onMounted  
} from 'vue'  
import {  
    useStore  
} from 'vuex'  
import {  
    onShow  
} from '[@dcloudio](/user/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>

更多关于uni-app ios内购问题的实战教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于uni-app ios内购问题的实战教程也可以访问 https://www.itying.com/category-93-b0.html


已解决,多余填了共享密钥

回到顶部