Tutti gli articoli
Deep dive8 min di lettura

Il rilevamento dei rimborsi in StoreKit 2 si riduce a una sola proprietà della transazione, ed è revocationDate

Quando Apple rimborsa uno dei tuoi clienti, il rimborso è già dentro la tua app, nella revocationDate della transazione, prima che il job del tuo server venga eseguito. Ecco dove il rilevamento dei rimborsi di StoreKit 2 compare sul dispositivo, cosa ti dicono revocationDate e revocationReason, e perché il client serve alla rapidità e il server alla verità.

Uno smartphone che brilla su una scrivania scura da sviluppatore accanto a un orologio meccanico, a illustrare il rilevamento dei rimborsi di StoreKit 2 che compare dentro un'app

Punti chiave

  • In StoreKit 2 un acquisto rimborsato porta una revocationDate non nil sulla sua Transaction, quindi la tua app può rilevare il rimborso da sola, senza chiamare il tuo server.
  • revocationDate viene impostata quando l'App Store rimborsa una transazione o quando un cliente la perde tramite Family Sharing, quindi una data non nil non è sempre un rimborso.
  • revocationReason ti dice il perché: developerIssue significa che il cliente ha segnalato un problema nella tua app, e other copre ogni altro motivo di rimborso.
  • Transaction.currentEntitlements esclude già gli acquisti rimborsati e revocati, quindi il filtro di accesso più pulito lato client è semplicemente se un prodotto compare ancora lì.
  • Transaction.updates consegna un rimborso avvenuto mentre la tua app era chiusa solo se inizi ad ascoltare all'avvio, quindi un Task mancante significa un rimborso perso.
  • Il rilevamento lato client scatta solo mentre l'app è aperta, ecco perché il REFUND delle App Store Server Notifications V2 resta il segnale autorevole che ti evita di pagare per servire un utente rimborsato.
  • Un rimborso può essere annullato, e quando accade, i campi di revoca vengono rimossi dalla transazione e ci si aspetta che tu ripristini l'accesso che avevi tolto.

La maggior parte delle app viene a sapere di un rimborso Apple dal proprio server, tramite una App Store Server Notification, e non nota mai che lo stesso rimborso è già dentro l'app. È sulla transazione, in una proprietà chiamata revocationDate, e leggerla permette alla tua app di togliere l'accesso a un cliente rimborsato alla prossima apertura, invece di attendere un job del backend. Il rilevamento dei rimborsi di StoreKit 2 è un segnale lato client che la maggior parte dei team salta. Ecco esattamente dove un rimborso compare sul dispositivo, cosa ti dice, cosa non ti dice, e perché deve stare accanto alle tue notifiche server e non al loro posto.

Dove compare un rimborso dentro StoreKit 2

StoreKit 2 ti consegna le transazioni come valori firmati, e un rimborso non elimina la transazione. La contrassegna. Due proprietà della Transaction portano il contrassegno, ed entrambe restano nil per tutta la vita di un acquisto sano. Quando una delle due diventa non nil, l'App Store ha ripreso l'acquisto.

revocationDate è il campo che cambia

revocationDate è un Date opzionale. La descrizione di Apple stessa è precisa: è la data in cui l'App Store ha rimborsato la transazione o l'ha revocata da Family Sharing. Per un acquisto ancora valido, vale nil. Nel momento in cui un rimborso viene elaborato, contiene il timestamp di quel rimborso. Quell'unico controllo, se revocationDate è non nil, è tutto il rilevamento dei rimborsi lato client. Tutto il resto è sfumatura accatastata sopra.

revocationReason ti dice perché Apple lo ha ritirato

revocationReason sta accanto alla data e ne spiega la causa. StoreKit le assegna due valori che contano per i rimborsi. developerIssue significa che il cliente ha detto ad Apple che il rimborso era dovuto a un problema reale o percepito nella tua app. other copre ogni altro motivo. Un terzo valore, upgradedToBundle, non è affatto un rimborso; segnala una transazione che l'App Store ha revocato perché il cliente è passato a un pacchetto di abbonamento. Leggi il motivo prima di agire, perché developerIssue è quello che vale la pena contare: un gruppo di questi è il tuo stesso prodotto che ti dice dove si è rotto.

ProprietàTipoCosa significa un valore non nil
revocationDateDate?L'App Store ha rimborsato questa transazione, o l'ha revocata tramite Family Sharing, in questa data
revocationReason è developerIssuemotivoIl cliente ha segnalato un problema reale o percepito nella tua app
revocationReason è othermotivoIl rimborso è avvenuto per qualche altro motivo che Apple non dettaglia
revocationReason è upgradedToBundlemotivoNon è un rimborso; la transazione è stata revocata perché il cliente è passato a un pacchetto di abbonamento

