2022年uniapp实现IOS应用内支付最完整流程

6,312 阅读7分钟

背景

前段时间项目上使用uniapp实现了一个App,安卓发布没有问题,但是IOS上架是个大问题。因为是数字藏品类的App,属于虚拟商品,只能使用应用内支付。在网上查了很多资料都没有找到很完整的教程,最后也是花了很长时间才解决上架问题。这里我将自己的开发经验和很多资料的精华部分汇总起来,给到同样遇到困难的小伙伴们。

概念

IOS内购是指苹果 App Store 的应用内购买,即In-App Purchase,简称IAP(以下本文关于内购都简称为IAP),是苹果为 App 内购买虚拟商品或服务提供的一套交易系统。为什么我们需要掌握IAP这套流程呢,因为App Store审核指南规定:

如果您想要在 app 内解锁特性或功能 (解锁方式有:订阅、游戏内货币、游戏关卡、优质内容的访问 限或解锁完整版等),则必须使用 App 内购买项目。App 不得使用自身机制来解锁内容或功能, 如许可证密钥、增强现实标记、二维码等。App 及其元数据不得包含按钮、外部链接或其他行动号 召用语,以指引用户使用非 App 内购买项目机制进行购买。

大概的意思就是对于虚拟商品,不得使用支付宝、微信、Apple Pay等第三方支付,只能使用自己的IAP支付,这里需要强调一下Apple Pay和IAP不是同一种支付方式,并且IAP会和苹果公司三七分成。

IOS证书和描述文件申请

开发前我们需要申请两套IOS证书和描述文件,一套测试的,一套正式的。uniapp官方给出了详细的申请流程:ask.dcloud.net.cn/article/152

账户准备

在App Store Connect后台填写银行账户信息,这里推荐另一篇的文章,写的还是很详细的:juejin.cn/post/704925…

image.png

沙箱测试账户和TestFlight账户准备

1、沙箱测试: 需要注意的是,沙箱测试人员的注册申请时必须使用没有注册过的邮箱的。申请完成后,在IOS或者IPAD上登录沙箱测试人员的账号,并且在设置 — App Store — 沙盒账户中也登录账户。

image.png

image.png

2、TestFlight账户 TestFlight账户需要是正式账户,通过沙箱测试后,在上架审核前最好使用正式账户进行一次付款测试。申请流程:在用户和访问添加开发者->邮件邀请成功后在TestFlight添加开发者。开发者测试的时候需要在手机上App Store下载TestFlight App,每一次更新版本都可以收到消息。

image.png

image.png

image.png

配置商品

创建流程: App内购买项目-> App内购买项目+ -> 创建。这里的产品ID是需要自行配置的,产品ID具有唯一性,为了避免与其他app的产品ID重复,建议使用本App特殊标识字符串+业务管理的商品ID。类型选择有4种,官方的解释是这样的,具体可根据自己的业务进行选择:

**消耗型项目**
提供不同类型的消耗型项目,例如游戏中用来推动进程的生命或宝石,约会 App 中用来提升个人资料曝光度的升
级,或者社交媒体 App 中的创作者数字提示。消耗型的 App 内购买项目在使用之后即失效,并可再次购买。采
用免费增值业务模式的 App 和游戏中经常提供这种项目。

**非消耗型项目**
提供非消耗型项目,解锁更多进阶功能。这些功能只需购买一次,并且不会过期。例如,照片 App 中的额外滤
镜、插图 App 中的额外画笔或游戏中的皮肤。非消耗型 App 内购买项目可以提供家人共享。

**自动续期订阅**
提供对 App 中内容、服务或进阶功能的持续访问权限。此类订阅会自动续期,除非用户选择取消。常见用例包括
提供媒体或内容库 (例如视频、音乐或文章) 访问权限、软件即服务 (例如云存储、效率或图形与设计) 以及教
育等等。自动续期订阅可以提供家人共享。

**非续期订阅**
对相关服务或内容提供有时限性的访问权限 (例如游戏中内容的季度订阅)。这种类型的订阅不会自动续期,如果
想要继续访问,用户需要在订阅结束时购买新的订阅。

在产品信息页配置好信息后,点击“存储”,存储后Status变成“准备提交”就说明产品配置成功了。这里有一点需要注意的是:配置好产品后就可以进行沙箱或者TestFlight测试了,不需要进行审核

image.png

image.png

image.png

内购流程

