Podczas testów IAP przed wydaniem trafiliśmy na typowy, ale bardzo mylący problem:
- Użytkownik klika „Kontynuuj zakup”
- Przycisk przechodzi w stan „Przetwarzanie”
- Po kilku sekundach wraca do „Kontynuuj zakup”
- Nie pojawia się okno potwierdzenia zakupu z App Store
W tym artykule opisujemy pełną ścieżkę diagnostyczną, kluczowe logi, root cause i praktyczną strategię naprawy przed publikacją.
1. Objaw
Lista produktów na ekranie zakupu ładowała się poprawnie, ale po kliknięciu kupna:
purchase()było wywoływane- Nie pojawiało się żadne okno systemowe
- UI ostatecznie wracało do stanu „niekupione”
Na pierwszy rzut oka wyglądało to tak, jakby flow zakupu w ogóle nie startował, ale logi pokazały coś innego.
2. Pierwszy krok: potwierdzić, czy flow zakupu faktycznie działa
Dodaliśmy logi do wszystkich krytycznych punktów ścieżki zakupu:
- Parametry kliknięcia przycisku
- Wejście do
purchaseSelectedProduct()i gałęzieguard - Przed/po
product.purchase() Transaction.updatesrefreshEntitlements()Transaction.currentEntitlementsTransaction.latest(for:)
Wynik:
purchase()zwracało.success- Weryfikacja transakcji przechodziła poprawnie i wykonywało się
transaction.finish() - Ale
entitledIDspozostawało puste
To oznacza: problemem nie było „zakup nie wystartował”, tylko „po zakupie uprawnienia nie zostały rozpoznane”.
3. Kluczowe ustalenia po głębszej analizie
Po rozszerzeniu logowania pojawiły się decydujące sygnały:
- Liczba iteracji
Transaction.currentEntitlementswynosiła0 Transaction.latest(for: monthly)zwracało poprawny rekord, ale już wygasły- Najważniejsze: przed zakupem istniały stare transakcje w
Transaction.unfinished - Bieżące
purchase()zwracało transakcję ze starego łańcucha, a nie z nowego interaktywnego zakupu
To wyjaśnia, dlaczego „nie ma popupu”:
- StoreKit najpierw zwraca starą transakcję możliwą do przetworzenia (unfinished / historyczny łańcuch)
- Kod kończy ją przez
finish() - Żądanie wygląda na „udane”, ale nie przechodzi przez nową ścieżkę potwierdzenia oczekiwaną przez użytkownika
4. Podsumowanie przyczyny źródłowej
To nie był pojedynczy błąd, tylko nałożenie się dwóch czynników:
- Niedokończone transakcje (
unfinished transaction) zakłócające flow zakupu - Łańcuch subskrypcji sandbox w stanie wygasłym (nawet przy
latest transactionnie musi to oznaczać aktywnego entitlement)
Efekt końcowy: brak popupu, pozorny sukces, puste uprawnienia.
5. Strategia naprawy (sprawdzona w praktyce)
5.1 Proaktywne czyszczenie unfinished transactions przed zakupem
Przy starcie aplikacji i przed zakupem przejdź przez Transaction.unfinished i wykonaj finish() dla zweryfikowanych transakcji, aby stare transakcje nie przechwytywały nowego flow zakupu.
5.2 Utrzymanie i wzmocnienie logów diagnostycznych (zalecane przed wydaniem)
Warto utrzymać te logi aż do publikacji:
- Snapshot
unfinished transaction subscription status(state, renewal info)- Liczbę iteracji
currentEntitlements - Szczegóły
transaction/expirationzlatest(for:)
5.3 Dwutorowa weryfikacja uprawnień (opcjonalne utwardzenie)
Jeśli w niektórych środowiskach currentEntitlements bywa chwilowo puste, użyj latest(for:) jako fallbacku (nadal wymagane jest sprawdzenie aktywnego statusu).
6. Checklista IAP przed publikacją
- Przed każdym testem sprawdź stan subskrypcji konta Sandbox (czy wygasła)
- Loguj liczbę
unfinishedna ścieżkach krytycznych - Przetestuj trzy scenariusze: „pierwszy zakup / odnowienie po wygaśnięciu / przywracanie zakupów”
- Sprawdź spójność między
currentEntitlementsilatest - Gdy wystąpi „brak popupu”, najpierw sprawdź, czy trafiłeś w starą ścieżkę transakcji
7. Wnioski
„Klikam kup i nie ma popupu” nie musi oznaczać problemu UI ani timingu wywołania.
W StoreKit 2 stan transakcji, kolejka unfinished i cykl życia subskrypcji bezpośrednio wpływają na końcowe zachowanie.
Dodanie tych logów i kroków czyszczących przed wydaniem znacząco ogranicza problemy IAP trudne do odtworzenia na produkcji.