currentEntitlements scarta già un acquisto rimborsato

Non devi sempre leggere tu stesso i campi di revoca. Transaction.currentEntitlements è la sequenza degli acquisti a cui un cliente ha ancora diritto in questo momento, e Apple la costruisce per lasciare fuori quelli che non dovresti onorare. Un prodotto che l'App Store ha rimborsato o revocato non vi compare. Nemmeno gli abbonamenti scaduti, né i consumabili, che spariscono nel momento in cui vengono usati.

Questo rende currentEntitlements il filtro più pulito per l'accesso. Chiedile cosa possiede il cliente, concedi esattamente quello, e un rimborso rimuove il diritto al posto tuo senza un solo controllo di revocationDate. I campi di revoca servono quando vuoi il dettaglio, la data e il motivo, per registrare l'evento o reagirvi. La lista dei diritti serve alla semplice domanda se tenere le luci accese.

Il rilevamento dei rimborsi di StoreKit 2 in pratica, dall'avvio e mentre l'app gira

Ci sono due momenti in cui la tua app può cogliere un rimborso sul dispositivo, e richiedono codice diverso. Uno è mentre l'app è aperta e un rimborso avviene in diretta o su un altro dispositivo. L'altro è all'avvio, recuperando tutto ciò che è cambiato mentre eri chiuso. Manca il secondo e il tuo rilevamento dei rimborsi di StoreKit 2 ha un buco proprio dove cade la maggior parte dei rimborsi, perché i clienti raramente hanno la tua app aperta quando ne chiedono uno.

Inizia ad ascoltare all'avvio o perdi i rimborsi avvenuti a chiusura

Transaction.updates è la sequenza asincrona che emette una transazione ogni volta che il sistema ne crea o aggiorna una fuori dalla tua app o su un altro dispositivo, un rimborso compreso. L'istruzione di Apple è netta: avvia un Task che la scorra non appena la tua app parte, altrimenti puoi perdere le transazioni che consegna una sola volta all'avvio. Un rimborso arrivato nella notte giunge tramite updates alla prossima apertura dell'app, ma solo se un ascoltatore è già in esecuzione per riceverlo. Nessun ascoltatore, nessun evento, e il rimborso resta invisibile finché qualcos'altro non lo riconcilia.

Un acquisto sullo stesso dispositivo non arriva tramite updates

Una trappola coglie chi prova i rimborsi a mano. Un acquisto normale fatto sullo stesso dispositivo non arriva tramite updates; StoreKit lo restituisce direttamente dal risultato della chiamata di acquisto. updates è per i cambiamenti fuori banda: rimborsi, approvazioni di Ask to Buy, riscatti di codici promozionali e acquisti fatti altrove. Quindi costruisci la tua gestione dei rimborsi attorno a updates e currentEntitlements, non attorno al flusso di acquisto, perché il rimborso non tornerà mai indietro per la strada che ha preso la vendita.

Una ricevuta di carta accartocciata su una superficie scura con un debole timbro rosso impresso sopra, a rappresentare una transazione rimborsata che StoreKit 2 contrassegna con una data di revoca

Cosa il rilevamento lato client non può fare per te

Leggere i rimborsi sul dispositivo è veloce ed è gratis, ma ha un limite, e fingere che non ce l'abbia è il modo in cui i ricavi si perdono. Il dispositivo sa solo ciò che StoreKit gli ha detto, e StoreKit parla solo mentre la tua app è in esecuzione. Un cliente che ottiene un rimborso e non riapre mai la tua app è un cliente che il tuo controllo lato client non vede mai.

Una revocationDate non è sempre un rimborso

Lo stesso campo cambia per Family Sharing. Quando un cliente perde l'accesso a un acquisto condiviso, perché l'organizzatore lo ha rimosso o la condivisione è finita, anche quella transazione riceve una revocationDate. Quindi una data non nil significa che il cliente non ha più questo acquisto, che è esattamente ciò che ti serve per il controllo dell'accesso, ma non significa sempre che del denaro sia tornato indietro. Se stai contando i rimborsi per i ricavi, separa le revoche di Family Sharing da quelle reali prima di fidarti del numero.

Un rimborso può essere annullato

Un rimborso non è sempre definitivo. Apple può annullarne uno, e quando lo fa, i campi di revoca vengono rimossi dalla transazione e l'acquisto torna valido. Se hai tolto l'accesso al rimborso, ci si aspetta che tu lo ripristini all'annullamento. Sul dispositivo questo compare come un altro evento updates con una transazione pulita; sul tuo server è una notifica distinta, REFUND_REVERSED. Gestisci solo il rimborso e lascerai a piedi un cliente pagante, senza accesso e con una ricevuta valida.

Quanto costa davvero una revoca tardiva

