User Flows & UX Decisions
Questo documento descrive i flussi utente completi per la gestione abbonamenti e ti aiuta a scegliere tra diverse opzioni UX.
π― Decisioni Chiaveβ
Prima di implementare, devi decidere su questi punti:
1. Dove mostrare la Subscription UI?β
Opzione A: Tab dedicata "Premium" (Consigliato)β
βββββββββββββββββββββββββββββββββββββββ
β π Home β π Map β β Premium β βοΈ Settings β
βββββββββββββββββββββββββββββββββββββββ
Pro: Sempre visibile, facile da trovare, alta conversione Contro: Occupa spazio nella tab bar
Opzione B: Dentro Settingsβ
Settings
βββ Profile
βββ Devices
βββ β Subscription β qui
βββ Notifications
βββ About
Pro: UI pulita, meno invasivo Contro: PiΓΉ nascosto, conversione piΓΉ bassa
Opzione C: Modal/Paywall quando serveβ
Mostra paywall solo quando l'utente prova a usare feature premium.
[Vuoi aggiungere il 6Β° dispositivo?]
[Passa a Pro per dispositivi illimitati!]
[ Scopri Pro ] [Annulla]
Pro: Contestuale, l'utente capisce il valore Contro: PuΓ² essere frustrante
π― Raccomandazioneβ
Ibrido: Opzione A + C
- Tab "Premium" per chi vuole esplorare
- Paywall contestuale per feature bloccate
2. Flusso Acquisto iOS (StoreKit 2)β
Flusso Completoβ
Opzione: Verifica Backend Immediata?β
A) Trust StoreKit (Consigliato per UX)
// Dopo purchase success, fidati di StoreKit
if case .success = result {
// Mostra subito Premium senza chiamare backend
showPremiumFeatures()
}
Pro: UX instantanea Contro: Se webhook fallisce, c'Γ¨ disallineamento (raro)
B) Verifica Backend
if case .success = result {
// Chiama backend per conferma
let status = try await api.checkSubscription(userId)
if status.isSubscribed {
showPremiumFeatures()
}
}
Pro: Stato sempre sincronizzato Contro: Latenza, puΓ² fallire
π― Raccomandazioneβ
Trust StoreKit + Sync periodico: Fidati di StoreKit per UX immediata, ma verifica con backend ogni volta che l'app torna in foreground.
3. Flusso Acquisto Android (Play Billing)β
Flusso Completoβ
Acknowledge: Quando?β
A) Subito dopo purchase (Consigliato)
override fun onPurchasesUpdated(result, purchases) {
purchases?.forEach { purchase ->
if (purchase.purchaseState == PURCHASED) {
acknowledgePurchase(purchase) // Subito!
updateUI()
}
}
}
B) Dopo verifica backend
// Rischio: se backend non risponde, rischi refund automatico
val verified = api.verifyPurchase(purchase.purchaseToken)
if (verified) acknowledgePurchase(purchase)
4. Flusso Acquisto Web (Stripe)β
Flusso Checkoutβ
Opzione: Checkout Embedded vs Redirectβ
A) Redirect a Stripe Hosted (Consigliato)
// Semplice, sicuro, meno responsabilitΓ PCI
const { error } = await stripe.redirectToCheckout({ sessionId });
Pro: Stripe gestisce tutto, compliance PCI semplificata Contro: Utente esce dal tuo sito
B) Embedded Checkout
// Checkout inline nel tuo sito
<Elements stripe={stripePromise}>
<CheckoutForm />
</Elements>
Pro: Esperienza seamless Contro: PiΓΉ complesso, piΓΉ responsabilitΓ
Dopo il Pagamentoβ
Opzione: Polling vs Webhook
A) Solo Webhook (Consigliato)
- Redirect a
/subscription/success - Mostra "Processing..."
- In background, webhook aggiorna DB
- Utente fa refresh o torna piΓΉ tardi
B) Polling
// success_url?session_id=xxx
const checkStatus = async () => {
const session = await fetch(`/api/checkout/status/${sessionId}`);
if (session.payment_status === 'paid') {
showSuccess();
} else {
setTimeout(checkStatus, 2000);
}
};
5. Gestione Abbonamento Esistenteβ
Dove mettere "Manage Subscription"?β
Settings
βββ Subscription
βββ Current Plan: Pro Monthly β
βββ Renews: Jan 15, 2026
βββ [Change Plan] β Upgrade/downgrade
βββ [Cancel Subscription]
Come gestire Cancel/Change?β
| Provider | Metodo |
|---|---|
| Stripe | Redirect a Billing Portal |
| Apple | Deep link a Settings App Store |
| Deep link a Play Store subscriptions |
// iOS - Apri gestione abbonamenti
if let url = URL(string: "https://apps.apple.com/account/subscriptions") {
UIApplication.shared.open(url)
}
// Android - Apri gestione abbonamenti
val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("https://play.google.com/store/account/subscriptions")
}
startActivity(intent)
6. Restore Purchases (Mobile)β
Quando mostrare "Restore Purchases"?β
A) Sempre visibile (Consigliato per Apple Review)
Subscription Screen
βββ [Pro Monthly - β¬9.99]
βββ [Pro Annual - β¬99.99]
βββ [Restore Purchases] β Sempre visibile
Apple richiede questo button per approvazione.
B) Solo se non abbonato
Mostra solo se currentEntitlements Γ¨ vuoto.
Flusso Restoreβ
7. Sincronizzazione Statoβ
Quando verificare lo stato subscription?β
| Momento | Azione |
|---|---|
| App Launch | Transaction.currentEntitlements / queryPurchases() |
| Foreground | Ri-verifica entitlements |
| Tab Premium aperta | Fetch da backend |
| Dopo 24h | Sync con backend |
Backend come Source of Truth?β
A) Client-first (Consigliato per UX)
- StoreKit/Billing Library sono source of truth per UI
- Backend Γ¨ per analytics e cross-platform check
B) Backend-first
- Ogni check passa dal backend
- PiΓΉ lento ma piΓΉ controllato
π Riepilogo Raccomandazioniβ
| Decisione | Scelta Consigliata |
|---|---|
| Dove UI | Tab Premium + Paywall contestuale |
| Verifica acquisto | Trust store SDK, sync periodico |
| Acknowledge Android | Subito dopo purchase |
| Web Checkout | Stripe Hosted (redirect) |
| Gestione abbonamento | Link nativi a store |
| Restore | Sempre visibile (Apple requirement) |
| Source of truth | Client-first, backend-sync |
β Decisioni Implementate (Gennaio 2026)β
Dove mostrare la Subscription UI?β
Scelta: Opzione B + C - In Settings con Paywall contestuale
Impostazioni β Gestisci PianoapreSubscriptionManagementView- Paywall contestuale quando utente prova ad aggiungere dispositivo senza licenze
Upgrade/Downgradeβ
Implementato: Sì, l'utente può cambiare piano
- Da
SubscriptionManagementViewβModifica PianoβPlanSelectionView - Pulsante dinamico: "Aumenta Licenze" / "Riduci Licenze" / "Piano Attuale"
- Protezione downgrade: Non puoi ridurre a meno dispositivi di quanti ne hai attivi
Gestione Cancellazioneβ
Implementato: Link diretto all'App Store
- Pulsante "Disdici Abbonamento" apre
https://apps.apple.com/account/subscriptions - Disclaimer: "Per aumentare o ridurre le licenze, usa 'Modifica Piano'. Non serve disdire l'abbonamento."
Free Trialβ
Non implementato - Decisione futura
Cross-Platformβ
Non implementato - Single provider policy (se compri su iOS, vale solo iOS)
π Documentazione Correlataβ
- iOS License Management - Dettagli implementazione iOS
- Webhook Flow - Come funzionano i webhook Apple/Stripe/Google
- Integration Guide - Come integrare il billing con altri servizi