Wszystkie artykuły
Deep dive8 min czytania

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.

Smartfon świecący na ciemnym biurku programisty obok mechanicznego zegara, ilustrujący wykrywanie zwrotów w StoreKit 2 pojawiające się wewnątrz aplikacji

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śćTypCo oznacza wartość różna od nil
revocationDateDate?App Store zwrócił tę transakcję lub unieważnił ją przez Family Sharing w tej dacie
revocationReason to developerIssuepowódKlient wskazał rzeczywisty lub odczuwany problem w Twojej aplikacji
revocationReason to otherpowódZwrot nastąpił z jakiegoś innego powodu, którego Apple nie wyszczególnia
revocationReason to upgradedToBundlepowódNie 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ż.

Zmięty papierowy paragon na ciemnej powierzchni z bladą czerwoną pieczęcią odbitą w poprzek, przedstawiający zwróconą transakcję, którą StoreKit 2 oznacza datą unieważnienia

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 żyjeUruchamia się, gdyZaufaj mu, że
revocationDate na transakcjiUrządzenie, StoreKit 2Twoja aplikacja odczytuje transakcjęPowie Ci, że konkretny zakup został zwrócony lub unieważniony
Transaction.updatesUrządzenie, StoreKit 2Zwrot wpływa, gdy aplikacja działa, lub przy starcie, jeśli nasłuchujeszZareaguje natychmiast dla obecnego klienta
currentEntitlementsUrządzenie, StoreKit 2Sprawdzasz, co klient posiada terazKontroluje dostęp bez śledzenia zwrotów przez Ciebie
powiadomienie REFUNDTwój serwer, App Store Server Notifications V2Apple przetwarza zwrot, aplikacja otwarta czy nieZatrzyma 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

RefundHalt

Autopilot zwrotów dla App Store i Google Play

Czytaj dalej

Kolejny wniosek o zwrot jest już w drodze.

Skonfiguruj RefundHalt w czasie potrzebnym na przeczytanie kolejnej wiadomości od pomocy technicznej o zwrocie, którego nie udało Ci się zakwestionować.