This document provides a comprehensive reference for the Iaptic StoreKit 2 package API.
Initialization
Basic Initialization
let iaptic = Iaptic(
appName: "your-app-name",
publicKey: "your-public-key"
)
Advanced Initialization
let iaptic = Iaptic(
baseURL: "https://validator.iaptic.com", // Optional: Custom API endpoint
appName: "your-app-name",
publicKey: "your-public-key",
verbose: true // Optional: Enable detailed logging
)
Core Methods
Validate Purchase Result
Validates a purchase result from StoreKit 2.
@MainActor
func validate(
productId: String,
purchaseResult: Product.PurchaseResult,
applicationUsername: String = ""
) async -> ValidationResult
Parameters:
productId
: The product identifierpurchaseResult
: The purchase result from StoreKit 2applicationUsername
: Optional user identifier for tracking purchases (defaults to empty string)
Returns: A ValidationResult
object containing validation details
Validate Verification Result
Validates a transaction verification result from StoreKit 2.
@MainActor
func validate(
productId: String,
verificationResult: VerificationResult<Transaction>,
applicationUsername: String = ""
) async -> ValidationResult
Parameters:
productId
: The product identifierverificationResult
: The verification result from StoreKit 2applicationUsername
: Optional user identifier for tracking purchases (defaults to empty string)
Returns: A ValidationResult
object containing validation details
Validate with JWS
Validates a transaction using its JWS representation.
func validateWithJWS(
productId: String? = nil,
jwsRepresentation: String,
applicationUsername: String = "",
transactionId: String = UUID().uuidString,
originalTransactionId: String? = nil,
retryCount: Int = 8,
retryDelay: TimeInterval = 5.0
) async -> ValidationResult
Parameters:
productId
: The product identifier (optional)jwsRepresentation
: The JWS representation of the transactionapplicationUsername
: Optional user identifier for tracking purchases (defaults to empty string)transactionId
: The transaction ID (defaults to a new UUID string)originalTransactionId
: The original transaction ID (optional)retryCount
: Number of retry attempts for network failures (defaults to 8)retryDelay
: Delay in seconds between retry attempts (defaults to 5.0)
Returns: A ValidationResult
object containing validation details
Get Verified Purchases
Retrieves the most recent verified purchases from the last validation.
func getVerifiedPurchases() -> [ValidationResult.Purchase]?
Returns: An array of Purchase
objects if available, or nil
if no verified purchases exist
ValidationResult
The ValidationResult
class provides detailed information about the validation result.
Properties
// Core validation status
var isValid: Bool
var isExpired: Bool
var isActive: Bool
// Error information
var errorCode: String?
var errorMessage: String?
// Purchase details
var purchases: [Purchase]?
// Additional information
var ineligibleForIntroPrice: [String]?
var productId: String?
var validationDate: Date?
var warning: String?
Purchase Object
The Purchase
struct represents a validated purchase with standardized properties.
Properties
// Core purchase information
var id: String
// Dates
var purchaseDate: Date?
var expiryDate: Date?
var renewalIntentChangeDate: Date?
var lastRenewalDate: Date?
// Subscription details
var isExpired: Bool?
var renewalIntent: String?
var cancelationReason: String?
var isBillingRetryPeriod: Bool?
var isTrialPeriod: Bool?
var isIntroPeriod: Bool?
var isAcknowledged: Bool?
var discountId: String?
var priceConsentStatus: String?
Error Handling
Error Codes
The errorCode
in ValidationResult
is a string value that can contain the following:
"PurchaseFailed"
: The purchase operation was not successful"InvalidURL"
: Invalid API URL"SerializationError"
: Error serializing request body"HTTPError"
: HTTP error with server communication"UnknownError"
: Unknown error occurred during validation"RequestError"
: Network request failed after multiple attempts
Error Handling Example
let validationResult = await iaptic.validate(
productId: product.id,
purchaseResult: result
)
if validationResult.isValid {
// Handle valid purchase
} else {
if let errorCode = validationResult.errorCode,
let errorMessage = validationResult.errorMessage {
switch errorCode {
case "InvalidURL":
print("Invalid URL: \(errorMessage)")
case "HTTPError":
print("HTTP Error: \(errorMessage)")
case "RequestError":
print("Network error: \(errorMessage)")
default:
print("Validation error: \(errorCode) - \(errorMessage)")
}
} else {
print("Unknown validation error")
}
}
Configuration Options
Logging
Control the verbosity of logging:
// Enable verbose logging
let iaptic = Iaptic(
appName: "your-app-name",
publicKey: "your-public-key",
verbose: true
)
// Disable verbose logging (default)
let iaptic = Iaptic(
appName: "your-app-name",
publicKey: "your-public-key",
verbose: false
)
Custom API Endpoint
Use a custom API endpoint:
let iaptic = Iaptic(
baseURL: "https://custom-api.example.com",
appName: "your-app-name",
publicKey: "your-public-key"
)
Advanced Usage
Check the user entitlement
func updateEntitlements() {
if let verifiedPurchases = self.iaptic.getVerifiedPurchases() {
self.entitlementManager?.hasPro = verifiedPurchases.contains { !($0.isExpired ?? false) && ($0.id == "my.pro.product") }
} else {
self.entitlementManager?.hasPro = false
}
}
Verify a receipt
func verifyWithIaptic(jwsRepresentation: String, productID: String) async {
let response = await iaptic.validateWithJWS(
productId: productID,
jwsRepresentation: jwsRepresentation,
applicationUsername: userID
)
if response.isValid {
print("✅ Transaction validated successfully with iaptic")
// Process the verified transaction
self.updateEntitlements()
} else {
print("❌ Transaction validation failed with iaptic: \(response)")
}
}
Refreshing purchases at startup
func refreshPurchases() {
Task {
print("🔍 Checking transactions")
for await result in Transaction.currentEntitlements {
print("⚙️ Processing entitlement")
switch result {
case .verified(let transaction):
print("✅ Entitlement verified for product: \(transaction.productID)")
await self.verifyWithIaptic(jwsRepresentation: result.jwsRepresentation, productID: transaction.productID)
case .unverified(let transaction, let error):
print("❌ Entitlement local verification failed: \(error.localizedDescription)")
print("⚠️ Unverified product ID: \(transaction.productID)")
await self.verifyWithIaptic(jwsRepresentation: result.jwsRepresentation, productID: transaction.productID)
}
}
print("✨ Finished checking transactions")
}
}
Handling Transaction Updates
Set up an observer for transaction updates:
func observeTransactionUpdates() -> Task<Void, Never> {
return Task(priority: .background) {
for await verificationResult in Transaction.updates {
switch verificationResult {
case .verified(let transaction):
await self.verifyWithIaptic(jwsRepresentation: verificationResult.jwsRepresentation, productID: transaction.productID)
await transaction.finish()
case .unverified(let transaction, let error):
// Even with local verification failure, validate with iaptic
await self.verifyWithIaptic(jwsRepresentation: verificationResult.jwsRepresentation, productID: transaction.productID)
await transaction.finish()
}
}
}
}
Restoring Purchases
Implement a restore purchases function:
func restorePurchases() async {
do {
// Sync with the App Store
try await AppStore.sync()
self.refreshPurchases()
} catch {
print("Error restoring purchases: \(error.localizedDescription)")
}
}
Checking Subscription Status
Check if a user has an active subscription:
func checkSubscriptionStatus() {
if let purchases = iaptic.getVerifiedPurchases() {
let hasActiveSubscription = purchases.contains { purchase in
// Check if it's not expired
if let isExpired = purchase.isExpired, isExpired {
return false
}
// Check if it has an expiry date in the future
if let expiryDate = purchase.expiryDate, expiryDate < Date() {
return false
}
return true
}
if hasActiveSubscription {
// User has an active subscription
unlockPremiumFeatures()
} else {
// No active subscription
showSubscriptionOptions()
}
} else {
// No verified purchases
showSubscriptionOptions()
}
}
Complete Integration Example
For a complete integration example, refer to our demo project.