Wykrywanie zwrotów w StoreKit 2 sprowadza się do jednej właściwości transakcji, i jest nią revocationDate
Gdy Apple zwraca pieniądze jednemu z Twoich klientów, zwrot już siedzi w Twojej aplikacji w revocationDate transakcji, zanim uruchomi się zadanie na Twoim serwerze. Oto gdzie wykrywanie zwrotów w StoreKit 2 pojawia się na urządzeniu, co mówią Ci revocationDate i revocationReason, oraz dlaczego klient służy szybkości, a serwer prawdzie.

Najważniejsze wnioski
- W StoreKit 2 zwrócony zakup niesie revocationDate różne od nil na swojej Transaction, więc Twoja aplikacja może sama wykryć zwrot, bez wywoływania Twojego serwera.
- revocationDate jest ustawiane, gdy App Store zwraca transakcję lub gdy klient traci ją przez Family Sharing, więc data różna od nil nie zawsze oznacza zwrot.
- revocationReason mówi Ci dlaczego: developerIssue oznacza, że klient wskazał problem w Twojej aplikacji, a other obejmuje każdy pozostały powód zwrotu.
- Transaction.currentEntitlements już wyklucza zwrócone i unieważnione zakupy, więc najczystszą kontrolą dostępu po stronie klienta jest po prostu to, czy produkt nadal się tam pojawia.
- Transaction.updates dostarcza zwrot, który nastąpił, gdy aplikacja była zamknięta, tylko jeśli zaczniesz nasłuchiwać przy starcie, więc brak Task oznacza przeoczony zwrot.
- Wykrywanie po stronie klienta działa tylko wtedy, gdy aplikacja jest otwarta, dlatego App Store Server Notifications V2 REFUND pozostaje miarodajnym sygnałem, który powstrzymuje Cię przed płaceniem za obsługę zwróconego użytkownika.
- Zwrot można cofnąć, a gdy tak się dzieje, pola unieważnienia są usuwane z transakcji i oczekuje się, że przywrócisz dostęp, który odciąłeś.
Większość aplikacji dowiaduje się o zwrocie Apple z własnego serwera, przez App Store Server Notification, i nigdy nie zauważa, że ten sam zwrot już siedzi wewnątrz aplikacji. Jest on na transakcji, we właściwości o nazwie revocationDate, a jej odczytanie pozwala Twojej aplikacji odciąć dostęp zwróconego klienta przy następnym otwarciu, zamiast czekać na zadanie backendu. Wykrywanie zwrotów w StoreKit 2 to sygnał po stronie klienta, który większość zespołów pomija. Oto dokładnie, gdzie zwrot pojawia się na urządzeniu, co Ci mówi, czego nie, i dlaczego należy on obok Twoich powiadomień serwerowych, a nie zamiast nich.
Gdzie zwrot pojawia się w StoreKit 2
StoreKit 2 podaje Ci transakcje jako podpisane wartości, a zwrot nie usuwa transakcji. Oznacza ją. Dwie właściwości na Transaction niosą to oznaczenie i obie pozostają nil przez całe życie zdrowego zakupu. Gdy jedna z nich staje się różna od nil, App Store odebrał zakup.
revocationDate to pole, które się przełącza
revocationDate to opcjonalne Date. Własny opis Apple jest ścisły: to data, w której App Store zwrócił transakcję lub unieważnił ją z Family Sharing. Dla zakupu, który wciąż jest w porządku, jest to nil. W chwili, gdy zwrot zostaje przetworzony, przechowuje znacznik czasu tego zwrotu. To jedno sprawdzenie, czy revocationDate jest różne od nil, jest całością wykrywania zwrotów po stronie klienta. Wszystko inne to niuans nałożony na wierzch.
revocationReason mówi Ci, dlaczego Apple to wycofał
revocationReason znajduje się obok daty i wyjaśnia przyczynę. StoreKit nadaje mu dwie wartości istotne dla zwrotów. developerIssue oznacza, że klient powiedział Apple, iż zwrot wynikał z rzeczywistego lub odczuwanego problemu w Twojej aplikacji. other obejmuje każdy pozostały powód. Trzecia wartość, upgradedToBundle, wcale nie jest zwrotem; oznacza transakcję, którą App Store unieważnił, ponieważ klient przeszedł na pakiet subskrypcji. Odczytaj powód, zanim zadziałasz, ponieważ developerIssue to ten wart liczenia: ich nagromadzenie to Twój własny produkt mówiący Ci, gdzie się zepsuł.
| Właściwość | Typ | Co oznacza wartość różna od nil |
|---|---|---|
revocationDate | Date? | App Store zwrócił tę transakcję lub unieważnił ją przez Family Sharing w tej dacie |
revocationReason to developerIssue | powód | Klient wskazał rzeczywisty lub odczuwany problem w Twojej aplikacji |
revocationReason to other | powód | Zwrot nastąpił z jakiegoś innego powodu, którego Apple nie wyszczególnia |
revocationReason to upgradedToBundle | powód | Nie zwrot; transakcja została unieważniona, ponieważ klient przeszedł na pakiet subskrypcji |
currentEntitlements już odrzuca zwrócony zakup
Nie zawsze musisz sam odczytywać pola unieważnienia. Transaction.currentEntitlements to sekwencja zakupów, do których klient wciąż jest teraz uprawniony, a Apple buduje ją tak, by pomijać te, których nie powinieneś honorować. Produkt, który App Store zwrócił lub unieważnił, w niej się nie pojawia. Tak samo wygasłe subskrypcje, czy produkty jednorazowe, które znikają w chwili, gdy zostaną zużyte.
To czyni currentEntitlements najczystszą bramką dostępu. Zapytaj ją, co klient posiada, przyznaj dokładnie to, a zwrot usunie uprawnienie za Ciebie bez ani jednego sprawdzenia revocationDate. Pola unieważnienia są na wypadek, gdy chcesz szczegółu, daty i powodu, by zalogować zdarzenie lub na nie zareagować. Lista uprawnień służy prostemu pytaniu, czy zostawić światło włączone.
Wykrywanie zwrotów w StoreKit 2 w praktyce, przy starcie i gdy aplikacja działa
Są dwa momenty, w których Twoja aplikacja może wychwycić zwrot na urządzeniu, i wymagają one różnego kodu. Jeden jest wtedy, gdy aplikacja jest otwarta, a zwrot dzieje się na żywo lub na innym urządzeniu. Drugi jest przy starcie, gdy nadrabiasz wszystko, co zmieniło się, gdy byłeś zamknięty. Przegap ten drugi, a Twoje wykrywanie zwrotów w StoreKit 2 ma dziurę dokładnie tam, gdzie wpada większość zwrotów, bo klienci rzadko mają Twoją aplikację otwartą, gdy o niego proszą.
Zacznij nasłuchiwać przy starcie, inaczej przegapisz zwroty, które nastąpiły w stanie zamkniętym
Transaction.updates to asynchroniczna sekwencja, która emituje transakcję, ilekroć system tworzy lub aktualizuje ją poza Twoją aplikacją albo na innym urządzeniu, zwrot włącznie. Instrukcja Apple jest bezceremonialna: uruchom Task, który po niej iteruje, gdy tylko Twoja aplikacja się uruchomi, inaczej możesz przegapić transakcje, które dostarcza tylko raz przy starcie. Zwrot, który wpłynął w nocy, przybywa przez updates przy następnym otwarciu aplikacji, ale tylko jeśli nasłuch już działa, by go odebrać. Brak nasłuchu, brak zdarzenia, a zwrot pozostaje niewidzialny, dopóki coś innego go nie uzgodni.
Zakup na tym samym urządzeniu nie przychodzi przez updates
Jedna pułapka łapie ludzi testujących zwroty ręcznie. Zwykły zakup dokonany na tym samym urządzeniu nie przybywa przez updates; StoreKit zwraca go wprost z wyniku wywołania zakupu. updates służy zmianom spoza głównego toru: zwrotom, zatwierdzeniom Ask to Buy, wykorzystaniom kodów ofertowych i zakupom dokonanym gdzie indziej. Zbuduj więc obsługę zwrotów wokół updates i currentEntitlements, a nie wokół przepływu zakupu, ponieważ zwrot nigdy nie wróci ścieżką, którą poszła sprzedaż.

