iOS StoreKit2新特性

1. 客户端相关:

 1. 使用iOS15以上可用的Swift异步并发API进行开发,代码可以做到最简洁。

 2. 使用JWS来校验订单,不需要服务器进行验票,客户端直接就能拿到支付成功后的订单。

 关键代码如下:

//根据productId获取product
products = try await Product.products(for: Set(productIds))

//product.type可获取订阅类型
product.type == .autoRenewable

//获取autoRenewable是否是第一次购买优惠
product.subscription?.isEligibleForIntroOffer

//支付,可以传入orderid的uuid用来关联指定用户的订单。orderid与userid的对应关系由云端维护
let uuid = Product.PurchaseOption.appAccountToken(UUID.init(uuidString: "orderid")!)
let result = try await product.purchase(options: [uuid])

//处理支付结果,此时苹果内部已经进行了JWS校验
switch result {
    case .success(let verificationResult):
        //处理成功
        if case .verified(let transaction) = verificationResult {
            //transaction.appAccountToken可以拿到支付时传入的uuid
            
            //在这里给服务器上传transaction.id、transaction.originalID服务器就可以去苹果服务器请求订单信息了。
            
            await transaction.finish()
        } else {
            throw verifyFailure
        }
    case .userCancelled:
        //处理取消
    case .pending:
        //交易可能会在未来成功,通过Transaction.updates进行通知。
}

//获取当前所有订阅记录
for await result in Transaction.currentEntitlements {
    if case .verified(let transaction) = result {
        
    }
}

//可以在应用内部弹出管理订阅的窗口了
try await AppStore.showManageSubscriptions(in: scene)

//应用内可以请求退款了,沙盒也可以测试退款。
try await transaction.beginRefundRequest(in: scene)

//Transaction.updates, 当网络不好支付结果返回.pending或者用户购买时退出了app或卸载重装,在这里会获得回调。
Task.detached {
    for await verificationResult in Transaction.updates {
        if case .verified(let transaction) = verificationResult {
            //在这里给服务器上传transaction.id、transaction.originalID服务器就可以去苹果服务器请求订单信息了。
            
            await transaction.finish()
        }
    }
}

//获取全部交易订单
Transaction.all

//获取某个产品最新交易订单
Transaction.latest(for: productId)

//获取所有当前订阅的交易
Transaction.currentEntitlements

//获取某个产品当前订阅的交易
Transaction.currentEntitlement(for: productId)

//同步不同设备之间的应用内购买(恢复购买)
try await AppStore.sync()

//内购对兑码 当服务器出问题了,为了挽留用户或吸引更多用户,
//可以给用户邮箱发送内购对兑码,让用户在app内进行兑换。非新功能。
SKPaymentQueue.default().presentCodeRedemptionSheet()

2. 服务器相关:

 1. 服务器API接口汇总:https://developer.apple.com/documentation/appstoreserverapi

 2. 服务器可以主动去苹果服务器查询订阅状态及交易历史记录。

  1. get_all_subscription_statuses: /inApps/v1/subscriptions 根据originalTransactionId获取购买交易信息及所有续订信息。

  2. get_transaction_history: inApps/v1/history/ 根据originalTransactionId分页获取购买交易信息

  (不包含续订信息,如果需要续订信息,当从交易信息中拿到是订阅类型的,再调用get_all_subscription_statuses接口去拿续订信息。如果能开始时就确定是订阅类型的,可以不调用此接口,直接调用get_all_subscription_statuses去获取所有数据)

 3. 可以根据订单ID去苹果服务器查询交易信息。https://developer.apple.com/documentation/appstoreserverapi/look_up_order_id?language=_9

  用户扣款后没有收到商品,会找到咱们的客服进行反馈,附带苹果邮箱里的扣款截图,咱们服务器就可以根据截图里的orderID去调用/inApps/v1/lookup/{customer_order_id}接口查找对应的Transaction信息,根据信息确定是否要补发商品。

StoreKit2_image1.png

 4. 接收内购状态改变通知,通知有删除有新增:

StoreKit2_image2.png

 5. 购买流程有变化:

  订阅类型:

   苹果服务器会通知我们的服务器

   我们也可以根据客户端上报的originalTransactionId调用/inApps/v1/subscriptions 接口去验证。

StoreKit2_image3.png

 6. 总体购买流程:

StoreKit2_image4.png

 7. 服务器可以主动查询退款订单详情。

  如果服务器宕机了或者没有收到退款通知,服务器可以调用API主动查询用户所有退款记录订单,只需要任意一个original_transaction_id。

  Apple Developer Documentation inApps/v2/refund/lookup/{originalTransactionId}

 8. 给用户补偿订阅时长。

  如果某个原因设计的活动中断了或取消了,可以给用户一些福利去安抚用户。

  Apple提供了一个API,允许一年有2次机会给订阅用户每次加90天免费补偿。

  Apple Developer Documentation inApps/v1/subscription/extend/{original_transaction_id

3. StoreKit2 丢单处理:

 还是有可能出现丢单的情况,例如购买成功了,Apple 返回结果时由于网络的原因导致失败了,但是此时会更容易解决。

 解决办法:

  冷启动时,可监听 Transaction 变化,收到成功的 Transaction 后重新上传,与 StoreKit 1 类似。

  在购买时向 product 内 appAccountToken 字段里塞入业务方 orderID 相关的信息,当用户反馈扣款了但是没发货时,可以让用户提供 Apple 的 orderID,通过它可以直接去苹果服务器获取对应的 Transaction 信息,找到 Transaction.appAccountToken ,再给用户发货。(这里可以做成一个自动化处理工具,只需要用户提供苹果的 orderID,就可以去查找对应的业务方 orderID 进行发货。)

4. 参考文章:

StoreKit2 有这么香?嗯,我试过了,真香 - 掘金

iOS StoreKit 2 新特性解析-51CTO.COM

5. 有任何问题欢迎评论区留言进行探讨。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容