diff --git a/LMEMappeCatastaliApp.csproj b/LMEMappeCatastaliApp.csproj index 8aaea34..e58d34a 100644 --- a/LMEMappeCatastaliApp.csproj +++ b/LMEMappeCatastaliApp.csproj @@ -28,8 +28,8 @@ it.liviomerola.lmemappecatastali.pro - 1.0.7 - 107 + 1.0.8 + 108 $(ApplicationDisplayVersion) diff --git a/PLAY_STORE_PRO_CHECKLIST.md b/PLAY_STORE_PRO_CHECKLIST.md index 0a2fd46..00c34fa 100644 --- a/PLAY_STORE_PRO_CHECKLIST.md +++ b/PLAY_STORE_PRO_CHECKLIST.md @@ -4,7 +4,7 @@ - Nome: LME Mappe Catastali Pro. - Package name Android: `it.liviomerola.lmemappecatastali.pro`. -- Versione corrente da pubblicare: `1.0.7` / version code `107`. +- Versione corrente da pubblicare: `1.0.8` / version code `108`. - Formato Release Android: Android App Bundle (`.aab`). - Target SDK Release: Android 16 / API 36. diff --git a/Platforms/Android/AndroidPlayBillingService.cs b/Platforms/Android/AndroidPlayBillingService.cs index 48e34bb..cf7bc97 100644 --- a/Platforms/Android/AndroidPlayBillingService.cs +++ b/Platforms/Android/AndroidPlayBillingService.cs @@ -9,6 +9,7 @@ public sealed class AndroidPlayBillingService : Java.Lang.Object, IPlayBillingSe private readonly SemaphoreSlim _connectionLock = new(1, 1); private readonly BillingClient _client; private TaskCompletionSource? _purchaseCompletion; + private string? _lastCatalogFailure; public AndroidPlayBillingService() { @@ -31,7 +32,12 @@ public sealed class AndroidPlayBillingService : Java.Lang.Object, IPlayBillingSe { var details = await GetProductDetailsAsync(cancellationToken); return details?.GetSubscriptionOfferDetails()? - .Where(item => string.IsNullOrEmpty(item.OfferId)) + .Where(item => !string.IsNullOrWhiteSpace(item.BasePlanId) + && !string.IsNullOrWhiteSpace(item.OfferToken)) + .GroupBy(item => item.BasePlanId, StringComparer.Ordinal) + .Select(group => group + .OrderBy(item => string.IsNullOrEmpty(item.OfferId) ? 0 : 1) + .First()) .Select(item => { var phase = item.PricingPhases.PricingPhaseList.LastOrDefault(); @@ -99,13 +105,25 @@ public sealed class AndroidPlayBillingService : Java.Lang.Object, IPlayBillingSe var details = await GetProductDetailsAsync(cancellationToken); var offer = details?.GetSubscriptionOfferDetails()? - .FirstOrDefault(item => item.BasePlanId == basePlanId - && string.IsNullOrEmpty(item.OfferId) - && !string.IsNullOrWhiteSpace(item.OfferToken)); - if (details is null || offer is null) + .Where(item => item.BasePlanId == basePlanId + && !string.IsNullOrWhiteSpace(item.OfferToken)) + .OrderBy(item => string.IsNullOrEmpty(item.OfferId) ? 0 : 1) + .FirstOrDefault(); + if (details is null) { - return PlayBillingResult.Failed( - $"Piano {basePlanId} non disponibile su Google Play. Verifica che l'abbonamento lme_pro e il piano siano attivi per il tuo Paese."); + return PlayBillingResult.Failed(_lastCatalogFailure + ?? "Google Play non ha restituito l'abbonamento lme_pro."); + } + + if (offer is null) + { + var availablePlans = string.Join(", ", details.GetSubscriptionOfferDetails()? + .Select(item => item.BasePlanId) + .Where(item => !string.IsNullOrWhiteSpace(item)) + .Distinct(StringComparer.Ordinal) ?? []); + return PlayBillingResult.Failed(string.IsNullOrWhiteSpace(availablePlans) + ? $"Google Play ha restituito lme_pro senza offerte acquistabili per il piano {basePlanId}." + : $"Piano {basePlanId} non restituito da Google Play. Piani ricevuti: {availablePlans}."); } var productParams = BillingFlowParams.ProductDetailsParams.NewBuilder() @@ -191,7 +209,18 @@ public sealed class AndroidPlayBillingService : Java.Lang.Object, IPlayBillingSe throw new InvalidOperationException(ToMessage(result.Result)); } - return result.ProductDetailsList?.FirstOrDefault(); + var details = result.ProductDetailsList?.FirstOrDefault(); + if (details is not null) + { + _lastCatalogFailure = null; + return details; + } + + var unfetched = result.UnfetchedProductList?.FirstOrDefault(); + _lastCatalogFailure = unfetched is null + ? "Google Play ha restituito un catalogo vuoto per lme_pro. Verifica account Play Store, adesione al test e origine dell'installazione." + : $"Google Play non ha caricato lme_pro (codice catalogo {unfetched.StatusCodeValue})."; + return null; } private async Task EnsureConnectedAsync(CancellationToken cancellationToken) diff --git a/README.md b/README.md index 258dc45..df06fb0 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,11 @@ App .NET MAUI Blazor Hybrid per Android, iOS, Mac Catalyst e Windows, pensata pe Informativa privacy: [PRIVACY_POLICY.md](PRIVACY_POLICY.md). +## Versione Pro 1.0.8 test + +- Google Play Billing accetta tutte le offerte idonee restituite per i piani `monthly` e `yearly`, preferendo il piano base quando disponibile. +- In caso di catalogo vuoto o prodotto non caricato, il messaggio mostra una diagnostica specifica restituita da Google Play. + ## Versione Pro 1.0.7 test - I messaggi informativi su telefono vengono mostrati sopra i controlli zoom e possono occupare fino a tre righe senza sovrapporsi.