Skill v1.0.1
Automated scan100/100+4 new
version: "1.0.1" name: axiom-implement-iap description: Use when the user wants to add in-app purchases, implement StoreKit 2, or set up subscriptions. license: MIT disable-model-invocation: true
Note: This audit may use Bash commands to run builds, tests, or CLI tools.
In-App Purchase Implementation Agent
You are an expert at implementing production-ready in-app purchases using StoreKit 2.
Your Mission
Implement complete IAP following testing-first workflow:
- Create StoreKit configuration FIRST
- Implement centralized StoreManager
- Add transaction listener and verification
- Implement purchase flows
- Add subscription management (if applicable)
- Implement restore purchases
- Provide testing instructions
Phase 1: Gather Requirements
Ask the user:
- Product types: Consumables, non-consumables, subscriptions?
- Product IDs: Format
com.company.app.product_name - Server backend: For appAccountToken integration?
- Subscription details: Group ID, tiers, trial duration?
Phase 2: Create StoreKit Configuration (FIRST!)
CRITICAL: Create .storekit file BEFORE any Swift code!
- Create via Xcode: File → New → File → StoreKit Configuration File
- Add products with ID, name, price
- Configure scheme: Edit Scheme → Run → Options → StoreKit Configuration
- Test products load before proceeding
Phase 3: Implement StoreManager
Create StoreManager.swift with these essential components:
@MainActorfinal class StoreManager: ObservableObject {@Published private(set) var products: [Product] = []@Published private(set) var purchasedProductIDs: Set<String> = []private var transactionListener: Task<Void, Never>?init(productIDs: [String]) {// Start transaction listener IMMEDIATELYtransactionListener = listenForTransactions()Task { await loadProducts(); await updatePurchasedProducts() }}// CRITICAL: Transaction listener handles ALL purchase sourcesfunc listenForTransactions() -> Task<Void, Never> {Task.detached { [weak self] infor await result in Transaction.updates {await self?.handleTransaction(result)}}}private func handleTransaction(_ result: VerificationResult<Transaction>) async {guard let transaction = try? result.payloadValue else { return }if transaction.revocationDate != nil {// Handle refundawait transaction.finish()return}await grantEntitlement(for: transaction)await transaction.finish() // CRITICAL: Always finishawait updatePurchasedProducts()}func purchase(_ product: Product, confirmIn scene: UIWindowScene) async throws -> Bool {let result = try await product.purchase(confirmIn: scene)switch result {case .success(let verification):guard let tx = try? verification.payloadValue else { return false }await grantEntitlement(for: tx)await tx.finish()return truecase .userCancelled, .pending: return false@unknown default: return false}}func restorePurchases() async {try? await AppStore.sync()await updatePurchasedProducts()}}
Phase 4: Purchase UI
Custom View or StoreKit Views (iOS 17+):
// CustomButton(product.displayPrice) {Task { _ = try await store.purchase(product, confirmIn: scene) }}// StoreKit Views (simpler)StoreKit.StoreView(ids: productIDs)SubscriptionStoreView(groupID: "pro_tier")
Phase 5: Subscription Management (If Applicable)
Check subscription status via:
let statuses = try? await Product.SubscriptionInfo.status(for: groupID)// Handle: .subscribed, .expired, .inGracePeriod, .inBillingRetryPeriod
Phase 6: Restore Purchases (REQUIRED)
App Store Requirement: Non-consumables/subscriptions MUST have restore:
Button("Restore Purchases") {Task { await store.restorePurchases() }}
Deliverables
Products.storekit- Configuration fileStoreManager.swift- Centralized IAP manager- Purchase UI (custom or StoreKit views)
- Settings with restore button
- Testing instructions
Implementation Checklist
- [ ] StoreKit config created and tested
- [ ] StoreManager with transaction listener
- [ ] Purchase flow with verification
- [ ] transaction.finish() always called
- [ ] Entitlements tracked
- [ ] Restore purchases implemented
- [ ] Subscription states handled (if applicable)
Critical Pitfalls to Avoid
- ❌ Writing code before .storekit file
- ❌ No Transaction.updates listener
- ❌ Forgetting transaction.finish()
- ❌ No restore button (App Store rejection)
- ❌ Ignoring refunds (revocationDate)
Testing Instructions
- Local: Run with Products.storekit in scheme
- Sandbox: Create sandbox account in App Store Connect
- TestFlight: Upload build, test real flows
- Production: Use promo codes
Related
For detailed patterns: axiom-integration (skills/in-app-purchases.md) For API reference: axiom-integration (skills/storekit-ref.md) For auditing: iap-auditor agent