Revocare l'accesso dopo un rimborso: il passaggio che Apple e Google non fanno per te
Sia Apple che Google possono completare un rimborso lasciando che il cliente mantenga comunque il suo acquisto. Ecco esattamente quando l'accesso viene rimosso in automatico, quando tocca al tuo server farlo, e qual è l'unica notifica che quasi nessuna integrazione gestisce.

Punti chiave
- Su Google Play, rimborsare un ordine non rimuove di per sé l'accesso dell'utente. L'accesso viene rimosso solo se in Play Console è selezionata l'opzione Remove entitlement, oppure se la chiamata API include revoke=true, che non è il comportamento predefinito.
- Un rimborso emesso senza l'opzione revoke non compare mai nella Voided Purchases API. Un job di revoca che legge solo quel feed ignorerà silenziosamente ognuno di quegli ordini.
- Apple può revocare automaticamente una licenza per i prodotti non consumabili e gli abbonamenti, ma un prodotto consumabile è già considerato consegnato nel momento in cui viene speso, quindi StoreKit non ha alcun meccanismo per revocarlo. È il tuo server a dover detrarre il saldo.
- Apple può anche annullare un proprio rimborso. Una notifica REFUND_REVERSED significa che un rimborso precedentemente concesso è stato annullato, revocationDate torna a null, e qualsiasi accesso o saldo rimosso deve essere ripristinato.
- Il campo voidedReason di Google può assumere nove valori, da remorse e accidental_purchase fino a fraud, friendly_fraud e unacknowledged_purchase, quindi lo stesso feed che ti dice quando revocare l'accesso ti dice anche quali revoche sono abusi e quali sono bug della tua integrazione.
- Ogni giorno in cui un acquisto rimborsato mantiene il proprio accesso è un giorno in cui paghi la potenza di calcolo, lo storage e le chiamate API che ci sono dietro, su denaro che hai già restituito.
Approvare un rimborso e rimuovere l'accesso sono due passaggi distinti, e solo uno dei due avviene in automatico. Gli sviluppatori che presumono che un rimborso elaborato equivalga a un acquisto revocato sbagliano su entrambi gli store principali, in modi diversi e per ragioni diverse. Sapere esattamente dove si ferma l'automazione è ciò che serve per revocare davvero l'accesso dopo un rimborso, invece di scoprire mesi dopo che non è mai successo.
Perché un rimborso non revoca l'accesso per impostazione predefinita
Apple e Google trattano entrambe il denaro e l'accesso come due operazioni distinte. Il lato denaro è una questione di policy dello store: una volta approvato, il rimborso è definitivo e il registro si aggiorna. Il lato accesso è opzionale, oppure è compito dello sviluppatore, o entrambe le cose, a seconda della piattaforma e del tipo di acquisto. Nessuno dei due store presume che tu voglia far sparire l'accesso nell'istante in cui il rimborso viene completato, perché molti casi legittimi, come un rimborso parziale, un credito di cortesia o un gesto di supporto, non devono affatto comportare la sottrazione del prodotto.
Google Play: la casella di controllo che nessuno nota
In Play Console, rimborsare un ordine è un modulo in due parti: si sceglie l'importo del rimborso, poi si decide separatamente se selezionare Remove entitlement. Lasciarla deselezionata è il percorso predefinito che seguono la maggior parte degli operatori di supporto, perché la maggior parte delle richieste di rimborso sono gesti di cortesia in cui il cliente mantiene ciò che ha acquistato. Il problema è che nulla nella schermata di conferma del rimborso avverte che questo è anche il modo in cui un cliente mantiene un abbonamento o un articolo in-app dopo un rimborso che intendevi far diventare definitivo.
Anche il percorso API nasconde la stessa trappola
La stessa scelta esiste anche nel codice. L'endpoint orders.refund accetta un parametro di query opzionale revoke; quando è true, l'accesso all'articolo viene terminato immediatamente, e per un abbonamento ricorrente ogni pagamento futuro viene annullato insieme ad esso. Se il parametro viene omesso, il valore predefinito è false: il rimborso viene registrato, il record dell'acquisto si aggiorna, e l'utente continua a usare il prodotto esattamente come prima.
| Azione | Accesso rimosso? | Compare nella Voided Purchases API? |
|---|---|---|
| Rimborso da Play Console, Remove entitlement non selezionato | No | No |
| Rimborso da Play Console, Remove entitlement selezionato | Sì, immediatamente | Sì |
| API orders.refund, parametro revoke omesso (predefinito false) | No | No |
| API orders.refund, revoke=true | Sì, immediatamente | Sì |
Quell'ultima riga è quella che trae in inganno le integrazioni. La Voided Purchases API esiste per permetterti di costruire una pipeline di revoca senza dover controllare Play Console a mano, ma elenca solo gli ordini effettivamente annullati con revoca. Un rimborso elaborato senza la casella, o senza revoke=true, è invisibile a quel feed per progettazione, non per un bug.

