Během předprodukčního testování IAP jsme narazili na typický, ale velmi matoucí problém:
- Uživatel klikne na „Pokračovat v nákupu“
- Tlačítko přejde do stavu „Zpracovává se“
- Po několika sekundách se vrátí na „Pokračovat v nákupu“
- Nezobrazí se potvrzovací okno nákupu z App Storu
V tomto článku shrnujeme celý postup diagnostiky, klíčové logy, root cause i praktický způsob opravy před vydáním.
1. Jak se problém projevoval
Seznam produktů na nákupní stránce se načítal správně, ale po kliknutí na nákup:
- Zavolalo se
purchase() - Neobjevilo se žádné systémové okno
- UI se nakonec vrátilo do stavu „nenakoupeno“
Na první pohled to vypadalo, že se nákup vůbec nespustil. Logy ale ukázaly jiný obraz.
2. První krok: ověřit, zda nákupní tok skutečně probíhá
Nejprve jsme doplnili logy do všech kritických bodů nákupního toku:
- Parametry kliknutí na tlačítko
- Vstup do
purchaseSelectedProduct()a jednotlivéguardvětve - Stav před/po
product.purchase() Transaction.updatesrefreshEntitlements()Transaction.currentEntitlementsTransaction.latest(for:)
Výsledek:
purchase()vrátilo.success- Ověření transakce proběhlo a
transaction.finish()se také vykonalo entitledIDsale zůstalo prázdné
To znamená: nejde o „nákup se nespustil“, ale o „po nákupu se nerozpoznala oprávnění“.
3. Klíčová zjištění při hlubší analýze
Po rozšíření logování se objevily rozhodující signály:
Transaction.currentEntitlementsměl počet iterací0Transaction.latest(for: monthly)obsahoval platný záznam, ale už expirovaný- Zásadní bylo, že před nákupem existovaly staré transakce v
Transaction.unfinished - Aktuální
purchase()vracelo transakci ze starého řetězce, nikoli z nového interaktivního nákupu
Tím se vysvětluje, proč se „nic neukázalo“:
- StoreKit nejdřív vrátil zpracovatelnou starou transakci (unfinished / historický tok)
- Kód ji označil jako
finish() - Nákup vypadal jako „úspěšný“, ale neprošel novým potvrzovacím dialogem, který uživatel očekává
4. Shrnutí příčiny
Nešlo o jedinou chybu, ale o kombinaci dvou faktorů:
- Nedokončené transakce (
unfinished transaction) narušily nákupní tok - Sandboxový subscription řetězec byl v expirovaném stavu (i když existuje
latest transaction, nemusí jít o aktivní entitlement)
Výsledné chování: bez pop-upu, zdánlivý úspěch, prázdná oprávnění.
5. Opravná strategie (ověřená v praxi)
5.1 Aktivní vyčištění unfinished transakcí před nákupem
Při startu aplikace i před kliknutím na nákup projděte Transaction.unfinished a u ověřených transakcí zavolejte finish(), aby staré transakce „nepřebily“ nový nákupní tok.
5.2 Zachovat a posílit diagnostické logy (doporučeno před release)
Doporučené logy ponechat zapnuté až do vydání:
- Snapshot
unfinished transaction subscription status(state, renewal info)- Počet iterací
currentEntitlements transaction/expirationulatest(for:)
5.3 Dvoukanálové vyhodnocení oprávnění (volitelné vylepšení)
Pokud je v některých prostředích currentEntitlements dočasně prázdné, lze použít latest(for:) jako fallback (stále je nutné ověřit, že entitlement je aktivní).
6. IAP checklist před vydáním
- Před každým testem ověřte stav subscription na Sandbox účtu (zda není expirovaný)
- V klíčové cestě logujte počet
unfinishedtransakcí - Otestujte tři scénáře: „první nákup / obnova po expiraci / obnova nákupu“
- Zkontrolujte konzistenci mezi
currentEntitlementsalatest - Pokud se objeví „žádné potvrzovací okno“, nejdřív ověřte zásah starého transakčního toku
7. Závěr
„Klik na koupit bez zobrazení okna“ nemusí být problém UI ani načasování volání.
Ve StoreKit 2 stav transakcí, fronta unfinished a životní cyklus předplatného přímo ovlivňují finální chování.
Doplnění těchto logů a předběžného čištění transakcí před release výrazně snižuje množství IAP problémů, které se v produkci špatně reprodukují.