2021 年 WWDC,在 iOS 15 系统上推出了一个新的 StoreKit 2 库,该库采用了完全新的 API 来解决应用内购买问题。
名词 | 解释 | |
---|---|---|
IAP | In-App Purchase:应用内购买 | |
StoreKit 1 | 原始的应用内购买 API | Choosing a StoreKit API for In-App Purchase |
StoreKit 2 | 应用内购买 API | Choosing a StoreKit API for In-App Purchase |
1 . 苹果后台能否查看到退款的订单详情?
不能。只能苹果处理退款后发通知给我们的服务器,告知发生了一笔退款
2 . 消耗性、非消耗性、非续期订阅、自动续订能不能在沙盒环境测试退款?
不能。系统没提供这种测试方式。
3 . 能够将用户反馈的苹果收据里的 orderID 与具体的交易进行关联吗?
不能。
4 . 在开发过程中,无法直接关联 transaction 与 orderID 之间联系,虽然有一个 applicationUserName 字段,可以存储一个信息。但是这个字段是不是 100%靠谱,在某些情况下会丢失存储的数据。
5 . 无法主动的去苹果服务器获取交易历史记录,退款信息。无法根据用户提供的苹果收据里的 orderID 主动关联上我们当前已知的订单。
6 . 目前 sk1 的 skproduct 无法区分消耗品,非消耗品,订阅商品,非连续订阅商品。
7 . sk1 存在队列监听,每次购买需要通过队列监听对应的购买状态的变更,所有的 transaction 的回调都在监听当中,不好区分哪些是补单的 transaction 和正常购买的 transaction。
StoreKit 2 新特性主要包含三部分:
StoreKit 2 主要的更新有这几个:
StoreKit 2 使用了 Swift 5.5 的新特性进行开发,完全修改了获取商品、发起交易、管理交易信息等接口 API 的实现方式。https://swift.org/blog/
例如获取商品方式语法不同:
// 1. 请求商品
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:identifiers];
request.delegate = pipoRequest;
[request start];
// 2. 实现 SKProductsRequestDelegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
// success
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error API_AVAILABLE(ios(3.0), macos(10.7))
{
// failed
}
// 获取商品
let products = try await Product.products(for: productIDs)
// 购买商品
func purchase(_ product: Product) async throws -> Transaction? {
//Begin a purchase.
let result = try await product.purchase()
switch result {
case .success(let verification):
let transaction = try checkVerified(verification)
//Deliver content to the user.
await updatePurchasedIdentifiers(transaction)
//Always finish a transaction.
await transaction.finish()
return transaction
case .userCancelled, .pending:
return nil
default:
return nil
}
}
方便我们利用的字段:
举个例子:
某些 APP 会有会员订阅服务,那些服务会有 1 个月,3 个月,12 个月等的自动续期,同时还会有一些第一次购买的优惠,这个第一次购买的优惠就是首购优惠,并且这个优惠跟 Apple ID
挂钩,跟 APP 内自己的账号体系无关,例如小马哥旗下产品,自有的账号体系是 QQ 号 + 微信号
,那么我们在之前是无法简单得判断你这个 Apple ID
是否享受过首购优惠
了,毕竟用户可以有多个 QQ 号
,或者多个 微信号
,在弹出苹果的购买页面前,我们是不知道这个 Apple ID
有没有享受过首购优惠的,会对用户产生误解,我在上一个页面还告诉我首个月只要 18 块钱,实际支付的时候为什么要 25 元了 ? 这个对用户的购买意愿肉眼可见是有下降的。
现在我们就可以通过 isEligibleForIntroOffer
这个属性,轻松又方便得提前拿到这些信息,对已经享受过的Apple ID
账号不展示这个优惠。
public static func products<Identifiers>(for identifiers: Identifiers) async throws -> [Product] where Identifiers : Collection, Identifiers.Element == String
PurchaseOption
结构体,该结构体里有新增的特别重要的字段appAccountToken, 类似 SKPayment.applicationUsername 字段,但是 appAccountToken 信息会永久保存在 Transaction 信息内。appAccountToken 字段是由开发者创建的;关联到 App 里的用户账号;使用 UUID 格式;永久存储在 Transaction 信息里。
PS:这里的 appAccountToken 字段苹果的意思是用来存储用户账号信息的,但是应该也可以用来存储 orderID 相关的信息,需要将 orderID 转成 UUID 格式塞到 Transaction 信息内,方便处理补单、退款等操作。
public func purchase(options: Set<Product.PurchaseOption> = []) async throws -> Product.PurchaseResult
let uuid = Product.PurchaseOption.appAccountToken(UUID.init(uuidString: "uid")!)
// 发起一笔购买之后,直接等待苹果的返回结果,无需在paymenqueue中等待transaction状态的更新。
//使用sk2发起的购买的订单的信息,在sk1所有的回调接口都不会得到相应的transaction的更新状态
let result = try await product.purchase(options: [uuid])
// demo
func purchase(_ product: Product) async throws -> Transaction? {
//Begin a purchase.
let result = try await product.purchase()
switch result {
case .success(let verification):
let transaction = try checkVerified(verification)
//Deliver content to the user.
await updatePurchasedIdentifiers(transaction)
//Always finish a transaction.
await transaction.finish()
return transaction
case .userCancelled, .pending:
return nil
default:
return nil
}
}
func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
//Check if the transaction passes StoreKit verification.
switch result {
case .unverified:
//StoreKit has parsed the JWS but failed verification. Don't deliver content to the user.
throw StoreError.failedVerification
case .verified(let safe):
//If the transaction is verified, unwrap and return it.
return safe
}
}
func listenForTransactions() -> Task<Void, Error> {
return Task.detached {
//Iterate through any transactions which didn't come from a direct call to `purchase()`.
for await result in Transaction.updates {
do {
let transaction = try self.checkVerified(result)
//Deliver content to the user.
await self.updatePurchasedIdentifiers(transaction)
//Always finish a transaction.
await transaction.finish()
} catch {
//StoreKit has a receipt it can read but it failed verification. Don't deliver content to the user.
print("Transaction failed verification")
}
}
}
}
针对 transaction 的更新,这个监听是让我们监听:
提供了三个新的交易(Transcation)相关的 API:
根据可以购买的订阅商品、非消耗品可以过滤出已经购买过的商品。
extension Transaction {
public static var all: Transaction.Transactions { get }
public static var currentEntitlements: Transaction.Transactions { get }
public static func currentEntitlement(for productID: String) async -> VerificationResult<Transaction>?
public static func latest(for productID: String) async -> VerificationResult<Transaction>?
public static var unfinished: Transaction.Transactions { get }
}
extension AppStore {
public static func sync() async throws
}
订阅类型项目的状态,比如主动获取最新的交易、获取更新订阅的状态,获取更新订阅的信息等。其中获取更新订阅的信息,可以获取更新的状态、品项 id、如果过期的话,可以知道过期的原因。(比如用户取消、扣费失败、订阅正常过期等。)获取的所有数据都是 JWS 格式验证。
可以直接唤起 App Store 里的管理订阅页面。
extension AppStore {
@available(iOS 15.0, *)
@available(macOS, unavailable)
@available(watchOS, unavailable)
@available(tvOS, unavailable)
public static func showManageSubscriptions(in scene: UIWindowScene) async throws
}
提供了新的发起退款 API,允许用户在开发者的 App 中直接进行退款申请。用户进行申请退款后,App 可以收到通知、另外苹果服务器也会通知开发者服务器。(沙盒环境也可进行退款测试了,但是 App Store 里还没开启这个功能。)
extension Transaction {
public static func beginRefundRequest(for transactionID: UInt64, in scene: UIWindowScene) async throws -> Transaction.RefundRequestStatus
}
构建开发者服务器可以实现以下几个功能:
服务器端在通过 /verifyReceipt
接口验证票据时,新 API 的数据结构也发生了变化。例如统一了购买时间、过期时间、原始购买时间格式,新增了 appAcountToken 字段、内购类型字段、退款时间、退款原因、促销优惠类型等。具体的可以参考 Manage in-app purchases on your server - WWDC21 - Videos - Apple Developer 视频,或者 Validating Receipts with the App Store | Apple Developer Documentation
新增了一些 API,可以主动去获取订阅状态、交易历史记录等等。具体可以参考这个文档:App Store Server API | Apple Developer Documentation
originalTransactionId
参数,就可以获取用户订阅的各种状态originalTransactionId
即可获取所有的历史记录当订阅状态发生变化时,Apple server 会主动通知我们的服务器,告知发生了哪些变化。功能跟之前的版本一样,但是删除了一些状态,也新增了一些状态。
为了方便测试沙盒环境的退款通知,App Store 可以为沙盒环境单独设置一个 server URL 配置。
例如第一次购买订阅类型商品时,购买成功后,Apple server 会主动通知 我们的 server,告知状态。此时我们的 server 可以不用再去 Apple server 那边验证了。及时以后想验证,也可通过 /inApps/v1/subscriptions
接口随时去验证。
续订、账单宽限期、用户退款等操作时除了可以接受苹果的通知外,也可以主动去请求苹果服务器,获取最新的状态。例如在自己服务器宕机或者因为某种原因导致没有接收到苹果的通知时,此时主动去请求苹果服务器获取交易历史记录,交易状态信息,就发挥出了巨大的作用。
对于 StoreKit 2,苹果已经废弃了用 receipt 收据验证逻辑,只需要提供交易的 originalTransactionId 即可获取到完整的交易信息。那么如何从 StoreKit 1 升级到 StoreKit 2 呢?
管理家庭共享。目前苹果对 非消耗型 和 自动订阅 类型品项是支持 家庭共享(family sharing),另外,苹果会返回一个字段 inAppOwnershipType 表示当前用户是否为购买品项的主用户。更方便的追踪用户的状态
Support customers and handle refunds - WWDC21 - Videos - Apple Developer
如何识别用户的购买项目。当用户扣款了,但是没有收到商品时,用户会过来返回问题并提供了苹果邮箱里的扣款信息截图。
那么我们的服务器端可以使用截图里的 invoice order ID 去请求 /inApps/v1/lookup/{customer_order_id}
这个接口查找到对应的 Transaction 信息。然后我们再去验证是否购买成功、是否已申请退款、是否需要补发商品等等。
https://developer.apple.com/documentation/appstoreserverapi/look_up_order_id
/inApps/v1/lookup/{customer_order_id}
如何查询该用户过去的退款信息?
目前的情况是,如果我们服务器宕机了或者没有收到退款通知,那么我们是不知道用户是有没有进行退款的。虽然 StoreKit 2 提供了一个获取交易记录的 API,但是如果通过该 API 来自己过滤退款的交易,不是一个最好的实现方式。所以 Apple 新提供了一个 API 可以查到这个用户的所有退款记录订单,只需要任意的一个 original_transaction_id。
https://developer.apple.com/documentation/appstoreserverapi/get_refund_history
/inApps/v1/refund/lookup/{original_transaction_id}
如何补偿订阅者的服务问题?
比如说当服务器出问题了,为了挽留用户/吸引更多用户,计划如何给用户发补偿。开发者可以提供一个内购对兑码(所有的内购类型都可以),在苹果后台那里生成。然后让用户在 App Store 进行兑换,也可以在 App 里通过 presentCodeRedemptionSheet()
接口调用,弹出系统的兑换界面:
如何安抚客户中断或取消的活动?
主要还是想给用户一些福利,安抚用户。类似其他 App 里的签到一个月,可以赠送用户 1 个月会员等活动。但这种方式的过期时间是由自己的服务器后端决定的。
这里 Apple 也提供了一个接口,允许开发者一年有 2 次机会给订阅内购用户每次加 90 天免费补偿。也就是有自动订阅类型的 App,可以开发者主动在服务器给用户补偿(免费延长)用户的订单时间,每次最多是 90 天。
https://developer.apple.com/documentation/appstoreserverapi/extend_a_subscription_renewal_date
/inApps/v1/subscription/extend/{original_transaction_id}
同上面 5.2.4,提供了一个 showManageSubscriptions 接口,可以直接唤起管理订阅页面。
extension AppStore {
@available(iOS 15.0, *)
@available(macOS, unavailable)
@available(watchOS, unavailable)
@available(tvOS, unavailable)
public static func showManageSubscriptions(in scene: UIWindowScene) async throws
}
同上面 5.2.5 request refund API
整个支付购买流程与原始 API 购买流程一样,区别是 3.1 步上传交易信息时,不再上传 receipt/token 信息,上传 transaction_id 就可以了。服务器端可以通过 transaction_id 去苹果服务器获取交易结果,不再需要使用 receipt/token 验证票据。
Choosing a StoreKit API for In-App Purchase | Apple Developer Documentation
如果您的应用程序依赖于以下任何功能,您可能需要使用原始的应用程序内购买 API:
对现有和旧应用程序使用原始 API。
老 App:
可以。
对于后端来说,Apple Server API V1 和 Apple Server API V2 都可以使用,与客户端是否升级到 StoreKit 2 无关。
可以参考上面新 API 购买流程图。
还是有可能出现丢单的情况,例如购买成功了,Apple 返回结果时由于网络的原因导致失败了,但是此时会更容易解决。
解决办法:
会,与 StoreKit 1 功能一样,只是调用的接口不同。
能看到,互相兼容了。
可以。
可以,storekit2 针对于 transaction 的返回信息当中,明确的告诉了我们当前的商品类型是什么。针对于服务端对于消耗品和订阅商品的两套不同逻辑,我们通过这个字段,就可以轻易的区分是否是订阅商品再请求对应的接口
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/RrkK5M3qYTs2cMi7sWTqrA
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。