Skip to main content

Troubleshooting

Common issues and solutions when working with flutter_inapp_purchase v6.0.0.

Prerequisites Checklist

Before troubleshooting, ensure you have completed the basic setup:

Flutter Setup

  • Flutter SDK 3.0.0 or higher
  • Dart SDK 2.17.0 or higher
  • flutter_inapp_purchase v6.0.0 added to pubspec.yaml
  • Run flutter pub get after adding dependency

Project Configuration

  • Minimum SDK versions set correctly:
    • Android: minSdkVersion 21 or higher
    • iOS: ios.deploymentTarget = '12.0' or higher
  • Platform-specific permissions configured
  • Bundle ID matches store configuration
# pubspec.yaml
dependencies:
flutter_inapp_purchase: ^6.0.0

# android/app/build.gradle
android {
compileSdkVersion 34
defaultConfig {
minSdkVersion 21
targetSdkVersion 34
}
}

# ios/Runner.xcodeproj/project.pbxproj
IPHONEOS_DEPLOYMENT_TARGET = 12.0;

App Store Setup (iOS)

Required Configurations

  1. App Store Connect Setup

    • App registered in App Store Connect
    • Bundle ID matches your app
    • In-App Purchases configured and approved
    • Test users added to sandbox
  2. Xcode Configuration

    • In-App Purchase capability enabled
    • Code signing configured
    • Bundle ID matches App Store Connect
  3. Product Configuration

    • Product IDs match exactly (case-sensitive)
    • Products are in "Ready to Submit" status
    • At least one screenshot uploaded per product
// Verify your product IDs match exactly
final productIds = [
'com.yourapp.premium', // Must match App Store Connect
'com.yourapp.coins_100', // Case-sensitive
];

// Test with actual product IDs from App Store Connect
final products = await FlutterInappPurchase.instance.getProducts(productIds);
debugPrint('Found ${products.length} products');

Google Play Setup (Android)

Required Configurations

  1. Google Play Console Setup

    • App uploaded to Play Console (at least Internal Testing)
    • In-app products created and activated
    • License testing accounts configured
    • App bundle signed and uploaded
  2. Android Configuration

    • BILLING permission in AndroidManifest.xml
    • Play Billing Library dependency (handled by plugin)
    • ProGuard rules configured if using code obfuscation
  3. Testing Setup

    • License testing accounts added in Play Console
    • Test using signed APK/AAB (not debug build)
    • Products are "Active" in Play Console
<!-- android/app/src/main/AndroidManifest.xml -->
<uses-permission android:name="com.android.vending.BILLING" />
// Test connection on Android
Future<void> testAndroidConnection() async {
try {
final result = await FlutterInappPurchase.instance.initConnection();
debugPrint('Android connection result: $result');

// Test product loading
final products = await FlutterInappPurchase.instance.getProducts([
'your_product_id_from_play_console'
]);
debugPrint('Loaded ${products.length} products');
} catch (e) {
debugPrint('Android connection failed: $e');
}
}

Common Issues

requestProducts() returns an empty array

Symptoms:

  • getProducts() or requestProducts() returns empty list
  • Products configured in store but not loading

Solutions:

class ProductLoadingTroubleshooter {
static Future<void> diagnoseProductLoading() async {
final productIds = ['your.product.id'];

// 1. Check connection first
final connected = await _ensureConnection();
if (!connected) {
debugPrint('❌ Store not connected');
return;
}

// 2. Try loading products with error handling
try {
await FlutterInappPurchase.instance.requestProducts(
RequestProductsParams(skus: productIds, type: PurchaseType.inapp),
);

final products = await FlutterInappPurchase.instance.getProducts(productIds);

if (products.isEmpty) {
debugPrint('❌ No products loaded');
await _diagnoseEmptyProducts(productIds);
} else {
debugPrint('✅ Loaded ${products.length} products');
for (final product in products) {
debugPrint('Product: ${product.productId} - ${product.title}');
}
}
} catch (e) {
debugPrint('❌ Product loading error: $e');
}
}

static Future<void> _diagnoseEmptyProducts(List<String> productIds) async {
debugPrint('Diagnosing empty product list...');

// Check product ID format
for (final id in productIds) {
debugPrint('Checking product ID: $id');

if (Platform.isIOS) {
// iOS product IDs should not contain bundle ID
if (id.contains('.')) {
debugPrint('⚠️ iOS product ID contains dots - verify format');
}
} else if (Platform.isAndroid) {
// Android product IDs are typically reverse domain notation
if (!id.contains('.')) {
debugPrint('⚠️ Android product ID missing dots - verify format');
}
}
}

// Suggest solutions
debugPrint('\n🔧 Troubleshooting steps:');
debugPrint('1. Verify product IDs match store configuration exactly');
debugPrint('2. Check if products are approved/active in store');
debugPrint('3. Ensure app version matches store configuration');
debugPrint('4. Try with test product IDs first');
}
}

useIAP hook not working

Problem: Provider or state management not working properly

Solutions:

// Ensure proper provider setup
class IAPProviderSetup {
static Widget setupProvider(Widget child) {
return IapProviderWidget(
child: child,
);
}
}

// In your main.dart
void main() {
runApp(
IAPProviderSetup.setupProvider(
MyApp(),
),
);
}

// Access provider in widgets
class ProductScreen extends StatelessWidget {

Widget build(BuildContext context) {
final iapProvider = IapProvider.of(context);

if (iapProvider == null) {
return Text('❌ IAP Provider not found - check widget tree');
}

if (!iapProvider.connected) {
return Text('⏳ Connecting to store...');
}

return Text('✅ Store connected');
}
}

Purchase flow issues

Common purchase problems and solutions:

class PurchaseFlowTroubleshooter {
static void setupComprehensivePurchaseFlow() {
// 1. Set up listeners BEFORE making purchases
FlutterInappPurchase.purchaseUpdated.listen((purchase) {
if (purchase != null) {
debugPrint('✅ Purchase successful: ${purchase.productId}');
_handlePurchaseSuccess(purchase);
}
});

FlutterInappPurchase.purchaseError.listen((error) {
if (error != null) {
debugPrint('❌ Purchase error: ${error.message}');
_handlePurchaseError(error);
}
});
}

static Future<void> makePurchaseWithDiagnostics(String productId) async {
debugPrint('🛒 Initiating purchase for: $productId');

try {
// Pre-purchase checks
final connected = await _verifyConnection();
if (!connected) {
throw Exception('Store not connected');
}

final productExists = await _verifyProduct(productId);
if (!productExists) {
throw Exception('Product not found: $productId');
}

// Make purchase
await FlutterInappPurchase.instance.requestPurchase(
request: RequestPurchase(
ios: RequestPurchaseIOS(sku: productId, quantity: 1),
android: RequestPurchaseAndroid(skus: [productId]),
),
type: PurchaseType.inapp,
);

debugPrint('📱 Purchase dialog should appear now');

} catch (e) {
debugPrint('❌ Purchase initiation failed: $e');
_suggestPurchaseSolutions(e);
}
}

static void _handlePurchaseError(PurchaseResult error) {
switch (error.responseCode) {
case 1:
debugPrint('User cancelled purchase');
break;
case 7:
debugPrint('User already owns this item - consuming...');
_handleAlreadyOwned(error);
break;
default:
debugPrint('Purchase error ${error.responseCode}: ${error.message}');
}
}
}

Connection issues

Connection problems and diagnostics:

class ConnectionDiagnostics {
static Future<void> runConnectionDiagnostics() async {
debugPrint('🔍 Running connection diagnostics...');

// Test 1: Basic connection
try {
await FlutterInappPurchase.instance.initConnection();
debugPrint('✅ Basic connection successful');
} catch (e) {
debugPrint('❌ Basic connection failed: $e');
return;
}

// Test 2: Platform-specific checks
if (Platform.isIOS) {
await _checkIOSConnection();
} else if (Platform.isAndroid) {
await _checkAndroidConnection();
}

// Test 3: Product loading test
await _testProductLoading();
}

static Future<void> _checkIOSConnection() async {
debugPrint('🍎 Checking iOS connection...');

try {
// Check if payments are allowed
final canMakePayments = await FlutterInappPurchase.instance.initialize();
if (!canMakePayments) {
debugPrint('❌ Device cannot make payments');
debugPrint('💡 Check: Screen Time restrictions, parental controls');
return;
}

debugPrint('✅ iOS payments are allowed');
} catch (e) {
debugPrint('❌ iOS connection check failed: $e');
}
}

static Future<void> _checkAndroidConnection() async {
debugPrint('🤖 Checking Android connection...');

try {
final connectionState = await FlutterInappPurchase.instance.getConnectionStateAndroid();
debugPrint('Android connection state: $connectionState');

if (connectionState != 'connected') {
debugPrint('❌ Android billing service not connected');
debugPrint('💡 Check: Google Play Services, Play Store app updates');
return;
}

debugPrint('✅ Android billing service connected');
} catch (e) {
debugPrint('❌ Android connection check failed: $e');
}
}
}

