WWDC Quick Look 💓 By SwiftGGTeam
Meet StoreKit 2

Meet StoreKit 2

元の動画を見る

ハイライト

StoreKit 2 は、アプリ内購入プロセス全体を Swift async/await で書き換えます。Product.products(for:)製品を取得するための 1 行のコード、product.purchase()1 行のコードで購入が開始され、各トランザクションには JWS 署名が付けられ、自動的に検証され、サブスクリプション ステータスが渡されます。currentEntitlements直接お問い合わせください。

主要内容

あなたはアプリ内購入システムを構築しています。あなたが書いた StoreKit 1 コード: 作成SKProductsRequest、エージェントメソッドの実装、処理SKPaymentTransactionステート マシンの長いコード、テスト用のサンドボックスと正式な環境の間の切り替え、およびレシートの解析は、頭痛の種になります。

StoreKit 2 は次のように述べています: これらはすべてもっとシンプルになる可能性があります。

新しい製品はコード行を要求します。新しい購入プロセスのコードは 1 行です。トランザクションの検証は自動的に行われます。サブスクリプション ステータスを直接クエリします。すべての API は Swift ネイティブの async/await であり、コードは同期ロジックのように読み取れます。

また、StoreKit 2 と StoreKit 1 は完全な互換性があります。古いトランザクションは新しい API に自動的に同期され、段階的に移行できます。

詳細

製品情報を取得する

03:13

ストアキット 2Product交換されたタイプSKProduct、製品タイプやサブスクリプション情報などのフィールドが追加されました。

let products = try await Product.products(for: ["fuel.small", "car.sports", "nav.pro"])

// タイプ別にグループ化する
let consumables = products.filter { $0.type == .consumable }
let nonConsumables = products.filter { $0.type == .nonConsumable }
let subscriptions = products.filter { $0.type == .autoRenewable }

キーポイント:

  • Product.products(for:)製品 ID コレクションを渡す静的メソッドです
  • 返されましたProduct含むtype属性、消耗品/非消耗品/サブスクリプションを自動的に識別
  • サポートBackingValueパッケージング タイプ、今後追加される新しいフィールドは古い SDK でもアクセスできます

購入を開始する

03:31

ご購入はProductインスタンスメソッドの場合、結果は次のようになりますPurchaseResult列挙する:

let result = try await product.purchase()

switch result {
case .success(let verification):
    // 購入は成功しました。検証には署名されたトランザクションが含まれています
    let transaction = try checkVerified(verification)
    await deliverContent(for: transaction)
    await transaction.finish()

case .userCancelled:
    // ユーザーがキャンセルする
    break

case .pending:
    // 検証待ち (例: 親の承認、銀行検証)
    break

case .unverified(_, let error):
    // 認証に失敗しました
    print("Purchase failed: \(error)")
}

キーポイント:

  • purchase()戻るPurchaseResult、成功、キャンセル、待機、失敗の 4 つの状態をカバーします。
  • 成功時に返されるverification自動検証結果が含まれます
  • 最後に電話しなければなりませんtransaction.finish()そうでない場合、トランザクションはキュー内に残ります。

自動トランザクション検証

11:19

StoreKit 2 のすべてのトランザクションは、JWS (JSON Web Signature) で署名されます。システムは、署名が App Store からのものであることを自動的に検証します。

func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
    switch result {
    case .verified(let safe):
        return safe
    case .unverified(_, let error):
        throw error
    }
}

キーポイント:

  • VerificationResult.verified.unverified 两个 case
  • システムは、署名が Apple からのものであり、バンドル ID と一致し、現在のデバイスで生成されたものであることを確認しました。
  • CryptoKit と証明書チェーンを使用して、JWS を自分で認証することも選択できます。

アプリアカウントトークン: ユーザーシステムに関連付けます

04:20

アプリに独自のアカウント システムがある場合は、次を使用できます。appAccountTokenStoreKit トランザクションをユーザーに関連付けます。

let accountToken = UUID(uuidString: user.id)!
let result = try await product.purchase(options: [.appAccountToken(accountToken)])

キーポイント:

  • appAccountTokenユーザーが生成した UUID 形式です
  • 購入が完了すると、トークンがトランザクションに表示されますappAccountTokenフィールドで
  • デバイス間で同期できるため、デバイスを変更しても同じアカウントの購入記録を追跡できます

トランザクションの更新をリッスンする

15:22

使用Transaction.updatesすべてのトランザクションの更新をリッスンします (他のデバイスでの購入、保護者の承認を得た完了、返金などを含む)。

Task {
    for await result in Transaction.updates {
        guard let transaction = try? checkVerified(result) else { continue }

        if transaction.revocationDate == nil {
            await deliverContent(for: transaction)
        }
        await transaction.finish()
    }
}

キーポイント:

  • Transaction.updatesこれは無限の非同期シーケンスであり、アプリの起動後にリッスンを開始します。
  • 返金処理中 (revocationDatenil 以外) およびアップグレード (isUpgradedそれは本当です)
  • アプリの起動時に監視を開始する必要があります。そうしないと、更新を見逃す可能性があります

取引履歴と現在の資産を確認する

