flutter_gb_in_app_purchases 1.1.0
flutter_gb_in_app_purchases: ^1.1.0 copied to clipboard
A Flutter package for in-app purchases using BLoC pattern with Geekbears Stack Base.
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.