Durante i test IAP prima della pubblicazione, ci siamo trovati davanti a un problema tipico ma molto confuso:

  • L’utente tocca “Continua acquisto”
  • Il pulsante passa a “Elaborazione”
  • Dopo pochi secondi torna a “Continua acquisto”
  • La finestra di conferma acquisto di App Store non compare

In questo articolo documentiamo l’intero percorso di analisi, i log chiave, la causa radice e una strategia di correzione applicabile prima del rilascio.

1. Sintomo

L’elenco prodotti nella schermata di acquisto si caricava correttamente, ma dopo il tap su acquista:

  • purchase() veniva chiamato
  • Non appariva alcuna finestra di sistema
  • La UI tornava infine allo stato non acquistato

A prima vista sembrava che il flusso d’acquisto non partisse affatto, ma i log raccontavano altro.

2. Primo controllo: il flusso d’acquisto viene davvero eseguito?

Abbiamo aggiunto log in tutti i punti critici del flusso:

  • Parametri del tap sul pulsante
  • Entry di purchaseSelectedProduct() e rami guard
  • Prima/dopo product.purchase()
  • Transaction.updates
  • refreshEntitlements()
  • Transaction.currentEntitlements
  • Transaction.latest(for:)

Risultato:

  • purchase() restituiva .success
  • La verifica della transazione andava a buon fine e veniva eseguito anche transaction.finish()
  • Ma entitledIDs rimaneva vuoto

Questo indica che: non era “l’acquisto non è partito”, ma “gli entitlement non venivano riconosciuti dopo l’acquisto”.

3. Evidenze decisive emerse con analisi più profonda

Estendendo i log, sono emersi segnali chiave:

  1. Il conteggio iterazioni di Transaction.currentEntitlements era 0
  2. Transaction.latest(for: monthly) riportava un record valido, ma scaduto
  3. Più importante: prima dell’acquisto c’erano vecchie transazioni in Transaction.unfinished
  4. L’attuale purchase() restituiva una transazione da una catena vecchia, non da un nuovo acquisto interattivo

Questo spiega perché “non compare il popup”:

  • StoreKit restituisce prima una vecchia transazione processabile (unfinished / catena storica)
  • Il codice la conclude con finish()
  • La richiesta sembra “riuscita”, ma non passa per il nuovo percorso di conferma acquisto atteso dall’utente

4. Riepilogo della causa radice

Non si trattava di un singolo punto di guasto, ma della combinazione di due fattori:

  • Transazioni unfinished che interferiscono con il flusso d’acquisto
  • Catena di abbonamento sandbox in stato scaduto (anche con latest transaction, non è detto che sia un entitlement attivo)

Comportamento finale: nessun popup, successo apparente, entitlement vuoti.

5. Strategia di correzione (validata in pratica)

5.1 Pulizia proattiva delle transazioni unfinished prima dell’acquisto

All’avvio app e prima dell’acquisto, scansiona Transaction.unfinished ed esegui finish() sulle transazioni verificate, così le vecchie transazioni non intercettano il nuovo flusso.

5.2 Mantenere e rafforzare i log diagnostici (consigliato prima del rilascio)

Conviene mantenere attivi fino al rilascio:

  • Snapshot di unfinished transaction
  • subscription status (state, renewal info)
  • Conteggio iterazioni di currentEntitlements
  • Dettagli transaction/expiration in latest(for:)

5.3 Verifica entitlement a doppio canale (hardening opzionale)

Se in alcuni ambienti currentEntitlements risulta temporaneamente vuoto, usa latest(for:) come fallback (continuando comunque a validare lo stato attivo).

6. Checklist IAP pre-rilascio

  1. Prima di ogni test, verifica lo stato abbonamento dell’account Sandbox (scaduto o no)
  2. Logga il conteggio unfinished nei percorsi critici
  3. Testa tre scenari: primo acquisto / rinnovo dopo scadenza / ripristino acquisti
  4. Controlla coerenza tra currentEntitlements e latest
  5. Se appare “nessun popup”, verifica prima se è stato colpito un vecchio percorso transazionale

7. Conclusione

“Toccare acquista senza popup” non implica per forza un problema UI o di timing della chiamata.
In StoreKit 2, stato delle transazioni, coda unfinished e ciclo di vita dell’abbonamento influenzano direttamente il comportamento finale.

Aggiungere questi log e la pulizia preventiva prima del rilascio riduce molti problemi IAP difficili da riprodurre in produzione.