easy_iap 0.2.0
easy_iap: ^0.2.0 copied to clipboard
Simple and elegant in-app purchase SDK for Flutter. Unified API for iOS App Store and Google Play Store with functional patterns (fold, when) and multi-language support.
Easy IAP #
Simple and elegant in-app purchase SDK for Flutter.
Features #
- Simple API - Initialize once, purchase with one line
- Functional patterns -
fold,when,onSuccess,onFailurefor clean error handling - Unified API - Same code for iOS App Store and Google Play Store
- Auto-restore - Automatically restore purchases on app launch
- Type-safe - Full Dart type safety with null safety
- Testable - Built-in
MockEasyIapfor unit testing - Reactive - Stream-based ownership updates with
IapBuilderwidget
Installation #
dependencies:
easy_iap: ^0.1.0
Quick Start #
1. Initialize #
import 'package:easy_iap/easy_iap.dart';
final iap = await EasyIap.init(
products: [
IapProduct.subscription('pro_monthly'),
IapProduct.subscription('pro_yearly'),
IapProduct.consumable('gems_100'),
IapProduct.nonConsumable('remove_ads'),
],
);
2. Purchase #
final result = await iap.purchase('pro_monthly');
result.fold(
onSuccess: (receipt) {
print('Purchased: ${receipt.product.id}');
unlockProFeatures();
},
onFailure: (failure) {
print('Failed: ${failure.message}');
showError(failure.displayMessage);
},
);
3. Check Ownership #
// Check subscription
if (iap.hasActiveSubscription) {
showProContent();
}
// Check specific product
if (iap.isOwned('remove_ads')) {
hideAds();
}
// Check specific subscription
if (iap.hasSubscription('pro_monthly')) {
showMonthlyBadge();
}
Result Handling #
Using fold (recommended) #
result.fold(
onSuccess: (receipt) => unlockFeature(),
onFailure: (failure) => showError(failure.message),
);
Using when (with cancelled handling) #
result.when(
success: (receipt) => unlockFeature(),
cancelled: () => showSnackBar('Purchase cancelled'),
failure: (failure) => showError(failure.message),
);
Using chaining #
result
.onSuccess((receipt) => saveReceipt(receipt))
.onFailure((failure) => logError(failure))
.onCancelled(() => analytics.logCancelled());
Smart failure handling #
result.onFailure((failure) {
if (failure.shouldIgnore) return; // User cancelled
if (failure.canRetry) {
showRetryButton();
} else if (failure.requiresUserAction) {
showPaymentSettings();
} else {
showError(failure.displayMessage);
}
});
Reactive UI #
IapBuilder #
IapBuilder(
iap: iap,
productId: 'pro_monthly',
owned: () => ProScreen(),
notOwned: () => UpgradeScreen(),
loading: () => LoadingScreen(),
)
SubscriptionBuilder #
SubscriptionBuilder(
iap: iap,
subscribed: (subscriptions) => PremiumContent(subscriptions),
notSubscribed: () => SubscribePrompt(),
)
PurchaseListener #
PurchaseListener(
iap: iap,
onPurchase: (receipt) {
showSuccessAnimation();
analytics.logPurchase(receipt);
},
child: YourContent(),
)
Product Types #
// One-time purchase, can buy multiple times (coins, gems)
IapProduct.consumable('gems_100')
// One-time purchase, permanent (remove ads, unlock level)
IapProduct.nonConsumable('remove_ads')
// Recurring payment (monthly, yearly subscription)
IapProduct.subscription('pro_monthly')
Server-Side Verification #
final iap = await EasyIap.init(
products: [...],
verifyPurchase: (receipt) async {
// Send to your server for verification
final response = await myApi.verifyPurchase(
productId: receipt.product.id,
token: receipt.purchaseToken,
platform: Platform.isIOS ? 'ios' : 'android',
);
return response.isValid;
},
);
Analytics Integration #
final iap = await EasyIap.init(
products: [...],
onPurchase: (receipt) async {
await analytics.logPurchase(
productId: receipt.product.id,
price: receipt.product.price,
isRestored: receipt.isRestored,
);
},
);
Testing #
MockEasyIap #
final mockIap = MockEasyIap(
products: [
IapProduct.subscription('pro_monthly'),
],
);
// Simulate successful purchase
mockIap.mockPurchaseSuccess('pro_monthly');
// Simulate failure
mockIap.mockPurchaseFailure('pro_monthly', 'payment_declined');
// Simulate cancellation
mockIap.mockPurchaseCancelled('pro_monthly');
// Set up owned products
mockIap.setOwned({'remove_ads'});
mockIap.addSubscription('pro_monthly');
Widget Testing #
testWidgets('shows pro content when subscribed', (tester) async {
final mockIap = MockEasyIap(products: [...]);
mockIap.addSubscription('pro_monthly');
await tester.pumpWidget(
MaterialApp(
home: IapBuilder(
iap: mockIap,
productId: 'pro_monthly',
owned: () => Text('Pro Content'),
notOwned: () => Text('Upgrade'),
),
),
);
expect(find.text('Pro Content'), findsOneWidget);
});
Platform Setup #
iOS #
Add to ios/Runner/Info.plist:
<key>SKProductIdentifiers</key>
<array>
<string>pro_monthly</string>
<string>pro_yearly</string>
</array>
Android #
Add to android/app/build.gradle:
dependencies {
implementation 'com.android.billingclient:billing:6.0.1'
}
Requirements #
| Platform | Minimum Version |
|---|---|
| Flutter | 3.22.0 |
| Dart | 3.4.0 |
| iOS | 12.0 |
| Android | API 21 |
License #
MIT License - see LICENSE for details.
Contributing #
Contributions are welcome! Please read our contributing guidelines.