Durant les proves d’IAP abans de publicar, ens vam trobar amb un problema típic però molt confús:

  • L’usuari prem «Continua la compra»
  • El botó passa a «Processant»
  • Al cap d’uns segons torna a «Continua la compra»
  • No apareix la finestra de confirmació de compra de l’App Store

Aquest article recull tot el camí de diagnosi, els logs clau, la causa arrel i una estratègia de correcció aplicable abans del llançament.

1. Símptoma

La llista de productes de la pantalla de compra es carregava correctament, però en prémer el botó:

  • Es cridava purchase()
  • No apareixia cap diàleg del sistema
  • La UI acabava tornant a estat de no compra

D’entrada semblava que «la compra no s’havia iniciat», però els logs deien una altra cosa.

2. Primera ronda: verificar si el flux de compra realment s’executa

Vam afegir logs a tots els punts crítics del flux:

  • Paràmetres del clic al botó
  • Entrada de purchaseSelectedProduct() i branques de guard
  • Abans/després de product.purchase()
  • Transaction.updates
  • refreshEntitlements()
  • Transaction.currentEntitlements
  • Transaction.latest(for:)

Resultat:

  • purchase() retornava .success
  • La verificació de transacció era correcta i també s’executava transaction.finish()
  • Però entitledIDs continuava buit

Això indica que: no era un problema de «no iniciar la compra», sinó de «no reconèixer els drets després de comprar».

3. Fets decisius detectats en aprofundir

Amb més logs, van aparèixer senyals clars:

  1. El recompte d’iteracions de Transaction.currentEntitlements era 0
  2. Transaction.latest(for: monthly) tenia un registre vàlid, però caducat
  3. El més important: abans de comprar hi havia transaccions antigues a Transaction.unfinished
  4. El purchase() actual retornava una transacció del flux antic, no d’una compra interactiva nova

Això explica per què «no surt cap finestra»:

  • StoreKit retorna primer una transacció antiga processable (unfinished / flux històric)
  • El codi la marca com a finish()
  • La compra sembla «correcta», però no entra al flux de confirmació nou que espera l’usuari

4. Causa arrel

No era un únic factor, sinó la combinació de dos:

  • Transaccions no finalitzades (unfinished transaction) que interfereixen en el flux de compra
  • Cadena de subscripció sandbox en estat caducat (encara que hi hagi latest transaction, pot no ser un entitlement actiu)

Per això el comportament final era: sense pop-up, èxit aparent i drets buits.

5. Estratègia de correcció (validada en pràctica)

5.1 Netejar transaccions unfinished abans de comprar

En iniciar l’app i abans de prémer comprar, recorre Transaction.unfinished i executa finish() per a cada transacció verificada, per evitar que les transaccions antigues interceptin el flux nou.

5.2 Mantenir i reforçar logs de diagnosi (recomanat abans d’enviar)

Val la pena conservar aquests logs fins al llançament:

  • Snapshot de unfinished transaction
  • subscription status (state, renewal info)
  • Nombre d’iteracions de currentEntitlements
  • transaction/expiration de latest(for:)

5.3 Doble canal per determinar drets (millora opcional)

Si en alguns entorns currentEntitlements queda temporalment buit, pots usar latest(for:) com a fallback (sempre validant que continuï actiu).

6. Checklist IAP abans de publicar

  1. Abans de cada prova, comprova l’estat de la subscripció del compte Sandbox (si ha caducat o no)
  2. Mostra el nombre d’elements unfinished als punts crítics
  3. Prova tres escenaris: «primera compra / renovació després de caducar / restauració de compra»
  4. Verifica que currentEntitlements i latest siguin coherents
  5. Si «no apareix finestra», mira primer si ha entrat en un flux de transacció antic

7. Conclusió

«Premre comprar i no veure cap finestra» no implica necessàriament un problema d’UI o de timing de crida.
A StoreKit 2, l’estat de les transaccions, la cua d’unfinished i el cicle de vida de la subscripció condicionen el resultat final.

Si abans de publicar afegeixes aquests logs i la neteja prèvia, reduiràs molts problemes d’IAP difícils de reproduir en producció.