Wszystkie artykuły
Deep dive8 min czytania

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.

Smartfon obok papierowej księgi rachunkowej, zamknięta mosiężna kłódka i wysuwająca się moneta, ilustrujące Google Play Voided Purchases API, które raportuje zamówienia zwrócone i obciążone zwrotnie

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

FieldCo zawiera
orderIdIdentyfikator zamówienia, który jednoznacznie identyfikuje zakup jednorazowy, zakup subskrypcji lub pojedyncze odnowienie subskrypcji. To twój klucz łączenia
purchaseTokenToken, który identyfikuje zakup jednorazowy lub subskrypcję. Nie rozróżnia odnowień, więc do tego używaj orderId
purchaseTimeMillisKiedy dokonano zakupu, w milisekundach od epoki
voidedTimeMillisKiedy zakup został anulowany, zwrócony lub obciążony zwrotnie, w milisekundach od epoki
voidedSourceKto zainicjował unieważnienie: 0 użytkownik, 1 deweloper, 2 Google
voidedReasonDlaczego zakup został unieważniony, liczba całkowita od 0 do 8
voidedQuantityUnieważ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.

voidedReasonLabelCo to dla ciebie oznacza
0OtherNie przypisano żadnej kategorii. Odbierz dostęp i przejdź dalej
1RemorseKupujący zmienił zdanie. Zwykły zwrot
2Not_receivedKlient twierdzi, że nigdy nie otrzymał produktu. Warto sprawdzić dostarczanie
3DefectiveProdukt nie działał. Sygnał jakości, zapisz go
4Accidental_purchaseNiezamierzony zakup, często na współdzielonym urządzeniu
5FraudGoogle oznaczył transakcję jako oszustwo
6Friendly_fraudChargeback, w którym prawowity posiadacz karty kwestionuje obciążenie, którego sam dokonał
7ChargebackBank klienta cofnął płatność. Ostateczne po stronie banku i teraz obciąża ciebie
8Unacknowledged_purchaseGoogle 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 fieldCo zawiera
purchaseTokenToken z pierwotnego zakupu
orderIdIdentyfikator zamówienia dla unieważnionej transakcji, nowy dla każdego odnowienia subskrypcji
productType1 dla subskrypcji, 2 dla zakupu jednorazowego
refundType1 dla pełnego zwrotu, 2 dla zwrotu częściowego opartego na ilości
Dłoń zamykająca mosiężną kłódkę na stosie paragonów obok smartfona, ilustrująca odbieranie dostępu po unieważnieniu zakupu w Google Play

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

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ć.