Copy this into your coding assistant and add your request.
Read https://openiap.dev/docs/errors and https://openiap.dev/llms.txt. Follow the reading instructions, detailed reference, and linked guides relevant to my task before making changes.
Inspect my existing project and reuse its framework and conventions. Ask me for missing product decisions. Implement the requested behavior and run the applicable checks.
Show the working result, the commands and actual test results, and any remaining limitations. Keep your explanation brief.
My request: [describe what customers should be able to do]
All purchase errors follow a consistent structure for easy handling. The PurchaseError shape is defined below.
swift
struct PurchaseError: Codable {
let code: ErrorCode // Generated error enum
let message: String // Human-readable message
let productId: String? // Related product SKU (if applicable)
let debugMessage: String? // Raw diagnostic (e.g. StoreKit error.localizedDescription)
let responseCode: Int? // Android QueryProduct BillingResult.responseCode
let productIds: [String]? // Android QueryProduct requested IDs
let productType: String? // Android BillingClient product type
let isEmptyProductList: Bool? // Android QueryProduct returned no products
let subResponseCodeAndroid: SubResponseCodeAndroid? // Play purchase-update detail
}
Common Error Codes
User Action Errors
Code
Description
Action
UserCancelled
User cancelled the purchase flow
No action needed, expected behavior
UserError
User-related error during purchase
Check user account status
DeferredPayment
Payment was deferred (pending family approval, etc.)
Wait for payment approval
Interrupted
Purchase flow was interrupted
Retry the purchase
Product Errors
Code
Description
Action
ItemUnavailable
Product not available in store
Check product configuration in store console
SkuNotFound
SKU not found in product list
Verify SKU exists in store configuration
SkuOfferMismatch
SKU offer ID mismatch
Check offer configuration for the SKU
QueryProduct
Failed to query product details
Check product IDs and retry
AlreadyOwned
Item already owned by user
Restore purchases to unlock content
ItemNotOwned
Item not owned by user
Purchase the item first
DuplicatePurchase
Duplicate purchase-updated event for the same token (Android)
Deduplicate by purchase token and ignore duplicates
User cancellation: The canonical serialized value is user-cancelled. Some wrappers still normalize legacy E_USER_CANCELLED aliases for compatibility, but new app code should compare the generated ErrorCode.UserCancelled enum value or the user-cancelled wire value.
Android diagnostics:QueryProduct errors from openiap-google 2.1.4 and later include the Google Play Billing responseCode, debugMessage, requested productIds, requested productType, and isEmptyProductList when available. Use these diagnostics to distinguish Play Console product setup issues from transient BillingClient failures. In OpenIAP 2.3.0 / openiap-google 2.3.1, purchase-update failures also carry subResponseCodeAndroid (requires Play Billing 8.0+) when Play Billing provides a more specific reason such as insufficient funds or offer ineligibility.
Network & Service Errors
Code
Description
Action
NetworkError
Network connection error
Check internet connection and retry
ServiceError
Store service error
Wait and retry, check store service status
RemoteError
Remote server error
Check server logs, retry request
InitConnection
Failed to initialize store connection
Check store service availability and retry
ServiceDisconnected
Store service disconnected
Reconnect to the store service
ServiceTimeout
Request reached the billing service timeout (Google Play 8.x+)
Retry the call after a short delay
ConnectionClosed
Connection to store service was closed
Reinitialize connection and retry
IapNotAvailable
In-app purchase service not available
Check device settings and IAP availability
BillingUnavailable
Billing service is unavailable
Check Google Play/App Store availability
FeatureNotSupported
Requested feature not supported
Check device/OS version compatibility
SyncError
Synchronization error with store
Retry synchronization
Validation Errors
Code
Description
Action
PurchaseVerificationFailed
Purchase verification failed
Check verification logic, retry validation
PurchaseVerificationFinished
Purchase verification already completed
Verification already completed, check records
PurchaseVerificationFinishFailed
Failed to finish purchase verification
Check verification state and retry
TransactionValidationFailed
Transaction validation failed
Verify transaction data and retry
EmptySkuList
Empty SKU list provided
Provide at least one SKU to query
Error Handling Examples
Error Handling Pattern
Implement error handlers that respond appropriately to each error type:
Treat user cancellation as an expected result of the purchase flow. Do not show an error alert, retry automatically, or report it as a service failure.
Note: These retry strategies are automatically handled within the OpenIAP module. You don't need to implement them manually.
Error Type
Can Retry
Strategy
NetworkError
Yes
Exponential backoff (2^n seconds)
ServiceError
Yes
Linear backoff (n * 5 seconds)
RemoteError
Yes
Fixed delay (10 seconds)
ConnectionClosed
Yes
Reinitialize and retry
SyncError
Yes
Exponential backoff
UserCancelled
No
Do not retry
AlreadyOwned
No
Restore instead
DeferredPayment
No
Wait for approval
NotPrepared
No
Initialize connection first
Platform-Specific Error Handling
iOS Error Codes
Native Code
Mapped Error
Description
0
Unknown
Unknown error
1
UserCancelled
User cancelled transaction
2
NetworkError
Network unavailable
3
ItemUnavailable
Product not available
4
ServiceError
App Store service error
5
PurchaseVerificationFailed
Purchase verification failed
6
AlreadyOwned
Product already purchased
ErrorCode Enum (Unified)
Complete list of error codes that can be returned by the IAP library.
swift
enum OpenIapError: Error {
case unknown
case userCancelled
case userError
case itemUnavailable
case remoteError
case networkError
case serviceError
case purchaseVerificationFailed
case purchaseVerificationFinished
case purchaseVerificationFinishFailed
case notPrepared
case notEnded
case alreadyOwned
case developerError
case billingResponseJsonParseError
case deferredPayment
case interrupted
case iapNotAvailable
case purchaseError
case syncError
case transactionValidationFailed
case activityUnavailable
case alreadyPrepared
case pending
case connectionClosed
case initConnection
case serviceDisconnected
case serviceTimeout
case queryProduct
case skuNotFound
case skuOfferMismatch
case itemNotOwned
case billingUnavailable
case featureNotSupported
case emptySkuList
case duplicatePurchase
}
Testing Error Scenarios
Testing Error Scenarios
iOS Sandbox Testing
Test Account
Simulated Error
test.purchase.failed@example.com
PurchaseVerificationFailed
test.purchase.cancelled@example.com
UserCancelled
test.purchase.unavailable@example.com
ItemUnavailable
Android Testing Methods
Test Method
Description
License Testing
Add test accounts in Google Play Console for real purchases without charges
Internal Testing Track
Deploy to internal testers for production-like testing
Closed Testing
Test with limited group of users before production release
Test Cards (Sandbox)
Use test payment methods configured in Play Console
⚠️ Important: Static test product IDs like android.test.purchased are deprecated and no longer work. Use real product IDs with test accounts instead. See Releases page for details →
Development Testing
For development testing, consider implementing mock error generators that can simulate various error conditions without requiring actual purchases. This allows you to: