Skip to main content
Hyo
Maintainer of flutter_inapp_purchase & expo-iap
View all authors

Flutter In‑App Purchase 6.5.0

· One min read
Hyo
Maintainer of flutter_inapp_purchase & expo-iap

We’ve shipped 6.5.0 with improved iOS parity to OpenIAP, safer parsing, and stronger pre‑commit checks.

Highlights

  • iOS: channel method rename buyProductrequestPurchase (Dart updated accordingly)
  • Standardized error codes/messages via OpenIAP (OpenIapError.defaultMessage)
  • More robust product parsing when native returns numbers for string fields
  • Better restore flow: iOS runs syncIOS() then getAvailablePurchases()
  • iOS helpers: clearTransactionIOS, getPromotedProductIOS, requestPurchaseOnPromotedProductIOS
  • Pre‑commit hook now formats, analyzes, and runs tests by default

Podspec now requires openiap ~> 1.1.8. The example Podfile uses the CocoaPods CDN and can pin the git tag if the trunk index lags.

See CHANGELOG for details. Thanks for all the feedback and reviews!

Version 6.4.6 - iOS Expired Subscriptions Fix

· One min read
Hyo
Maintainer of flutter_inapp_purchase & expo-iap

We're releasing version 6.4.6 to fix an important issue with retrieving expired subscriptions in iOS sandbox environment.

Version Information

  • Current Version: 6.4.6
  • Previous Stable: 6.4.5

Bug Fix

iOS Expired Subscriptions in Sandbox

Fixed issue #543 where getPurchaseHistories() couldn't retrieve expired subscription records in StoreKit 2 sandbox environment.

Root Cause:

  • iOS was using Transaction.currentEntitlements which only returns active/valid purchases
  • Changed to use Transaction.all when expired subscriptions need to be included

API Enhancement

PurchaseOptions for getAvailablePurchases

Added PurchaseOptions parameter to getAvailablePurchases() for OpenIAP compliance:

// Get all purchases including expired subscriptions
final purchases = await FlutterInappPurchase.instance.getAvailablePurchases(
PurchaseOptions(
onlyIncludeActiveItemsIOS: false, // Include expired subscriptions
),
);

Deprecation Notice

getPurchaseHistories() is now deprecated and will be removed in v7.0.0. Use getAvailablePurchases() with PurchaseOptions instead.

Migration Guide

// Before
final history = await iap.getPurchaseHistories();

// After
final history = await iap.getAvailablePurchases(
PurchaseOptions(
onlyIncludeActiveItemsIOS: false,
),
);

This fix ensures iOS developers can properly test subscription lifecycle in sandbox environments.

Version 6.4.0 - Simplified requestProducts API

· 2 min read
Hyo
Maintainer of flutter_inapp_purchase & expo-iap

We're releasing version 6.4.0 with a simplified API for requesting products, based on valuable user feedback.

Version Information

  • Current Version: 6.4.0
  • Previous Stable: 6.3.3

Breaking Changes

Simplified requestProducts API

Based on feedback from issue #527, we've simplified the requestProducts method to use direct parameters instead of a wrapper object:

Before (6.3.x):

final products = await iap.requestProducts(
RequestProductsParams(
productIds: ['product_id'],
type: PurchaseType.inapp,
),
);

After (6.4.0):

final products = await iap.requestProducts(
productIds: ['product_id'],
type: PurchaseType.inapp, // Optional, defaults to PurchaseType.inapp
);

The RequestProductsParams class has been removed entirely, making the API cleaner and more intuitive.

Deprecated Items Removed

In v6.4.0, we've also removed the following deprecated items that were marked for removal:

  • subscriptionOfferDetails field → use subscriptionOfferDetailsAndroid instead
  • prorationMode field → use replacementModeAndroid instead
  • AndroidProrationMode typedef → use AndroidReplacementMode instead

These items were deprecated in v6.3.x and are now completely removed in v6.4.0.

A Note on Recent Changes

We understand there have been several breaking changes in recent releases, and we sincerely apologize for any inconvenience this may have caused. These changes are part of our concerted effort to rapidly address the long maintenance gap this library experienced and bring it up to modern standards.

We're pleased to announce that with version 6.4.0, we believe the major restructuring phase is now complete. The API should remain stable moving forward, allowing you to build with confidence.

Migration Guide

To migrate from 6.3.x to 6.4.0:

  1. Remove any imports of RequestProductsParams
  2. Update all calls to requestProducts to use the new parameter format:
    • Replace RequestProductsParams(productIds: ids, type: type) with direct named parameters
    • The type parameter now defaults to PurchaseType.inapp if not specified

Looking Forward

With this release, we've addressed the accumulated technical debt and modernized the API structure. Future releases will focus on:

  • Adding new features
  • Performance improvements
  • Bug fixes
  • Documentation enhancements

All while maintaining API stability.

Thank you for your patience and continued support as we've worked to improve flutter_inapp_purchase.

Version 6.3.3 - Replacement Mode Migration & OpenIAP Updates

· 3 min read
Hyo
Maintainer of flutter_inapp_purchase & expo-iap

We're excited to announce the release of flutter_inapp_purchase version 6.3.3! This release includes important updates to align with OpenIAP specifications and Google Play Billing Library v8.

Major Changes

1. Replacement Mode Migration (Android)

We've updated our Android implementation to use the new replacementMode terminology, replacing the deprecated prorationMode. This aligns with Google Play Billing Library v8 naming conventions.

What's Changed

  • prorationModereplacementMode
  • AndroidProrationMode enum → AndroidReplacementMode enum
  • Updated enum values for better clarity:
    • immediateWithTimeProrationwithTimeProration
    • immediateAndChargeProratedPricechargeProratedPrice
    • immediateWithoutProrationwithoutProration
    • immediateAndChargeFullPricechargeFullPrice

Migration Example

Before (older API):

await FlutterInappPurchase.instance.requestPurchase(
request: RequestPurchase(
ios: RequestPurchaseIOS(sku: 'premium_yearly'),
android: RequestPurchaseAndroid(
skus: ['premium_yearly'],
replacementModeAndroid: AndroidProrationMode.immediateWithTimeProration,
purchaseTokenAndroid: existingSub.purchaseToken, // [DEPRECATED] Use purchaseToken instead
),
),
type: PurchaseType.subs,
);

After:

await FlutterInappPurchase.instance.requestPurchase(
request: RequestPurchase(
ios: RequestPurchaseIOS(sku: 'premium_yearly'),
android: RequestPurchaseAndroid(
skus: ['premium_yearly'],
replacementModeAndroid: AndroidReplacementMode.withTimeProration,
purchaseTokenAndroid: existingSub.purchaseToken, // [DEPRECATED] Use purchaseToken instead
),
),
type: PurchaseType.subs,
);

2. OpenIAP Specification Compliance

All types have been updated to fully comply with OpenIAP specifications:

  • New base types: ProductCommon, PurchaseCommon, ProductSubscriptionCommon
  • Proper platform-specific type extensions
  • Consistent naming conventions with platform suffixes (iOS, Android)

3. Enhanced Android Product Field Support

Added missing Android-specific fields to achieve full OpenIAP specification compliance:

  • nameAndroid: The product name from Google Play
  • oneTimePurchaseOfferDetailsAndroid: Structured pricing details including:
    • priceCurrencyCode: ISO 4217 currency code
    • formattedPrice: Formatted price string
    • priceAmountMicros: Price in micro-units

These fields ensure Android products now have near 100% field compatibility with the OpenIAP specification (up from 60%).

4. Improved Null Safety

Fixed all null safety issues and InvalidType errors throughout the codebase.

Deprecation Notice - Will be removed in v6.4.0

The following items are deprecated in this release and will be removed in v6.4.0:

Android Replacement Mode

  • prorationMode parameter
  • prorationModeAndroid parameter
  • AndroidProrationMode enum
  • ✅ Use replacementMode, replacementModeAndroid, and AndroidReplacementMode instead

Purchase Fields

  • transactionId → Use id instead
  • purchaseTokenAndroid → Use purchaseToken instead
  • jwsRepresentationIOS → Use purchaseToken instead

iOS Product Fields

  • displayName → Use displayNameIOS instead
  • isFamilyShareable → Use isFamilyShareableIOS instead
  • jsonRepresentation → Use jsonRepresentationIOS instead
  • subscription → Use subscriptionInfoIOS instead
  • discounts → Use discountsIOS instead
  • introductoryPrice → Use introductoryPriceIOS instead

Android Product Fields

  • name → Use nameAndroid instead
  • oneTimePurchaseOfferDetails → Use oneTimePurchaseOfferDetailsAndroid instead
  • subscriptionOfferDetails → Use subscriptionOfferDetailsAndroid instead

Methods

  • DiscountIOS.fromJSON() → Use DiscountIOS.fromJson() instead

Breaking Changes

None in this release. All changes are backward compatible with deprecation warnings.

Bug Fixes

  • Fixed duplicate ReplacementMode enum definition
  • Resolved null safety errors in types.dart
  • Fixed InvalidType errors throughout the codebase
  • Corrected platform-specific field naming inconsistencies

What's Next

In version 6.4.0, we will:

  • Remove all deprecated APIs listed above
  • Complete the OpenIAP specification implementation
  • Add support for new StoreKit 2 features

Upgrading

To upgrade to version 6.3.3:

dependencies:
flutter_inapp_purchase: ^6.3.3

Then run:

flutter pub get

Action Required

Please update your code to use the new APIs before upgrading to v6.4.0. The deprecated APIs are still functional in v6.3.3 but will be removed in the next major update.

Support

If you encounter any issues or have questions:

Thank you for using flutter_inapp_purchase!

Version 6.3.0 - Enhanced OpenIAP Compliance & Better Testing

· 6 min read
Hyo
Maintainer of flutter_inapp_purchase & expo-iap

We're excited to announce the release of flutter_inapp_purchase 6.3.0! This version brings significant improvements to OpenIAP specification compliance, enhanced type safety, critical bug fixes, and a completely reorganized test suite—all while maintaining full backward compatibility.

What's New in 6.3.0

Critical Bug Fixes

Android Purchase State Mapping Fix (#524)

Fixed a critical bug where Android purchase states were incorrectly mapped, potentially causing transactions to be misinterpreted:

Before (Incorrect):

  • 0 → PURCHASED ❌
  • 1 → PENDING ❌

After (Correct):

  • 0 → UNSPECIFIED_STATE ✅
  • 1 → PURCHASED ✅
  • 2 → PENDING ✅

This fix aligns with the official Google Play Billing documentation and prevents misinterpreting UNSPECIFIED_STATE as a completed purchase.

Deprecated Methods

The following methods are now deprecated in favor of the unified requestProducts API:

// ❌ Deprecated methods (still work but will be removed in next major version)
await iap.getProducts(productIds);
await iap.getSubscriptions(productIds);

// ✅ Use requestProducts instead
await iap.requestProducts(
RequestProductsParams(
productIds: productIds,
type: PurchaseType.inapp, // or PurchaseType.subs
),
);

Enhanced OpenIAP Field Support

One of the biggest improvements in 6.3.0 is comprehensive field mapping following the OpenIAP specification. We've added extensive support for platform-specific fields that were previously unavailable.

iOS StoreKit 2 Integration

Products and subscriptions now include comprehensive iOS fields:

final products = await FlutterInappPurchase.instance.requestProducts(
RequestProductsParams(productIds: ['premium_monthly']),
);

final product = products.first;
print('Display Name: ${product.displayName}');
print('Family Shareable: ${product.isFamilyShareable}');
print('JSON Representation: ${product.jsonRepresentation}');

// Access subscription-specific info
if (product.subscription != null) {
print('Subscription Group: ${product.subscription!.subscriptionGroupId}');
print('Promotional Offers: ${product.promotionalOfferIdsIOS}');
}

Purchase objects now include StoreKit 2 verification and metadata:

FlutterInappPurchase.instance.purchaseUpdatedListener.listen((purchase) {
// Enhanced verification data
print('Verification Result: ${purchase.verificationResultIOS}');
print('Environment: ${purchase.environmentIOS}'); // "Sandbox" | "Production"
print('Expiration Date: ${purchase.expirationDateIOS}');

// JWS token for server validation
if (purchase.purchaseToken != null) {
// Send to your server for App Store validation
validateWithAppStore(purchase.purchaseToken!);
}
});

Android Google Play Enhanced Fields

Android purchases now include comprehensive Google Play Billing fields:

// Enhanced Android purchase validation
if (purchase.platform == IapPlatform.android) {
print('Order ID: ${purchase.orderIdAndroid}');
print('Package Name: ${purchase.packageNameAndroid}');
print('Signature: ${purchase.signatureAndroid}');
print('Acknowledged: ${purchase.acknowledgedAndroid}');

// Use for server-side validation
final validationData = {
'purchaseToken': purchase.purchaseToken,
'signature': purchase.signatureAndroid,
'packageName': purchase.packageNameAndroid,
};
await validateWithGooglePlay(validationData);
}

Improved Type Safety & Reliability

We've significantly improved the plugin's type safety and error handling:

Better JSON Parsing

Fixed critical type casting issues that could cause runtime errors:

// Before: Could throw casting exceptions
// Map<Object?, Object?> causing runtime errors

// After: Safe conversion with proper error handling
Map<String, dynamic>.from(item as Map)

Enhanced Subscription Detection

Improved subscription detection logic across platforms with correct state mapping.

Test Suite Overhaul

We've completely reorganized our test suite for better maintainability and coverage:

Organized by Business Flows

  • Purchase Flow Tests (test/purchase_flow_test.dart): General purchase operations, error handling, and platform-specific behaviors
  • Subscription Flow Tests (test/subscription_flow_test.dart): Subscription management, active subscription detection, and lifecycle operations
  • Available Purchases Tests (test/available_purchases_test.dart): Purchase history, restoration, and transaction management

Improved Coverage

  • Test coverage improved from 26% to 28.2%
  • All 95 tests now pass consistently
  • Better mock data consistency and more reliable assertions

Migration Guide

Migration is Seamless

The best part? No breaking changes! Version 6.3.0 is fully backward compatible:

// Your existing code continues to work unchanged
final iap = FlutterInappPurchase.instance;
await iap.initConnection();

// Use the new unified API (recommended)
final products = await iap.requestProducts(
RequestProductsParams(
productIds: ['your_product_id'],
type: PurchaseType.inapp, // or PurchaseType.subs
),
);

await iap.requestPurchase(
request: RequestPurchase(
ios: RequestPurchaseIOS(sku: 'your_product_id'),
android: RequestPurchaseAndroid(skus: ['your_product_id']),
),
type: PurchaseType.inapp,
);

Simply update your pubspec.yaml:

dependencies:
flutter_inapp_purchase: ^6.3.0

Optional - Use New Fields

You can now access additional platform-specific fields:

Accessing iOS-Specific Product Information

// Before (still works)
final products = await FlutterInappPurchase.instance.requestProducts(
RequestProductsParams(productIds: ['premium_monthly']),
);
print('Price: ${products.first.price}');

// After (enhanced with new fields)
final product = products.first;
print('Price: ${product.price}');
print('Display Name: ${product.displayName}'); // New
print('Family Shareable: ${product.isFamilyShareable}'); // New

// Access subscription info
if (product.subscription != null) {
print('Subscription Group: ${product.subscription!.subscriptionGroupId}');
}

Accessing Enhanced Purchase Information

// Purchase handling with new fields
FlutterInappPurchase.instance.purchaseUpdatedListener.listen((purchase) {
print('Product ID: ${purchase.productId}');
print('Transaction ID: ${purchase.transactionId}');

// New iOS fields
if (purchase.verificationResultIOS != null) {
print('Verification: ${purchase.verificationResultIOS}');
}
if (purchase.environmentIOS != null) {
print('Environment: ${purchase.environmentIOS}'); // "Sandbox" | "Production"
}

// New Android fields
if (purchase.signatureAndroid != null) {
print('Signature: ${purchase.signatureAndroid}');
}
});

Enhanced Server-Side Validation

The new fields enable more robust server-side validation:

Future<bool> validatePurchase(Purchase purchase) async {
final validationData = {
'transactionId': purchase.transactionId,
'productId': purchase.productId,
'purchaseToken': purchase.purchaseToken,
};

if (purchase.platform == IapPlatform.ios) {
// Use StoreKit 2 JWS token
validationData['jwsRepresentation'] = purchase.purchaseToken;
validationData['environment'] = purchase.environmentIOS;

return await validateWithAppStore(validationData);
} else {
// Use Google Play signature validation
validationData['signature'] = purchase.signatureAndroid;
validationData['packageName'] = purchase.packageNameAndroid;

return await validateWithGooglePlay(validationData);
}
}

Technical Improvements

Under the Hood

  • Enhanced JSON serialization/deserialization with better error handling
  • Improved mock data consistency across all test files
  • Better error messages and debugging information
  • Standardized field naming following OpenIAP conventions
  • Fixed critical Android purchase state mapping

Developer Experience

  • Comprehensive test coverage for all business flows
  • Better documentation with practical examples
  • Type-safe APIs with improved null safety
  • Consistent behavior across iOS and Android platforms
  • Unified API with deprecated methods properly marked

Testing Improvements

Better Test Organization

If you were running tests before, you'll notice improved organization:

# Tests are now organized by flow
flutter test test/purchase_flow_test.dart # General purchases
flutter test test/subscription_flow_test.dart # Subscriptions
flutter test test/available_purchases_test.dart # Purchase history

Enhanced Test Coverage

  • Test coverage improved from 26% to 28.2%
  • All 95 tests now pass consistently
  • Better mock data and more reliable assertions

Why Upgrade?

  1. Critical Bug Fix: Android purchase state mapping now correctly interprets transaction states
  2. Enhanced OpenIAP Compliance: Access to comprehensive platform-specific fields
  3. Better Reliability: Improved type safety and error handling
  4. Future-Proof: Foundation for upcoming OpenIAP features
  5. Better Testing: More comprehensive and reliable test suite
  6. No Breaking Changes: Seamless upgrade experience

Troubleshooting

Since 6.3.0 is fully backward compatible, you shouldn't encounter any breaking changes. If you do experience issues:

  1. Clean and rebuild:

    flutter clean
    flutter pub get
    flutter run
  2. Check for proper imports:

    import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart';
  3. Verify platform support:

    • iOS: Requires iOS 11.0+ (StoreKit 2 features need iOS 15.0+)
    • Android: Requires API level 19+ with Google Play Billing Library
  4. Update deprecated method calls: Replace getProducts() and getSubscriptions() with requestProducts()

Resources

Community

This release wouldn't be possible without our amazing community. Special thanks to all contributors, testers, and users who provided feedback and helped improve the plugin. Special recognition to @quancr258 for reporting the critical Android purchase state mapping issue (#524).


What's Next?

We're continuing to work on expanding OpenIAP compliance and improving the developer experience. Stay tuned for future releases!

Happy coding with flutter_inapp_purchase 6.3.0!


+Published on August 19, 2025 by the flutter_inapp_purchase team

Version 6.0.0-rc.1 - StoreKit 2 & Billing Client v8 Support

· 4 min read
Hyo
Maintainer of flutter_inapp_purchase & expo-iap

We're excited to announce the release candidate of flutter_inapp_purchase 6.0.0-rc.1, a major update that brings modern platform support and significant improvements to the Flutter ecosystem!

⚠️ Note: This is a Release Candidate version. While feature-complete and tested, it may still contain bugs. Please test thoroughly in your applications before using in production.

flutter_inapp_purchase 6.0.0 Release

✨ What's New in 6.0.0-rc.1

🍎 iOS StoreKit 2 Support

flutter_inapp_purchase now fully supports StoreKit 2 for iOS 15.0+, providing:

  • Modern Transaction Handling: Improved purchase flows with better error handling
  • Enhanced Security: Built-in receipt validation and fraud prevention
  • Better Performance: Optimized for iOS 15+ devices
  • Automatic Fallback: Seamless fallback to StoreKit 1 for older iOS versions
// StoreKit 2 automatically used on iOS 15.0+
await FlutterInappPurchase.instance.requestPurchase(
request: RequestPurchase(
ios: RequestPurchaseIosProps(sku: 'premium_upgrade'),
android: RequestPurchaseAndroidProps(skus: ['premium_upgrade']),
),
type: PurchaseType.inapp,
);