Un rimborso è raramente solo il prezzo di vendita che lascia il tuo conto. Quando viene liquidato, di solito hai già speso denaro reale per servire quell'acquisto, e quella spesa non torna. Le immagini generate costano minuti di GPU. Le risposte della chat costano chiamate all'API del modello che ti sono state fatturate a token. I caricamenti costano spazio di archiviazione che paghi ancora per conservare. Se l'acquisto ha finanziato un pagamento a un creatore, quel denaro è già uscito dalla porta. Niente di tutto ciò si annulla con il rimborso.

Il rilevamento lato client riduce la finestra sull'unica parte che puoi ancora controllare, cioè la spesa futura. Prima sai che un acquisto è rimborsato, prima smetti di servirlo. Ma il dispositivo ti avvisa solo mentre l'app è aperta, quindi un utente rimborsato che non torna più mantiene l'accesso lato server che gli hai concesso, costandoti in silenzio ogni volta che un job in background o un dispositivo sincronizzato agisce per suo conto. Il client rende la revoca veloce. Non la rende garantita.

Usa il client per la rapidità e il server per la verità

Il design pulito usa entrambi i segnali per ciò in cui ciascuno è bravo. Sul dispositivo, Transaction.updates e currentEntitlements ti danno una reazione locale e istantanea nel momento in cui un cliente rimborsato apre l'app, utile per l'interfaccia e per completare i cambiamenti di diritto senza un viaggio di andata e ritorno. Sul server, le App Store Server Notifications V2 inviano un messaggio REFUND che arriva sia che l'app venga riaperta sia che no, ed è l'unico segnale che ferma in modo affidabile la spesa del tuo backend su un account rimborsato.

SegnaleDove viveScatta quandoAffidati a esso per
revocationDate su una transazioneDispositivo, StoreKit 2La tua app legge la transazioneDirti che un acquisto specifico è stato rimborsato o revocato
Transaction.updatesDispositivo, StoreKit 2Un rimborso arriva mentre l'app gira, o all'avvio se ascoltiReagire all'istante per un cliente presente
currentEntitlementsDispositivo, StoreKit 2Controlli cosa possiede il cliente oraFiltrare l'accesso senza tracciare tu stesso i rimborsi
Notifica REFUNDIl tuo server, App Store Server Notifications V2Apple elabora il rimborso, app aperta o noFermare la spesa lato server su un cliente che non torna mai

Collega i segnali del dispositivo per il cliente che ha il telefono in mano, e la notifica del server per quello che non ce l'ha. Il rimborso compare in entrambi i posti di proposito. Leggerne solo uno è il modo in cui un account rimborsato continua a costarti dopo che la vendita è già andata.

Domande frequenti

Come rilevo un rimborso in StoreKit 2?
Controlla la revocationDate della transazione. È nil per un acquisto valido e contiene una data non appena l'App Store rimborsa la transazione, quindi una revocationDate non nil è il segnale che un acquisto è stato rimborsato o revocato.
Qual è la differenza tra revocationDate e revocationReason?
revocationDate è quando l'App Store ha ripreso l'acquisto, e revocationReason è il perché. Il motivo è developerIssue quando il cliente ha segnalato un problema nella tua app e other per qualsiasi altra cosa.
Un acquisto rimborsato compare ancora in currentEntitlements?
No. Transaction.currentEntitlements esclude gli acquisti che l'App Store ha rimborsato o revocato, quindi un prodotto rimborsato esce da solo dai diritti del cliente, il che ne fa un filtro sicuro per l'accesso.
StoreKit avviserà la mia app di un rimborso avvenuto mentre l'app era chiusa?
Solo se ascolti fin dall'avvio. Transaction.updates consegna quei cambiamenti una sola volta all'avvio, quindi devi avviare un Task che la scorra quando la tua app parte oppure il rimborso viene perso finché qualcos'altro non lo riconcilia.
Il rilevamento dei rimborsi lato client basta da solo?
No. Il dispositivo viene a sapere di un rimborso solo mentre la tua app è in esecuzione, quindi un cliente che non riapre mai l'app è invisibile per esso. Il REFUND delle App Store Server Notifications V2 è il segnale che ti raggiunge comunque.
Una revocationDate significa sempre che il cliente è stato rimborsato?
No. revocationDate viene impostata anche quando un cliente perde un acquisto tramite Family Sharing, quindi una data non nil significa che non ha più l'acquisto ma non sempre che il denaro sia stato restituito.

Fonti e approfondimenti

RefundHalt

Il pilota automatico dei rimborsi per App Store e Google Play

Continua a leggere

La prossima richiesta di rimborso è già in arrivo.

Configura RefundHalt nel tempo che impiegheresti a leggere un'altra e-mail dell'assistenza su un rimborso che non sei riuscito a contestare.