Come leggere il campo voidedReason Quando un acquisto compare come voided, il campo voidedReason ne indica il motivo, su una scala di nove valori: other, remorse, not_received, defective, accidental_purchase, fraud, friendly_fraud, chargeback e unacknowledged_purchase. Non è solo un dato contabile. Remorse e accidental_purchase sono rimborsi ordinari. Fraud, friendly_fraud e chargeback sono quelli che vale la pena instradare verso una coda di revisione antifrode invece che verso un semplice percorso di revoca e archiviazione, perché lo stesso account ha statisticamente più probabilità di ripetersi.
Un dettaglio operativo è importante se costruisci qualcosa direttamente su questo feed: purchases.voidedpurchases.list ha un limite di 6,000 query al giorno e 30 query in una qualsiasi finestra di 30 secondi. Un design che effettua polling in un ciclo stretto raggiunge presto quel limite; il pattern previsto è un pull pianificato con token di paginazione, non un polling continuo.
Apple: l'App Store può revocare una licenza, ma non un prodotto consumabile
Le App Store Server Notifications V2 di Apple inviano una notifica REFUND nel momento in cui un rimborso viene completato, sia per i consumabili sia per i non consumabili, gli abbonamenti auto-rinnovabili e quelli non rinnovabili. Ciò che accade dopo quella notifica dipende interamente dal tipo di acquisto, perché il meccanismo di revoca di StoreKit copre solo due dei quattro tipi.
Perché i consumabili sono l'eccezione
Un prodotto non consumabile o un abbonamento hanno un diritto, un entitlement, permanente che i sistemi di Apple possono disattivare. Un consumabile no: una volta usato, è considerato consegnato, e non resta nulla in StoreKit da revocare. Se un cliente ha acquistato 500 monete, le ha spese e ha ottenuto un rimborso, dal punto di vista di Apple quelle monete sono già sparite. L'unico registro che le contiene ancora è il tuo. La notifica REFUND è il tuo unico segnale per trovare l'ID della transazione originale corrispondente e detrarre quel saldo dai tuoi stessi archivi; nulla dal lato Apple lo farà al posto tuo.
| Notifica | Cosa significa | Cosa deve fare il tuo server |
|---|---|---|
| REFUND | Apple ha rimborsato un acquisto consumabile, non consumabile, auto-rinnovabile o non rinnovabile | Revoca immediatamente l'accesso per i non consumabili e gli abbonamenti; detrai tu stesso il saldo del consumabile |
| REFUND_DECLINED | Apple ha rifiutato la richiesta di rimborso di un cliente | Niente. L'accesso non è mai stato a rischio |
| REFUND_REVERSED | Apple ha annullato un rimborso già concesso | Ripristina qualsiasi accesso o saldo che la notifica REFUND ti ha indotto a rimuovere |
| CONSUMPTION_REQUEST | Apple vuole le tue prove prima di decidere su un rimborso | Rispondi entro 12 ore; questa notifica arriva prima di una REFUND, non dopo |
La notifica che quasi nessuno gestisce
REFUND_REVERSED è quella che quasi tutte le integrazioni ignorano del tutto, perché descrive Apple che annulla una propria decisione, un caso per cui pochi sviluppatori progettano al primo passaggio. Quando arriva, revocationDate sulla transazione torna a null: per quanto riguarda Apple, l'acquisto è di nuovo valido. Se il tuo server ha cancellato un entitlement, annullato il record di un abbonamento o azzerato un saldo consumabile quando è arrivata la REFUND originale, REFUND_REVERSED è il segnale per rimettere tutto a posto.
Come colmare il divario, passo dopo passo
- Su Google Play, imposta come predefinito revoke=true su ogni percorso di rimborso, sia tramite Play Console sia tramite API, a meno che un caso di supporto specifico non richieda che il cliente mantenga l'accesso.
- Effettua il polling di purchases.voidedpurchases.list secondo una pianificazione con token di paginazione, non in un ciclo stretto, restando sotto le 6,000 richieste al giorno e le 30 ogni 30 secondi.
- Instrada i valori di voidedReason pari a fraud, friendly_fraud e chargeback verso una coda di revisione invece che verso una revoca automatica e chiusura immediata.
- Su Apple, iscriviti alle App Store Server Notifications V2 e revoca l'accesso ai non consumabili e agli abbonamenti nel momento in cui una notifica REFUND li identifica.
- Conserva l'ID della transazione originale per ogni acquisto consumabile, così che una notifica REFUND possa individuare il saldo esatto da detrarre senza una ricerca manuale.
- Gestisci esplicitamente REFUND_REVERSED e registra ogni revoca rispetto al proprio ID di transazione o di ordine, in modo che un annullamento possa ripristinare esattamente ciò che era stato rimosso.
Quanto costa davvero questo divario
Non si tratta di equità nei confronti del cliente, ma di ciò che continua a funzionare dopo che il denaro se n'è andato. Un abbonamento rimborsato che continua a chiamare la tua API, a immagazzinare dati e a consumare potenza di calcolo non è un problema morale, è una voce di spesa: stai pagando la bolletta dell'infrastruttura per un acquisto sul quale hai già restituito l'incasso. Gli store non tengono traccia di questo costo al posto tuo, perché dal loro punto di vista il rimborso è completo nel momento in cui il denaro si muove. Il lato dell'accesso è interamente un conto che devi controllare tu.
Questo è lo stesso motivo per cui RefundHalt tratta la revoca come parte della pipeline di rimborso, non come un ripensamento successivo: un evento REFUND e una modifica dell'accesso vengono registrati come un'unica azione, e un evento REFUND_REVERSED ripristina lo stato automaticamente invece di aspettare che qualcuno si accorga che il cliente è ancora escluso da qualcosa che Apple gli ha restituito.
Domande frequenti
- Rimborsare un acquisto su Google Play revoca automaticamente l'accesso?
- No. L'accesso viene rimosso solo se in Play Console è selezionato Remove entitlement oppure se la chiamata API orders.refund include revoke=true. Nessuno dei due è il comportamento predefinito, quindi un rimborso può essere registrato mentre il cliente continua a usare il prodotto.
- Cosa succede quando un acquisto in-app consumabile viene rimborsato sull'App Store?
- Apple invia una notifica REFUND, ma StoreKit non ha modo di revocare un consumabile perché è considerato consegnato una volta speso. Il server dello sviluppatore deve trovare la transazione originale e detrarre il saldo manualmente.
- Cos'è la notifica REFUND_REVERSED e richiede una gestione particolare?
- REFUND_REVERSED significa che Apple ha annullato un rimborso già concesso; revocationDate torna a null e l'acquisto è di nuovo valido. Qualsiasi accesso o saldo rimosso dalla REFUND originale deve essere ripristinato, e la maggior parte delle integrazioni non ascolta affatto questa notifica.
- Perché un ordine Google Play rimborsato potrebbe non comparire nella Voided Purchases API?
- L'API elenca solo gli ordini annullati con revoca. Se un rimborso è stato elaborato senza l'opzione revoke, l'ordine non viene affatto restituito da quell'endpoint, quindi una pipeline che si basa solo su di essa perderà completamente quei rimborsi.
- Con quale frequenza posso interrogare la Voided Purchases API di Google?
- L'endpoint ha un limite di 6,000 query al giorno e 30 query in una qualsiasi finestra di 30 secondi, quindi è pensato per un pull pianificato con token di paginazione, non per un polling continuo.
Fonti e approfondimenti
- Apple: Handling refund notifications
- Apple: notificationType (App Store Server Notifications)
- Apple: revocationReason (App Store Server Notifications)
- Google: Voided Purchases API overview
- Google: purchases.voidedpurchases REST resource
- Google: orders.refund API reference (revoke parameter)
- Google Play Console Help: manage your app's orders and issue refunds
RefundHalt
Il pilota automatico dei rimborsi per App Store e Google Play
Continua a leggere
The refunds you cannot contest: what Apple and Google decide without asking you
Only two refund flows ever ask for your evidence: Apple's consumption request and Google Play's chargeback review. Everything else, the 48-hour Play refund, support refunds, voided purchases, unacknowledged trials, is decided without you. Here is the full map, what you can prevent, and what you can only record.
Apple's CONSUMPTION_REQUEST, explained: the 12 hours that decide your refund
When a customer asks Apple for a refund, Apple asks you first. Here's exactly what the CONSUMPTION_REQUEST notification contains, what Apple expects back, how to answer one end to end, and why most developers silently forfeit.