Platform-specific issues

iOS Specific:

class IOSTroubleshooting {
static Future<void> diagnoseIOSIssues() async {
debugPrint('🍎 Diagnosing iOS-specific issues...');

// Check sandbox vs production
if (kDebugMode) {
debugPrint('Running in DEBUG mode - using iOS Sandbox');
debugPrint('💡 Ensure you have sandbox test account signed in');
} else {
debugPrint('Running in RELEASE mode - using Production');
}

// Check StoreKit availability
try {
final promoted = await FlutterInappPurchase.instance.getPromotedProduct();
debugPrint('StoreKit promotional products available: ${promoted != null}');
} catch (e) {
debugPrint('StoreKit check failed: $e');
}

// Common iOS issues
debugPrint('\n🔧 Common iOS solutions:');
debugPrint('1. Sign out and back into sandbox account in Settings');
debugPrint('2. Verify products are "Ready to Submit" in App Store Connect');
debugPrint('3. Check Bundle ID matches exactly');
debugPrint('4. Ensure In-App Purchase capability is enabled');
}
}

Android Specific:

class AndroidTroubleshooting {
static Future<void> diagnoseAndroidIssues() async {
debugPrint('🤖 Diagnosing Android-specific issues...');

// Check Play Store availability
try {
final store = await FlutterInappPurchase.instance.getStore();
debugPrint('Current store: $store');

if (store != 'play') {
debugPrint('❌ Not using Google Play Store');
debugPrint('💡 App must be installed from Play Store for purchases');
}
} catch (e) {
debugPrint('Store check failed: $e');
}

// Check if running on signed build
debugPrint('Build mode: ${kDebugMode ? "DEBUG" : "RELEASE"}');
if (kDebugMode) {
debugPrint('⚠️ Debug builds may not work with real products');
debugPrint('💡 Use signed build for testing real products');
}

// Common Android issues
debugPrint('\n🔧 Common Android solutions:');
debugPrint('1. Use signed APK/AAB, not debug build');
debugPrint('2. Add license testing account in Play Console');
debugPrint('3. Ensure products are "Active" in Play Console');
debugPrint('4. Upload app to at least Internal Testing track');
}
}

Debugging Tips

1. Enable verbose logging

class DebugLogging {
static void enableVerboseLogging() {
if (kDebugMode) {
// Log all purchase events
FlutterInappPurchase.purchaseUpdated.listen((purchase) {
debugPrint('📱 PURCHASE UPDATE: ${purchase?.toJson()}');
});

FlutterInappPurchase.purchaseError.listen((error) {
debugPrint('❌ PURCHASE ERROR: ${error?.toJson()}');
});

FlutterInappPurchase.connectionUpdated.listen((result) {
debugPrint('🔗 CONNECTION UPDATE: $result');
});
}
}
}

2. Log purchase events

class PurchaseEventLogger {
static void logPurchaseFlow(String step, [Map<String, dynamic>? data]) {
final timestamp = DateTime.now().toIso8601String();
debugPrint('[$timestamp] PURCHASE: $step');

if (data != null) {
data.forEach((key, value) {
debugPrint(' $key: $value');
});
}
}

// Usage
static void example() {
logPurchaseFlow('INITIATED', {'productId': 'premium'});
logPurchaseFlow('DIALOG_SHOWN');
logPurchaseFlow('COMPLETED', {'transactionId': 'txn_123'});
}
}

3. Monitor connection state

class ConnectionMonitor {
static void startMonitoring() {
Timer.periodic(Duration(seconds: 10), (timer) async {
try {
final connected = await _checkConnection();
debugPrint('🔗 Connection status: ${connected ? "CONNECTED" : "DISCONNECTED"}');

if (!connected) {
debugPrint('⚠️ Connection lost - attempting reconnect...');
await FlutterInappPurchase.instance.initConnection();
}
} catch (e) {
debugPrint('❌ Connection check failed: $e');
}
});
}
}

