<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
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 |
不能。只能蘋果處理退款後發通知給我們的伺服器,告知發生了一筆退款
不能。系統沒提供這種測試方式。
不能。
不支援使用蘋果收據裡的 orderID 去蘋果伺服器查詢交易資訊,沒有提供這個 API(StoreKit 2 出來後支援去查詢 StoreKit1 的交易了,developer.apple.com/documentati… )。
StoreKit 2 新特性主要包含三部分:
StoreKit 2 主要的更新有這幾個:
StoreKit 2 使用了 Swift 5.5 的新特性進行開發,完全修改了獲取商品、發起交易、管理交易資訊等介面 API 的實現方式。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 驗證票據。
9.1 如何選擇新 API(StoreKit 2) 還是原始 API(StoreKit 1)
Choosing a StoreKit API for In-App Purchase | Apple Developer Documentation
如果您的應用程式依賴於以下任何功能,您可能需要使用原始的應用程式內購買 API:
對現有和舊應用程式使用原始 API。
老 App:
9.2 使用者端使用 StoreKit 1,伺服器端升級到 StoreKit 2 的 API,能否這樣使用?
可以。
對於後端來說,Apple Server API V1 和 Apple Server API V2 都可以使用,與使用者端是否升級到 StoreKit 2 無關。
9.3 Native SDK 使用 StoreKit 2 後,互動流程會不會有什麼變化?以及與伺服器通訊流程會不會發生什麼變化?
可以參考上面新 API 購買流程圖。
9.4 StoreKit 2 會不會出現丟單的情況,以及怎麼解決丟單問題?
還是有可能出現丟單的情況,例如購買成功了,Apple 返回結果時由於網路的原因導致失敗了,但是此時會更容易解決。
解決辦法:
9.5 購買成功但是未 finishTransaction,下次冷啟動後還會重新下發 Transaction 嗎?
會,與 StoreKit 1 功能一樣,只是呼叫的介面不同。
9.6 從 StoreKit 1 升級到 StoreKit 2 後,能否看到之前使用 StoreKit 1 購買的商品?
能看到,互相相容了。
9.7 針對使用 StoreKit1 的 app,是否可以放棄讀取本地 receipt 的方式傳給伺服器端來驗證,直接採用 StoreKit2 的 transaction_id 傳遞給蘋果伺服器端進行驗證票據?
可以。
9.8 針對於蘋果返回的 transaction 資訊,我們是否能判斷這個 transaction 的狀態資訊對應的商品型別是哪種?
可以,storekit2 針對於 transaction 的返回資訊當中,明確的告訴了我們當前的商品型別是什麼。針對於伺服器端對於消耗品和訂閱商品的兩套不同邏輯,我們通過這個欄位,就可以輕易的區分是否是訂閱商品再請求對應的介面
以上就是iOS StoreKit 2 新特性盤點解析的詳細內容,更多關於iOS StoreKit 2 新特性的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45