以下是编者对InAppPurchase这几年重要的更新或调整的梳理:
事件
变化
2020年11月18日
AppStore小型企业计划
日历年收入在100万美元以下的小型和独立开发者将可以享受15%的佣金费率,仅为AppStore标准佣金费率30%的一半,付费app和App内购买项目的收益抽成将降低15%。
2020年11月23日
针对在线多人活动的app内购买项目规定
3.1.3(d)一对一服务:如果您的App允许购买两个人之间的一对一实时服务(例如,学生辅导、医疗咨询、看房服务或健身训练),您可以使用App内购买项目以外的其他购买方式来收取相应款项。一对几和一对多的实时服务则必须使用App内购买项目。
2021年8月26日
Apple与美国开发者就AppStore达成和解
美国开发者提起的AppStore集体诉讼与苹果和解,Apple设立一亿美元的基金来帮助美国的小型业务开发者,符合条件的开发者获得250美元至3万美元的现金)。
2021年9月1日
日本公平贸易委员会结束对AppStore的调查
2022年1月14日
针对在荷兰AppStore上分发的约会App的更新
2022年5月16日
自动续期订阅提价更新
目前,当自动续期订阅提价时,订阅者必须在App提价之前选择接受。新调整:符合某些特定条件并在提前通知用户的情况下,开发者在为自动续订订阅提价时,无需用户额外采取行动,亦不会中断服务。(前提条件:每年提价不超过一次,同时订阅价格上调不超过5美元和50%,或者年度订阅价格上调不超过50美元和50%,并且是在法律允许的范围内。)
2022年6月30日
针对在韩国分发App的更新
说到内购,环绕着的新闻,总起到一些波澜,从2021年苹果推出AppStore小型企业计划,降低15%的佣金,大家的讨论一直源源不断,对于小型企业和开发者,确实是明显感受到15%带来的回报!本文不去讨论合理性,AppStore从2008年推出就是一个创举,它改变了世界对App的认识。我们本文更多的是讨论如果利用这些变化,为用户提供更好的服务或体验!
本文主要从四方面进行探讨:
所以,目前iOS16和StoreKit2不能解决的问题:
2022年,如何选择OriginalStoreKit还是StoreKit2
验证AppTransaction的方法:
StoreKit2带来了新的四个字段:
Pricelocale
Serverenvironment
获取到的字段值:
环境
值
说明
AppStore
Production
AppStore商店包环境的交易
AppStoreSandbox或TestFlight
Sandbox
Develop或TestFlight环境的交易
XcodeStoreKitTesting
Xcode
使用Xcode进行StoreKit测试的交易
Recentsubscriptionstartdate
Sentinelvalues
另外,在不支持的系统和环境中,就会使用Sentinelvalues哨兵值(占位符值),例如Pricelocal下使用Locale(identifier:"xx\_XX"),而Recentsubscriptionstartdate使用Date.distantPast等。这是为什么呢?
针对SwiftUI增加了优惠代码兑换接口和应用内评分接口。
具体的StoreKitmessages交互流程图:
获取AppStoremessages消息,使用SwiftUI实现的代码示例:
然后显示AppStoremessages消息,需要通过SwiftUI环境变量displayStoreKitMessage来解析和显示,使用SwiftUI实现的代码示例:
而现在,苹果打通了applicationUsername和appAccountToken,当用OriginalStoreKit创建订单时,applicationUsername字段赋值使用UUID格式内容时,则可以在服务端通知或者解析receipt票据时,可以获取这个UUID值,也就是订单可以关联确认。
我们回顾一下,我们为什么需要使用applicationUsername?我们是希望每个交易transaction可以关联用户订单号,对于订阅类型和非消耗类型品项,关联用户UID就能满足需求,但是对于非消耗型品项,其实,需要关联用户UID还有订单号OrderID,因为非消耗型品项可以重复购买并且没有UID的强关联。举例来说,游戏里的用户账号可能不止一个,或者一个账号下的游戏角色,通常不止有一个角色,所以购买非消耗型品项时,开发者希望关联的是当前用户UID和此角色RoleID生成的开发者订单号OrderID,但此时,UUID格式并不能满足开发者自定义的需求!
所以,applicationUsername和appAccountToken的透传值,对开发者有一定的关联作用,但其实还不完美。
具体的细节这里不说,就重点说说代码。首先,需要更新app的Info.plist文件,添加权限:
配置示例:
在iOS和iPadOS15.4运行的代码示例:
//当前设备不能支付,则不能进行购买~guardAppStore.canMakePaymentselse{return}do{//打开外部购买流程letres=tryawaitExternalPurchase.presentNoticeSheet()//打开结果switchres{case.continued:print("用户选择继续查看外部购买")case.cancelled:print("用户选择取消,不查看外部购买")@unknowndefault:fatalError()}}catch{//异常流程print(error.localizedDescription)}注意事项:
阅读器App是指将提供以下一种或多种数字内容类型作为其主要功能的App:杂志、报纸、图书、音频、音乐或视频。
同理,首先,需要更新app的Info.plist文件,添加权限:
@available(iOS16.0,*)funcexternalLinkAccount(){//当前设备不能支付,则不能进行购买~guardAppStore.canMakePaymentselse{return}Task{//判断是否有打开外部链接帐户的权限letcanOpen=awaitExternalLinkAccount.canOpenguardcanOpenelse{print("不能打开外部链接帐户")return}do{//打开外部链接帐户tryawaitExternalLinkAccount.open()}catch{print(error.localizedDescription)}}}注意事项:
今年WWDC22苹果新增了三个新接口,并且对部分接口增加了过滤功能,这里我们列了一个表格:
接口
链接
WWDC21
查询用户订单的收据,使用订单ID从收据中获取用户的应用内购买项目收据信息。
查询用户历史收据,获取用户在您的app的应用内购买交易历史记录。
查询用户内购退款,获取app中为用户退款的所有应用内购买项目的列表。
查询用户订阅项目状态,获取您app中用户所有订阅的状态。
提交防欺诈信息,当用户申请退款时,苹果通知(CONSUMPTION_REQUEST)开发者服务器,开发者可在12小时内,提供用户的信息(比如游戏金币是否已消费、用户充值过多少钱、退款过多少钱等),最后苹果收到这些信息,协助“退款决策系统”来决定是否允许用户退款。
延长用户订阅的时长,使用原始交易标识符延长用户有效订阅的续订日期。(相当于免费给用户增加订阅时长)
WWDC22
测试AppStore服务器通知,让AppStore服务器通知向开发者服务器发送测试通知。
获取AppStore服务器通知的测试结果,获取发送到开发者服务器的AppStore服务器测试通知的检查状态。
获取AppStore服务器通知的历史通知,获取AppStore服务器尝试发送到开发者服务器的通知列表。
目前支持的查询参数列表:
查询参数
作用
可选值
productType
包含在交易历史记录中的产品类型。您的查询可以指定多个productType。
AUTO_RENEWABLE,NON_RENEWABLE,CONSUMABLE,NON_CONSUMABLE
productId
包含在交易历史记录中的产品标识符。您的查询可以指定多个productID。
-
subscriptionGroupIdentifier
包含在交易历史记录中的订阅组标识符。您的查询可能会指定多个subscriptionGroupIdentifier。
startDate
endDate
inAppOwnershipType
按应用程序内所有权类型限制交易历史记录。
PURCHASED,FAMILY_SHARED。
excludeRevoked
交易历史记录是否排除退款和撤销的交易。默认值为false。
true,false
sort
交易历史记录的可选排序顺序。响应按最近修改的日期对交易记录进行排序。默认值为ASCENDING(升序),因此您首先会收到最旧的交易记录。
ASCENDING,DESCENDING
revision
获取下一组最多20笔交易的令牌。所有回复都包含一个revision令牌。注意:对于使用revision令牌的请求,请包含与初始请求相同的查询参数。使用上一个History中的revision令牌。除初始请求外,所有请求都需要revision。
查询示例:
productId、productType和subscriptionGroupIdentifier查询参数可以同时指定多个值。例如,要按NON_CONSUMABLE(非消耗型)和AUTO_RENEWABLE(自动续期产品类型)字符来筛选交易历史记录,请求中包含以下内容:
最后,交易历史记录接口返回结果只支持以下情况:
测试AppStore服务器通知
获取AppStore服务器通知的测试结果
返回的响应有两个参数:
具体的signedPayload解码后的格式内容如下示例:
获取AppStore服务器通知的历史通知
查询接口的示例:
接口每次最多返回20条通知历史记录,所以响应会返回一个paginationToken字段,用来查询更多分页的通知结果。paginationToken获取下一组最多20条通知历史记录,所有有更多历史记录的响应都包含paginationToken字段。
除了StoreKit2增加了environment、recentSubscriptionStartDate字段,AppStoreServerAPI的JWS格式的签名交易也包含。
JWStransactioninfoDecodedPayload:
JWSrenewalinfoDecodedPayload:
从这个图片可以看出,AppStoreServerAPI是AppStore服务器和开发者服务器之前,相互可以响应的流程。而AppStoreServerNotificationsV1和V2通知,是AppStore服务器主动通知开发者服务器,开发者服务器不能主动请求,所以导致了一些场景的缺陷。
服务器宕机是很常见的问题,但是宕机后,开发者就无法接收AppStore服务器的通知。
重试成功后,开发者服务器接收到的通知,可以并不再是顺序显示:
用户需要不断从订阅中获得价值,才会持续地订阅您的App。定期更新您的App,提供新内容和增强功能,以鼓励订阅者继续订阅。
AppStoreServerNotificationsV2提供了更多的通知类型,达到28个,未来还会增加更多。
这里一个用户订阅过程的可能会发生的通知:
从这个图中,开发者可以思考到什么?
Subscriptionloyalty(订阅忠诚度)
为避免由于账单问题而导致服务中断,请在AppStoreConnect中启用账单宽限期。Apple将尝试解决账单问题,并在订阅者保留订阅访问权限的同时恢复订阅。如果订阅在这个期限内恢复,则付费服务天数的计数和您的收入都不会中断。如果用户在60天后重新订阅,则付费服务的天数将重置,您将收到一年的标准订阅费用,直到付费服务满一年为止。
简单来说,通过订阅通知,分析用户的忠诚度,根据用户不同的行为习惯和选择决定(通知),然后分析用户行为的背后原因,从而优化开发者的服务,从而提升订阅的忠诚度!
开发人员将能够更轻松地创建沙盒用户,并测试沙盒购买。相比以前少了安全提示问题、安全提示问题答案、出生日期三个选项。
增加了AllowPurchase&Renewals开关,用于测试订阅到期自动扣费和失败重试。
最重要是,增加了内购项目的创建!
最后,是苹果弃用XML流文档的形式与AppStoreConnect的交互,未来开发者,都需要迁移到AppStoreConnectAPI!
关于app数据,Xcode提供了功率、性能指标和诊断等新接口。
目前苹果支持送审的内容:
可以看到iOS除了新版本app送审,现在支持In-AppEvent、自定义产品、产品面优化测试等。而tvOS和macOS目前还没有,可能明年WWDC23应该就支持一波了吧!
另外,需要提示一下,送审新版本app、In-AppEvent、自定义产品、产品面优化测试等,苹果是建议开发者可以合并提交一起送审,因为这样苹果会以当前送审的内容一起审核,提高苹果的审核效率?总之,提审这些项目后,如果有项目审核不通过,可以单独发布审核通过的内容。
关于InAppPurchase和AppStore,随着这几年苹果的开放,已经很大程度上解决了开发者大多数的问题,从退款查询到所有订单查询,从被动通知到主动获取通知,从内购税率降低到提高App曝光量,苹果已经提供了非常多的接口、案例展示和建议。比如,自动续期订阅类型,目前已经复杂到不能再复杂,订阅群组、免费试用期限、推介促销优惠、促销优惠、优惠代码、计费重试、重新激活、续期等。
最后,大家觉得InAppPurchase和AppStore还有什么疑惑或痛点吗?