前言

Hello!又是很长时间没有写博客了,因为最近又开始从事新项目,也是第一次接触关于uniapp开发原生IOS应用的项目,在这里做一些关于我在项目中使用苹果内购支付所实现的方式以及要注意的事项,希望能给正在做uniapp开发ios应用需要使用苹果内购支付的小伙伴一些帮助!

问题

为什么开发ios应用需要使用苹果内购支付?

原因在于,苹果要求所有开发者在上架Appstore中的应用,如果应用中出现了虚拟商品的购买,必须使用苹果内购支付,并且绝对不能出现其他支付方式,例如微信、支付宝等支付方面的sdk,当然,如果你不怕被苹果下架的风险,你可以尝试使用webview跳转的方式,但是如果你的代码中使用了其他支付方式的sdk或者代码,是很大可能无法通过苹果严格的审核的。

ios内购为什么要专门拿出来说,对比其他支付方式有什么区别吗?

首先,他与微信、支付宝等都属于支付渠道的一种,本质上没有区别,但是由于苹果服务器的原因,导致一些非常特殊的问题,例如:回调时间长甚至没有回调、掉单、回调异常等情况,这种情况对比其他支付方式真的很鸡肋,特别是在uniapp的开发环境下,居然没有超时的回调,简直是大坑,不过这个在后面我会提到解决方案。

ios内购,事务

苹果支付走的是事务列表,每生成一笔订单就会走一笔订单,如果已经完成的订单需要使用苹果提供的关闭订单的api来进行关闭订单,否则会出现回调有误的情况。

前期准备

开通内购

你需要拥有ios开发者平台的开发者账号,申请开通内购支付,并配置相应的内购档位名称和参数。

内购档位需要配置ios后台有的档位,ios档位是有规定指定的金额,不是自定义档位金额的。

注意:此处的档位id需要配置到后端返回给前端,需要跟苹果后台配置的一一对应,否则也会拉起失败!

自定义基座包

注意,测试支付需要制作自定义基座包,需要配置相应的证书,证书可以参考uniapp的文档进行配置。测试证书制作正式包无法测试支付,只能用于自定义基座包。

获取内购证书

正式上线

正式上线需要将证书替换为正式证书,通过testfight软件进行进一步测试!

实现步骤

Unipay(不常用)

由于我使用的是uniapp开发原生应用,本身uniapp对于支付方式就有专门的封装,如果你没有后端,那你可以尝试使用uniPay,下面是文档的链接
Unipay官方文档

基本用法(常用)

使用uniapp的uni.requestPayment来实现是比较常用的方式,下面是支付的文档,不过看看就好,还是有挺多坑的,具体的支付流程可以参考一下官方文档,不过逻辑还有代码的正确性需要自己考量,下面我会介绍我的方式

苹果支付

开启内购模块

在manifest.json文件中勾选Payment(支付)中的Apple应用内支付。

注意:不要勾选其他支付模块,如果你开发ios原生应用的话。

获取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作为队列,放入缓存中,这一步其实是为了判断订单是否已经关闭。

由于苹果服务器的原因,很可能你主动调用关闭订单,没有立即关闭,所以你需要在进入应用的时候重新主动关闭。

苹果回调了上一笔订单的票据

这也算是一个比较奇怪的问题,不过回想也是可以理解的,由于苹果服务器回调时间长的问题,不仅仅是支付回调慢,就连关闭订单的回调也是非常慢,这就导致了用户在支付下一笔订单时,上一笔订单并没有完全关闭,结果就是用户在支付第二笔订单时回调的结果是上一笔订单的票据,因此推荐手动关闭订单,自己选择合适的时机去关闭订单

解决方案:
我们可以在本地做一个本地队列,在有回调的时候,检查本地队列是否含有本次票据对应的订单号,如果存在,就将osn拿出来进行上报,并删除对应的队列,如果不存在,就将票据和订单号进行关联存放,然后进行上报和关闭订单的操作,这里主要是考虑到长时间没有回调用户主动关闭app的情况,在用户下一次点击的时候能够主动帮他进行上报

总体流程图

