iOS?StoreKit?2?新特性盤(pán)點(diǎn)解析
一、背景
2021 年 WWDC,在 iOS 15 系統(tǒng)上推出了一個(gè)新的 StoreKit 2 庫(kù),該庫(kù)采用了完全新的 API 來(lái)解決應(yīng)用內(nèi)購(gòu)買問(wèn)題。
- Meet StoreKit 2 - WWDC21 - Videos - Apple Developer:重點(diǎn)內(nèi)容:Storekit 2 API 介紹和代碼演示,以及 appAccountToken
- Manage in-app purchases on your server - WWDC21 - Videos - Apple Developer:重點(diǎn)內(nèi)容:JWS 簽名交易的服務(wù)器驗(yàn)證,新服務(wù)器 API,新服務(wù)器通知,沙盒測(cè)試的新功能
- Support customers and handle refunds - WWDC21 - Videos - Apple Developer:重點(diǎn)內(nèi)容:用來(lái)支持用戶和處理退款的 Storekit 2 API 以及服務(wù)器 API
二、物料
名詞 | 解釋 | |
---|---|---|
IAP | In-App Purchase:應(yīng)用內(nèi)購(gòu)買 | |
StoreKit 1 | 原始的應(yīng)用內(nèi)購(gòu)買 API | Choosing a StoreKit API for In-App Purchase |
StoreKit 2 | 應(yīng)用內(nèi)購(gòu)買 API | Choosing a StoreKit API for In-App Purchase |
三、StoreKit 1 存在的問(wèn)題
- 蘋(píng)果后臺(tái)能否查看到退款的訂單詳情?
不能。只能蘋(píng)果處理退款后發(fā)通知給我們的服務(wù)器,告知發(fā)生了一筆退款
- 消耗性、非消耗性、非續(xù)期訂閱、自動(dòng)續(xù)訂能不能在沙盒環(huán)境測(cè)試退款?
不能。系統(tǒng)沒(méi)提供這種測(cè)試方式。
- 能夠?qū)⒂脩舴答伒奶O(píng)果收據(jù)里的 orderID 與具體的交易進(jìn)行關(guān)聯(lián)嗎?
不能。
- 服務(wù)器端 Receipt 收據(jù)解析后,沒(méi)有包含 orderID 信息,所以無(wú)法直接關(guān)聯(lián)他們之間的聯(lián)系。
不支持使用蘋(píng)果收據(jù)里的 orderID 去蘋(píng)果服務(wù)器查詢交易信息,沒(méi)有提供這個(gè) API(StoreKit 2 出來(lái)后支持去查詢 StoreKit1 的交易了,developer.apple.com/documentati… )。
- 在開(kāi)發(fā)過(guò)程中,無(wú)法直接關(guān)聯(lián) transaction 與 orderID 之間聯(lián)系,雖然有一個(gè) applicationUserName 字段,可以存儲(chǔ)一個(gè)信息。但是這個(gè)字段是不是 100%靠譜,在某些情況下會(huì)丟失存儲(chǔ)的數(shù)據(jù)。
- 無(wú)法主動(dòng)的去蘋(píng)果服務(wù)器獲取交易歷史記錄,退款信息。無(wú)法根據(jù)用戶提供的蘋(píng)果收據(jù)里的 orderID 主動(dòng)關(guān)聯(lián)上我們當(dāng)前已知的訂單。
- 目前 sk1 的 skproduct 無(wú)法區(qū)分消耗品,非消耗品,訂閱商品,非連續(xù)訂閱商品。
- sk1 存在隊(duì)列監(jiān)聽(tīng),每次購(gòu)買需要通過(guò)隊(duì)列監(jiān)聽(tīng)對(duì)應(yīng)的購(gòu)買狀態(tài)的變更,所有的 transaction 的回調(diào)都在監(jiān)聽(tīng)當(dāng)中,不好區(qū)分哪些是補(bǔ)單的 transaction 和正常購(gòu)買的 transaction。
四、StoreKit v2 新特性
StoreKit 2 新特性主要包含三部分:
- StoreKit 2:關(guān)于在 App 里 API 的更新和變化,包含應(yīng)用內(nèi)更改訂閱、退款等;
- Server to Server:蘋(píng)果服務(wù)器與開(kāi)發(fā)者服務(wù)器之間的通訊,包括蘋(píng)果通知、開(kāi)發(fā)者主動(dòng)請(qǐng)求蘋(píng)果服務(wù)器、新的驗(yàn)證收據(jù)流程等;
- Sandbox Test:關(guān)于沙盒測(cè)試環(huán)境相關(guān)的更新,還有一些注意事件等。
五、StoreKit 2 API
StoreKit 2 主要的更新有這幾個(gè):
- 使用 swift 新特性開(kāi)發(fā)
- 更新收據(jù)和交易(數(shù)據(jù)格式和字段變更)
- 更多訂閱類型的接口
- 相同的 StoreKit 框架
5.1 只支持 Swift 開(kāi)發(fā)
StoreKit 2 使用了 Swift 5.5 的新特性進(jìn)行開(kāi)發(fā),完全修改了獲取商品、發(fā)起交易、管理交易信息等接口 API 的實(shí)現(xiàn)方式。swift.org/blog/
例如獲取商品方式語(yǔ)法不同:
原始獲取商品方式
// 1. 請(qǐng)求商品 SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:identifiers]; request.delegate = pipoRequest; [request start]; // 2. 實(shí)現(xiàn) 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) // 購(gòu)買商品 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 } }
5.2 新 API
- 商品
- 購(gòu)買
- 交易信息
- 交易歷史
- 訂閱狀態(tài)
5.2.1 Product
- 新增了一些商品類型,訂閱信息,這些字段信息在 StoreKit 1 里是沒(méi)有的。
方便我們利用的字段:
- 通過(guò)新增的 product type 我們可以輕易的知道當(dāng)前的商品是消耗品還是訂閱商品
- 針對(duì)于自動(dòng)連續(xù)訂閱的第一次購(gòu)買優(yōu)惠,我們可以直接感知到當(dāng)前的商品是不是用戶的 Apple ID 下的第一次購(gòu)買
舉個(gè)例子:
某些 APP 會(huì)有會(huì)員訂閱服務(wù),那些服務(wù)會(huì)有 1 個(gè)月,3 個(gè)月,12 個(gè)月等的自動(dòng)續(xù)期,同時(shí)還會(huì)有一些第一次購(gòu)買的優(yōu)惠,這個(gè)第一次購(gòu)買的優(yōu)惠就是首購(gòu)優(yōu)惠,并且這個(gè)優(yōu)惠跟 Apple ID
掛鉤,跟 APP 內(nèi)自己的賬號(hào)體系無(wú)關(guān),例如小馬哥旗下產(chǎn)品,自有的賬號(hào)體系是 QQ 號(hào) + 微信號(hào)
,那么我們?cè)谥笆菬o(wú)法簡(jiǎn)單得判斷你這個(gè) Apple ID
是否享受過(guò)首購(gòu)優(yōu)惠
了,畢竟用戶可以有多個(gè) QQ 號(hào)
,或者多個(gè) 微信號(hào)
,在彈出蘋(píng)果的購(gòu)買頁(yè)面前,我們是不知道這個(gè) Apple ID
有沒(méi)有享受過(guò)首購(gòu)優(yōu)惠的,會(huì)對(duì)用戶產(chǎn)生誤解,我在上一個(gè)頁(yè)面還告訴我首個(gè)月只要 18 塊錢(qián),實(shí)際支付的時(shí)候?yàn)槭裁匆?25 元了 ? 這個(gè)對(duì)用戶的購(gòu)買意愿肉眼可見(jiàn)是有下降的。
現(xiàn)在我們就可以通過(guò) isEligibleForIntroOffer
這個(gè)屬性,輕松又方便得提前拿到這些信息,對(duì)已經(jīng)享受過(guò)的Apple ID
賬號(hào)不展示這個(gè)優(yōu)惠。
- 提供了新的獲取商品接口
public static func products<Identifiers>(for identifiers: Identifiers) async throws -> [Product] where Identifiers : Collection, Identifiers.Element == String
- 提供了新的購(gòu)買商品接口。其中購(gòu)買商品時(shí)增加了一些可選參數(shù)
PurchaseOption
結(jié)構(gòu)體,該結(jié)構(gòu)體里有新增的特別重要的字段appAccountToken, 類似 SKPayment.applicationUsername 字段,但是 appAccountToken 信息會(huì)永久保存在 Transaction 信息內(nèi)。
appAccountToken 字段是由開(kāi)發(fā)者創(chuàng)建的;關(guān)聯(lián)到 App 里的用戶賬號(hào);使用 UUID 格式;永久存儲(chǔ)在 Transaction 信息里。
PS:這里的 appAccountToken 字段蘋(píng)果的意思是用來(lái)存儲(chǔ)用戶賬號(hào)信息的,但是應(yīng)該也可以用來(lái)存儲(chǔ) orderID 相關(guān)的信息,需要將 orderID 轉(zhuǎn)成 UUID 格式塞到 Transaction 信息內(nèi),方便處理補(bǔ)單、退款等操作。
public func purchase(options: Set<Product.PurchaseOption> = []) async throws -> Product.PurchaseResult let uuid = Product.PurchaseOption.appAccountToken(UUID.init(uuidString: "uid")!) // 發(fā)起一筆購(gòu)買之后,直接等待蘋(píng)果的返回結(jié)果,無(wú)需在paymenqueue中等待transaction狀態(tài)的更新。 //使用sk2發(fā)起的購(gòu)買的訂單的信息,在sk1所有的回調(diào)接口都不會(huì)得到相應(yīng)的transaction的更新?tīng)顟B(tài) 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 } }
- 處理驗(yàn)證 Transaction。系統(tǒng)會(huì)驗(yàn)證是否是一個(gè)合法的 Transaction,此時(shí)系統(tǒng)不再提供 base64 的 receip string 信息,只需要上傳 transaction.id 和 transaction.originalID,服務(wù)器端根據(jù)需要選擇合適的 ID 進(jìn)行驗(yàn)證。
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 } }
- 監(jiān)聽(tīng) Transaction 更新
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") } } } }
針對(duì) transaction 的更新,這個(gè)監(jiān)聽(tīng)是讓我們監(jiān)聽(tīng):
- 這筆訂單用戶開(kāi)啟了一筆購(gòu)買,這筆訂單在蘋(píng)果那邊還沒(méi)有得到結(jié)果,用戶殺死 app 或者用戶卸載了 app 并重新安裝 app,這個(gè)時(shí)候我們可以通過(guò)這個(gè)監(jiān)聽(tīng)收到對(duì)應(yīng)的 transaction 更新
- 用戶發(fā)起一筆購(gòu)買,這筆購(gòu)買可能因?yàn)榫W(wǎng)絡(luò)狀態(tài)不好的因素,在端上收到了失敗的 transaction 回調(diào),但是后續(xù)蘋(píng)果發(fā)現(xiàn)這種 case,重新下發(fā) transaction 到端上進(jìn)行對(duì)應(yīng)的驗(yàn)證。
5.2.2 Transaction History
提供了三個(gè)新的交易(Transcation)相關(guān)的 API:
- All transactions:全部的購(gòu)買交易訂單,在 transaction 里面獲取
- Latest transactions:最新的購(gòu)買交易訂單。
- Current entitlements:所有當(dāng)前訂閱的交易,以及所有購(gòu)買(且未退還)的非消耗品。
根據(jù)可以購(gòu)買的訂閱商品、非消耗品可以過(guò)濾出已經(jīng)購(gòu)買過(guò)的商品。
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 } }
- 同步不同設(shè)備的購(gòu)買記錄。這個(gè) API 可以替換 StoreKit 1 里面的恢復(fù)購(gòu)買 API,調(diào)用該方法后,系統(tǒng)會(huì)彈出提示框要求輸入 AppleID 帳號(hào)密碼信息。
extension AppStore { public static func sync() async throws }
5.2.3 Subscription status
訂閱類型項(xiàng)目的狀態(tài),比如主動(dòng)獲取最新的交易、獲取更新訂閱的狀態(tài),獲取更新訂閱的信息等。其中獲取更新訂閱的信息,可以獲取更新的狀態(tài)、品項(xiàng) id、如果過(guò)期的話,可以知道過(guò)期的原因。(比如用戶取消、扣費(fèi)失敗、訂閱正常過(guò)期等。)獲取的所有數(shù)據(jù)都是 JWS 格式驗(yàn)證。
5.2.4 show manager subscriptions
可以直接喚起 App Store 里的管理訂閱頁(yè)面。
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
提供了新的發(fā)起退款 API,允許用戶在開(kāi)發(fā)者的 App 中直接進(jìn)行退款申請(qǐng)。用戶進(jìn)行申請(qǐng)退款后,App 可以收到通知、另外蘋(píng)果服務(wù)器也會(huì)通知開(kāi)發(fā)者服務(wù)器。(沙盒環(huán)境也可進(jìn)行退款測(cè)試了,但是 App Store 里還沒(méi)開(kāi)啟這個(gè)功能。)
extension Transaction { public static func beginRefundRequest(for transactionID: UInt64, in scene: UIWindowScene) async throws -> Transaction.RefundRequestStatus }
小結(jié):
- StoreKit 2 庫(kù)采用 Swift 5.5 版本最新特性重寫(xiě),只支持 Swift、iOS 15+,提供了一些新的 API 接口,導(dǎo)致新的支付流程會(huì)發(fā)生一些變化。
- 提供了獲取交易歷史記錄、可購(gòu)買的商品列表(自動(dòng)續(xù)期訂閱以及非消耗品)信息
- 提供了獲取訂閱狀態(tài)、管理訂閱狀態(tài)接口
- 支持在 App 內(nèi)發(fā)起退款
六、Server to Server
構(gòu)建開(kāi)發(fā)者服務(wù)器可以實(shí)現(xiàn)以下幾個(gè)功能:
- 接收內(nèi)購(gòu)狀態(tài)改變通知
- 通過(guò)接口跟蹤內(nèi)購(gòu)狀態(tài)變化(獲取訂閱狀態(tài)、獲取所有的交易歷史記錄)
- 隨時(shí)驗(yàn)證用戶的訪問(wèn)權(quán)限(是否已購(gòu)買,是否已退款等信息)
- 管理訂閱狀態(tài)
- 跟蹤退款信息
6.1 Validate status with receipts
服務(wù)器端在通過(guò) /verifyReceipt
接口驗(yàn)證票據(jù)時(shí),新 API 的數(shù)據(jù)結(jié)構(gòu)也發(fā)生了變化。例如統(tǒng)一了購(gòu)買時(shí)間、過(guò)期時(shí)間、原始購(gòu)買時(shí)間格式,新增了 appAcountToken 字段、內(nèi)購(gòu)類型字段、退款時(shí)間、退款原因、促銷優(yōu)惠類型等。具體的可以參考 Manage in-app purchases on your server - WWDC21 - Videos - Apple Developer 視頻,或者 Validating Receipts with the App Store | Apple Developer Documentation
6.2 Check status with APIs
新增了一些 API,可以主動(dòng)去獲取訂閱狀態(tài)、交易歷史記錄等等。具體可以參考這個(gè)文檔:App Store Server API | Apple Developer Documentation
- 獲取訂閱狀態(tài):get_all_subscription_statuses,只需要一個(gè)
originalTransactionId
參數(shù),就可以獲取用戶訂閱的各種狀態(tài) - 獲取交易歷史記錄:get_transaction_history,只需要用戶任意一個(gè)交易里的
originalTransactionId
即可獲取所有的歷史記錄 - 在您的服務(wù)器收到消費(fèi)請(qǐng)求通知后,將有關(guān)應(yīng)用程序內(nèi)消費(fèi)品購(gòu)買的消費(fèi)信息發(fā)送到應(yīng)用程序商店。(主要用于用戶申請(qǐng)退款后,告知 App Store 購(gòu)買的商品有沒(méi)有被消費(fèi),用于評(píng)估是否需要退款)。send_consumption_information
- App Store Server API standards:JSON Web Signature (JWS)
6.3 Track status with notifications
當(dāng)訂閱狀態(tài)發(fā)生變化時(shí),Apple server 會(huì)主動(dòng)通知我們的服務(wù)器,告知發(fā)生了哪些變化。功能跟之前的版本一樣,但是刪除了一些狀態(tài),也新增了一些狀態(tài)。
為了方便測(cè)試沙盒環(huán)境的退款通知,App Store 可以為沙盒環(huán)境單獨(dú)設(shè)置一個(gè) server URL 配置。
6.4 購(gòu)買流程變化
例如第一次購(gòu)買訂閱類型商品時(shí),購(gòu)買成功后,Apple server 會(huì)主動(dòng)通知 我們的 server,告知狀態(tài)。此時(shí)我們的 server 可以不用再去 Apple server 那邊驗(yàn)證了。及時(shí)以后想驗(yàn)證,也可通過(guò) /inApps/v1/subscriptions
接口隨時(shí)去驗(yàn)證。
續(xù)訂、賬單寬限期、用戶退款等操作時(shí)除了可以接受蘋(píng)果的通知外,也可以主動(dòng)去請(qǐng)求蘋(píng)果服務(wù)器,獲取最新的狀態(tài)。例如在自己服務(wù)器宕機(jī)或者因?yàn)槟撤N原因?qū)е聸](méi)有接收到蘋(píng)果的通知時(shí),此時(shí)主動(dòng)去請(qǐng)求蘋(píng)果服務(wù)器獲取交易歷史記錄,交易狀態(tài)信息,就發(fā)揮出了巨大的作用。
6.5 服務(wù)器遷移升級(jí)到 JWS 格式
對(duì)于 StoreKit 2,蘋(píng)果已經(jīng)廢棄了用 receipt 收據(jù)驗(yàn)證邏輯,只需要提供交易的 originalTransactionId 即可獲取到完整的交易信息。那么如何從 StoreKit 1 升級(jí)到 StoreKit 2 呢?
- 上傳 receipt 到我們的服務(wù)器
- 拿著 receipt 去蘋(píng)果服務(wù)器驗(yàn)證,并獲取 originalTransactionId 信息
- 根據(jù) originalTransactionId 去蘋(píng)果服務(wù)器獲取歷史交易記錄,找到特定 originalTransactionId,Transaction
- 如果是訂閱類型的商品,可以繼續(xù)去獲取訂閱狀態(tài)
6.6 Manager family sharing
管理家庭共享。目前蘋(píng)果對(duì) 非消耗型 和 自動(dòng)訂閱 類型品項(xiàng)是支持 家庭共享(family sharing),另外,蘋(píng)果會(huì)返回一個(gè)字段 inAppOwnershipType 表示當(dāng)前用戶是否為購(gòu)買品項(xiàng)的主用戶。更方便的追蹤用戶的狀態(tài)
6.7 Sandbox test
- 清楚沙盒賬號(hào)購(gòu)買記錄
- 新增沙盒環(huán)境的回調(diào) URL 配置
- 改變沙盒賬號(hào)國(guó)家/地區(qū)
- 調(diào)整沙盒續(xù)訂頻率
- 安全性改良:testFlight 版本驗(yàn)證票據(jù)將失敗
小結(jié)
- 以前只支持 Apple server 單向發(fā)送狀態(tài)變化的通知給我們自己的服務(wù)器,現(xiàn)在支持主動(dòng)去請(qǐng)求 Apple server 獲取歷史交易記錄,訂閱狀態(tài)信息
- StoreKit 2 服務(wù)器端接口支持了新的格式(JWS 格式)。
- 沙盒環(huán)境測(cè)試優(yōu)化
七、Customer Support and Handle refunds(客服支持和退款處理)
Support customers and handle refunds - WWDC21 - Videos - Apple Developer
7.1 How do I identify the in-app purchase made by this customer?
如何識(shí)別用戶的購(gòu)買項(xiàng)目。當(dāng)用戶扣款了,但是沒(méi)有收到商品時(shí),用戶會(huì)過(guò)來(lái)返回問(wèn)題并提供了蘋(píng)果郵箱里的扣款信息截圖。
那么我們的服務(wù)器端可以使用截圖里的 invoice order ID 去請(qǐng)求 /inApps/v1/lookup/{customer_order_id}
這個(gè)接口查找到對(duì)應(yīng)的 Transaction 信息。然后我們?cè)偃ヲ?yàn)證是否購(gòu)買成功、是否已申請(qǐng)退款、是否需要補(bǔ)發(fā)商品等等。
https://developer.apple.com/documentation/appstoreserverapi/look_up_order_id
/inApps/v1/lookup/{customer_order_id}
7.2 How do I lookup this customer's past refunds?
如何查詢?cè)撚脩暨^(guò)去的退款信息?
目前的情況是,如果我們服務(wù)器宕機(jī)了或者沒(méi)有收到退款通知,那么我們是不知道用戶是有沒(méi)有進(jìn)行退款的。雖然 StoreKit 2 提供了一個(gè)獲取交易記錄的 API,但是如果通過(guò)該 API 來(lái)自己過(guò)濾退款的交易,不是一個(gè)最好的實(shí)現(xiàn)方式。所以 Apple 新提供了一個(gè) API 可以查到這個(gè)用戶的所有退款記錄訂單,只需要任意的一個(gè) original_transaction_id。
https://developer.apple.com/documentation/appstoreserverapi/get_refund_history
/inApps/v1/refund/lookup/{original_transaction_id}
7.3 How do I compensate subscribers for a service issue?
如何補(bǔ)償訂閱者的服務(wù)問(wèn)題?
比如說(shuō)當(dāng)服務(wù)器出問(wèn)題了,為了挽留用戶/吸引更多用戶,計(jì)劃如何給用戶發(fā)補(bǔ)償。開(kāi)發(fā)者可以提供一個(gè)內(nèi)購(gòu)對(duì)兌碼(所有的內(nèi)購(gòu)類型都可以),在蘋(píng)果后臺(tái)那里生成。然后讓用戶在 App Store 進(jìn)行兌換,也可以在 App 里通過(guò) presentCodeRedemptionSheet()
接口調(diào)用,彈出系統(tǒng)的兌換界面:
7.4 How do I appease customers for outages or canceled events?
如何安撫客戶中斷或取消的活動(dòng)?
主要還是想給用戶一些福利,安撫用戶。類似其他 App 里的簽到一個(gè)月,可以贈(zèng)送用戶 1 個(gè)月會(huì)員等活動(dòng)。但這種方式的過(guò)期時(shí)間是由自己的服務(wù)器后端決定的。
這里 Apple 也提供了一個(gè)接口,允許開(kāi)發(fā)者一年有 2 次機(jī)會(huì)給訂閱內(nèi)購(gòu)用戶每次加 90 天免費(fèi)補(bǔ)償。也就是有自動(dòng)訂閱類型的 App,可以開(kāi)發(fā)者主動(dòng)在服務(wù)器給用戶補(bǔ)償(免費(fèi)延長(zhǎng))用戶的訂單時(shí)間,每次最多是 90 天。
https://developer.apple.com/documentation/appstoreserverapi/extend_a_subscription_renewal_date
/inApps/v1/subscription/extend/{original_transaction_id}
7.5 App 內(nèi)如何管理訂閱
同上面 5.2.4,提供了一個(gè) showManageSubscriptions 接口,可以直接喚起管理訂閱頁(yè)面。
extension AppStore { @available(iOS 15.0, *) @available(macOS, unavailable) @available(watchOS, unavailable) @available(tvOS, unavailable) public static func showManageSubscriptions(in scene: UIWindowScene) async throws }
7.6 APP 內(nèi) 如何申請(qǐng)退款
同上面 5.2.5 request refund API
八、API 購(gòu)買流程
原始 API 購(gòu)買流程
- 請(qǐng)求 Product
- 發(fā)起購(gòu)買
- 接收回調(diào),成功后上傳 receipt
- 服務(wù)器驗(yàn)證 receipt 并發(fā)貨
新 API 購(gòu)買流程
整個(gè)支付購(gòu)買流程與原始 API 購(gòu)買流程一樣,區(qū)別是 3.1 步上傳交易信息時(shí),不再上傳 receipt/token 信息,上傳 transaction_id 就可以了。服務(wù)器端可以通過(guò) transaction_id 去蘋(píng)果服務(wù)器獲取交易結(jié)果,不再需要使用 receipt/token 驗(yàn)證票據(jù)。
九、QA
9.1 如何選擇新 API(StoreKit 2) 還是原始 API(StoreKit 1)
Choosing a StoreKit API for In-App Purchase | Apple Developer Documentation
如果您的應(yīng)用程序依賴于以下任何功能,您可能需要使用原始的應(yīng)用程序內(nèi)購(gòu)買 API:
- 為批量采購(gòu)計(jì)劃(VPP)提供支持。有關(guān)更多信息,請(qǐng)參閱設(shè)備管理。
- 提供應(yīng)用程序預(yù)購(gòu)。有關(guān)更多信息,請(qǐng)參閱為預(yù)購(gòu)提供應(yīng)用程序。
- 您的應(yīng)用程序從高級(jí)版更改為免費(fèi)版,反之亦然。
對(duì)現(xiàn)有和舊應(yīng)用程序使用原始 API。
- 如果是一個(gè)全新的 App,只支持 iOS 15+系統(tǒng)且支持 Swift5 開(kāi)發(fā)成本不大,推薦使用 StoreKit 2,但是使用 StoreKit 1 也沒(méi)有問(wèn)題;
老 App:
- 從 SDK 角度來(lái)看,可以單獨(dú)新增一個(gè)子倉(cāng)支持 StoreKit 2。即使目前不支持,以后遲早也會(huì)支持。
- 從宿主角度來(lái)看,要不要激進(jìn)的引入新功能。引入新功能后會(huì)不會(huì)帶來(lái) OC 與 Swift 混編的問(wèn)題,以及引入新功能后需要做好相應(yīng)的 backup 方案。
9.2 客戶端使用 StoreKit 1,服務(wù)器端升級(jí)到 StoreKit 2 的 API,能否這樣使用?
可以。
對(duì)于后端來(lái)說(shuō),Apple Server API V1 和 Apple Server API V2 都可以使用,與客戶端是否升級(jí)到 StoreKit 2 無(wú)關(guān)。
9.3 Native SDK 使用 StoreKit 2 后,交互流程會(huì)不會(huì)有什么變化?以及與服務(wù)器通信流程會(huì)不會(huì)發(fā)生什么變化?
可以參考上面新 API 購(gòu)買流程圖。
9.4 StoreKit 2 會(huì)不會(huì)出現(xiàn)丟單的情況,以及怎么解決丟單問(wèn)題?
還是有可能出現(xiàn)丟單的情況,例如購(gòu)買成功了,Apple 返回結(jié)果時(shí)由于網(wǎng)絡(luò)的原因?qū)е率×?,但是此時(shí)會(huì)更容易解決。
解決辦法:
- 冷啟動(dòng)時(shí),可監(jiān)聽(tīng) Transaction 變化,收到成功的 Transaction 后重新上傳,與 StoreKit 1 類似。
- 在購(gòu)買時(shí)向 product 內(nèi) appAccountToken 字段里塞入業(yè)務(wù)方 orderID 相關(guān)的信息,當(dāng)用戶反饋扣款了但是沒(méi)發(fā)貨時(shí),可以讓用戶提供 Apple 的 orderID,通過(guò)它可以直接去蘋(píng)果服務(wù)器獲取對(duì)應(yīng)的 Transaction 信息,找到 Transaction.appAccountToken ,再給用戶發(fā)貨。(這里可以做成一個(gè)自動(dòng)化處理工具,只需要用戶提供蘋(píng)果的 orderID,就可以去查找對(duì)應(yīng)的業(yè)務(wù)方 orderID 進(jìn)行發(fā)貨。)
9.5 購(gòu)買成功但是未 finishTransaction,下次冷啟動(dòng)后還會(huì)重新下發(fā) Transaction 嗎?
會(huì),與 StoreKit 1 功能一樣,只是調(diào)用的接口不同。
9.6 從 StoreKit 1 升級(jí)到 StoreKit 2 后,能否看到之前使用 StoreKit 1 購(gòu)買的商品?
能看到,互相兼容了。
9.7 針對(duì)使用 StoreKit1 的 app,是否可以放棄讀取本地 receipt 的方式傳給服務(wù)端來(lái)驗(yàn)證,直接采用 StoreKit2 的 transaction_id 傳遞給蘋(píng)果服務(wù)端進(jìn)行驗(yàn)證票據(jù)?
可以。
9.8 針對(duì)于蘋(píng)果返回的 transaction 信息,我們是否能判斷這個(gè) transaction 的狀態(tài)信息對(duì)應(yīng)的商品類型是哪種?
可以,storekit2 針對(duì)于 transaction 的返回信息當(dāng)中,明確的告訴了我們當(dāng)前的商品類型是什么。針對(duì)于服務(wù)端對(duì)于消耗品和訂閱商品的兩套不同邏輯,我們通過(guò)這個(gè)字段,就可以輕易的區(qū)分是否是訂閱商品再請(qǐng)求對(duì)應(yīng)的接口
WWDC 視頻
- Meet StoreKit 2 - WWDC21 - Videos - Apple Developer:重點(diǎn)內(nèi)容:Storekit 2 API 介紹和代碼演示,以及 appAccountToken
- Manage in-app purchases on your server - WWDC21 - Videos - Apple Developer:重點(diǎn)內(nèi)容:JWS 簽名交易的服務(wù)器驗(yàn)證,新服務(wù)器 API,新服務(wù)器通知,沙盒測(cè)試的新功能
- Support customers and handle refunds - WWDC21 - Videos - Apple Developer:重點(diǎn)內(nèi)容:用來(lái)支持用戶和處理退款的 Storekit 2 API 以及服務(wù)器 API
- What’s new with in-app purchase - WWDC20 - Videos - Apple Developer:重點(diǎn)內(nèi)容:退款通知,家人共享和 SKAdNetwork
- In-App Purchases and Using Server-to-Server Notifications - WWDC19 - Videos - Apple Developer:重點(diǎn)內(nèi)容:App Store 服務(wù)器通知詳解
- What's New in StoreKit - WWDC17 - Videos - Apple Developer:重點(diǎn)內(nèi)容:標(biāo)準(zhǔn) IAP 支付流程,applicationUserName,promoted IAP
以上就是iOS StoreKit 2 新特性盤(pán)點(diǎn)解析的詳細(xì)內(nèi)容,更多關(guān)于iOS StoreKit 2 新特性的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
iOS實(shí)現(xiàn)輪盤(pán)動(dòng)態(tài)效果
這篇文章主要為大家詳細(xì)介紹了iOS實(shí)現(xiàn)輪盤(pán)動(dòng)態(tài)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04IOS 時(shí)間和時(shí)間戳之間轉(zhuǎn)化示例
我們經(jīng)常從服務(wù)器后臺(tái)拿到時(shí)間戳的時(shí)間,以下代碼可以實(shí)現(xiàn)將時(shí)間戳轉(zhuǎn)為可讀的時(shí)間格式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-01-01iOS 8使用UIBlurEffect實(shí)現(xiàn)毛玻璃特效
這篇文章主要為大家詳細(xì)介紹了iOS 8使用UIBlurEffect類和UIVisualEffectView類實(shí)現(xiàn)毛玻璃特效,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05ios 使用xcode11 新建項(xiàng)目工程的步驟詳解
這篇文章主要介紹了ios 使用xcode11 新建項(xiàng)目工程 (值得注意的問(wèn)題),本文分步驟通過(guò)圖文的形式給大家展示,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04iOS實(shí)現(xiàn)通過(guò)按鈕添加和刪除控件的方法
這篇文章主要為大家詳細(xì)介紹了iOS通過(guò)按鈕添加和刪除控件的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-03-03