Gdy zakup w Google Play zostaje zwrócony lub obciążony zwrotnie, Voided Purchases API jest sposobem, w jaki się o tym dowiadujesz
Google Play po cichu unieważnia zakup, gdy zostaje on zwrócony lub obciążony zwrotnie. Voided Purchases API to lista tych zamówień, dzięki której możesz odebrać dostęp. Oto każde pole, okno 30 dni, opcja revoke, która ukrywa zamówienia, oraz ile to kosztuje.

Najważniejsze wnioski
- Voided Purchases API, czyli metoda purchases.voidedpurchases.list, zwraca zamówienia, które Google Play anulował, zwrócił lub obciążył zwrotnie, dzięki czemu możesz zbudować system odbierania dostępu, który odcina dostęp do tego, czego klient już nie posiada.
- Pojawiają się tylko zamówienia z revoke. Zwrot dokonany przez dewelopera bez opcji revoke jest niewidoczny dla tego API, więc jeśli chcesz odebrać dostęp, musisz dokonać zwrotu z włączonym revoke.
- Okno wynosi 30 dni. startTime nie może być starszy niż 30 dni temu, więc serwer, który jest wyłączony dłużej niż miesiąc, traci te unieważnione zamówienia na dobre. Odpytuj według harmonogramu.
- voidedSource mówi ci, kto unieważnił zamówienie: 0 to użytkownik, 1 to deweloper, 2 to Google. voidedReason mówi ci dlaczego, od 0 Other po 7 Chargeback i 8 Unacknowledged_purchase.
- Real-time developer notifications wysyłają VoidedPurchaseNotification w momencie unieważnienia zakupu, ale traktuj to jako sygnał. Wywołaj Voided Purchases API po autorytatywną listę, zanim odbierzesz dostęp.
- Rozpoznawaj odnowienia subskrypcji po orderId, a nie po purchaseToken. Jeden purchaseToken obejmuje każde odnowienie subskrypcji, więc sam token nie odróżni dwóch odnowień.
- Limity to 6,000 zapytań dziennie i 30 zapytań w dowolnym oknie 30 sekund, więc przeglądaj wyniki stronami za pomocą continuation token i odpytuj według okna czasowego, nigdy jednym wywołaniem na zamówienie.
Zwrot w Google Play nie puka do twoich drzwi. Pieniądze się przemieszczają, klient nadal ma otwartą aplikację i dopóki sam nie zajrzysz, po twojej stronie nic się nie zmienia. Voided Purchases API to miejsce, do którego zaglądasz. Podaje ci listę zamówień, które zostały anulowane, zwrócone lub obciążone zwrotnie, dzięki czemu możesz odebrać dostęp do tego, za co klient już nie zapłacił. Skieruj tam zaplanowane zadanie, odczytaj listę, odetnij uprawnienie. To cała pętla.
Jest jeden haczyk, o który potyka się większość zespołów, i nie ma go w kodzie. Pojawiają się tutaj tylko zamówienia, które zostały objęte revoke. Jeśli zwrócisz zakup w Play Console bez zaznaczenia opcji revoke, to zamówienie nigdy nie dotrze do tego API, a twoje zadanie działa bez zarzutu, podczas gdy zwrócony klient zachowuje wszystko, co mu sprzedałeś. Ten wpis omawia API pole po polu, liczby, które je ograniczają, oraz miejsce, w którym wyciekają pieniądze, gdy je pominiesz.
Co właściwie zwraca Voided Purchases API
API odpowiada na jedno pytanie: które zamówienia dla tej aplikacji zostały ostatnio unieważnione. Unieważnienie (void) obejmuje trzy wyniki, które wszystkie kończą się odzyskaniem pieniędzy przez klienta. Anulowanie, zwrot albo chargeback. Dotyczy jednorazowych produktów w aplikacji oraz subskrypcji, a zakres wybierasz jednym parametrem. Ustaw type na 0, a otrzymasz tylko unieważnione zakupy produktów w aplikacji, co jest wartością domyślną. Ustaw na 1, a otrzymasz razem unieważnione zakupy w aplikacji i unieważnione zakupy subskrypcji.
Każdy wpis na liście to obiekt voided purchase. Pól jest niewiele i każde z nich ma znaczenie.
Pola obiektu voided purchase
| Field | Co zawiera |
|---|---|
| orderId | Identyfikator zamówienia, który jednoznacznie identyfikuje zakup jednorazowy, zakup subskrypcji lub pojedyncze odnowienie subskrypcji. To twój klucz łączenia |
| purchaseToken | Token, który identyfikuje zakup jednorazowy lub subskrypcję. Nie rozróżnia odnowień, więc do tego używaj orderId |
| purchaseTimeMillis | Kiedy dokonano zakupu, w milisekundach od epoki |
| voidedTimeMillis | Kiedy zakup został anulowany, zwrócony lub obciążony zwrotnie, w milisekundach od epoki |
| voidedSource | Kto zainicjował unieważnienie: 0 użytkownik, 1 deweloper, 2 Google |
| voidedReason | Dlaczego zakup został unieważniony, liczba całkowita od 0 do 8 |
| voidedQuantity | Unieważniona ilość ze zwrotu częściowego opartego na ilości, zwracana tylko wtedy, gdy includeQuantityBasedPartialRefund ma wartość true |
Przeczytaj voidedReason, zanim zaczniesz działać
voidedReason to pole, które zamienia surową listę w decyzję. Zwrot z powodu żalu kupującego i chargeback z banku trafiają na tę samą listę, ale nie są tym samym zdarzeniem, a sierpniowy cennik czyni jedno z nich kosztownym. Oto pełny zestaw.
| voidedReason | Label | Co to dla ciebie oznacza |
|---|---|---|
| 0 | Other | Nie przypisano żadnej kategorii. Odbierz dostęp i przejdź dalej |
| 1 | Remorse | Kupujący zmienił zdanie. Zwykły zwrot |
| 2 | Not_received | Klient twierdzi, że nigdy nie otrzymał produktu. Warto sprawdzić dostarczanie |
| 3 | Defective | Produkt nie działał. Sygnał jakości, zapisz go |
| 4 | Accidental_purchase | Niezamierzony zakup, często na współdzielonym urządzeniu |
| 5 | Fraud | Google oznaczył transakcję jako oszustwo |
| 6 | Friendly_fraud | Chargeback, w którym prawowity posiadacz karty kwestionuje obciążenie, którego sam dokonał |
| 7 | Chargeback | Bank klienta cofnął płatność. Ostateczne po stronie banku i teraz obciąża ciebie |
| 8 | Unacknowledged_purchase | Google automatycznie zwrócił zakup, którego twoja aplikacja nigdy nie potwierdziła (acknowledge) |
Okno 30 dni to pułapka, która opróżnia twoją listę
Voided Purchases API może pokazać tylko unieważnione zakupy z ostatnich 30 dni. Parametr startTime domyślnie przyjmuje bieżący czas minus 30 dni i nie można ustawić go starszego. endTime domyślnie przyjmuje teraz. Zatem endpoint to przesuwane okno jednego miesiąca, a nie archiwum.
Konsekwencja jest dosadna. Jeśli twoje zadanie odpytujące się zepsuje i nikt tego nie zauważy przez pięć tygodni, unieważnienia z pierwszego tygodnia wypadły już z API. Nie ma wywołania, które je przywróci. Nie odbierzesz dostępu do tych zamówień, a nawet nie dowiesz się, że istniały, chyba że zarejestrowałeś je w inny sposób. To API to siatka bezpieczeństwa z dziurą wielkości twojej najgorszej awarii.
Opcja revoke decyduje, czy zamówienie w ogóle się pojawi
To najczęstszy powód, dla którego zespół zgłasza API jako zepsute. Zwracane są tylko zamówienia z revoke. Zwroty zainicjowane przez użytkownika, anulowania, chargebacki i zwroty zainicjowane przez Google zawsze mają revoke, więc zawsze się pojawiają. Zwrot zainicjowany przez dewelopera jest inny. Gdy sam zwracasz zamówienie, przez Play Console lub Orders API, wybierasz, czy je również objąć revoke. Zwróć bez revoke, a zamówienie jest rozliczone z klientem, ale nigdy nie pojawia się w Voided Purchases API.
Wynikająca z tego zasada jest prosta. Jeśli twoim zamiarem jest odebranie dostępu, dokonaj zwrotu z włączoną opcją revoke. W przeciwnym razie oddałeś pieniądze i zostawiłeś otwarte drzwi, a twoje zadanie odbierające dostęp, choćby najlepiej napisane, nie ma na czym działać.
Jak je odpytywać bez przekraczania limitu
Endpoint ma ograniczenie tempa, a limity są na tyle niskie, że naiwna pętla je osiągnie. Masz 6,000 zapytań dziennie, liczonych w czasie pacyficznym, i nie więcej niż 30 zapytań w dowolnym okresie 30 sekund. Ten budżet jest w porządku przy odpytywaniu oknami i wrogi wobec projektów typu jedno-żądanie-na-zamówienie.
Okna zapytań i continuation token
maxResults domyślnie wynosi 1,000, co jest zarazem górnym pułapem. Gdy okno zawiera więcej niż jedną stronę unieważnień, odpowiedź niesie obiekt tokenPagination z nextPageToken. Przekaż ten token z powrotem w kolejnym wywołaniu, aby przejść przez strony. Ustaw startTime i endTime, aby ograniczyć interesujące cię okno, przeglądaj strony, aż token się skończy, a potem przesuń okno. Ten wzorzec utrzymuje cię w obrębie zarówno limitu serii 30 sekund, jak i dziennego limitu.
Real-time developer notifications zamykają lukę
Odpytywanie codziennie wciąż zostawia do jednego dnia ślepoty, a okno 30 dni karze długie przerwy. Real-time developer notifications usuwają opóźnienie. Google publikuje VoidedPurchaseNotification do posiadanego przez ciebie tematu Cloud Pub/Sub w momencie unieważnienia zakupu, a twój backend odbiera go w ciągu sekund. Komunikat jest mały.
| RTDN field | Co zawiera |
|---|---|
| purchaseToken | Token z pierwotnego zakupu |
| orderId | Identyfikator zamówienia dla unieważnionej transakcji, nowy dla każdego odnowienia subskrypcji |
| productType | 1 dla subskrypcji, 2 dla zakupu jednorazowego |
| refundType | 1 dla pełnego zwrotu, 2 dla zwrotu częściowego opartego na ilości |