【iOS内购支付】Uniapp拉起苹果内购支付注意事项、实现步骤以及踩过的坑(手把手教程)相关推荐

  1. 码支付最新接口,苹果CMS码支付最新接口

    2021年12月20日 今天刚更新的,在这分享一下,兼容了码支付模式的接口,接口地址如下: https://gogozhifu.com/mzf 码支付的对接方式就是按照他默认的方式,自行上网查文档. ...

  2. Unity接入苹果内购

    文章目录 前言 一.苹果内购是什么? 二.Unity接入内购 1.开启内购 2.使用内购 总结 前言 Unity接入苹果内购前,需要提前配置好产品的内购ID以及拥有苹果账号,关于这个提前准备各位需要自 ...

  3. php 苹果支付验证,IOS苹果内购 PHP后端验证票据

    大体流程: 1.IOS端需要在iTunes Connect上面添加配置一些内购商品,并审核通过,每个内购商品有自己的唯一标识product_id. 2.PHP后端要有一套与之对应的内购商品.IOS应用 ...

  4. uniapp苹果内购总结

    uniapp苹果内购总结 1.创建App内购项目,设置好税务和银行卡等配置信息 2.HbuilderX 勾选Apple应用内支付 对于app内有虚拟物品交易的,一律需要接上苹果支付, uniapp支付 ...

  5. uni app ios 苹果内购

    app ios 苹果内购 的步骤 1,准备工作先要uniapp 开发ios 内购需要准备的沙盒 测试账号,在苹果手机登录沙盒账号 也就是把自己的Apple ID退出登录沙盒账号,manifest.js ...

  6. iOS:苹果内购实践

    iOS 苹果的内购 一.介绍 苹果规定,凡是虚拟的物品(例如:QQ音乐的乐币)进行交易时,都必须走苹果的内购通道,苹果要收取大约30%的抽成,所以不允许接入第三方的支付方式(微信.支付宝等),当然开发 ...

  7. java(jfinal) 接入ios苹果内购(连续包月订阅),服务端将二次验证。

    大致流程: 1.ios端进行支付,然后收到苹果的一串数据(也叫收据),然后ios端将其转码为BASE64编码的字符串. 2.ios端请求服务端接口,将数据传给服务端,服务端拿到数据后,通过一系列处理后 ...

  8. Java支付--苹果内购

    一.苹果支付 ①逻辑流程图 二.苹果支付流程介绍 ①用户购买产品下发支付请求到app端; ②app端收到请求后调用苹果服务进行支付扣款; ③苹果服务扣款成功后返回receipt_data加密数据和支付 ...

  9. 虚拟产品之苹果内购支付/支付宝支付/微信支付的区别

    1.支付宝支付: 同步通知,异步业务逻辑处理. 直接配置异步请求接口. 2.微信支付: 同步通知,异步业务逻辑处理. 直接配置异步请求接口. 3.苹果内购支付: 同步通知,同步业务逻辑处理. 需要io ...

最新文章

  1. java连连看(GUI)
  2. python压测接口_python的一个接口压测脚本
  3. csu 1019 Simple Line Editor
  4. ipad和android平板应用程序,Android平板要比iPad差吗?
  5. 6 日期字符串转日期_Java日期时间API系列6-----Jdk8中java.time包中的新的日期时间API类...
  6. StringBuffer append整数0001的问题
  7. zoj 1676Network Wars(胡博涛论文题,01分数规划+最小割)
  8. 一个常用的Android工具库
  9. python+opencv+PIL,在图片和视频中写入中文(汉字)
  10. Linux双网关配置(电信网通)
  11. android操作XML的几种方式(转)
  12. 爱普生epson lq-630k打印机没有反应,发送成功,就是不打印
  13. 吴恩达机器学习中文版课后题(中文题目+数据集+python版答案)week2 逻辑回归
  14. 一步到位Composer直接打开SOLIDWORKS贴图
  15. iPhone 可以DIY了?苹果推出自助维修计划
  16. 集体备课模板_幼儿园集体备课教案模板课件(19页)-原创力文档
  17. 39. 组合总和(Python)
  18. Matlab如何画对比柱状图
  19. UE4 游戏窗口前台后台监听
  20. 《你的孤独,虽败犹荣》阅读笔记

热门文章

  1. Linux安装pip和setuptools
  2. hdu 3911 线段树+区间合并
  3. 李笑来激起千层浪,赵姐夫力拒众强敌
  4. mysql 分区的作用_MySQL分区的优点
  5. Git- 连接远程仓库
  6. Cesium 透视投影矩阵推导
  7. 网易游戏(互娱)游戏研发一面二面(已收到offer)
  8. fable樱桃php频道,【爵士乐利器】Fable Sounds Broadway Big Band v1.3 97.7G
  9. python3 基础题目练习
  10. 长期睡软床会引发腰椎间盘突出吗?