19:36

StoreKit 2 は、次の 3 つのクエリ メソッドを提供します。

// 1. すべての履歴トランザクションを取得する
for await result in Transaction.all {
    let transaction = try checkVerified(result)
    // 過去の各トランザクションを処理する
}

// 2. 製品の最新のトランザクションを取得する
if let result = await Transaction.latest(for: "nav.pro") {
    let transaction = try checkVerified(result)
    // 检查是否仍有效
}

// 3. 現在の実効的な権利と利益を取得する (最も一般的に使用される)
for await result in Transaction.currentEntitlements {
    let transaction = try checkVerified(result)
    // 対応するコンテンツのロックを解除する
}

キーポイント:

  • currentEntitlements現在有効な非消耗品およびアクティブなサブスクリプションのみが返されます
  • 返金されたトランザクションはcurrentEntitlements真ん中
  • 消耗品は永続的なものではないため含まれていません。
  • ユーザーがデバイスを切り替えた後に初めてアプリを開くと、手動で復元することなく自動的に特典を取得できます。

サブスクリプションステータスのクエリ

22:47

サブスクリプション ステータスは、最新のトランザクション、更新ステータス、更新情報の 3 つの部分で構成されます。

let statuses = await product.subscription?.status

for status in statuses {
    // 1. 最新の取引
    let latestTransaction = try checkVerified(status.transaction)

    // 2. 更新ステータス: 購読済み / 期限切れ / inGracePeriod / inBillingRetryPeriod / 取り消し済み
    switch status.state {
    case .subscribed:
        // 通常購読の場合
    case .expired:
        // 期限切れ
    case .inGracePeriod:
        // 猶予期間内に
    default:
        break
    }

    // 3. 续订信息(也带 JWS 签名)
    let renewalInfo = try checkVerified(status.renewalInfo)
    if renewalInfo.autoRenewStatus == .off {
        // ユーザーは自動更新をオフにしており、保持オファーを表示できます
    }
    if renewalInfo.autoRenewProductID != product.id {
        // ユーザーがサブスクリプション レベルをダウングレードしました
    }
}

キーポイント:

  • statusユーザーは独自のサブスクリプションとファミリー共有サブスクリプションの両方を持っている可能性があるため、配列を返します。
  • アレイ内の最高レベルのサービスを取得する必要があります
  • renewalInfo 包含 autoRenewStatusautoRenewProductIDexpirationReason 等关键字段
  • 更新情報も JWS によって署名され、システムが自動的に検証します。

JWS署名構造

34:03

StoreKit 2 は、次の 3 つの部分で構成される JSON Web 署名標準を使用します。

  1. ヘッダー: アルゴリズム (ECDSA) と証明書チェーン (x5c) が含まれます
  2. ペイロード: トランザクション ID、製品 ID、購入日などのコア データ。
  3. 署名: ヘッダーとペイロードを使用して生成された署名

自己検証時の追加チェック:

  • ペイロード内のバンドル ID がアプリと一致することを確認してください
  • 使用AppStore.deviceVerificationIDデバイス検証を実行します。ノンスとデバイス ID を連結して SHA384 ハッシュを作成し、それをペイロードのデバイス検証フィールドと比較します。

キーポイント:

  • 証明書チェーンは JWS データに含まれており、検証にインターネット接続は必要ありません。
  • StoreKit 2 では検証が自動的に完了するため、ほとんどのシナリオを自分で実装する必要はありません。
  • サーバー側検証リファレンス App Store サーバー API

重要ポイント

  1. 新しいプロジェクトは、アプリ内購入に StoreKit 2 を直接使用します。 async/await を使用すると、購入プロセス コードを数十行のプロキシ メソッドから数行の連続コードに圧縮できます。自動検証により、レシートの解析にかかる手間が省けます。入口API:Product.products(for:) + product.purchase()

  2. 使用currentEntitlements購入内容を手動で復元する代わりの方法。ユーザーがデバイスを変更した後に初めてアプリを開くと、「購入を再開」ボタンをクリックすることなく、すべての有効な権利と利益を自動的に取得できます。消耗品は除きます。入口API:Transaction.currentEntitlements

  3. 有効期限が切れる前にサブスクリプションを保持してください。合格renewalInfo.autoRenewStatusユーザーが自動更新をオフにしているかどうかを検出し、オフにしている場合は、有効期限が切れる前に返金オファーを表示します。入口API:Product.subscription?.status + .renewalInfo

  4. 使用appAccountToken独自のアカウント システムをリンクします。アプリに別のログイン システムがある場合は、次のように UUID を生成します。appAccountTokenこれは購入時に送信され、後で取引記録で特定のユーザーを追跡できます。入口API:.appAccountToken(UUID) purchase option。

  5. アプリの起動後すぐにトランザクション監視を開始しますTransaction.updatesこれは無限の非同期シーケンスであり、アプリの起動時に監視する必要があります。そうしないと、他のデバイスでの購入完了、保護者の承認、返金などのイベントが見逃される可能性があります。入口API:Task { for await result in Transaction.updates { ... } }

関連セッション

コメント

GitHub Issues · utterances