aptoide_iap_android 0.1.3
aptoide_iap_android: ^0.1.3 copied to clipboard
Flutter plugin for the Aptoide Android Billing SDK. Supports in-app purchases, subscriptions, and free trials via the Aptoide Connect platform.
aptoide_iap_android #
Flutter plugin for the Aptoide Android Billing SDK.
Integrates in-app purchases (consumables & non-consumables) and subscriptions (including free trials) for apps distributed via Aptoide Connect.
Platform support #
| Android | iOS | Web |
|---|---|---|
| ✅ | — | — |
Minimum Android SDK: 21
Installation #
# pubspec.yaml
dependencies:
aptoide_iap_android: ^0.1.0
flutter pub get
Android project setup #
In your project-level build.gradle, add the JitPack repository:
allprojects {
repositories {
google()
mavenCentral()
maven { url "https://jitpack.io" } // required by Aptoide Billing SDK
}
}
The plugin automatically adds the Aptoide Billing SDK dependency to your app.
No additional build.gradle changes are needed in your app module.
Setup #
1. Get your public key #
Log in to Aptoide Connect → Your App → IAB Key.
2. Initialize once at app startup #
Initialize before any UI interaction, ideally in main() or your root widget's initState.
Subscribe to streams before calling initialize to avoid missing the first events.
import 'package:aptoide_iap_android/aptoide_iap_android.dart';
const _publicKey = 'YOUR_APTOIDE_CONNECT_PUBLIC_KEY';
Future<void> setupBilling() async {
// Subscribe to real-time purchase updates first
AptoideIapAndroid.purchasesUpdatedStream.listen((event) async {
if (event.billingResult.isOk) {
for (final purchase in event.purchases) {
await _handlePurchase(purchase);
}
} else {
print('Purchase error: ${event.billingResult.debugMessage}');
}
});
// Connect
final result = await AptoideIapAndroid.initialize(publicKey: _publicKey);
if (result.isOk) {
// Check for purchases that weren't processed in a previous session
await _checkPendingConsumables();
await _checkActiveSubscriptions();
}
}
The 4-step integration #
Step 1 — Connection #
// Connect
final BillingResult result = await AptoideIapAndroid.initialize(
publicKey: 'YOUR_KEY',
);
// Check readiness before making purchases
final bool ready = await AptoideIapAndroid.isReady;
// Disconnect when done (e.g. app shutdown)
await AptoideIapAndroid.endConnection();
Listen to connection state changes:
AptoideIapAndroid.billingStateStream.listen((event) {
if (event.state == BillingConnectionState.connected) {
print('Billing ready');
} else {
print('Billing disconnected');
}
});
Step 2 — Query products #
Always fetch product details from Aptoide Connect and use the returned prices in your UI.
This is mandatory for app review.
// One-time in-app products
final QueryProductDetailsResult result =
await AptoideIapAndroid.queryProductDetails(
productIds: ['coins_100', 'remove_ads'],
productType: ProductType.inapp,
);
for (final product in result.productDetailsList) {
final price = product.oneTimePurchaseOfferDetails?.formattedPrice ?? '';
print('${product.title} — $price');
}
// Subscriptions
final QueryProductDetailsResult subResult =
await AptoideIapAndroid.queryProductDetails(
productIds: ['premium_monthly'],
productType: ProductType.subs,
);
for (final product in subResult.productDetailsList) {
final phase =
product.subscriptionOfferDetails?.first.pricingPhases.first;
print('${product.title} — ${phase?.formattedPrice}/${phase?.billingPeriod}');
}
// Handle products that failed to load
for (final unfetched in result.unfetchedProductList) {
print('Failed to load ${unfetched.productId}: ${unfetched.responseCode}');
}
Step 3 — Launch a purchase #
// One-time purchase
await AptoideIapAndroid.launchBillingFlow(
productId: 'coins_100',
productType: ProductType.inapp,
obfuscatedAccountId: currentUserId, // optional, but recommended
developerPayload: 'order-ref-abc123', // optional, for your own tracking
);
// Subscription with free trial
final freeTrialSupported =
await AptoideIapAndroid.isFeatureSupported(FeatureType.freeTrials) == 0;
await AptoideIapAndroid.launchBillingFlow(
productId: 'premium_monthly',
productType: ProductType.subs,
obfuscatedAccountId: currentUserId, // required for free trials
freeTrial: freeTrialSupported,
);
The purchase result is delivered via purchasesUpdatedStream (not from the launchBillingFlow return value).
Step 4 — Process & consume purchases #
Future<void> _handlePurchase(Purchase purchase) async {
// 1. Validate server-side (strongly recommended)
// https://docs.catappult.io/docs/iap-validators-server-to-server-check-client
final valid = await myServer.validatePurchase(
purchaseToken: purchase.purchaseToken,
packageName: purchase.packageName,
);
if (!valid) return;
// 2. Deliver the item to the user
await myStore.grantItem(purchase.products.first);
// 3. Consume to allow re-purchase (consumables) or activate (subscriptions)
final ConsumeResult result = await AptoideIapAndroid.consumePurchase(
purchaseToken: purchase.purchaseToken,
);
if (!result.billingResult.isOk) {
print('Consume failed: ${result.billingResult.debugMessage}');
}
}
Handle purchases from previous sessions on startup:
Future<void> _checkPendingConsumables() async {
final result = await AptoideIapAndroid.queryPurchases(ProductType.inapp);
for (final purchase in result.purchases) {
await _handlePurchase(purchase);
}
}
Future<void> _checkActiveSubscriptions() async {
final result = await AptoideIapAndroid.queryPurchases(ProductType.subs);
// purchases present = active or pending subscriptions
// purchases absent = subscription expired; revoke access
final activeIds = result.purchases.map((p) => p.products).expand((i) => i).toSet();
await myStore.syncSubscriptions(activeIds);
}
Complete API reference #
AptoideIapAndroid (static) #
| Method / Property | Return type | Description |
|---|---|---|
initialize({publicKey}) |
Future<BillingResult> |
Connect to the Aptoide Billing service |
endConnection() |
Future<void> |
Disconnect and release resources |
isReady |
Future<bool> |
Whether the client is connected |
queryProductDetails({productIds, productType}) |
Future<QueryProductDetailsResult> |
Fetch product metadata & prices |
launchBillingFlow({productId, productType, …}) |
Future<BillingResult> |
Start the Aptoide purchase UI |
queryPurchases(productType) |
Future<QueryPurchasesResult> |
List active/pending purchases |
consumePurchase({purchaseToken}) |
Future<ConsumeResult> |
Consume / acknowledge a purchase |
isFeatureSupported(FeatureType) |
Future<int> |
0 = supported |
purchasesUpdatedStream |
Stream<PurchaseUpdateEvent> |
Real-time purchase events |
billingStateStream |
Stream<BillingStateEvent> |
Connect / disconnect events |
Enums #
| Type | Values |
|---|---|
ProductType |
inapp, subs |
FeatureType |
freeTrials, obfuscatedAccountId |
BillingConnectionState |
connected, disconnected |
Key BillingResponseCode constants #
| Constant | Value | Meaning |
|---|---|---|
ok |
0 | Success |
userCanceled |
1 | User dismissed the purchase UI |
serviceUnavailable |
2 | Network problem |
itemAlreadyOwned |
7 | Non-consumable already purchased |
tooManyRequests |
1429 | Rate limit exceeded — use exponential back-off |
Important guidelines #
| Rule | Reason |
|---|---|
Initialize in the root widget / Application, not inside an Activity |
Context lifecycle issues if the Activity is destroyed |
| Always consume purchases after delivery | Unconsumed purchases are auto-refunded after 48 hours |
| Display API-provided prices | Mandatory for Aptoide app review approval |
| Avoid SDK calls on the main thread | This plugin handles threading internally |
| Validate purchases server-side before delivery | Prevents fraud |
Use exponential back-off on 1429 errors |
Avoids being blocked by rate limiting |
Testing without an Aptoide Connect account #
Use the sandbox credentials provided in the official docs:
applicationId : com.appcoins.sample
IAB_KEY : MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyEt94j9rt0UvpkZ2jPMZZ16y
UrBOtjpIQCWi/F3HN0+iwSAeEJyDw7xIKfNTEc0msm+m6ud1kJpLK3oCsK61syZ8bYQ
lNZkUxTaWNof1nMnbw3Xu5nuYMuowmzDqNMWg5jNooy6oxwIgVcdvbyGi5RIlxqbo2vS
AwpbAAZE2HbUrysKhLME7IOrdRR8MQbSbKEy/9MtfKz0uZCJGi9h+dQb0b69H7Yo+/BN
/ayBSJzOPlaqmiHK5lZsnZhK+ixpB883fr+PgSczU7qGoktqoe6Fs+nhk9bLElljCs5ZI
l9/NmOSteipkbplhqLY7KwapDmhrtBgrTetmnW9PU/eCWQIDAQAB
Useful links #
- Aptoide Billing SDK docs
- Release notes
- Purchase validation (server-to-server)
- Migration from Google Play Billing
- Example app