Flutter GB In-App Purchases
A comprehensive Flutter package for handling in-app purchases using the BLoC pattern with Geekbears Stack Base. This package provides a robust, platform-agnostic solution for managing consumables, non-consumables, and subscription products across iOS and Android.
Features
- ποΈ Clean Architecture: Built with BLoC pattern and dependency injection
- π± Cross-Platform: Full support for iOS (StoreKit) and Android (Google Play)
- π Product Types: Support for consumables, non-consumables, and subscriptions
- π‘οΈ Transaction Verification: Built-in server-side verification with customizable endpoints
- π Subscription Management: Handle subscription upgrades, downgrades, and changes
- π Real-time Updates: Stream-based transaction monitoring
- π§ Configurable: Highly customizable configuration options
- π§ͺ Testable: Built with testing in mind using dependency injection
Supported Platforms
- β iOS (StoreKit)
- β Android (Google Play)
Supported Product Types
- Consumables: Products that can be purchased multiple times (coins, lives, etc.)
- Non-Consumables: One-time purchases (premium features, remove ads)
- Renewable Subscriptions: Auto-renewing subscriptions
- Non-Renewable Subscriptions: Time-limited subscriptions
Getting Started
Installation
Add this to your package's pubspec.yaml
file:
dependencies:
flutter_gb_in_app_purchases: ^1.0.0
Prerequisites
- Flutter SDK >= 3.35.0
- Dart SDK >= 3.9.0
- iOS 14.0+ / Android API 21+
iOS Setup
- Configure your app in App Store Connect
- Add your product identifiers
- Ensure your app is signed with a valid provisioning profile
Android Setup
- Configure your app in Google Play Console
- Add your product IDs
- Ensure your app is signed and uploaded to Play Console
Usage
1. Basic Setup
First, configure the dependency injection:
import 'package:flutter_gb_in_app_purchases/flutter_gb_in_app_purchases.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Configure dependency injection
await configurePurchasesInjection(
environment: AppEnvironment.production,
purchasesConfig: InAppPurchasesConfig(
// Optional: API endpoint to fetch product listings
getProductsListingApiEndpoint: () => Uri.parse('https://your-api.com/products'),
// Optional: Parse product listings response
getProductsListingResponseParser: (response) {
// Parse your API response and return PurchaseProductListing list
return [];
},
// Optional: Transaction verification endpoint
purchaseVerificationApiEndpoint: (transaction, params) {
return Uri.parse('https://your-api.com/verify-purchase');
},
// Optional: Custom request mapper for verification
purchaseVerificationRequestMapper: (request, transaction) {
return request..body = {'transaction_id': transaction.id};
},
// Optional: Product type mapper
productTypeMapper: (item, listings) {
// Determine product type based on your business logic
return ProductType.consumable;
},
// Complete purchases without verification (not recommended)
completeWithoutVerifying: false,
),
);
runApp(MyApp());
}
2. Using the BLoC
Wrap your app or specific widgets with the purchases BLoC:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_gb_in_app_purchases/flutter_gb_in_app_purchases.dart';
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => PurchasesBloc(
purchasesBlocConfig: PurchasesBlocConfig(
autoInitialize: true,
userReferenceMapper: () async => 'user_123', // Your user ID
verificationParamBuilder: (transaction) {
return {'user_id': 'user_123'};
},
),
),
child: MaterialApp(
home: PurchaseScreen(),
),
);
}
}
3. Initialize and Get Products
class PurchaseScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<PurchasesBloc, PurchasesState>(
builder: (context, state) {
return Scaffold(
appBar: AppBar(title: Text('In-App Purchases')),
body: Column(
children: [
// Initialize purchases
ElevatedButton(
onPressed: () {
context.read<PurchasesBloc>().initialize();
},
child: Text('Initialize Purchases'),
),
// Get store catalog
ElevatedButton(
onPressed: () {
context.read<PurchasesBloc>().add(
PurchasesEvent.getStoreCatalog(
productIds: {'premium_upgrade', 'coins_100'},
),
);
},
child: Text('Load Products'),
),
// Display products
if (state.storeCatalog.data != null)
Expanded(
child: ListView.builder(
itemCount: state.storeCatalog.data!.products.length,
itemBuilder: (context, index) {
final product = state.storeCatalog.data!.products[index];
return ListTile(
title: Text(product.title),
subtitle: Text(product.description),
trailing: Text(product.price),
onTap: () {
context.read<PurchasesBloc>().purchase(product);
},
);
},
),
),
],
),
);
},
);
}
}
4. Handle Purchase States
class PurchaseListener extends StatelessWidget {
final Widget child;
const PurchaseListener({Key? key, required this.child}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocListener<PurchasesBloc, PurchasesState>(
listener: (context, state) {
// Handle purchase completion
if (state.isPurchasing.data == true) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Purchase completed!')),
);
}
// Handle errors
if (state.storeCatalog.data?.isLeft() == true) {
final failure = state.storeCatalog.data?.fold(
(failure) => failure,
(success) => null,
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: ${failure?.message}')),
);
}
},
child: child,
);
}
}
5. Restore Purchases
// Restore purchases (typically called from a settings screen)
context.read<PurchasesBloc>().restorePurchases();
6. Subscription Changes (Android)
// Change subscription (upgrade/downgrade)
context.read<PurchasesBloc>().changeSubscription(newProduct);
Configuration Options
InAppPurchasesConfig
Property | Type | Description |
---|---|---|
getProductsListingApiEndpoint |
Uri Function()? |
API endpoint to fetch product listings |
getProductsListingResponseParser |
List<PurchaseProductListing> Function(String)? |
Parse product listings API response |
purchaseVerificationApiEndpoint |
Uri Function(Transaction, Map<String, dynamic>?)? |
Transaction verification endpoint |
purchaseVerificationRequestMapper |
BaseRequest Function(Request, Transaction)? |
Customize verification request |
completeWithoutVerifying |
bool |
Complete purchases without server verification (default: false) |
productTypeMapper |
ProductItemTypeMapper? |
Map products to their types |
logger |
Logger? |
Custom logger instance |
PurchasesBlocConfig
Property | Type | Description |
---|---|---|
userReferenceMapper |
UserReferenceMapper? |
Function to get current user ID |
verificationParamBuilder |
VerificationParamBuilder? |
Build verification parameters |
autoInitialize |
bool |
Auto-initialize on bloc creation (default: false) |
logEvents |
bool |
Enable event logging (default: false) |
verifyDebounced |
Duration |
Debounce time for verification (default: 3s) |
transactionCompleteDebounced |
Duration |
Debounce time for completion (default: 3s) |
API Integration
Product Listings API
Your API should return product information in this format:
{
"products": [
{
"product_id": "premium_upgrade",
"title": "Premium Upgrade",
"description": "Unlock premium features",
"price": "4.99",
"type": "non-consumable"
}
]
}
Transaction Verification API
Your verification endpoint will receive transaction details and should return verification results.
State Management
The package provides comprehensive state management through BLoC:
- Initialization: Track initialization status
- Product Loading: Monitor catalog loading states
- Purchase States: Track individual purchase progress
- Transaction Updates: Real-time transaction monitoring
- Verification Status: Track server-side verification
Error Handling
The package provides detailed error handling for common scenarios:
- Network failures
- Invalid products
- Purchase failures
- Verification failures
- Platform-specific errors
Testing
The package is built with testing in mind. Use the mock configuration for testing:
final mockConfig = InAppPurchasesConfig.mock();
Dependencies
This package depends on:
flutter_gb_stack_base
: Geekbears base utilitiesin_app_purchase
: Core in-app purchase functionalityflutter_bloc
: State managementdartz
: Functional programming utilitiesbuilt_value
: Immutable data structuresrxdart
: Reactive extensions
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
For support, please open an issue in the GitLab repository or contact the Geekbears team.
Changelog
See CHANGELOG.md for a list of changes and version history.
Libraries
- application/application
- application/bloc/bloc
- application/bloc/containers/containers
- application/bloc/containers/store_purchases_catalog_builder/store_purchases_catalog_builder_bloc
- application/bloc/shared/purchases/purchases_bloc
- config/config
- config/purchases_bloc_config
- config/purchases_config
- dependency_injection
- domain/domain
- domain/entities/catalog_product
- domain/entities/entities
- domain/entities/product_item
- domain/entities/purchase_product_listing
- domain/entities/shopping_cart
- domain/entities/transaction
- domain/entities/transaction_verification
- domain/services/purchase_service
- domain/services/services
- flutter_gb_in_app_purchases
- infrastructure/facade/facade
- infrastructure/facade/in_app_purchases_facade
- infrastructure/infrastructure
- infrastructure/models/catalog_product_model
- infrastructure/models/models
- infrastructure/models/product_item_model
- infrastructure/models/purchase_product_listing_model
- infrastructure/models/serializers
- infrastructure/models/shopping_cart_model
- infrastructure/models/transaction_model
- infrastructure/models/transaction_verification_model
- infrastructure/services/purchase_service_impl
- infrastructure/services/services
- presentation/containers/containers
- presentation/containers/purchase_bloc_event_listener_container
- presentation/containers/purchase_bloc_provider_container
- presentation/containers/purchase_store_catalog_builder_container
- presentation/presentation
- utils/enums/enums
- utils/enums/product_type
- utils/enums/store_platform
- utils/enums/transaction_status
- utils/failures/failures
- utils/logger
- utils/utils