← スキル一覧に戻る

storekit2-integration
by Matejkob
Personal AGENT.md configuration
⭐ 0🍴 0📅 2026年1月18日
SKILL.md
name: storekit2-integration description: Implement StoreKit 2 in-app purchases, subscriptions, consumables, and backend integration. Use when working with IAP, App Store purchases, transaction handling, JWS verification, subscription management, promotional offers, or migrating from StoreKit 1.
StoreKit 2 Integration Guide
Expert guidance for implementing StoreKit 2 with custom backend integration for subscriptions, consumables, and rentals.
Quick Reference
Minimum Requirements
- iOS 15+ for StoreKit 2 APIs
- iOS 17+ for SwiftUI views (
ProductView,StoreView,SubscriptionStoreView) - Xcode 14+ (Xcode 15+ for SwiftUI views)
Product Types
| Type | Restores | Family Sharing | Use Case |
|---|---|---|---|
| Consumable | No | No | Coins, tips, one-time rentals |
| Non-Consumable | Yes | Opt-in | Permanent unlocks, ad removal |
| Auto-Renewable | Yes | Opt-in | Streaming subscriptions |
| Non-Renewing | Yes | No | Season passes, time-limited access |
Critical Transaction IDs
| Field | Description | Use For |
|---|---|---|
transaction.id | Unique per transaction | Logging, deduplication |
transaction.originalID | Stable across renewals | Database linking |
Always use originalID for database operations.
Essential Store Manager
import StoreKit
@MainActor
final class StoreManager: ObservableObject {
@Published private(set) var products: [Product] = []
@Published private(set) var purchasedProductIDs: Set<String> = []
private var updateListenerTask: Task<Void, Error>?
init() {
// CRITICAL: Start listener at launch
updateListenerTask = listenForTransactions()
Task { await updatePurchasedProducts() }
}
deinit { updateListenerTask?.cancel() }
private func listenForTransactions() -> Task<Void, Error> {
Task {
for await result in Transaction.updates {
guard !Task.isCancelled else { break }
do {
let transaction = try checkVerified(result)
if transaction.revocationDate != nil {
purchasedProductIDs.remove(transaction.productID)
await transaction.finish()
continue
}
try await sendJWSToBackend(result.jwsRepresentation)
await updatePurchasedProducts()
await transaction.finish() // Only after backend confirms
} catch { /* Don't finish - will retry */ }
}
}
}
private func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
switch result {
case .unverified(_, let error): throw error
case .verified(let safe): return safe
}
}
}
Purchase Flow
func purchase(_ product: Product, userUUID: UUID) async throws -> Transaction? {
let result = try await product.purchase(options: [.appAccountToken(userUUID)])
switch result {
case .success(let verification):
let transaction = try checkVerified(verification)
guard transaction.revocationDate == nil else { throw PurchaseError.revoked }
try await sendJWSToBackend(verification.jwsRepresentation)
await transaction.finish() // Only after backend confirms
return transaction
case .pending:
throw PurchaseError.pending // Will come through Transaction.updates
case .userCancelled:
return nil
@unknown default:
return nil
}
}
Backend Verification Checklist
- Verify JWS signature against certificate chain
- Validate chain traces to Apple Root CA (G3)
- Check
environmentfield matches expected (Production/Sandbox) - Use
originalTransactionIDfor database operations - Handle idempotency (same transaction may arrive multiple times)
- Check
revocationDatebefore granting access
Detailed References
- App Implementation: See APP_IMPLEMENTATION.md for complete StoreManager, entitlements, restore, and SwiftUI views
- Backend Integration: See BACKEND_INTEGRATION.md for JWS verification, App Store Server API, and server notifications
- Subscription Management: See SUBSCRIPTION_MANAGEMENT.md for states, billing retry, grace periods, and offers
- Common Mistakes: See COMMON_MISTAKES.md for fatal errors to avoid
- Testing: See TESTING.md for Xcode StoreKit, Sandbox, and testing strategies
Key Best Practices
- Always include
appAccountToken- Links purchases to your users - Listen to
Transaction.updatesat app launch - Catches renewals, Family Sharing, Ask to Buy - Finish transactions only after backend confirms - Prevents lost purchases
- Always check
revocationDate- Handles refunds and Family Sharing removal - Use
originalIDnotid- Stable across subscription renewals - Validate certificate chain on backend - Prevents forged receipts
- Check environment on backend - Reject Sandbox in Production
StoreKit 1 vs StoreKit 2
| StoreKit 1 | StoreKit 2 |
|---|---|
SKPaymentTransactionObserver | Transaction.updates async sequence |
SKReceiptRefreshRequest | AppStore.sync() |
/verifyReceipt endpoint | JWS verification or App Store Server API |
SKProductsRequest | Product.products(for:) async |
SKPaymentQueue.add() | product.purchase() async |
Official Resources
スコア
総合スコア
60/100
リポジトリの品質指標に基づく評価
✓SKILL.md
SKILL.mdファイルが含まれている
+20
✓LICENSE
ライセンスが設定されている
+10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
レビュー
💬
レビュー機能は近日公開予定です