Ile cię to kosztuje w pieniądzach
API to instalacja hydrauliczna, ale powodem, by je podłączyć, jest rachunek. Każde unieważnienie na tej liście odwzorowuje się na realną liczbę, a dwa z nich stają się droższe.
Rachunek za chargeback spada na ciebie od 3 sierpnia 2026
Począwszy od 3 sierpnia 2026, Google przenosi koszt chargebacku na dewelopera. Tracisz cenę zakupu i na dodatek płacisz bankową opłatę za chargeback. voidedReason równy 7 to już nie tylko utracona sprzedaż, to pozycja z doliczoną opłatą. Nie możesz cofnąć chargebacku, jest ostateczny po stronie banku, ale możesz zatamować krwawienie po nim. Szybkie wychwycenie unieważnienia pozwala ci odebrać uprawnienie i, w przypadku wszystkiego, co wciąż dostarczasz, przestać wydawać na klienta, który został zwrócony, a potem cofnięty.
Wciąż płacisz za obsługę klienta, któremu zwrócono pieniądze
Cena zakupu przepada w chwili pojawienia się unieważnienia. To, co wciąż kontrolujesz, to koszt dalszego dostarczania. Każda godzina, w której zwrócone uprawnienie pozostaje aktywne, to płacenie za rzeczy, których klient już nie finansuje: moc obliczeniową, wywołania API modeli, przechowywanie oraz wszelkie wypłaty dla twórców lub partnerów powiązane z jego użyciem. System odbierania dostępu napędzany tym API to sposób, w jaki wyłączasz ten licznik. Pomiń go, a finansujesz produkt dla ludzi, których sklep już zaspokoił.
Friendly fraud to wzorzec wart śledzenia trendu
voidedReason równy 5 lub 6 to nie jednorazowy przypadek. Fraud i friendly fraud grupują się według konta, według urządzenia, a czasem według promocji. API daje ci voidedSource i voidedReason przy każdym unieważnieniu, co wystarcza, by śledzić trend nadużyć według konta, zamiast traktować każde cofnięcie jako odosobniony koszt. Klient, który dokonuje chargebacku dwukrotnie, mówi ci coś, czego nie powiedział pierwszy zwrot.
Podłączanie tego po sposobie RefundHalt
Model jest niewielki, gdy masz już wszystkie elementy. Nasłuchuj VoidedPurchaseNotification w czasie rzeczywistym, aby nic nie czekało całego dnia. Wywołuj Voided Purchases API jako źródło prawdy, kluczując po orderId, aby odnowienia subskrypcji nigdy się nie myliły. Odczytuj voidedSource i voidedReason, aby chargeback był obsługiwany inaczej niż zwrot z powodu żalu. Odpytuj według harmonogramu na tyle ścisłego, by okno 30 dni nigdy nie ugryzło, i dokonuj zwrotu z włączoną opcją revoke, ilekroć twoim zamiarem jest odcięcie dostępu.
To część, którą RefundHalt uruchamia za ciebie. Odbiera powiadomienia w czasie rzeczywistym, uzgadnia każde unieważnienie z API, odbiera dokładnie to zamówienie, a nie cały produkt, i oddziela bankowy chargeback od zwykłego zwrotu, aby te kosztowne zostały oznaczone, a nie pogrzebane. Dostajesz odebrany dostęp w ciągu sekund i zapis tego, kto co i dlaczego unieważnił, bez samodzielnego stawiania potoku Pub/Sub i zadania odpytującego.
Często zadawane pytania
- Dlaczego moje zwrócone zamówienia nie pokazują się w Voided Purchases API?
- Ponieważ zwracane są tylko zamówienia z revoke. Zwroty użytkowników, anulowania, chargebacki i zwroty zainicjowane przez Google zawsze mają revoke i zawsze się pojawiają. Zwrot zainicjowany przez dewelopera pojawia się tylko wtedy, gdy wybrałeś także opcję revoke. Jeśli zwróciłeś zamówienie bez objęcia go revoke, zamówienie jest rozliczone, ale niewidoczne dla tego API, więc dokonuj zwrotu z włączonym revoke, ilekroć zamierzasz odebrać dostęp.
- Jak daleko wstecz sięga Voided Purchases API?
- Trzydzieści dni. Parametr startTime domyślnie przyjmuje bieżący czas minus 30 dni i nie można ustawić go starszego, więc endpoint to przesuwane okno jednego miesiąca, a nie archiwum. Unieważnione zamówienie, które przekroczy 30 dni, znika z API bez możliwości odzyskania, dlatego odpytujesz według harmonogramu i wspierasz to powiadomieniami w czasie rzeczywistym.
- Czy do odbierania dostępu powinienem używać real-time developer notifications, czy Voided Purchases API?
- Używaj obu. VoidedPurchaseNotification nadchodzi w ciągu sekund i mówi ci, żebyś spojrzał, ale własne wytyczne Google mówią, by traktować je jako sygnał, a nie źródło prawdy. Wywołaj Voided Purchases API, aby potwierdzić bieżący stan, a potem odbierz dostęp. Powiadomienie usuwa opóźnienie, a API daje ci autorytatywne voidedSource i voidedReason, na podstawie których możesz działać.
- Jak odróżnić chargeback od zwykłego zwrotu w API?
- Odczytaj pole voidedReason. Wartość 7 to chargeback, co oznacza, że bank klienta cofnął płatność, a 6 to friendly fraud. Wartość 1 to zwrot z powodu żalu. Ma to znaczenie, ponieważ od 3 sierpnia 2026 Google przenosi cenę zakupu z chargebacku i opłatę bankową na dewelopera, więc voidedReason równy 7 kosztuje cię więcej niż zwykły zwrot.
- Czy Voided Purchases API obejmuje subskrypcje?
- Tak. Ustaw parametr type na 1, aby otrzymać zarówno unieważnione zakupy w aplikacji, jak i unieważnione zakupy subskrypcji. Wartość domyślna, type 0, zwraca tylko zakupy produktów w aplikacji. W przypadku subskrypcji rozpoznawaj dokładny unieważniony okres po orderId, ponieważ jeden purchaseToken obejmuje każde odnowienie, a dla każdej transakcji odnowienia generowany jest nowy orderId.
Źródła i materiały dodatkowe
- Google Play Developer API: Voided Purchases API guide
- Google Play Developer API: purchases.voidedpurchases.list method
- Google Play Developer API: purchases.voidedpurchases resource (voidedSource and voidedReason)
- Android Developers: Real-time developer notifications reference (VoidedPurchaseNotification)
- Android Developers: Fight fraud and abuse with Play Billing
RefundHalt
Autopilot zwrotów dla App Store i Google Play
Czytaj dalej
Trzy powiadomienia o zwrotach z App Store przychodzą po decyzji Apple, a REFUND_REVERSED oddaje sprzedaż
Apple wysyła cztery wiadomości o zwrotach przez App Store Server Notifications V2, a większość aplikacji obsługuje tylko dwie. REFUND mówi, żeby cofnąć dostęp, REFUND_DECLINED oznacza zachowanie sprzedaży, a REFUND_REVERSED oddaje sprzedaż i prosi o przywrócenie tego, co zabrałeś. Oto czego wymaga każde z nich.
Każde żądanie zwrotu od Apple ma teraz powód, a consumptionRequestReason to sposób, by go odczytać
Od WWDC24 każde żądanie Apple CONSUMPTION_REQUEST niesie consumptionRequestReason, czyli podany przez samego klienta powód chęci uzyskania zwrotu. Istnieje pięć wartości, od UNINTENDED_PURCHASE do LEGAL, i każda powinna zmieniać to, co odsyłasz w swoim oknie 12 godzin. Oto jak odczytać każdą z nich.