requestSubscription()
Initiates a subscription purchase flow.
Overview
The requestSubscription()
method starts the platform's native subscription purchase flow. This is a legacy method that's being replaced by requestPurchase()
with PurchaseType.subs
.
Signature
Future requestSubscription(
String productId, {
int? prorationModeAndroid,
String? obfuscatedAccountIdAndroid,
String? obfuscatedProfileIdAndroid,
String? purchaseTokenAndroid,
int? offerTokenIndex,
})
Parameters
productId
- The subscription product identifierprorationModeAndroid
- (Android only) How to handle proration for upgrades/downgradesobfuscatedAccountIdAndroid
- (Android only) Obfuscated user account identifierobfuscatedProfileIdAndroid
- (Android only) Obfuscated profile identifierpurchaseTokenAndroid
- (Android only) Token of existing subscription for upgradesofferTokenIndex
- (Android only) Index of specific offer to purchase
Usage Examples
Basic Subscription
// Simple subscription purchase
await FlutterInappPurchase.instance.requestSubscription('com.example.monthly');
Subscription with User Account
// Subscription with user identifier for restoration
await FlutterInappPurchase.instance.requestSubscription(
'com.example.monthly',
obfuscatedAccountIdAndroid: 'user123_hashed',
);
Subscription Upgrade (Android)
// Upgrade from monthly to yearly with proration
await FlutterInappPurchase.instance.requestSubscription(
'com.example.yearly',
prorationModeAndroid: AndroidProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE,
purchaseTokenAndroid: currentMonthlyToken,
obfuscatedAccountIdAndroid: 'user123_hashed',
);
Complete Implementation
class SubscriptionManager {
final _iap = FlutterInappPurchase.instance;
String? _currentSubscriptionToken;
Future<void> subscribe(String subscriptionId) async {
try {
await _iap.requestSubscription(
subscriptionId,
obfuscatedAccountIdAndroid: await _getUserId(),
);
// Result will be delivered via purchaseUpdated stream
} catch (e) {
print('Subscription failed: $e');
_handleSubscriptionError(e);
}
}
Future<void> upgradeSubscription({
required String newSubscriptionId,
required int prorationMode,
}) async {
if (_currentSubscriptionToken == null) {
print('No active subscription to upgrade');
return;
}
try {
await _iap.requestSubscription(
newSubscriptionId,
prorationModeAndroid: prorationMode,
purchaseTokenAndroid: _currentSubscriptionToken,
obfuscatedAccountIdAndroid: await _getUserId(),
);
} catch (e) {
print('Upgrade failed: $e');
}
}
Future<String?> _getUserId() async {
// Generate obfuscated user ID
// Should be consistent across sessions
return 'user_${DateTime.now().millisecondsSinceEpoch}';
}
void _handleSubscriptionError(dynamic error) {
// Handle subscription-specific errors
if (error.toString().contains('already owned')) {
print('User already has an active subscription');
}
}
}
Proration Modes (Android)
When upgrading or downgrading subscriptions on Android, you can specify how to handle the proration:
// Immediate upgrade with prorated charge
await _iap.requestSubscription(
'com.example.yearly',
prorationModeAndroid: AndroidProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE,
purchaseTokenAndroid: monthlyToken,
);
// Immediate upgrade with full price
await _iap.requestSubscription(
'com.example.yearly',
prorationModeAndroid: AndroidProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE,
purchaseTokenAndroid: monthlyToken,
);
// Deferred upgrade (at next renewal)
await _iap.requestSubscription(
'com.example.yearly',
prorationModeAndroid: AndroidProrationMode.DEFERRED,
purchaseTokenAndroid: monthlyToken,
);
Handling Subscription Results
void setupSubscriptionListeners() {
// Listen for successful subscriptions
FlutterInappPurchase.purchaseUpdated.listen((PurchasedItem? item) {
if (item != null && _isSubscription(item.productId)) {
print('Subscription successful: ${item.productId}');
// Store the token for future upgrades
if (Platform.isAndroid) {
_currentSubscriptionToken = item.purchaseToken;
}
// Verify and activate subscription
_activateSubscription(item);
// Finish the transaction
_iap.finishTransactionIOS(item);
}
});
// Listen for errors
FlutterInappPurchase.purchaseError.listen((PurchaseResult? error) {
if (error != null) {
print('Subscription error: ${error.message}');
}
});
}
bool _isSubscription(String? productId) {
const subscriptionIds = [
'com.example.monthly',
'com.example.yearly',
'com.example.premium',
];
return subscriptionIds.contains(productId);
}
Best Practices
- Store Purchase Tokens: On Android, store purchase tokens for upgrade/downgrade scenarios
- Handle Already Owned: Check for existing subscriptions before purchasing
- User Account Linking: Always include user identifiers for cross-device restoration
- Proration Understanding: Educate users about proration when upgrading
Migration Guide
This method is being deprecated in favor of the more flexible requestPurchase()
method:
Old Way
await _iap.requestSubscription(
'com.example.monthly',
obfuscatedAccountIdAndroid: 'user123',
);
New Way
await _iap.requestPurchase(
request: RequestPurchase(
ios: RequestPurchaseIOS(
sku: 'com.example.monthly',
appAccountToken: 'user123',
),
android: RequestPurchaseAndroid(
skus: ['com.example.monthly'],
obfuscatedAccountIdAndroid: 'user123',
),
),
type: PurchaseType.subs,
);
Platform Differences
iOS
- No proration modes (handled automatically by App Store)
- User account token passed differently
- Subscription groups managed by App Store
Android
- Supports various proration modes
- Requires purchase token for upgrades
- Multiple offers per subscription possible
Related Methods
getSubscriptions()
- Fetch subscription products (deprecated - usegetProducts()
instead)requestPurchase()
- Modern purchase methodgetAvailablePurchases()
- Check active subscriptions
Common Issues
- Missing Purchase Token: Ensure you store the purchase token from the initial subscription
- Invalid Proration Mode: Use valid AndroidProrationMode constants
- Subscription Groups: On iOS, users can only have one active subscription per group