В Android / Google Play при восстановлении подписки с помощью Покупки в приложении Flutter детали базового плана НЕ возвращаются.
Объект PurchaseDetails содержит только:
- productID
- purchaseID
- transactionDate
- status
Future _handleAndroidPurchases(List purchaseDetailsList) async {
customLoggerFunction("=== _handleAndroidPurchases() ===");
customLoggerFunction("Received ${purchaseDetailsList.length} purchase updates");
for (final purchase in purchaseDetailsList) {
customLoggerFunction("Processing purchase: ${purchase.productID} | status: ${purchase.status}");
// Complete pending purchases immediately
if (purchase.status == PurchaseStatus.pending) {
customLoggerFunction("Pending purchase: ${purchase.productID}");
try {
await _iap.completePurchase(purchase);
customLoggerFunction("
} catch (e) {
customLoggerFunction("
}
continue;
}
// Ignore error or canceled
if (purchase.status == PurchaseStatus.error || purchase.status == PurchaseStatus.canceled) {
customLoggerFunction("Ignored: ${purchase.status}");
continue;
}
// Only process purchased (new) or restored
if (purchase.status != PurchaseStatus.purchased && purchase.status != PurchaseStatus.restored) {
continue;
}
// Duplicate detection using transaction ID
final transactionId = purchase.purchaseID ?? purchase.transactionDate ?? "";
if (transactionId.isNotEmpty && _processedTransactions.contains(transactionId)) {
customLoggerFunction("
continue;
}
// Prevent concurrent activation
if (_isActivatingPurchase) {
customLoggerFunction("
continue;
}
// Mark as processed early (before activation)
if (transactionId.isNotEmpty) {
_processedTransactions.add(transactionId);
customLoggerFunction("
}
else if (purchase.status == PurchaseStatus.restored) {
// ─────── RESTORE ───────
// For restores: rely on saved plan type from previous successful purchase
customLoggerFunction("
PremiumPlanVariables? restoredPlan;
// Strategy 1: Load saved plan type (this is what worked in your old code)
try {
final savedPlanType = await SecureStorageService.readSecureData(AppLocalKeys.sPlanType);
customLoggerFunction("
if (savedPlanType != null) {
restoredPlan = switch (savedPlanType) {
"weekly" => PremiumPlanVariables.weekly,
"monthly" => PremiumPlanVariables.monthly,
"yearly" => PremiumPlanVariables.yearly,
_ => PremiumPlanVariables.monthly,
};
customLoggerFunction("
}
} catch (e) {
customLoggerFunction("
}
// Strategy 2: If no saved plan, query offers and use a safe fallback (prefer longer plans)
if (restoredPlan == null) {
customLoggerFunction("No saved plan → querying product details for fallback");
try {
final response = await _iap.queryProductDetails({purchase.productID}.toSet());
if (response.productDetails.isNotEmpty) {
customLoggerFunction("
customLoggerFunction("Product Detailss ${response.productDetails.length}");
for(final product in response.productDetails) {
customLoggerFunction("Product Id ${product.id}");
customLoggerFunction("Product Title ${product.title}");
customLoggerFunction("Product Price ${product.price}");
}
final product = response.productDetails.first;
// final product = response.productDetails.first;
if (product is GooglePlayProductDetails) {
final offers = product.productDetails.subscriptionOfferDetails;
if (offers != null && offers.isNotEmpty) {
// Prefer yearly > monthly > weekly to avoid showing "Weekly" wrongly
final order = ['yearly', 'monthly', 'weekly'];
for (final plan in order) {
final match = offers.firstWhereOrNull(
(o) => o.basePlanId.toLowerCase() == plan || o.basePlanId.toLowerCase().contains(plan) == true,
);
if (match != null) {
restoredPlan = switch (plan) {
'yearly' => PremiumPlanVariables.yearly,
'monthly' => PremiumPlanVariables.monthly,
'weekly' => PremiumPlanVariables.weekly,
_ => PremiumPlanVariables.monthly,
};
customLoggerFunction("Fallback preferred plan from offers: $restoredPlan");
break;
}
}
// Ultimate fallback
if (restoredPlan == null) {
final firstBasePlanId = offers.first.basePlanId ?? 'monthly';
customLoggerFunction("
restoredPlan = switch (firstBasePlanId.toLowerCase()) {
_ when firstBasePlanId.toLowerCase().contains('weekly') => PremiumPlanVariables.weekly,
_ when firstBasePlanId.toLowerCase().contains('monthly') => PremiumPlanVariables.monthly,
_ when firstBasePlanId.toLowerCase().contains('yearly') => PremiumPlanVariables.yearly,
_ => PremiumPlanVariables.monthly,
};
}
}
}
}
} catch (e, s) {
}
}
// Final fallback for restore
restoredPlan ??= PremiumPlanVariables.monthly;
selectedAndroidPlan = restoredPlan;
// Activate restore (no acknowledge needed)
await _verifyAndActivatePlan(purchase.productID, purchase);
}
Подробнее здесь: https://stackoverflow.com/questions/798 ... e-purchase
Мобильная версия