Testing Strategies

1. Staged testing approach

class StagedTesting {
static Future<void> runStagedTests() async {
debugPrint('🧪 Starting staged testing...');

// Stage 1: Connection test
debugPrint('\n📊 Stage 1: Connection Test');
final connected = await _testConnection();
if (!connected) return;

// Stage 2: Product loading test
debugPrint('\n📊 Stage 2: Product Loading Test');
final productsLoaded = await _testProductLoading();
if (!productsLoaded) return;

// Stage 3: Purchase flow test
debugPrint('\n📊 Stage 3: Purchase Flow Test');
await _testPurchaseFlow();

debugPrint('\n✅ All tests completed');
}
}

2. Test different scenarios

class ScenarioTesting {
static Future<void> testAllScenarios() async {
final scenarios = [
'first_time_user',
'returning_user',
'user_with_existing_purchases',
'network_interruption',
'app_backgrounded_during_purchase',
];

for (final scenario in scenarios) {
debugPrint('🎭 Testing scenario: $scenario');
await _testScenario(scenario);
}
}
}

3. Device testing matrix

class DeviceTestMatrix {
static const testMatrix = {
'iOS': [
{'version': '15.0', 'device': 'iPhone 12'},
{'version': '16.0', 'device': 'iPhone 14'},
{'version': '17.0', 'device': 'iPhone 15'},
],
'Android': [
{'version': '11', 'device': 'Pixel 5'},
{'version': '12', 'device': 'Samsung S22'},
{'version': '13', 'device': 'Pixel 7'},
],
};

static void logTestResults(String platform, String version, bool passed) {
debugPrint('📱 Test Result: $platform $version - ${passed ? "PASSED" : "FAILED"}');
}
}

Error Code Reference

class ErrorCodeReference {
static String getErrorDescription(int code) {
switch (code) {
case 0:
return 'OK - Success';
case 1:
return 'User Canceled - User pressed back or canceled a dialog';
case 2:
return 'Service Unavailable - Network connection is down';
case 3:
return 'Billing Unavailable - Billing API version is not supported';
case 4:
return 'Item Unavailable - Requested product is not available';
case 5:
return 'Developer Error - Invalid arguments provided to the API';
case 6:
return 'Error - Fatal error during the API action';
case 7:
return 'Item Already Owned - User already owns the item';
case 8:
return 'Item Not Owned - User does not own the item';
default:
return 'Unknown Error Code: $code';
}
}

static void logError(PurchaseResult error) {
debugPrint('❌ Error ${error.responseCode}: ${getErrorDescription(error.responseCode ?? -1)}');
debugPrint(' Message: ${error.message}');
debugPrint(' Debug Message: ${error.debugMessage}');
}
}

Getting Help

Bug report template

When reporting issues, please include:

**Environment:**
- flutter_inapp_purchase version: 6.0.0
- Flutter version: [run `flutter --version`]
- Platform: iOS/Android
- Device/OS version:

**Store Setup:**
- [ ] Products configured in App Store Connect/Play Console
- [ ] App uploaded to store (Internal Testing for Android)
- [ ] Test accounts configured

**Issue Description:**
[Describe what you expected vs what actually happened]

**Code Sample:**
```dart
// Minimal reproducible code

Logs:

// Error logs and debug output
// Enable verbose logging first

Troubleshooting Attempted:

  • Verified product IDs match store configuration
  • Tested with signed build (Android)
  • Checked connection status
  • Reviewed platform-specific setup

Additional Context: [Any other relevant information]


### Debug checklist

Before reporting bugs, verify:

- [ ] Product IDs match store configuration exactly
- [ ] App is properly configured in respective store
- [ ] Using signed build for testing (Android)
- [ ] Connection established before making requests
- [ ] Purchase listeners set up before purchase attempts
- [ ] Error handling implemented
- [ ] Tested on physical device
- [ ] Verbose logging enabled

For additional help:
- Check [GitHub Issues](https://github.com/hyochan/flutter_inapp_purchase/issues)
- Review [API Documentation](../api/flutter-inapp-purchase.md)
- Test with [Example App](https://github.com/hyochan/flutter_inapp_purchase/tree/main/example)