【苹果内购支付】关于uniapp拉起苹果内购支付注意事项、实现步骤以及踩过的坑
前言
Hello!又是很长时间没有写博客了,因为最近又开始从事新项目,也是第一次接触关于uniapp开发原生IOS应用的项目,在这里做一些关于我在项目中使用苹果内购支付所实现的方式以及要注意的事项,希望能给正在做uniapp开发ios应用需要使用苹果内购支付的小伙伴一些帮助!
问题
为什么开发ios应用需要使用苹果内购支付?
原因在于,苹果要求所有开发者在上架Appstore中的应用,如果应用中出现了虚拟商品
的购买,必须使用苹果内购支付,并且绝对不能出现其他支付方式,例如微信、支付宝等支付方面的sdk,当然,如果你不怕被苹果下架的风险,你可以尝试使用webview跳转的方式,但是如果你的代码中使用了其他支付方式的sdk或者代码,是很大可能无法通过苹果严格的审核的。
ios内购为什么要专门拿出来说,对比其他支付方式有什么区别吗?
首先,他与微信、支付宝等都属于支付渠道的一种,本质上没有区别,但是由于苹果服务器的原因,导致一些非常特殊的问题,例如:回调时间长甚至没有回调、掉单、回调异常等情况,这种情况对比其他支付方式真的很鸡肋,特别是在uniapp的开发环境下,居然没有超时的回调,简直是大坑,不过这个在后面我会提到解决方案。
ios内购,事务
苹果支付走的是事务列表,每生成一笔订单就会走一笔订单,如果已经完成的订单需要使用苹果提供的关闭订单的api来进行关闭订单,否则会出现回调有误的情况。
实现步骤
Unipay(不常用)
由于我使用的是uniapp开发原生应用,本身uniapp对于支付方式就有专门的封装,如果你没有后端,那你可以尝试使用uniPay,下面是文档的链接
Unipay官方文档
基本用法(常用)
使用uniapp的uni.requestPayment
来实现是比较常用的方式,下面是支付的文档,不过看看就好,还是有挺多坑的,具体的支付流程可以参考一下官方文档,不过逻辑还有代码的正确性需要自己考量,下面我会介绍我的方式
苹果支付
获取iap通道
获取iap通道是判断当前设备是否支持苹果内购支付的必要条件,所以一定要先判断是否含有iap支付通道,如果含有支付通道,才可以走支付逻辑,否则直接return
即可,不需要任何逻辑。
export function Init() {return new Promise((resolve, reject) => {//使用uni.getProvider来获取通道uni.getProvider({service: 'payment',success: (res) => {let iapChannel = res.providers.find((channel) => {return (channel.id === 'appleiap')})//成功之后会返回通道resolve(iapChannel)},})})
}
返回示例
如果你获取到的iap通道为null,那么你可以直接return,因为当前环境是不支持苹果内购支付的,也就不用走其他逻辑了。
获取已完成但未支付的订单
由于苹果服务器的原因,导致某些情况会出现回调时间长甚至没有回调的情况,因此,这一步必须要做,因为如果不做这一步操作,会导致下一次的支付回调了上一次的事务这种异常情况。
其中,获取订单和关闭订单是一起操作的,所以我把他们整合在了一起。
获取订单
export function restore(iapChannel) {console.log("获取苹果服务器已支付且未关闭的交易列表")return new Promise((resolve, reject) => {iapChannel.restoreCompletedTransactions({manualFinishTransaction: true,username: ''}, (res) => {resolve(res)}, (err) => {reject(err);})});
}
关闭订单
export function finishTransaction(transaction, iapChannel) {console.log("关闭订单")return new Promise((resolve, reject) => {iapChannel.finishTransaction(transaction, (res) => {console.log("关闭订单成功", res)resolve(res);}, (err) => {reject(err);});});
}
整合:
export function getReview(iapChannel, token, dev) {//请求是否有已完成未关闭的订单restore(iapChannel).then(res => {//如果有并且状态为已支付则请求关闭并回调给后端console.log(res)if (res.length > 0) {//轮询关闭订单res.map(item => {finishTransaction(item, iapChannel)//如果状态为已完成的状态if (item.transactionState == '1') {//后端逻辑,此处省略,通常是完成上报凭证的操作,来完成补单//请求后端接口上传支付凭证 submitMisson(PayBack, productId, iapChannel).then(res => {uni.showToast({icon: 'none',title: '上一笔订单已支付成功,请稍后留意余额'})console.log(res)})}})} })
}
注意事项
这里可以选择在合适的时机进行调用,可以选择静默处理,因为在支付的过程中,是不会允许移除事务的,所以如果调用获取订单的回调时间长,也可以不用处理,但一定要做这一步操作。
请求苹果档位列表
这一步一定要做,否则无法拉起内购支付,目的就是判断当前的内购档位信息是否有配置在苹果后台中。
/*** 调用ID为“appleiap”的PaymentChannel对象的requestOrder方法,像Appstore请求有效的商品详情。* 注意:IAP支付必须在调用payment.request方法之前,调用requestOrder方法,否则调用payment.request将会报错。*/
export function requestOrder(iapChannel, productIds) {uni.showLoading({title: '初始化中~',mask: true})return new Promise((resolve, reject) => {iapChannel.requestOrder(productIds, (orderList) => { //必须调用此方法才能进行 iap 支付console.log('requestOrder success: ' + JSON.stringify(orderList));resolve(orderList)uni.hideLoading()}, (e) => {console.log('requestOrder failed: ' + JSON.stringify(e));uni.hideLoading()uni.showToast({icon: 'none',title: '当前环境不支持内购支付'})reject(e)});})
}
拉起支付
这里建议将manualFinishTransaction设置为true,手动关闭订单,否则自动关闭订单可能出现订单关闭失效的情况。
uni.requestPayment({provider: 'appleiap',orderInfo: {manualFinishTransaction: true, //true为手动关闭订单,false为自动关闭订单username: res.data.osn, //透传参数productid: productId, //档位id},success: (e) => {// e 类型为 Transaction, 详见下面的描述//后端逻辑省略轮询订单情况}
})
踩过的坑
回调时间长,导致掉单
如果你的应用有客服反馈的功能,那么可以申请客服反馈查询后端订单情况,进行补单的操作。
如果没有,那么你就只能手动补单,一般来说,补单需要提供订单号和票据信息。
但是由于用户手动关闭应用,导致订单号丢失,票据信息和订单号对应起来,因此我们要做一个手动队列的处理。
解决方案:在用户下单时候,将订单号和档位id关联起来做一个队列
也就是key:档位id,value: 订单号数组
原因是用户可以关闭应用之后,重新点击支付,生成了一笔新的订单号,但是回调是上一笔的票据,因此需要做一个订单号数组。
每次支付的时候获取缓存中的队列数据,如果该档位存在订单号,说明上一笔订单并没有上报成功,因此取队列中的第一个订单号作为上报订单,上报成功之后将这笔订单移除,这样就不会影响用户的正常支付,获取到上一笔订单的回调问题,影响页面逻辑。
例如:支付成功跳转成功落地页,但是回到的信息是上一笔订单这种现象。
主动关闭订单
由于上一步操作虽然正常上报,但是并没有将已完成的订单移除,所以我们还需要做一个队列,用来移除已完成的订单。
上报成功之后,将票据和osn作为队列,放入缓存中,这一步其实是为了判断订单是否已经关闭。
由于苹果服务器的原因,很可能你主动调用关闭订单,没有立即关闭,所以你需要在进入应用的时候重新主动关闭。
相关文章:

【苹果内购支付】关于uniapp拉起苹果内购支付注意事项、实现步骤以及踩过的坑
前言 Hello!又是很长时间没有写博客了,因为最近又开始从事新项目,也是第一次接触关于uniapp开发原生IOS应用的项目,在这里做一些关于我在项目中使用苹果内购支付所实现的方式以及要注意的事项,希望能给正在做uniapp开…...

一:BT、BLE版本说明及对比
蓝牙版本说明1.常见名词说明2.BT&BLE特性对比3.BT各版本对比4.BLE各版对比1.常见名词说明 名称说明BR(Basic Rate)基本码率EDR(Enhanced Data Rate)增强码率BLE(Bluetooth Low Energy)低功耗蓝牙HS(High Speed)高速蓝牙BT(BlueTooth)蓝牙技术LE(Low Energy)低能耗AFH(Adap…...

php宝塔搭建部署实战多模板cms管理系统源码
大家好啊,我是测评君,欢迎来到web测评。 本期给大家带来一套php开发的多模板cms管理系统源码。感兴趣的朋友可以自行下载学习。 技术架构 PHP7.0 nginx mysql5.7 JS CSS HTMLcnetos7以上 宝塔面板 文字搭建教程 下载源码,宝塔添加一…...

【数据结构初阶】手把手带你实现栈
前言 在进入数据结构初阶的学习之后,我们学习了顺序表和链表,当然栈也是一种特殊的数据结构,他的特点是后进先出。 栈的概念及结构 栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删…...
liunx 端口号开放和关闭
1.先查看防火墙是否开启的状态,以及开放端口的情况: systemctl status firewalld.service(查看防火墙开启还是关闭) sudo firewall-cmd --list-all(可以查看端口开放情况) 2.使用以下命令来开启或者关闭虚拟机的防火墙 systemctl stop firewalld.ser…...
【oracle】问题分析常用查询语句
1、查看当前的数据库连接数 select count(*) from v$process ; --当前的数据库连接数2、数据库允许的最大连接数 select value from v$parameter where name processes; --数据库允许的最大连接数3、查看当前有哪些用户正在使用数据 select osuser, a.username, cpu_time/ex…...

将vue-devtools打包成edge插件
文章目录一、从github拉vue-devtools源码二、用npm安装yarn三、使用yarn安装并编译源码四、将vue-devtools打包成edge插件五、离线安装edge插件一、从github拉vue-devtools源码 目前最新的版本是v6.5.0,地址:https://github.com/vuejs/devtools 二、用n…...
SpringBoot常见面试题汇总(超详细回答)
1.什么是SpringBoot?Spring Boot 是一个基于 Spring 框架的开源框架,用于快速创建独立的、生产级别的、可运行的 Spring 应用程序。它采用了约定优于配置的理念,使开发者可以不需要手动配置大量的 Spring 配置文件,而快速搭建出符…...

上海亚商投顾:沪指窄幅震荡 ChatGPT概念再度走高
上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。市场情绪沪指今日窄幅震荡,创业板指低开低走,午后跌幅扩大至1%,宁德时代一度跌近4%。6G概…...

【C语言进阶:指针的进阶】函数指针
本章重点内容: 字符指针指针数组数组指针数组传参和指针传参函数指针函数指针数组指向函数指针数组的指针回调函数指针和数组面试题的解析⚡函数指针 函数指针:指向函数的指针。 通过之前的学习我们知道数组指针中存放的是数组的地址,那么函…...

Sqoop 使用详解
Sqoop 概述Sqoop 是Apache 旗下的一款开源工具,用于Hadoop与关系型数据库之间传送数据,其核心功能有两个:导入数据和导出数据。导入数据是指将MySQL、Oracle等关系型数据库导入Hadoop的HDFS、Hive、HBase等数据存储系统;导出数据是…...
基于MATLAB开发AUTOSAR软件应用层Code mapping专题-part 1 code mapping总体介绍与Function标签页介绍
Hello,大家好,这篇文章开始我们进入一个新的专题,code mapping,即讲解AUTOSAR的元素和哪些模型元素是对应,这也是很多初学的朋友很疑惑的点,最近也有不少粉丝和朋友咨询我,说看了之前的文章基本了解了AUTOSAR有哪些元素()在数据字典的专题里我们逐个讲解过),但是就是…...

第十四节 包、权限修饰符、final、常量
包 1.同一个包下的类,相互可以直接访问。 2.不同包下的类要导包后才能访问。 AIT回车键导包。 权限修饰符 什么是权限修饰符? ●权限修饰符:是用来控制一个成员能够被访问的范围。 ●可以修饰成员变量,方法,构造器,内部类&…...

C++类和对象:初始化列表、static成员和友元
目录 一. 初始化列表 1.1 对象实例化时成员变量的创建及初始化 1.2 初始化列表 1.3 使用初始化列表和在函数体内初始化成员变量的效率比较 1.4 成员变量的初始化顺序 1.5 explicit关键字 二. static成员 2.1 static属性的成员变量 2.2 static属性的成员函数 三. 友元 …...

Windows 11 安装 Docker Desktop
Windows 环境安装 WSL2 WSL 简介 WSL 全称是 Windows Subsystem for Linux ,适用于 Linux 的 Windows 子系统,可让开发人员按原样运行 GNU/Linux 环境,包括大多数命令行工具、实用工具和应用程序,且不会产生传统虚拟机或双启动设…...

设计模式-第6章(工厂模式)
工厂模式简单工厂实现工厂模式实现简单工厂 VS 工厂方法商场收银程序再再升级(简单工厂策略装饰工厂方法)工厂方法模式总结简单工厂实现 在简单工厂类中,通过不同的运算符,创建具体的运算类。 public class OperationFactory {pu…...

【JAVA】线程和进程
🏆今日学习目标:线程和进程 😃创作者:颜颜yan_ ✨个人主页:颜颜yan_的个人主页 ⏰本期期数:第三期 🎉专栏系列:JAVA 线程和进程前言一、进程与线程1.进程2.线程二、线程的创建2.1 继…...

移动app安全测试工具好物分享
移动互联网时代,我们的生活和工作深受移动app的影响。随着移动app的广泛应用,安全问题成为人们最关注的话题之一。移动app安全除了和软件开发密不可分之外,软件测试的作用也是不容忽视的。移动app安全测试是指测试人员利用各种测试手段验证Ap…...

原生微信小程序引入npm和安装Vant Weapp
目录一、引入npm安装Vant Weapp1、引入npm2、安装Vant Weapp3、修改 app.json4、修改 project.config.json二、构建npm一、引入npm安装Vant Weapp 环境:Windows10 开发工具:微信开发者工具 本地环境:已安装过node.js 1、引入npm cmd进入到你…...

ChatGPT文章自动发布WordPress
WordPress可以用ChatGPT发文章吗?答案是肯定的,ChatGPT官方有提供api接口,多以目前有很多的SEO工具具有自动文章生成自动发布的功能,使用SEO工具,我们可以通过疑问词和关键词进行文章生成,并定时发布到我们…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...

2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...