Czego wykrywanie po stronie klienta nie może dla Ciebie zrobić
Odczytywanie zwrotów na urządzeniu jest szybkie i darmowe, ale ma sufit, a udawanie, że go nie ma, to sposób, w jaki przychód wycieka. Urządzenie wie tylko to, co powiedział mu StoreKit, a StoreKit mówi tylko wtedy, gdy Twoja aplikacja działa. Klient, który dostaje zwrot i nigdy więcej nie otwiera Twojej aplikacji, to klient, którego Twoja kontrola po stronie klienta nigdy nie widzi.
revocationDate nie zawsze oznacza zwrot
To samo pole przełącza się przy Family Sharing. Gdy klient traci dostęp do współdzielonego zakupu, ponieważ organizator go usunął albo współdzielenie się skończyło, ta transakcja też dostaje revocationDate. Data różna od nil oznacza więc, że klient nie ma już tego zakupu, co jest dokładnie tym, czego potrzebujesz do kontroli dostępu, ale nie zawsze oznacza, że pieniądze wróciły. Jeśli liczysz zwroty na potrzeby przychodu, oddziel unieważnienia z Family Sharing od prawdziwych, zanim zaufasz liczbie.
Zwrot można cofnąć
Zwrot nie zawsze jest ostateczny. Apple może go cofnąć, a gdy to robi, pola unieważnienia są usuwane z transakcji i zakup znów jest ważny. Jeśli odciąłeś dostęp przy zwrocie, oczekuje się, że przywrócisz go przy cofnięciu. Na urządzeniu pokazuje się to jako kolejne zdarzenie updates z czystą transakcją; na Twoim serwerze to odrębne powiadomienie REFUND_REVERSED. Obsłuż tylko zwrot, a zostawisz płacącego klienta bez dostępu i z działającym paragonem.
Ile naprawdę kosztuje spóźnione unieważnienie
Zwrot rzadko jest tylko ceną sprzedaży opuszczającą Twoje konto. Zanim się rozliczy, zwykle wydałeś już prawdziwe pieniądze na obsługę tego zakupu, a ten wydatek nie wraca. Wygenerowane obrazy kosztują minuty GPU. Odpowiedzi czatu kosztują wywołania API modelu, za które rozliczono Cię za token. Przesłane pliki kosztują pamięć, za której trzymanie wciąż płacisz. Jeśli zakup sfinansował wypłatę dla twórcy, te pieniądze już wyszły za drzwi. Nic z tego nie odwraca się wraz ze zwrotem.
Wykrywanie po stronie klienta kurczy okno na tę jedną część, którą wciąż możesz kontrolować, czyli przyszły wydatek. Im wcześniej wiesz, że zakup jest zwrócony, tym wcześniej przestajesz go obsługiwać. Ale urządzenie mówi Ci to tylko wtedy, gdy aplikacja jest otwarta, więc zwrócony użytkownik, który nigdy nie wraca, zachowuje każdy dostęp po stronie serwera, który przyznałeś, cicho kosztując Cię za każdym razem, gdy zadanie w tle lub zsynchronizowane urządzenie działa w jego imieniu. Klient sprawia, że unieważnienie jest szybkie. Nie sprawia, że jest gwarantowane.
Używaj klienta do szybkości, a serwera do prawdy
Czysty projekt używa obu sygnałów do tego, w czym każdy jest dobry. Na urządzeniu Transaction.updates i currentEntitlements dają Ci natychmiastową, lokalną reakcję w chwili, gdy zwrócony klient otwiera aplikację, dobrą dla interfejsu i do domknięcia zmian uprawnień bez podróży w obie strony. Na serwerze App Store Server Notifications V2 wysyłają komunikat REFUND, który przybywa niezależnie od tego, czy aplikacja kiedykolwiek zostanie znów otwarta, a to jedyny sygnał, który niezawodnie powstrzymuje Twój backend przed wydawaniem na zwrócone konto.
| Sygnał | Gdzie żyje | Uruchamia się, gdy | Zaufaj mu, że |
|---|---|---|---|
revocationDate na transakcji | Urządzenie, StoreKit 2 | Twoja aplikacja odczytuje transakcję | Powie Ci, że konkretny zakup został zwrócony lub unieważniony |
Transaction.updates | Urządzenie, StoreKit 2 | Zwrot wpływa, gdy aplikacja działa, lub przy starcie, jeśli nasłuchujesz | Zareaguje natychmiast dla obecnego klienta |
currentEntitlements | Urządzenie, StoreKit 2 | Sprawdzasz, co klient posiada teraz | Kontroluje dostęp bez śledzenia zwrotów przez Ciebie |
powiadomienie REFUND | Twój serwer, App Store Server Notifications V2 | Apple przetwarza zwrot, aplikacja otwarta czy nie | Zatrzyma wydatek po stronie serwera na klienta, który nigdy nie wraca |
Wepnij sygnały urządzenia dla klienta, który trzyma telefon, a powiadomienie serwera dla tego, który go nie trzyma. Zwrot pojawia się w obu miejscach celowo. Odczytanie tylko jednego z nich to sposób, w jaki zwrócone konto dalej Cię kosztuje, gdy sprzedaż już dawno przepadła.
Często zadawane pytania
- Jak wykryć zwrot w StoreKit 2?
- Sprawdź revocationDate transakcji. Jest nil dla ważnego zakupu i przechowuje datę, gdy tylko App Store zwróci transakcję, więc revocationDate różne od nil to sygnał, że zakup został zwrócony lub unieważniony.
- Jaka jest różnica między revocationDate a revocationReason?
- revocationDate to moment, w którym App Store odebrał zakup, a revocationReason to dlaczego. Powodem jest developerIssue, gdy klient wskazał problem w Twojej aplikacji, i other dla wszystkiego innego.
- Czy zwrócony zakup nadal pojawia się w currentEntitlements?
- Nie. Transaction.currentEntitlements wyklucza zakupy, które App Store zwrócił lub unieważnił, więc zwrócony produkt sam wypada z uprawnień klienta, co czyni go bezpieczną bramką dostępu.
- Czy StoreKit powie mojej aplikacji o zwrocie, który nastąpił, gdy aplikacja była zamknięta?
- Tylko jeśli nasłuchujesz od startu. Transaction.updates dostarcza te zmiany raz przy starcie, więc musisz uruchomić Task, który po nich iteruje, gdy tylko Twoja aplikacja się uruchomi, inaczej zwrot zostanie przeoczony, dopóki coś innego go nie uzgodni.
- Czy wykrywanie zwrotów po stronie klienta wystarcza samo w sobie?
- Nie. Urządzenie dowiaduje się o zwrocie tylko wtedy, gdy Twoja aplikacja działa, więc klient, który nigdy więcej nie otwiera aplikacji, jest dla niego niewidzialny. App Store Server Notifications V2 REFUND to sygnał, który dociera do Ciebie niezależnie od tego.
- Czy revocationDate zawsze oznacza, że klient otrzymał zwrot?
- Nie. revocationDate jest ustawiane także, gdy klient traci zakup przez Family Sharing, więc data różna od nil oznacza, że już nie ma zakupu, ale nie zawsze, że pieniądze zostały zwrócone.
Źródła i materiały dodatkowe
- Apple Developer: Transaction.revocationDate (StoreKit)
- Apple Developer: Transaction.RevocationReason (StoreKit)
- Apple Developer: Transaction.currentEntitlements (StoreKit)
- Apple Developer: Transaction.updates (StoreKit)
- Apple Developer: App Store Server Notifications V2 notificationType (REFUND, REFUND_REVERSED)
- Apple Developer Tech Talk: Support customers with StoreKit 2 and App Store Server API
RefundHalt
Autopilot zwrotów dla App Store i Google Play
Czytaj dalej
Każde okno zwrotu w App Store i Google Play to odliczanie, a oto ile godzin daje ci każde z nich
Każdy zwrot w App Store i Google Play uruchamia zegar, a większość z nich biegnie bez ciebie. Najkrótsze okno zwrotu Apple to 12 godzin, okno chargebacku Google to 24, a od 3 sierpnia 2026 przegapione okno chargebacku to rachunek, a nie tylko utracona sprzedaż. Oto każdy termin, który dotyka twojego konta.
Powiadomienie o anulowanym zakupie pozwala Twojemu serwerowi Google Play odebrać dostęp w chwili, gdy pojawia się zwrot
Google Play może wypchnąć do Twojego serwera powiadomienie o anulowanym zakupie w tej samej chwili, gdy zakup zostaje zwrócony, obciążony zwrotnie lub anulowany. Niesie ono purchaseToken, orderId, productType oraz refundType i oznacza jedno, odbierz dostęp. Oto jak je odczytać i podłączyć.