uniapp内购流程主要分为4步:从服务端获取产品ID->校验苹果支付通道->校验产品ID->调用uniapp提供的支付函数,对于支付失败的情况可以根据业务需求在uni.requestPayment函数的fail中配置逻辑。

1、从服务端获取产品ID

由于苹果的产品ID是自定义的,所以最好在自己app的管理后台设置好,这样就可以实现动态上架,需要注意的是:只有苹果的产品ID审核通过后再配置,不然产品ID是无效的。我们前端可以在支付前从服务端获取到苹果产品ID,为下一步做准备。

async pay() {
    let that = this
    let data = {
            ticket: that.payParams.ticket,
            randStr: that.payParams.randStr,
            captcha: that.payParams.captcha,
            nftId: that.payParams.nftId,
            price: that.payParams.price,
            applet: false, //是否是小程序登陆
            payType: that.payTypeList[that.currentPay].value,
    }

    if (that.isCanPay) {
        //给支付按钮上锁
        that.isCanPay = false
        let res = await payAppGood(data)
            if (res) {
                //苹果支付
                let applePayObj={
                        productId:res.data.product_id, //产品ID
                        outTradeNo:res.data.out_trade_no //业务需要的交易ID,这个不是必须的
                }
                //校验苹果支付通道
                that.getAppleChannel(applePayObj)
            } 
    }
}

2、校验苹果支付通道

getAppleChannel(productData) {
    let that = this
    uni.showLoading({
        title: '检测支付环境...',
        mask: true
    })
    plus.payment.getChannels((channels) => {
        for (let i in channels) {
            // 判断是否苹果支付
            if (channels[i].id === 'appleiap') {
                    //这里的iapChannel需要提前在data中声明
                    that.iapChannel = channels[i]
                    //校验产品ID是否上架App Store
                    that.compareAppleProduce(productData)
            }
        }
    },(err) => {
            uni.hideLoading()
            uni.showToast({
                title: "获取支付通道失败:" + err.message,
                icon: 'none'
            })
    })
}

3、校验产品ID

//校验苹果产品可以支付
compareAppleProduce(productData) {
        let that = this
        that.iapChannel.requestOrder([productData.productId], (event) => {
                uni.hideLoading()
                //这里使用for循环是为了适配多产品ID情况
                for (let index in event) {
                        //需要产品id和自定义交易id
                        that.applePay(productData.productId,productData.outTradeNo)
                }
        }, (err) => {
                uni.hideLoading()
                uni.showToast({
                        title: "该商品未录入:" + err.message,
                        icon: 'none'
                })
        })
}

4、调用uniapp提供的支付函数

//苹果支付
applePay(productId,outTradeNo) {
    let that = this
    uni.showLoading({
        title: '支付中...',
        mask: true
    })
    uni.requestPayment({
        provider: 'appleiap',
        orderInfo: {
            productid: productId
        },
        async success(res) {
            let data = {
                    outTradeNo: outTradeNo,
                    transactionId:res.transactionIdentifier,
                    payload: res.transactionReceipt
            }
            //成功后的回调,根据业务自定义逻辑
            let result = await applePayCallback(data)
            if (result) {
                //给支付按钮解锁
                that.isCanPay = true
                //自定义提示,可以忽略
                that.payTipShow('支付成功')
            }
        },
        fail(e) {
            //给支付按钮解锁
            that.isCanPay = true
            //自定义提示,可以忽略
            that.payTipShow('支付已取消')
        },
        complete() {
            uni.hideLoading()
        }
    })
}

测试发布

在发布前记得在manifest.json文件中勾选Apple应用内支付。发布测试ipa流程:点击Hbuilder上方的发行->原生App-云打包,这里的profile文件和密钥证书是最初申请的测试IOS证书和描述文件,打包后可以将生成的ipa包上传到“蒲公英”上,手机访问链接就可以安装了,很方便。只有在苹果管理后台添加的设备才可以安装

image.png

image.png

正式发布

在沙箱测试完没有问题后就可以进行正式发布了,正式发布打包同测试发布打包一样,只需要将测试的IOS证书和描述文件换成正式的就可以了。

上传ipa需要准备一台Mac,并且在App Store下载Transporter,将打包好的ipa拖入Transporter进行交付。

交付后需要使用TestFlight进行一次正式环境支付,如果没有问题就可以直接填写App信息提交审核了。App审核可能会遇到各种各样的问题,这里就需要开发者根据要求耐心修改,直到上架。

结语

最后小编祝大家App审核畅通无阻,有问题可以在评论区提出,大家一起解决。